feat(server): synthetic root doc (#14794)

This commit is contained in:
DarkSky
2026-04-06 17:16:34 +08:00
committed by GitHub
parent 64149d909a
commit 193ec14ad3
35 changed files with 1784 additions and 409 deletions
@@ -32,6 +32,7 @@ import { FrameworkScope, useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { filter, firstValueFrom, timeout } from 'rxjs';
import { PageNotFound } from '../../404';
import { ShareFooter } from './share-footer';
@@ -40,9 +41,24 @@ import * as styles from './share-page.css';
import {
fetchSharedPublishMode,
getResolvedPublishMode,
isSharePagePermissionError,
isSharePageTimeoutError,
} from './share-page.utils';
import { useSharedModeQuerySync } from './use-shared-mode-query-sync';
const waitForSharedDocRecord = async (
docsService: DocsService,
docId: string
): Promise<void> => {
if (docsService.list.doc$(docId).value) {
return;
}
await firstValueFrom(
docsService.list.doc$(docId).pipe(filter(Boolean), timeout(3000))
);
};
const useUpdateBasename = (workspace: Workspace | null) => {
const location = useLocation();
const basename = location.pathname.match(/\/workspace\/[^/]+/g)?.[0] ?? '/';
@@ -131,6 +147,7 @@ const SharePageInner = ({
const [page, setPage] = useState<Doc | null>(null);
const [editor, setEditor] = useState<Editor | null>(null);
const [noPermission, setNoPermission] = useState(false);
const [loadFailed, setLoadFailed] = useState(false);
const [fetchedPublishMode, setFetchedPublishMode] = useState<
DocMode | null | undefined
>(() => (publishMode === undefined ? undefined : null));
@@ -198,6 +215,7 @@ const SharePageInner = ({
name: 'StaticCloudDocStorage',
opts: {
id: workspaceId,
publicRootDocId: docId,
serverBaseUrl: serverService.server.baseUrl,
},
},
@@ -218,7 +236,10 @@ const SharePageInner = ({
sharedWorkspace.engine.doc
.waitForDocLoaded(sharedWorkspace.id)
.then(async () => {
const { doc } = sharedWorkspace.scope.get(DocsService).open(docId);
const docsService = sharedWorkspace.scope.get(DocsService);
await waitForSharedDocRecord(docsService, docId);
const { doc } = docsService.open(docId);
doc.blockSuiteDoc.load();
doc.blockSuiteDoc.readonly = true;
@@ -241,7 +262,17 @@ const SharePageInner = ({
})
.catch(err => {
console.error(err);
setNoPermission(true);
if (isSharePagePermissionError(err)) {
setNoPermission(true);
return;
}
if (isSharePageTimeoutError(err)) {
setLoadFailed(true);
return;
}
setLoadFailed(true);
});
}, [
docId,
@@ -308,6 +339,10 @@ const SharePageInner = ({
return <PageNotFound noPermission />;
}
if (loadFailed) {
return <PageNotFound />;
}
if (!workspace || !page || !editor || !currentPublishMode) {
return null;
}
@@ -1,4 +1,6 @@
import { UserFriendlyError } from '@affine/error';
import { type DocMode, DocModes } from '@blocksuite/affine/model';
import { TimeoutError } from 'rxjs';
export const getResolvedPublishMode = (
queryMode: DocMode | null,
@@ -35,7 +37,7 @@ export const fetchSharedPublishMode = async ({
signal?: AbortSignal;
}): Promise<DocMode | null> => {
const url = new URL(
`/api/workspaces/${encodeURIComponent(workspaceId)}/docs/${encodeURIComponent(docId)}`,
`/api/workspaces/${encodeURIComponent(workspaceId)}/public-docs/${encodeURIComponent(docId)}`,
serverBaseUrl
);
const headers = {
@@ -71,3 +73,12 @@ export const getSearchWithMode = (search: string, mode: DocMode) => {
const nextSearch = searchParams.toString();
return nextSearch ? `?${nextSearch}` : '';
};
export const isSharePagePermissionError = (error: unknown) =>
error instanceof UserFriendlyError &&
(error.isStatus(403) ||
error.name === 'DOC_ACTION_DENIED' ||
error.name === 'SPACE_ACCESS_DENIED');
export const isSharePageTimeoutError = (error: unknown) =>
error instanceof TimeoutError;