mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
fix(workspace): check affine login auth (#2070)
This commit is contained in:
@@ -25,14 +25,19 @@ export const createAffineDownloadProvider = (
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
affineApis.downloadWorkspace(id, false).then(binary => {
|
affineApis
|
||||||
hashMap.set(id, binary);
|
.downloadWorkspace(id, false)
|
||||||
providerLogger.debug('applyUpdate');
|
.then(binary => {
|
||||||
BlockSuiteWorkspace.Y.applyUpdate(
|
hashMap.set(id, binary);
|
||||||
blockSuiteWorkspace.doc,
|
providerLogger.debug('applyUpdate');
|
||||||
new Uint8Array(binary)
|
BlockSuiteWorkspace.Y.applyUpdate(
|
||||||
);
|
blockSuiteWorkspace.doc,
|
||||||
});
|
new Uint8Array(binary)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
providerLogger.error('downloadWorkspace', e);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
disconnect: () => {
|
disconnect: () => {
|
||||||
providerLogger.info('disconnect download provider', id);
|
providerLogger.info('disconnect download provider', id);
|
||||||
|
|||||||
@@ -15,29 +15,37 @@ const logger = new DebugLogger('auth-token');
|
|||||||
const revalidate = async () => {
|
const revalidate = async () => {
|
||||||
const storage = getLoginStorage();
|
const storage = getLoginStorage();
|
||||||
if (storage) {
|
if (storage) {
|
||||||
const tokenMessage = parseIdToken(storage.token);
|
try {
|
||||||
logger.debug('revalidate affine user');
|
const tokenMessage = parseIdToken(storage.token);
|
||||||
if (isExpired(tokenMessage)) {
|
logger.debug('revalidate affine user');
|
||||||
logger.debug('need to refresh token');
|
if (isExpired(tokenMessage)) {
|
||||||
const response = await affineAuth.refreshToken(storage);
|
logger.debug('need to refresh token');
|
||||||
if (response) {
|
const response = await affineAuth.refreshToken(storage);
|
||||||
setLoginStorage(response);
|
if (response) {
|
||||||
storageChangeSlot.emit();
|
setLoginStorage(response);
|
||||||
|
storageChangeSlot.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useAffineRefreshAuthToken(
|
export function useAffineRefreshAuthToken(
|
||||||
// every 30 seconds, check if the token is expired
|
// every 30 seconds, check if the token is expired
|
||||||
refreshInterval = 30 * 1000
|
refreshInterval = 30 * 1000
|
||||||
) {
|
) {
|
||||||
useSWR('autoRefreshToken', {
|
const { data } = useSWR<boolean>('autoRefreshToken', {
|
||||||
|
suspense: true,
|
||||||
fetcher: revalidate,
|
fetcher: revalidate,
|
||||||
refreshInterval,
|
refreshInterval,
|
||||||
revalidateOnFocus: true,
|
revalidateOnFocus: true,
|
||||||
revalidateOnReconnect: true,
|
revalidateOnReconnect: true,
|
||||||
revalidateOnMount: true,
|
revalidateOnMount: true,
|
||||||
});
|
});
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,19 @@ export function useSyncRouterWithCurrentWorkspaceId(router: NextRouter) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentWorkspaceId) {
|
if (currentWorkspaceId) {
|
||||||
|
if (currentWorkspaceId !== workspaceId) {
|
||||||
|
const target = metadata.find(workspace => workspace.id === workspaceId);
|
||||||
|
if (!target) {
|
||||||
|
// workspaceId is invalid, redirect to currentWorkspaceId
|
||||||
|
void router.push({
|
||||||
|
pathname: router.pathname,
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
workspaceId: currentWorkspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const targetWorkspace = metadata.find(
|
const targetWorkspace = metadata.find(
|
||||||
|
|||||||
@@ -17,7 +17,14 @@ import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
|||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import type { FC, PropsWithChildren, ReactElement } from 'react';
|
import type { FC, PropsWithChildren, ReactElement } from 'react';
|
||||||
import { lazy, Suspense, useCallback, useEffect, useState } from 'react';
|
import {
|
||||||
|
lazy,
|
||||||
|
Suspense,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { openQuickSearchModalAtom, openWorkspacesModalAtom } from '../atoms';
|
import { openQuickSearchModalAtom, openWorkspacesModalAtom } from '../atoms';
|
||||||
import {
|
import {
|
||||||
@@ -176,6 +183,10 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
|
|||||||
}, [i18n]);
|
}, [i18n]);
|
||||||
const currentWorkspaceId = useAtomValue(rootCurrentWorkspaceIdAtom);
|
const currentWorkspaceId = useAtomValue(rootCurrentWorkspaceIdAtom);
|
||||||
const jotaiWorkspaces = useAtomValue(rootWorkspacesMetadataAtom);
|
const jotaiWorkspaces = useAtomValue(rootWorkspacesMetadataAtom);
|
||||||
|
const meta = useMemo(
|
||||||
|
() => jotaiWorkspaces.find(x => x.id === currentWorkspaceId),
|
||||||
|
[currentWorkspaceId, jotaiWorkspaces]
|
||||||
|
);
|
||||||
const set = useSetAtom(rootWorkspacesMetadataAtom);
|
const set = useSetAtom(rootWorkspacesMetadataAtom);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
logger.info('mount');
|
logger.info('mount');
|
||||||
@@ -228,6 +239,9 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [currentWorkspaceId, jotaiWorkspaces]);
|
}, [currentWorkspaceId, jotaiWorkspaces]);
|
||||||
|
|
||||||
|
const Provider =
|
||||||
|
(meta && WorkspacePlugins[meta.flavour].UI.Provider) ?? DefaultProvider;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* fixme(himself65): don't re-render whole modals */}
|
{/* fixme(himself65): don't re-render whole modals */}
|
||||||
@@ -240,7 +254,9 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
|
|||||||
<Suspense
|
<Suspense
|
||||||
fallback={<PageLoading text={t('Finding Current Workspace')} />}
|
fallback={<PageLoading text={t('Finding Current Workspace')} />}
|
||||||
>
|
>
|
||||||
<WorkspaceLayoutInner>{children}</WorkspaceLayoutInner>
|
<Provider>
|
||||||
|
<WorkspaceLayoutInner>{children}</WorkspaceLayoutInner>
|
||||||
|
</Provider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</CurrentWorkspaceContext>
|
</CurrentWorkspaceContext>
|
||||||
</>
|
</>
|
||||||
@@ -397,11 +413,8 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
|
|||||||
);
|
);
|
||||||
}, [setIsResizing, setSidebarOpen, setSliderWidth]);
|
}, [setIsResizing, setSidebarOpen, setSliderWidth]);
|
||||||
|
|
||||||
const Provider =
|
|
||||||
WorkspacePlugins[currentWorkspace.flavour].UI.Provider ?? DefaultProvider;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
</Head>
|
</Head>
|
||||||
@@ -465,6 +478,6 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
|
|||||||
</MainContainerWrapper>
|
</MainContainerWrapper>
|
||||||
</StyledPage>
|
</StyledPage>
|
||||||
<QuickSearch />
|
<QuickSearch />
|
||||||
</Provider>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
|
||||||
import type { NextPage } from 'next';
|
import type { NextPage } from 'next';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { Suspense, useEffect } from 'react';
|
import { Suspense, useEffect } from 'react';
|
||||||
@@ -27,10 +26,7 @@ const IndexPageInner = () => {
|
|||||||
const targetWorkspace =
|
const targetWorkspace =
|
||||||
(lastWorkspaceId &&
|
(lastWorkspaceId &&
|
||||||
workspaces.find(({ id }) => id === lastWorkspaceId)) ||
|
workspaces.find(({ id }) => id === lastWorkspaceId)) ||
|
||||||
// fixme(himself65):
|
workspaces.at(0);
|
||||||
// when affine workspace is expired and the first workspace is affine,
|
|
||||||
// the page will crash
|
|
||||||
workspaces.find(({ flavour }) => flavour === WorkspaceFlavour.LOCAL);
|
|
||||||
|
|
||||||
if (targetWorkspace) {
|
if (targetWorkspace) {
|
||||||
const pageId =
|
const pageId =
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { config, prefixUrl } from '@affine/env';
|
import { AFFINE_STORAGE_KEY, config, prefixUrl } from '@affine/env';
|
||||||
import { initPage } from '@affine/env/blocksuite';
|
import { initPage } from '@affine/env/blocksuite';
|
||||||
import { currentAffineUserAtom } from '@affine/workspace/affine/atom';
|
import { currentAffineUserAtom } from '@affine/workspace/affine/atom';
|
||||||
import {
|
import {
|
||||||
clearLoginStorage,
|
clearLoginStorage,
|
||||||
createAffineAuth,
|
createAffineAuth,
|
||||||
getLoginStorage,
|
getLoginStorage,
|
||||||
|
isExpired,
|
||||||
parseIdToken,
|
parseIdToken,
|
||||||
setLoginStorage,
|
setLoginStorage,
|
||||||
SignMethod,
|
SignMethod,
|
||||||
@@ -12,9 +13,13 @@ import {
|
|||||||
import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||||
import type { AffineWorkspace } from '@affine/workspace/type';
|
import type { AffineWorkspace } from '@affine/workspace/type';
|
||||||
import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type';
|
import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type';
|
||||||
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
import {
|
||||||
|
cleanupWorkspace,
|
||||||
|
createEmptyBlockSuiteWorkspace,
|
||||||
|
} from '@affine/workspace/utils';
|
||||||
import { createJSONStorage } from 'jotai/utils';
|
import { createJSONStorage } from 'jotai/utils';
|
||||||
import React from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
|
import { Suspense, useEffect } from 'react';
|
||||||
import { mutate } from 'swr';
|
import { mutate } from 'swr';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
@@ -23,8 +28,8 @@ import { PageNotFoundError } from '../../components/affine/affine-error-eoundary
|
|||||||
import { WorkspaceSettingDetail } from '../../components/affine/workspace-setting-detail';
|
import { WorkspaceSettingDetail } from '../../components/affine/workspace-setting-detail';
|
||||||
import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list';
|
import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list';
|
||||||
import { PageDetailEditor } from '../../components/page-detail-editor';
|
import { PageDetailEditor } from '../../components/page-detail-editor';
|
||||||
|
import { PageLoading } from '../../components/pure/loading';
|
||||||
import { useAffineRefreshAuthToken } from '../../hooks/affine/use-affine-refresh-auth-token';
|
import { useAffineRefreshAuthToken } from '../../hooks/affine/use-affine-refresh-auth-token';
|
||||||
import { AffineSWRConfigProvider } from '../../providers/AffineSWRConfigProvider';
|
|
||||||
import { BlockSuiteWorkspace } from '../../shared';
|
import { BlockSuiteWorkspace } from '../../shared';
|
||||||
import { affineApis } from '../../shared/apis';
|
import { affineApis } from '../../shared/apis';
|
||||||
import { toast } from '../../utils';
|
import { toast } from '../../utils';
|
||||||
@@ -32,7 +37,6 @@ import type { WorkspacePlugin } from '..';
|
|||||||
import { QueryKey } from './fetcher';
|
import { QueryKey } from './fetcher';
|
||||||
|
|
||||||
const storage = createJSONStorage(() => localStorage);
|
const storage = createJSONStorage(() => localStorage);
|
||||||
const kAffineLocal = 'affine-local-storage-v2';
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
type: z.number(),
|
type: z.number(),
|
||||||
@@ -41,7 +45,7 @@ const schema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getPersistenceAllWorkspace = () => {
|
const getPersistenceAllWorkspace = () => {
|
||||||
const items = storage.getItem(kAffineLocal);
|
const items = storage.getItem(AFFINE_STORAGE_KEY);
|
||||||
const allWorkspaces: AffineWorkspace[] = [];
|
const allWorkspaces: AffineWorkspace[] = [];
|
||||||
if (
|
if (
|
||||||
Array.isArray(items) &&
|
Array.isArray(items) &&
|
||||||
@@ -71,6 +75,22 @@ const getPersistenceAllWorkspace = () => {
|
|||||||
|
|
||||||
export const affineAuth = createAffineAuth(prefixUrl);
|
export const affineAuth = createAffineAuth(prefixUrl);
|
||||||
|
|
||||||
|
function AuthContext({ children }: PropsWithChildren): ReactElement {
|
||||||
|
const login = useAffineRefreshAuthToken();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!login) {
|
||||||
|
console.warn('No login, redirecting to local workspace page...');
|
||||||
|
}
|
||||||
|
}, [login]);
|
||||||
|
if (!login) {
|
||||||
|
return (
|
||||||
|
<PageLoading text="No login, redirecting to local workspace page..." />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
||||||
flavour: WorkspaceFlavour.AFFINE,
|
flavour: WorkspaceFlavour.AFFINE,
|
||||||
loadPriority: LoadPriority.HIGH,
|
loadPriority: LoadPriority.HIGH,
|
||||||
@@ -99,7 +119,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
workspace => workspace.flavour !== WorkspaceFlavour.AFFINE
|
workspace => workspace.flavour !== WorkspaceFlavour.AFFINE
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
storage.removeItem(kAffineLocal);
|
storage.removeItem(AFFINE_STORAGE_KEY);
|
||||||
clearLoginStorage();
|
clearLoginStorage();
|
||||||
rootStore.set(currentAffineUserAtom, null);
|
rootStore.set(currentAffineUserAtom, null);
|
||||||
},
|
},
|
||||||
@@ -132,13 +152,13 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
delete: async workspace => {
|
delete: async workspace => {
|
||||||
const items = storage.getItem(kAffineLocal);
|
const items = storage.getItem(AFFINE_STORAGE_KEY);
|
||||||
if (
|
if (
|
||||||
Array.isArray(items) &&
|
Array.isArray(items) &&
|
||||||
items.every(item => schema.safeParse(item).success)
|
items.every(item => schema.safeParse(item).success)
|
||||||
) {
|
) {
|
||||||
storage.setItem(
|
storage.setItem(
|
||||||
kAffineLocal,
|
AFFINE_STORAGE_KEY,
|
||||||
items.filter(item => item.id !== workspace.id)
|
items.filter(item => item.id !== workspace.id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -148,12 +168,17 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
await mutate(matcher => matcher === QueryKey.getWorkspaces);
|
await mutate(matcher => matcher === QueryKey.getWorkspaces);
|
||||||
},
|
},
|
||||||
get: async workspaceId => {
|
get: async workspaceId => {
|
||||||
|
// fixme(himself65): rewrite the auth logic
|
||||||
try {
|
try {
|
||||||
if (!getLoginStorage()) {
|
const loginStorage = getLoginStorage();
|
||||||
const workspaces = getPersistenceAllWorkspace();
|
if (
|
||||||
return (
|
loginStorage == null ||
|
||||||
workspaces.find(workspace => workspace.id === workspaceId) ?? null
|
isExpired(parseIdToken(loginStorage.token))
|
||||||
);
|
) {
|
||||||
|
rootStore.set(currentAffineUserAtom, null);
|
||||||
|
storage.removeItem(AFFINE_STORAGE_KEY);
|
||||||
|
cleanupWorkspace(WorkspaceFlavour.AFFINE);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
const workspaces: AffineWorkspace[] = await AffinePlugin.CRUD.list();
|
const workspaces: AffineWorkspace[] = await AffinePlugin.CRUD.list();
|
||||||
return (
|
return (
|
||||||
@@ -168,8 +193,20 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
},
|
},
|
||||||
list: async () => {
|
list: async () => {
|
||||||
const allWorkspaces = getPersistenceAllWorkspace();
|
const allWorkspaces = getPersistenceAllWorkspace();
|
||||||
if (!getLoginStorage()) {
|
const loginStorage = getLoginStorage();
|
||||||
return allWorkspaces;
|
// fixme(himself65): rewrite the auth logic
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
loginStorage == null ||
|
||||||
|
isExpired(parseIdToken(loginStorage.token))
|
||||||
|
) {
|
||||||
|
rootStore.set(currentAffineUserAtom, null);
|
||||||
|
storage.removeItem(AFFINE_STORAGE_KEY);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
storage.removeItem(AFFINE_STORAGE_KEY);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const workspaces = await affineApis.getWorkspaces().then(workspaces => {
|
const workspaces = await affineApis.getWorkspaces().then(workspaces => {
|
||||||
@@ -189,7 +226,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
permission: workspace.permission,
|
permission: workspace.permission,
|
||||||
} satisfies z.infer<typeof schema>;
|
} satisfies z.infer<typeof schema>;
|
||||||
});
|
});
|
||||||
const old = storage.getItem(kAffineLocal);
|
const old = storage.getItem(AFFINE_STORAGE_KEY);
|
||||||
if (
|
if (
|
||||||
Array.isArray(old) &&
|
Array.isArray(old) &&
|
||||||
old.every(item => schema.safeParse(item).success)
|
old.every(item => schema.safeParse(item).success)
|
||||||
@@ -201,7 +238,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
data.push(item);
|
data.push(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
storage.setItem(kAffineLocal, [...data]);
|
storage.setItem(AFFINE_STORAGE_KEY, [...data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const affineWorkspace: AffineWorkspace = {
|
const affineWorkspace: AffineWorkspace = {
|
||||||
@@ -231,7 +268,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
permission: workspace.permission,
|
permission: workspace.permission,
|
||||||
} satisfies z.infer<typeof schema>;
|
} satisfies z.infer<typeof schema>;
|
||||||
});
|
});
|
||||||
storage.setItem(kAffineLocal, [...dump]);
|
storage.setItem(AFFINE_STORAGE_KEY, [...dump]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('fetch affine workspaces failed', e);
|
console.error('fetch affine workspaces failed', e);
|
||||||
}
|
}
|
||||||
@@ -240,8 +277,11 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
|
|||||||
},
|
},
|
||||||
UI: {
|
UI: {
|
||||||
Provider: ({ children }) => {
|
Provider: ({ children }) => {
|
||||||
useAffineRefreshAuthToken();
|
return (
|
||||||
return <AffineSWRConfigProvider>{children}</AffineSWRConfigProvider>;
|
<Suspense fallback={<PageLoading text="Checking login status..." />}>
|
||||||
|
<AuthContext>{children}</AuthContext>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
PageDetail: ({ currentWorkspace, currentPageId }) => {
|
PageDetail: ({ currentWorkspace, currentPageId }) => {
|
||||||
const page = currentWorkspace.blockSuiteWorkspace.getPage(currentPageId);
|
const page = currentWorkspace.blockSuiteWorkspace.getPage(currentPageId);
|
||||||
|
|||||||
1
packages/env/src/constant.ts
vendored
1
packages/env/src/constant.ts
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
export const AFFINE_STORAGE_KEY = 'affine-local-storage-v2';
|
||||||
export const DEFAULT_WORKSPACE_NAME = 'Demo Workspace';
|
export const DEFAULT_WORKSPACE_NAME = 'Demo Workspace';
|
||||||
export const UNTITLED_WORKSPACE_NAME = 'Untitled';
|
export const UNTITLED_WORKSPACE_NAME = 'Untitled';
|
||||||
export const DEFAULT_HELLO_WORLD_PAGE_ID = 'hello-world';
|
export const DEFAULT_HELLO_WORLD_PAGE_ID = 'hello-world';
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ export function createWorkspaceApis(prefixUrl = '/') {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(r =>
|
.then(r =>
|
||||||
r.status === 403
|
!r.ok
|
||||||
? Promise.reject(new RequestError(MessageCode.noPermission))
|
? Promise.reject(new RequestError(MessageCode.noPermission))
|
||||||
: r
|
: r
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import {
|
|||||||
isExpired,
|
isExpired,
|
||||||
parseIdToken,
|
parseIdToken,
|
||||||
} from '@affine/workspace/affine/login';
|
} from '@affine/workspace/affine/login';
|
||||||
|
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||||
|
import { cleanupWorkspace } from '@affine/workspace/utils';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import * as url from 'lib0/url';
|
import * as url from 'lib0/url';
|
||||||
import * as websocket from 'lib0/websocket';
|
import * as websocket from 'lib0/websocket';
|
||||||
@@ -28,6 +30,10 @@ export class WebsocketClient {
|
|||||||
|
|
||||||
public connect(callback: (message: any) => void) {
|
public connect(callback: (message: any) => void) {
|
||||||
const loginResponse = getLoginStorage();
|
const loginResponse = getLoginStorage();
|
||||||
|
if (!loginResponse || isExpired(parseIdToken(loginResponse.token))) {
|
||||||
|
cleanupWorkspace(WorkspaceFlavour.AFFINE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
assertExists(loginResponse, 'loginResponse is null');
|
assertExists(loginResponse, 'loginResponse is null');
|
||||||
const encodedParams = url.encodeQueryParams({
|
const encodedParams = url.encodeQueryParams({
|
||||||
token: loginResponse.token,
|
token: loginResponse.token,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { createWorkspaceApis } from '@affine/workspace/affine/api';
|
import type { createWorkspaceApis } from '@affine/workspace/affine/api';
|
||||||
|
import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||||
import { createAffineBlobStorage } from '@affine/workspace/blob';
|
import { createAffineBlobStorage } from '@affine/workspace/blob';
|
||||||
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
|
||||||
import type { Generator, StoreOptions } from '@blocksuite/store';
|
import type { Generator, StoreOptions } from '@blocksuite/store';
|
||||||
@@ -7,6 +8,12 @@ import { createIndexeddbStorage, Workspace } from '@blocksuite/store';
|
|||||||
import { createSQLiteStorage } from './blob/sqlite-blob-storage';
|
import { createSQLiteStorage } from './blob/sqlite-blob-storage';
|
||||||
import { WorkspaceFlavour } from './type';
|
import { WorkspaceFlavour } from './type';
|
||||||
|
|
||||||
|
export function cleanupWorkspace(flavour: WorkspaceFlavour) {
|
||||||
|
rootStore.set(rootWorkspacesMetadataAtom, metas =>
|
||||||
|
metas.filter(meta => meta.flavour !== flavour)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const hashMap = new Map<string, Workspace>();
|
const hashMap = new Map<string, Workspace>();
|
||||||
|
|
||||||
export function createEmptyBlockSuiteWorkspace(
|
export function createEmptyBlockSuiteWorkspace(
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import type { Page } from '@playwright/test';
|
|||||||
|
|
||||||
import { getMetas } from './utils';
|
import { getMetas } from './utils';
|
||||||
|
|
||||||
|
export const webUrl = 'http://localhost:8080';
|
||||||
|
|
||||||
export async function openHomePage(page: Page) {
|
export async function openHomePage(page: Page) {
|
||||||
await page.goto('http://localhost:8080');
|
await page.goto(webUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initHomePageWithPinboard(page: Page) {
|
export async function initHomePageWithPinboard(page: Page) {
|
||||||
|
|||||||
@@ -131,10 +131,6 @@ export async function loginUser(
|
|||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openHomePage(page: Page) {
|
|
||||||
return page.goto('http://localhost:8080');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getMetas(page: Page): Promise<PageMeta[]> {
|
export async function getMetas(page: Page): Promise<PageMeta[]> {
|
||||||
return page.evaluate(
|
return page.evaluate(
|
||||||
() => globalThis.currentWorkspace.blockSuiteWorkspace.meta.pageMetas ?? []
|
() => globalThis.currentWorkspace.blockSuiteWorkspace.meta.pageMetas ?? []
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import type { Page } from '@playwright/test';
|
import type { Page } from '@playwright/test';
|
||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
|
import { clickCollaborationPanel } from './setting';
|
||||||
|
import { clickSideBarSettingButton } from './sidebar';
|
||||||
|
|
||||||
interface CreateWorkspaceParams {
|
interface CreateWorkspaceParams {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
@@ -34,3 +37,14 @@ export async function assertCurrentWorkspaceFlavour(
|
|||||||
const actual = await page.evaluate(() => globalThis.currentWorkspace.flavour);
|
const actual = await page.evaluate(() => globalThis.currentWorkspace.flavour);
|
||||||
expect(actual).toBe(flavour);
|
expect(actual).toBe(flavour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function enableAffineCloudWorkspace(page: Page) {
|
||||||
|
await clickSideBarSettingButton(page);
|
||||||
|
await page.waitForTimeout(50);
|
||||||
|
await clickCollaborationPanel(page);
|
||||||
|
await page.getByTestId('local-workspace-enable-cloud-button').click();
|
||||||
|
await page.getByTestId('confirm-enable-cloud-button').click();
|
||||||
|
await page.waitForSelector("[data-testid='member-length']", {
|
||||||
|
timeout: 20000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
|
import { openHomePage } from '../../libs/load-page';
|
||||||
import { waitMarkdownImported } from '../../libs/page-logic';
|
import { waitMarkdownImported } from '../../libs/page-logic';
|
||||||
import { test } from '../../libs/playwright';
|
import { test } from '../../libs/playwright';
|
||||||
import {
|
import {
|
||||||
clickNewPageButton,
|
clickNewPageButton,
|
||||||
clickSideBarCurrentWorkspaceBanner,
|
clickSideBarCurrentWorkspaceBanner,
|
||||||
} from '../../libs/sidebar';
|
} from '../../libs/sidebar';
|
||||||
import { getBuiltInUser, loginUser, openHomePage } from '../../libs/utils';
|
import { getBuiltInUser, loginUser } from '../../libs/utils';
|
||||||
|
|
||||||
test('collaborative', async ({ page, browser }) => {
|
test('collaborative', async ({ page, browser }) => {
|
||||||
await openHomePage(page);
|
await openHomePage(page);
|
||||||
|
|||||||
18
tests/parallels/affine/affine-lost-auth.spec.ts
Normal file
18
tests/parallels/affine/affine-lost-auth.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { openHomePage } from '../../libs/load-page';
|
||||||
|
import { waitMarkdownImported } from '../../libs/page-logic';
|
||||||
|
import { test } from '../../libs/playwright';
|
||||||
|
import { clickSideBarAllPageButton } from '../../libs/sidebar';
|
||||||
|
import { createFakeUser, loginUser } from '../../libs/utils';
|
||||||
|
import { enableAffineCloudWorkspace } from '../../libs/workspace';
|
||||||
|
|
||||||
|
test('authorization expired', async ({ page }) => {
|
||||||
|
await openHomePage(page);
|
||||||
|
await waitMarkdownImported(page);
|
||||||
|
const [a] = await createFakeUser();
|
||||||
|
await loginUser(page, a);
|
||||||
|
await enableAffineCloudWorkspace(page);
|
||||||
|
await clickSideBarAllPageButton(page);
|
||||||
|
await page.evaluate(() => localStorage.removeItem('affine-login-v2'));
|
||||||
|
await openHomePage(page);
|
||||||
|
await waitMarkdownImported(page);
|
||||||
|
});
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
|
import { openHomePage } from '../../libs/load-page';
|
||||||
import { waitMarkdownImported } from '../../libs/page-logic';
|
import { waitMarkdownImported } from '../../libs/page-logic';
|
||||||
import { test } from '../../libs/playwright';
|
import { test } from '../../libs/playwright';
|
||||||
import { clickNewPageButton } from '../../libs/sidebar';
|
import { clickNewPageButton } from '../../libs/sidebar';
|
||||||
import { createFakeUser, loginUser, openHomePage } from '../../libs/utils';
|
import { createFakeUser, loginUser } from '../../libs/utils';
|
||||||
import { createWorkspace } from '../../libs/workspace';
|
import { createWorkspace } from '../../libs/workspace';
|
||||||
|
|
||||||
test('public single page', async ({ page, browser }) => {
|
test('public single page', async ({ page, browser }) => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
|
import { openHomePage } from '../../libs/load-page';
|
||||||
import { waitMarkdownImported } from '../../libs/page-logic';
|
import { waitMarkdownImported } from '../../libs/page-logic';
|
||||||
import { test } from '../../libs/playwright';
|
import { test } from '../../libs/playwright';
|
||||||
import { clickPublishPanel } from '../../libs/setting';
|
import { clickPublishPanel } from '../../libs/setting';
|
||||||
@@ -7,7 +8,7 @@ import {
|
|||||||
clickSideBarAllPageButton,
|
clickSideBarAllPageButton,
|
||||||
clickSideBarSettingButton,
|
clickSideBarSettingButton,
|
||||||
} from '../../libs/sidebar';
|
} from '../../libs/sidebar';
|
||||||
import { createFakeUser, loginUser, openHomePage } from '../../libs/utils';
|
import { createFakeUser, loginUser } from '../../libs/utils';
|
||||||
import { createWorkspace } from '../../libs/workspace';
|
import { createWorkspace } from '../../libs/workspace';
|
||||||
|
|
||||||
test('enable public workspace', async ({ page, context }) => {
|
test('enable public workspace', async ({ page, context }) => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
|
import { openHomePage } from '../../libs/load-page';
|
||||||
import { waitMarkdownImported } from '../../libs/page-logic';
|
import { waitMarkdownImported } from '../../libs/page-logic';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
@@ -7,17 +8,15 @@ const userA = require('../../fixtures/userA.json');
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const userB = require('../../fixtures/userB.json');
|
const userB = require('../../fixtures/userB.json');
|
||||||
import { test } from '../../libs/playwright';
|
import { test } from '../../libs/playwright';
|
||||||
import { clickCollaborationPanel } from '../../libs/setting';
|
|
||||||
import {
|
import {
|
||||||
clickNewPageButton,
|
clickNewPageButton,
|
||||||
clickSideBarAllPageButton,
|
|
||||||
clickSideBarCurrentWorkspaceBanner,
|
clickSideBarCurrentWorkspaceBanner,
|
||||||
clickSideBarSettingButton,
|
|
||||||
} from '../../libs/sidebar';
|
} from '../../libs/sidebar';
|
||||||
import { createFakeUser, loginUser, openHomePage } from '../../libs/utils';
|
import { createFakeUser, loginUser } from '../../libs/utils';
|
||||||
import {
|
import {
|
||||||
assertCurrentWorkspaceFlavour,
|
assertCurrentWorkspaceFlavour,
|
||||||
createWorkspace,
|
createWorkspace,
|
||||||
|
enableAffineCloudWorkspace,
|
||||||
openWorkspaceListModal,
|
openWorkspaceListModal,
|
||||||
} from '../../libs/workspace';
|
} from '../../libs/workspace';
|
||||||
|
|
||||||
@@ -41,15 +40,7 @@ test('should enable affine workspace successfully', async ({ page }) => {
|
|||||||
const name = `test-${Date.now()}`;
|
const name = `test-${Date.now()}`;
|
||||||
await createWorkspace({ name }, page);
|
await createWorkspace({ name }, page);
|
||||||
await page.waitForTimeout(50);
|
await page.waitForTimeout(50);
|
||||||
await clickSideBarSettingButton(page);
|
await enableAffineCloudWorkspace(page);
|
||||||
await page.waitForTimeout(50);
|
|
||||||
await clickCollaborationPanel(page);
|
|
||||||
await page.getByTestId('local-workspace-enable-cloud-button').click();
|
|
||||||
await page.getByTestId('confirm-enable-cloud-button').click();
|
|
||||||
await page.waitForSelector("[data-testid='member-length']", {
|
|
||||||
timeout: 20000,
|
|
||||||
});
|
|
||||||
await clickSideBarAllPageButton(page);
|
|
||||||
await clickNewPageButton(page);
|
await clickNewPageButton(page);
|
||||||
await page.locator('[data-block-is-title="true"]').type('Hello, world!', {
|
await page.locator('[data-block-is-title="true"]').type('Hello, world!', {
|
||||||
delay: 50,
|
delay: 50,
|
||||||
|
|||||||
25
tests/parallels/router.spec.ts
Normal file
25
tests/parallels/router.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
|
import { openHomePage, webUrl } from '../libs/load-page';
|
||||||
|
import { waitMarkdownImported } from '../libs/page-logic';
|
||||||
|
import { test } from '../libs/playwright';
|
||||||
|
import { clickSideBarAllPageButton } from '../libs/sidebar';
|
||||||
|
|
||||||
|
test('goto not found page', async ({ page }) => {
|
||||||
|
await openHomePage(page);
|
||||||
|
await waitMarkdownImported(page);
|
||||||
|
const currentUrl = page.url();
|
||||||
|
const invalidUrl = currentUrl.replace(/\/$/, '') + '/invalid';
|
||||||
|
await page.goto(invalidUrl);
|
||||||
|
expect(await page.getByTestId('notFound').isVisible()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('goto not found workspace', async ({ page }) => {
|
||||||
|
await openHomePage(page);
|
||||||
|
await waitMarkdownImported(page);
|
||||||
|
await clickSideBarAllPageButton(page);
|
||||||
|
const currentUrl = page.url();
|
||||||
|
await page.goto(new URL('/workspace/invalid/all', webUrl).toString());
|
||||||
|
await clickSideBarAllPageButton(page);
|
||||||
|
expect(page.url()).toEqual(currentUrl);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user