Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve site level redirects #2577

Merged
merged 5 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified bun.lockb
Binary file not shown.
13 changes: 13 additions & 0 deletions packages/gitbook/e2e/pages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,19 @@ const testCases: TestsCase[] = [
},
],
},
{
name: 'Site Redirects',
baseUrl: 'https://gitbook-open-e2e-sites.gitbook.io/gitbook-doc/',
tests: [
{
name: 'Redirect to SSO page',
url: 'a/redirect/to/sso',
run: async (page) => {
await expect(page.locator('h1')).toHaveText('SSO');
},
},
],
},
{
name: 'Share links',
baseUrl: 'https://gitbook.gitbook.io/gbo-tests-share-links/',
Expand Down
2 changes: 1 addition & 1 deletion packages/gitbook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static"
},
"dependencies": {
"@gitbook/api": "^0.76.0",
"@gitbook/api": "^0.77.0",
"@gitbook/cache-do": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
Expand Down
54 changes: 38 additions & 16 deletions packages/gitbook/src/app/(site)/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { RevisionPage } from '@gitbook/api';
import { redirect } from 'next/navigation';

import {
getRevisionPageByPath,
getDocument,
ContentTarget,
getSpaceContentData,
getSiteData,
getSiteRedirectBySource,
} from '@/lib/api';
import { resolvePagePath, resolvePageId } from '@/lib/pages';
import { getSiteContentPointer } from '@/lib/pointer';
Expand Down Expand Up @@ -41,6 +42,7 @@ export async function fetchContentData() {
site,
sections,
spaces,
shareKey: content.siteShareKey,
customization,
scripts,
ancestors: [],
Expand All @@ -54,7 +56,15 @@ export async function fetchContentData() {
export async function fetchPageData(params: PagePathParams | PageIdParams) {
const contentData = await fetchContentData();

const page = await resolvePage(contentData.contentTarget, contentData.pages, params);
const page = await resolvePage({
organizationId: contentData.space.organization,
siteId: contentData.site.id,
spaceId: contentData.contentTarget.spaceId,
revisionId: contentData.contentTarget.revisionId,
pages: contentData.pages,
shareKey: contentData.shareKey,
params,
});
const document = page?.page.documentId
? await getDocument(contentData.space.id, page.page.documentId)
: null;
Expand All @@ -70,11 +80,17 @@ export async function fetchPageData(params: PagePathParams | PageIdParams) {
* Resolve a page from the params.
* If the path can't be found, we try to resolve it from the API to handle redirects.
*/
async function resolvePage(
contentTarget: ContentTarget,
pages: RevisionPage[],
params: PagePathParams | PageIdParams,
) {
async function resolvePage(input: {
organizationId: string;
siteId: string;
spaceId: string;
revisionId: string;
shareKey: string | undefined;
pages: RevisionPage[];
params: PagePathParams | PageIdParams;
}) {
const { organizationId, siteId, spaceId, revisionId, pages, shareKey, params } = input;

if ('pageId' in params) {
return resolvePageId(pages, params.pageId);
}
Expand All @@ -88,20 +104,26 @@ async function resolvePage(
return page;
}

// If page can't be found, we try with the API, in case we have a redirect
// We use the raw pathname to handle special/malformed redirects setup by users in the GitSync.
// The page rendering will take care of redirecting to a normalized pathname.
//
// We don't test path that are too long as GitBook doesn't support them and will return a 404 anyway.
if (rawPathname.length <= 512) {
const resolved = await getRevisionPageByPath(
contentTarget.spaceId,
contentTarget.revisionId,
rawPathname,
);
// If page can't be found, we try with the API, in case we have a redirect at space level.
// We use the raw pathname to handle special/malformed redirects setup by users in the GitSync.
// The page rendering will take care of redirecting to a normalized pathname.
const resolved = await getRevisionPageByPath(spaceId, revisionId, rawPathname);
if (resolved) {
return resolvePageId(pages, resolved.id);
}

// If a page still can't be found, we try with the API, in case we have a redirect at site level.
const resolvedSiteRedirect = await getSiteRedirectBySource({
organizationId,
siteId,
source: rawPathname.startsWith('/') ? rawPathname : `/${rawPathname}`,
taranvohra marked this conversation as resolved.
Show resolved Hide resolved
siteShareKey: input.shareKey,
});
if (resolvedSiteRedirect) {
return redirect(resolvedSiteRedirect.target);
taranvohra marked this conversation as resolved.
Show resolved Hide resolved
}
}

return undefined;
Expand Down
43 changes: 43 additions & 0 deletions packages/gitbook/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,49 @@ export const getDocument = cache({
timeout: 20 * 1000,
});

/**
* Resolve a site redirect by its source path.
*/
export const getSiteRedirectBySource = cache({
name: 'api.getSiteRedirectBySource',
tag: ({ siteId }) => getAPICacheTag({ tag: 'site', site: siteId }),
get: async (
args: {
organizationId: string;
siteId: string;
/** Site share key that can be used as context to resolve site space published urls */
siteShareKey: string | undefined;
source: string;
},
options: CacheFunctionOptions,
) => {
try {
const response = await api().orgs.getSiteRedirectBySource(
args.organizationId,
args.siteId,
{
shareKey: args.siteShareKey,
source: args.source,
},
{
...noCacheFetchOptions,
signal: options.signal,
},
);
return cacheResponse(response, cacheTtl_1day);
} catch (error) {
if ((error as GitBookAPIError).code === 404) {
return {
data: null,
...cacheTtl_1day,
};
}

throw error;
}
},
});

/**
* Get the infos about a site by its ID.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/react-contentkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dependencies": {
"classnames": "^2.5.1",
"@gitbook/api": "^0.76.0",
"@gitbook/api": "^0.77.0",
"assert-never": "^1.2.1"
},
"peerDependencies": {
Expand Down
Loading