Implement bypass for URL lookup alternatives (#4620)

This commit is contained in:
conico974
2026-09-18 13:50:06 +02:00
committed by GitHub
parent a52daf8b15
commit 99984ebbb3
6 changed files with 107 additions and 2 deletions
@@ -70,6 +70,7 @@ runs:
GITBOOK_BLOCK_SEARCH_INDEXATION: ${{ inputs.environment == 'preview' && 'true' || '' }}
GITBOOK_ALLOW_CUSTOMIZATION_OVERRIDE: ${{ inputs.environment == 'preview' && 'true' || '' }}
GITBOOK_DISABLE_INSIGHTS: ${{ inputs.environment == 'preview' && 'true' || '' }}
GITBOOK_DISABLE_LOOKUP_ALTERNATIVES: ${{ inputs.environment == 'staging' && 'true' || '' }}
shell: bash
- name: Upload the DO worker
@@ -78,6 +78,11 @@ runs:
echo "GITBOOK_DISABLE_INSIGHTS=true" >> .vercel/.env.${{ inputs.environment }}.local
echo "--- .vercel/.env.${{ inputs.environment }}.local after inject ---"
cat .vercel/.env.${{ inputs.environment }}.local
- name: Inject staging build env vars
if: ${{ inputs.environment == 'staging' }}
shell: bash
run: |
echo "GITBOOK_DISABLE_LOOKUP_ALTERNATIVES=true" >> .vercel/.env.${{ inputs.environment }}.local
- name: Build Project Artifacts
run: bun run vercel build --target=${{ inputs.environment }} --token=${{ inputs.vercelToken }}
shell: bash
+2
View File
@@ -7,6 +7,7 @@ import {
GITBOOK_APP_URL,
GITBOOK_ASSETS_URL,
GITBOOK_DISABLE_INSIGHTS,
GITBOOK_DISABLE_LOOKUP_ALTERNATIVES,
GITBOOK_DISABLE_TRACKING,
GITBOOK_FONTS_URL,
GITBOOK_ICONS_URL,
@@ -37,6 +38,7 @@ export async function GET(_req: NextRequest) {
GITBOOK_INTEGRATIONS_CONTENT_HOST,
GITBOOK_DISABLE_TRACKING,
GITBOOK_DISABLE_INSIGHTS,
GITBOOK_DISABLE_LOOKUP_ALTERNATIVES,
// Secret envs
GITBOOK_SECRET: !!GITBOOK_SECRET,
+57 -1
View File
@@ -1,6 +1,11 @@
import { describe, expect, it } from 'bun:test';
import { getURLLookupAlternatives, getURLLookupPathname, normalizeURL } from './urls';
import {
getURLLookupAlternatives,
getURLLookupPathname,
normalizeURL,
shouldBypassLookupAlternatives,
} from './urls';
describe('getURLLookupPathname', () => {
const previewRoot = 'https://sites.gitbook.com/preview/site_example/section';
@@ -832,3 +837,54 @@ describe('normalizeURL with encoded paths', () => {
expect(result.searchParams.get('filter')).toBe(risonValue);
});
});
describe('getURLLookupAlternatives with bypass', () => {
it('only looks up the full URL', () => {
expect(
getURLLookupAlternatives(new URL('https://docs.mycompany.com/a/b/c'), { bypass: true })
).toEqual({
revision: undefined,
changeRequest: undefined,
basePath: undefined,
urls: [{ url: 'https://docs.mycompany.com/a/b/c', extraPath: '', primary: true }],
});
});
it('only looks up the full URL for a variant', () => {
expect(
getURLLookupAlternatives(new URL('https://test.gitbook.io/v/variant/space'), {
bypass: true,
}).urls
).toEqual([
{ url: 'https://test.gitbook.io/v/variant/space', extraPath: '', primary: true },
]);
});
it.each(['revisions', 'changes'])('keeps the alternatives for %s', (kind) => {
const url = new URL(`https://docs.mycompany.com/a/~/${kind}/id/page`);
expect(getURLLookupAlternatives(url, { bypass: true })).toEqual(
getURLLookupAlternatives(url, { bypass: false })
);
});
});
describe('shouldBypassLookupAlternatives', () => {
const bypassURLs = ['https://docs.mycompany.com/section', 'https://other.mycompany.com/'];
it.each([
'https://docs.mycompany.com/section',
'https://docs.mycompany.com/section/page',
'https://other.mycompany.com/',
'https://other.mycompany.com/page',
])('matches %s', (url) => {
expect(shouldBypassLookupAlternatives(normalizeURL(new URL(url)), bypassURLs)).toBe(true);
});
it.each([
'https://docs.mycompany.com/sectionpage',
'https://docs.mycompany.com/',
'https://unknown.mycompany.com/section',
])('does not match %s', (url) => {
expect(shouldBypassLookupAlternatives(normalizeURL(new URL(url)), bypassURLs)).toBe(false);
});
});
+36 -1
View File
@@ -1,3 +1,4 @@
import { GITBOOK_DISABLE_LOOKUP_ALTERNATIVES } from '../env';
import { joinPath, removeTrailingSlash } from '../paths';
import { isProxyRootRequest } from '../proxy';
import { DataFetcherError, getExposableError } from './errors';
@@ -42,6 +43,34 @@ function getContentPathSegments(pathSegments: string[]): string[] {
return pathSegments;
}
/**
* Site URL prefixes resolved with the full URL only, for sites where a shorter alternative
* would resolve to the wrong content.
*/
const LOOKUP_ALTERNATIVES_BYPASS_URLS: string[] = ['https://proxy.gitbook.site/sites/site_p4Xo4'];
/**
* Whether the lookup of this (normalized) URL should skip the shorter alternatives.
*/
export function shouldBypassLookupAlternatives(
url: URL,
bypassURLs: string[] = LOOKUP_ALTERNATIVES_BYPASS_URLS
): boolean {
if (GITBOOK_DISABLE_LOOKUP_ALTERNATIVES) {
return true;
}
return bypassURLs.some((bypassURL) => {
const prefix = normalizeURL(new URL(bypassURL));
const prefixPath = removeTrailingSlash(prefix.pathname);
return (
prefix.origin === url.origin &&
(removeTrailingSlash(url.pathname) === prefixPath ||
url.pathname.startsWith(`${prefixPath}/`))
);
});
}
/**
* For a given GitBook URL, return a list of alternative URLs that could be matched against to lookup the content.
* The approach is optimized to aim at reusing cached lookup results as much as possible.
@@ -62,8 +91,9 @@ function getContentPathSegments(pathSegments: string[]): string[] {
* - Public content has a custom hostname in the organization with a variant: docs.company.com/<space>/v/<variant>/<path>
* - Public content has a custom hostname in the organization with a variant and a share-link: docs.company.com/<space>/<link>/v/<variant>/<path>
*/
export function getURLLookupAlternatives(input: URL) {
export function getURLLookupAlternatives(input: URL, options: { bypass?: boolean } = {}) {
const url = normalizeURL(input);
const bypass = options.bypass ?? shouldBypassLookupAlternatives(url);
let basePath: string | undefined = undefined;
let changeRequest: string | undefined = undefined;
@@ -123,6 +153,11 @@ export function getURLLookupAlternatives(input: URL) {
pushAlternative(contentURL, pathSegments.slice(revisionOrChangeIdIndex + 1).join('/'));
}
// Revisions and changes above still need their alternatives to extract the base path.
else if (bypass) {
pushAlternative(url, '');
}
// URL looks like a collection url (with /v/ in the path)
// We only start matching after the /v/ segment and we ignore everything before it
// to avoid potentially matching as a page not found under the default space in the collection
+6
View File
@@ -84,6 +84,12 @@ export const GITBOOK_DISABLE_TRACKING = Boolean(
*/
export const GITBOOK_DISABLE_INSIGHTS = process.env.GITBOOK_DISABLE_INSIGHTS === 'true';
/**
* Whether to resolve site content with the full URL only, skipping the shorter lookup alternatives.
*/
export const GITBOOK_DISABLE_LOOKUP_ALTERNATIVES =
process.env.GITBOOK_DISABLE_LOOKUP_ALTERNATIVES === 'true';
/**
* Hostname serving the integrations.
*/