diff --git a/apps/web/src/components/message-center-handler/index.tsx b/apps/web/src/components/message-center-handler/index.tsx
index f53ed3756b..0339e2d300 100644
--- a/apps/web/src/components/message-center-handler/index.tsx
+++ b/apps/web/src/components/message-center-handler/index.tsx
@@ -1,14 +1,14 @@
import { toast } from '@affine/component';
-import { getApis, MessageCenter } from '@affine/datacenter';
+import { MessageCenter } from '@affine/datacenter';
+import { AffineProvider } from '@affine/datacenter';
import { useRouter } from 'next/router';
-import { useEffect } from 'react';
+import { ReactNode, useCallback, useEffect } from 'react';
-export function MessageCenterHandler({
- children,
-}: {
- children?: React.ReactNode;
-}) {
+import { useGlobalState } from '@/store/app';
+
+export function MessageCenterHandler({ children }: { children?: ReactNode }) {
const router = useRouter();
+ const dataCenter = useGlobalState(useCallback(store => store.dataCenter, []));
useEffect(() => {
const instance = MessageCenter.getInstance();
if (instance) {
@@ -18,7 +18,15 @@ export function MessageCenterHandler({
// todo: more specific message for accessing different resources
// todo: error toast style
toast('You have no permission to access this workspace');
- getApis().auth.clear();
+ // todo(himself65): remove dynamic lookup
+ const affineProvider = dataCenter.providers.find(
+ p => p.id === 'affine'
+ );
+ if (affineProvider && affineProvider instanceof AffineProvider) {
+ affineProvider.apis.auth.clear();
+ } else {
+ console.error('cannot find affine provider, please fix this ASAP');
+ }
// the status of the app right now is unknown, and it won't help if we let
// the app continue and let the user auth the app.
// that's why so we need to reload the page for now.
@@ -30,7 +38,7 @@ export function MessageCenterHandler({
}
});
}
- }, [router]);
+ }, [dataCenter?.providers, router]);
return <>{children}>;
}
diff --git a/apps/web/src/pages/_app.tsx b/apps/web/src/pages/_app.tsx
index b9814150a8..0b09b8495e 100644
--- a/apps/web/src/pages/_app.tsx
+++ b/apps/web/src/pages/_app.tsx
@@ -78,19 +78,19 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
,
]}
>
-
- {NoNeedAppStatePageList.includes(router.route) ? (
- getLayout()
- ) : (
- }>
-
+ {NoNeedAppStatePageList.includes(router.route) ? (
+ getLayout()
+ ) : (
+ }>
+
+
{getLayout()}
-
-
- )}
-
+
+
+
+ )}
>
diff --git a/packages/data-center/src/index.ts b/packages/data-center/src/index.ts
index de2a3b9d74..0d39aa2a3a 100644
--- a/packages/data-center/src/index.ts
+++ b/packages/data-center/src/index.ts
@@ -28,6 +28,7 @@ export const getDataCenter = _initializeDataCenter();
export type { DataCenter };
export { getLogger } from './logger';
export * from './message';
+export { AffineProvider } from './provider/affine';
export * from './provider/affine/apis';
export * from './types';
export { WorkspaceUnit } from './workspace-unit';
diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts
index 1a7ac7066a..2eaa8e1516 100644
--- a/packages/data-center/src/provider/affine/affine.ts
+++ b/packages/data-center/src/provider/affine/affine.ts
@@ -1,5 +1,6 @@
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
import assert from 'assert';
+import { KyInstance } from 'ky/distribution/types/ky';
import { MessageCenter } from '../../message';
import type { User } from '../../types';
@@ -12,16 +13,12 @@ import type {
} from '../base';
import { BaseProvider } from '../base';
import type { Apis, WorkspaceDetail } from './apis';
-// import { IndexedDBProvider } from '../local/indexeddb';
import { getApis, Workspace } from './apis';
+import { createGoogleAuth } from './apis/google';
+import { createAuthClient, createBareClient } from './apis/request';
import { WebsocketClient } from './channel';
import { WebsocketProvider } from './sync';
-import {
- createBlocksuiteWorkspaceWithAuth,
- createWorkspaceUnit,
- loadWorkspaceUnit,
- migrateBlobDB,
-} from './utils';
+import { createWorkspaceUnit, loadWorkspaceUnit, migrateBlobDB } from './utils';
type ChannelMessage = {
ws_list: Workspace[];
@@ -39,6 +36,9 @@ const {
} = BlocksuiteWorkspace;
export class AffineProvider extends BaseProvider {
+ private readonly bareClient: KyInstance;
+ private readonly authClient: KyInstance;
+
public id = 'affine';
private _wsMap: Map = new Map();
private _apis: Apis;
@@ -52,7 +52,14 @@ export class AffineProvider extends BaseProvider {
constructor({ apis, ...params }: AffineProviderConstructorParams) {
super(params);
- this._apis = apis || getApis();
+ this.bareClient = createBareClient('/');
+ const googleAuth = createGoogleAuth(this.bareClient);
+ this.authClient = createAuthClient(this.bareClient, googleAuth);
+ this._apis = apis || getApis(this.bareClient, this.authClient, googleAuth);
+ }
+
+ public get apis() {
+ return Object.freeze(this._apis);
}
override async init() {
@@ -80,6 +87,7 @@ export class AffineProvider extends BaseProvider {
window.location.host
}/api/global/sync/`,
this._logger,
+ this._apis.auth,
{
params: {
token: this._apis.auth.refresh,
@@ -370,16 +378,19 @@ export class AffineProvider extends BaseProvider {
public override async createWorkspace(
meta: CreateWorkspaceInfoParams
): Promise {
- const workspaceUnitForUpload = await createWorkspaceUnit({
- id: '',
- name: meta.name,
- avatar: undefined,
- owner: await this.getUserInfo(),
- published: false,
- memberCount: 1,
- provider: this.id,
- syncMode: 'core',
- });
+ const workspaceUnitForUpload = await createWorkspaceUnit(
+ {
+ id: '',
+ name: meta.name,
+ avatar: undefined,
+ owner: await this.getUserInfo(),
+ published: false,
+ memberCount: 1,
+ provider: this.id,
+ syncMode: 'core',
+ },
+ this._apis
+ );
const { id } = await this._apis.createWorkspace(
new Blob([
encodeStateAsUpdate(workspaceUnitForUpload.blocksuiteWorkspace!.doc)
@@ -387,16 +398,19 @@ export class AffineProvider extends BaseProvider {
])
);
- const workspaceUnit = await createWorkspaceUnit({
- id,
- name: meta.name,
- avatar: undefined,
- owner: await this.getUserInfo(),
- published: false,
- memberCount: 1,
- provider: this.id,
- syncMode: 'core',
- });
+ const workspaceUnit = await createWorkspaceUnit(
+ {
+ id,
+ name: meta.name,
+ avatar: undefined,
+ owner: await this.getUserInfo(),
+ published: false,
+ memberCount: 1,
+ provider: this.id,
+ syncMode: 'core',
+ },
+ this._apis
+ );
this._workspaces.add(workspaceUnit);
@@ -446,7 +460,8 @@ export class AffineProvider extends BaseProvider {
});
await migrateBlobDB(workspaceUnit.id, id);
- const blocksuiteWorkspace = await createBlocksuiteWorkspaceWithAuth(id);
+ const blocksuiteWorkspace =
+ await this._apis.createBlockSuiteWorkspaceWithAuth(id);
assert(workspaceUnit.blocksuiteWorkspace);
await applyUpdate(
blocksuiteWorkspace,
diff --git a/packages/data-center/src/provider/affine/apis/__tests__/auth.spec.ts b/packages/data-center/src/provider/affine/apis/__tests__/auth.spec.ts
index 8ef6129a35..2b26c02ac7 100644
--- a/packages/data-center/src/provider/affine/apis/__tests__/auth.spec.ts
+++ b/packages/data-center/src/provider/affine/apis/__tests__/auth.spec.ts
@@ -1,11 +1,11 @@
import { describe, expect, test } from 'vitest';
-import { Auth } from '../auth';
+import { GoogleAuth } from '../google';
describe('class Auth', () => {
test('parse tokens', () => {
const tokenString = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NzU2Nzk1MjAsImlkIjo2LCJuYW1lIjoidGVzdCIsImVtYWlsIjoidGVzdEBnbWFpbC5jb20iLCJhdmF0YXJfdXJsIjoiaHR0cHM6Ly90ZXN0LmNvbS9hdmF0YXIiLCJjcmVhdGVkX2F0IjoxNjc1Njc4OTIwMzU4fQ.R8GxrNhn3gNumtapthrP6_J5eQjXLV7i-LanSPqe7hw`;
- expect(Auth.parseIdToken(tokenString)).toEqual({
+ expect(GoogleAuth.parseIdToken(tokenString)).toEqual({
avatar_url: 'https://test.com/avatar',
created_at: 1675678920358,
email: 'test@gmail.com',
@@ -17,6 +17,6 @@ describe('class Auth', () => {
test('parse invalid tokens', () => {
const tokenString = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.aaa.R8GxrNhn3gNumtapthrP6_J5eQjXLV7i-LanSPqe7hw`;
- expect(Auth.parseIdToken(tokenString)).toEqual(null);
+ expect(GoogleAuth.parseIdToken(tokenString)).toEqual(null);
});
});
diff --git a/packages/data-center/src/provider/affine/apis/auth.ts b/packages/data-center/src/provider/affine/apis/google.ts
similarity index 87%
rename from packages/data-center/src/provider/affine/apis/auth.ts
rename to packages/data-center/src/provider/affine/apis/google.ts
index 1bbeb9413e..72bf49d93f 100644
--- a/packages/data-center/src/provider/affine/apis/auth.ts
+++ b/packages/data-center/src/provider/affine/apis/google.ts
@@ -8,10 +8,10 @@ import {
signOut,
} from 'firebase/auth';
import { decode } from 'js-base64';
+import { KyInstance } from 'ky/distribution/types/ky';
import { getLogger } from '../../../logger';
import { storage } from '../storage';
-import { bareClient } from './request';
export interface AccessTokenMessage {
created_at: number;
@@ -43,20 +43,24 @@ const AFFINE_LOGIN_STORAGE_KEY = 'affine:login';
* Use refresh token to get a new access token (JWT)
* The returned token also contains the user info payload.
*/
-const doLogin = (params: LoginParams): Promise =>
- bareClient.post('api/user/token', { json: params }).json();
+const createDoLogin =
+ (bareClient: KyInstance) =>
+ (params: LoginParams): Promise =>
+ bareClient.post('api/user/token', { json: params }).json();
-export class Auth {
+export class GoogleAuth {
private readonly _logger;
private _accessToken = ''; // idtoken (JWT)
private _refreshToken = '';
private _user: AccessTokenMessage | null = null;
private _padding?: Promise;
+ private readonly _doLogin: ReturnType;
- constructor() {
+ constructor(bareClient: KyInstance) {
this._logger = getLogger('token');
this._logger.enabled = true;
+ this._doLogin = createDoLogin(bareClient);
this.restoreLogin();
}
@@ -64,7 +68,7 @@ export class Auth {
setLogin(login: LoginResponse) {
this._accessToken = login.token;
this._refreshToken = login.refresh;
- this._user = Auth.parseIdToken(this._accessToken);
+ this._user = GoogleAuth.parseIdToken(this._accessToken);
this.triggerChange(this._user);
this.storeLogin();
@@ -94,14 +98,14 @@ export class Auth {
}
async initToken(token: string) {
- const res = await doLogin({ token, type: 'Google' });
+ const res = await this._doLogin({ token, type: 'Google' });
this.setLogin(res);
return this._user;
}
async refreshToken(refreshToken?: string) {
if (!this._padding) {
- this._padding = doLogin({
+ this._padding = this._doLogin({
type: 'Refresh',
token: refreshToken || this._refreshToken,
});
@@ -181,9 +185,11 @@ export class Auth {
}
}
-export const auth = new Auth();
+export function createGoogleAuth(bareAuth: KyInstance): GoogleAuth {
+ return new GoogleAuth(bareAuth);
+}
-export const getAuthorizer = () => {
+export const getAuthorizer = (googleAuth: GoogleAuth) => {
let _firebaseAuth: FirebaseAuth | null = null;
const logger = getLogger('authorizer');
@@ -225,7 +231,7 @@ export const getAuthorizer = () => {
const idToken = await getToken();
let loginUser: AccessTokenMessage | null = null;
if (idToken) {
- loginUser = await auth.initToken(idToken);
+ loginUser = await googleAuth.initToken(idToken);
} else {
const firebaseAuth = getAuth();
if (firebaseAuth) {
@@ -237,7 +243,7 @@ export const getAuthorizer = () => {
});
const user = await signInWithPopup(firebaseAuth, googleAuthProvider);
const idToken = await user.user.getIdToken();
- loginUser = await auth.initToken(idToken);
+ loginUser = await googleAuth.initToken(idToken);
}
}
return loginUser;
diff --git a/packages/data-center/src/provider/affine/apis/index.ts b/packages/data-center/src/provider/affine/apis/index.ts
index cfdea1c7bf..4b4aa04206 100644
--- a/packages/data-center/src/provider/affine/apis/index.ts
+++ b/packages/data-center/src/provider/affine/apis/index.ts
@@ -1,10 +1,10 @@
-// export { token } from './token.js';
-export type { Callback } from './auth';
+export type { Callback } from './google';
-import { getAuthorizer } from './auth';
-import { auth } from './auth';
-import * as user from './user';
-import * as workspace from './workspace';
+import { KyInstance } from 'ky/distribution/types/ky';
+
+import { createGoogleAuth, getAuthorizer, GoogleAuth } from './google';
+import { createUserApis } from './user';
+import { createWorkspaceApis } from './workspace';
// See https://twitter.com/mattpocockuk/status/1622730173446557697
// TODO: move to ts utils?
@@ -14,27 +14,31 @@ type Prettify = {
} & {};
export type Apis = Prettify<
- typeof user &
- Omit & {
+ ReturnType &
+ ReturnType & {
signInWithGoogle: ReturnType[0];
onAuthStateChanged: ReturnType[1];
signOutFirebase: ReturnType[2];
- } & { auth: typeof auth }
+ } & { auth: ReturnType }
>;
-export const getApis = (): Apis => {
+export const getApis = (
+ bareClient: KyInstance,
+ authClient: KyInstance,
+ googleAuth: GoogleAuth
+): Apis => {
const [signInWithGoogle, onAuthStateChanged, signOutFirebase] =
- getAuthorizer();
+ getAuthorizer(googleAuth);
return {
- ...user,
- ...workspace,
+ ...createUserApis(bareClient, authClient),
+ ...createWorkspaceApis(bareClient, authClient, googleAuth),
signInWithGoogle,
signOutFirebase,
onAuthStateChanged,
- auth,
+ auth: googleAuth,
};
};
-export type { AccessTokenMessage } from './auth';
+export type { AccessTokenMessage } from './google';
export type { Member, Workspace, WorkspaceDetail } from './workspace';
export * from './workspace';
diff --git a/packages/data-center/src/provider/affine/apis/request.ts b/packages/data-center/src/provider/affine/apis/request.ts
index 7783c96d7e..521bb3a072 100644
--- a/packages/data-center/src/provider/affine/apis/request.ts
+++ b/packages/data-center/src/provider/affine/apis/request.ts
@@ -1,7 +1,7 @@
import ky from 'ky-universal';
import { MessageCenter } from '../../../message';
-import { auth } from './auth';
+import { GoogleAuth } from './google';
type KyInstance = typeof ky;
@@ -9,52 +9,57 @@ const messageCenter = MessageCenter.getInstance();
const _sendMessage = messageCenter.getMessageSender('affine');
-export const bareClient: KyInstance = ky.extend({
- prefixUrl: '/',
- retry: 1,
- // todo: report timeout error
- timeout: 60000,
- hooks: {
- beforeError: [
- error => {
- const { response } = error;
- if (response.status === 401) {
- _sendMessage(MessageCenter.messageCode.noPermission);
- }
- return error;
- },
- ],
- },
-});
+export const createBareClient = (prefixUrl: string): KyInstance =>
+ ky.extend({
+ prefixUrl: prefixUrl,
+ retry: 1,
+ // todo: report timeout error
+ timeout: 60000,
+ hooks: {
+ beforeError: [
+ error => {
+ const { response } = error;
+ if (response.status === 401) {
+ _sendMessage(MessageCenter.messageCode.noPermission);
+ }
+ return error;
+ },
+ ],
+ },
+ });
-const refreshTokenIfExpired = async () => {
- if (auth.isLogin && auth.isExpired) {
+const refreshTokenIfExpired = async (googleAuth: GoogleAuth) => {
+ if (googleAuth.isLogin && googleAuth.isExpired) {
try {
- await auth.refreshToken();
+ await googleAuth.refreshToken();
} catch (err) {
return new Response('Unauthorized', { status: 401 });
}
}
};
-export const client: KyInstance = bareClient.extend({
- hooks: {
- beforeRequest: [
- async request => {
- if (auth.isLogin) {
- await refreshTokenIfExpired();
- request.headers.set('Authorization', auth.token);
- } else {
- return new Response('Unauthorized', { status: 401 });
- }
- },
- ],
+export const createAuthClient = (
+ bareClient: KyInstance,
+ googleAuth: GoogleAuth
+): KyInstance =>
+ bareClient.extend({
+ hooks: {
+ beforeRequest: [
+ async request => {
+ if (googleAuth.isLogin) {
+ await refreshTokenIfExpired(googleAuth);
+ request.headers.set('Authorization', googleAuth.token);
+ } else {
+ return new Response('Unauthorized', { status: 401 });
+ }
+ },
+ ],
- beforeRetry: [
- async ({ request }) => {
- await refreshTokenIfExpired();
- request.headers.set('Authorization', auth.token);
- },
- ],
- },
-});
+ beforeRetry: [
+ async ({ request }) => {
+ await refreshTokenIfExpired(googleAuth);
+ request.headers.set('Authorization', googleAuth.token);
+ },
+ ],
+ },
+ });
diff --git a/packages/data-center/src/provider/affine/apis/user.ts b/packages/data-center/src/provider/affine/apis/user.ts
index 4a8827c6e8..15d3045a77 100644
--- a/packages/data-center/src/provider/affine/apis/user.ts
+++ b/packages/data-center/src/provider/affine/apis/user.ts
@@ -1,4 +1,4 @@
-import { client } from './request';
+import { KyInstance } from 'ky/distribution/types/ky';
export interface GetUserByEmailParams {
email: string;
@@ -13,9 +13,13 @@ export interface User {
create_at: string;
}
-export async function getUserByEmail(
- params: GetUserByEmailParams
-): Promise {
- const searchParams = new URLSearchParams({ ...params });
- return client.get('api/user', { searchParams }).json();
+export function createUserApis(bareClient: KyInstance, authClient: KyInstance) {
+ return {
+ getUserByEmail: async (
+ params: GetUserByEmailParams
+ ): Promise => {
+ const searchParams = new URLSearchParams({ ...params });
+ return authClient.get('api/user', { searchParams }).json();
+ },
+ } as const;
}
diff --git a/packages/data-center/src/provider/affine/apis/workspace.ts b/packages/data-center/src/provider/affine/apis/workspace.ts
index 1a74bcfa44..f156e1263b 100644
--- a/packages/data-center/src/provider/affine/apis/workspace.ts
+++ b/packages/data-center/src/provider/affine/apis/workspace.ts
@@ -1,5 +1,8 @@
+import { KyInstance } from 'ky/distribution/types/ky';
+
import { MessageCenter } from '../../../message';
-import { bareClient, client } from './request';
+import { createBlocksuiteWorkspace as _createBlocksuiteWorkspace } from '../../../utils';
+import { GoogleAuth } from './google';
import type { User } from './user';
const messageCenter = MessageCenter.getInstance();
@@ -15,6 +18,7 @@ class RequestError extends Error {
this.cause = cause;
}
}
+
export interface GetWorkspaceDetailParams {
id: string;
}
@@ -39,37 +43,11 @@ export interface Workspace {
create_at: number;
}
-export async function getWorkspaces(): Promise {
- try {
- return await client
- .get('api/workspace', {
- headers: {
- 'Cache-Control': 'no-cache',
- },
- })
- .json();
- } catch (error) {
- sendMessage(messageCode.loadListFailed);
- throw new RequestError('load list failed', error);
- }
-}
-
export interface WorkspaceDetail extends Workspace {
owner: User;
member_count: number;
}
-export async function getWorkspaceDetail(
- params: GetWorkspaceDetailParams
-): Promise {
- try {
- return await client.get(`api/workspace/${params.id}`).json();
- } catch (error) {
- sendMessage(messageCode.getDetailFailed);
- throw new RequestError('get detail failed', error);
- }
-}
-
export interface Permission {
id: string;
type: PermissionType;
@@ -97,161 +75,194 @@ export interface GetWorkspaceMembersParams {
id: string;
}
-export async function getWorkspaceMembers(
- params: GetWorkspaceDetailParams
-): Promise {
- try {
- return await client.get(`api/workspace/${params.id}/permission`).json();
- } catch (error) {
- sendMessage(messageCode.getMembersFailed);
- throw new RequestError('get members failed', error);
- }
-}
-
export interface CreateWorkspaceParams {
name: string;
}
-export async function createWorkspace(
- encodedYDoc: Blob
-): Promise<{ id: string }> {
- try {
- return await client.post('api/workspace', { body: encodedYDoc }).json();
- } catch (error) {
- sendMessage(messageCode.createWorkspaceFailed);
- throw new RequestError('create workspace failed', error);
- }
-}
-
export interface UpdateWorkspaceParams {
id: string;
public: boolean;
}
-export async function updateWorkspace(
- params: UpdateWorkspaceParams
-): Promise<{ public: boolean | null }> {
- try {
- return await client
- .post(`api/workspace/${params.id}`, {
- json: {
- public: params.public,
- },
- })
- .json();
- } catch (error) {
- sendMessage(messageCode.updateWorkspaceFailed);
- throw new RequestError('update workspace failed', error);
- }
-}
-
export interface DeleteWorkspaceParams {
id: string;
}
-export async function deleteWorkspace(
- params: DeleteWorkspaceParams
-): Promise {
- try {
- await client.delete(`api/workspace/${params.id}`);
- } catch (error) {
- sendMessage(messageCode.deleteWorkspaceFailed);
- throw new RequestError('delete workspace failed', error);
- }
-}
-
export interface InviteMemberParams {
id: string;
email: string;
}
-/**
- * Notice: Only support normal(contrast to private) workspace.
- */
-export async function inviteMember(params: InviteMemberParams): Promise {
- try {
- await client.post(`api/workspace/${params.id}/permission`, {
- json: {
- email: params.email,
- },
- });
- } catch (error) {
- sendMessage(messageCode.inviteMemberFailed);
- throw new RequestError('invite member failed', error);
- }
-}
-
export interface RemoveMemberParams {
permissionId: number;
}
-export async function removeMember(params: RemoveMemberParams): Promise {
- try {
- await client.delete(`api/permission/${params.permissionId}`);
- } catch (error) {
- sendMessage(messageCode.removeMemberFailed);
- throw new RequestError('remove member failed', error);
- }
-}
-
export interface AcceptInvitingParams {
invitingCode: string;
}
-export async function acceptInviting(
- params: AcceptInvitingParams
-): Promise {
- try {
- return await bareClient
- .post(`api/invitation/${params.invitingCode}`)
- .json();
- } catch (error) {
- sendMessage(messageCode.acceptInvitingFailed);
- throw new RequestError('accept inviting failed', error);
- }
-}
-
-export async function uploadBlob(params: { blob: Blob }): Promise {
- return client.put('api/blob', { body: params.blob }).text();
-}
-
-export async function getBlob(params: {
- blobId: string;
-}): Promise {
- try {
- return await client.get(`api/blob/${params.blobId}`).arrayBuffer();
- } catch (error) {
- sendMessage(messageCode.getBlobFailed);
- throw new RequestError('get blob failed', error);
- }
-}
-
export interface LeaveWorkspaceParams {
id: number | string;
}
-export async function leaveWorkspace({ id }: LeaveWorkspaceParams) {
- try {
- await client.delete(`api/workspace/${id}/permission`);
- } catch (error) {
- sendMessage(messageCode.leaveWorkspaceFailed);
- throw new RequestError('leave workspace failed', error);
- }
-}
+export function createWorkspaceApis(
+ bareClient: KyInstance,
+ authClient: KyInstance,
+ googleAuth: GoogleAuth
+) {
+ return {
+ getWorkspaces: async (): Promise => {
+ try {
+ return await authClient
+ .get('api/workspace', {
+ headers: {
+ 'Cache-Control': 'no-cache',
+ },
+ })
+ .json();
+ } catch (error) {
+ sendMessage(messageCode.loadListFailed);
+ throw new RequestError('load list failed', error);
+ }
+ },
+ getWorkspaceDetail: async (
+ params: GetWorkspaceDetailParams
+ ): Promise => {
+ try {
+ return await authClient.get(`api/workspace/${params.id}`).json();
+ } catch (error) {
+ sendMessage(messageCode.getDetailFailed);
+ throw new RequestError('get detail failed', error);
+ }
+ },
+ getWorkspaceMembers: async (
+ params: GetWorkspaceDetailParams
+ ): Promise => {
+ try {
+ return await authClient
+ .get(`api/workspace/${params.id}/permission`)
+ .json();
+ } catch (error) {
+ sendMessage(messageCode.getMembersFailed);
+ throw new RequestError('get members failed', error);
+ }
+ },
+ createWorkspace: async (encodedYDoc: Blob): Promise<{ id: string }> => {
+ try {
+ return await authClient
+ .post('api/workspace', { body: encodedYDoc })
+ .json();
+ } catch (error) {
+ sendMessage(messageCode.createWorkspaceFailed);
+ throw new RequestError('create workspace failed', error);
+ }
+ },
+ updateWorkspace: async (
+ params: UpdateWorkspaceParams
+ ): Promise<{ public: boolean | null }> => {
+ try {
+ return await authClient
+ .post(`api/workspace/${params.id}`, {
+ json: {
+ public: params.public,
+ },
+ })
+ .json();
+ } catch (error) {
+ sendMessage(messageCode.updateWorkspaceFailed);
+ throw new RequestError('update workspace failed', error);
+ }
+ },
+ deleteWorkspace: async (params: DeleteWorkspaceParams): Promise => {
+ try {
+ await authClient.delete(`api/workspace/${params.id}`);
+ } catch (error) {
+ sendMessage(messageCode.deleteWorkspaceFailed);
+ throw new RequestError('delete workspace failed', error);
+ }
+ },
-export async function downloadWorkspace(
- workspaceId: string,
- published = false
-): Promise {
- try {
- if (published) {
- return await bareClient
- .get(`api/public/doc/${workspaceId}`)
- .arrayBuffer();
- }
- return await client.get(`api/workspace/${workspaceId}/doc`).arrayBuffer();
- } catch (error) {
- sendMessage(messageCode.downloadWorkspaceFailed);
- throw new RequestError('download workspace failed', error);
- }
+ /**
+ * Notice: Only support normal(contrast to private) workspace.
+ */
+ inviteMember: async (params: InviteMemberParams): Promise => {
+ try {
+ await authClient.post(`api/workspace/${params.id}/permission`, {
+ json: {
+ email: params.email,
+ },
+ });
+ } catch (error) {
+ sendMessage(messageCode.inviteMemberFailed);
+ throw new RequestError('invite member failed', error);
+ }
+ },
+ removeMember: async (params: RemoveMemberParams): Promise => {
+ try {
+ await authClient.delete(`api/permission/${params.permissionId}`);
+ } catch (error) {
+ sendMessage(messageCode.removeMemberFailed);
+ throw new RequestError('remove member failed', error);
+ }
+ },
+ acceptInviting: async (
+ params: AcceptInvitingParams
+ ): Promise => {
+ try {
+ return await bareClient
+ .post(`api/invitation/${params.invitingCode}`)
+ .json();
+ } catch (error) {
+ sendMessage(messageCode.acceptInvitingFailed);
+ throw new RequestError('accept inviting failed', error);
+ }
+ },
+ uploadBlob: async (params: { blob: Blob }): Promise => {
+ return authClient.put('api/blob', { body: params.blob }).text();
+ },
+ getBlob: async (params: { blobId: string }): Promise => {
+ try {
+ return await authClient.get(`api/blob/${params.blobId}`).arrayBuffer();
+ } catch (error) {
+ sendMessage(messageCode.getBlobFailed);
+ throw new RequestError('get blob failed', error);
+ }
+ },
+ leaveWorkspace: async ({ id }: LeaveWorkspaceParams) => {
+ try {
+ await authClient.delete(`api/workspace/${id}/permission`);
+ } catch (error) {
+ sendMessage(messageCode.leaveWorkspaceFailed);
+ throw new RequestError('leave workspace failed', error);
+ }
+ },
+ downloadWorkspace: async (
+ workspaceId: string,
+ published = false
+ ): Promise => {
+ try {
+ if (published) {
+ return await bareClient
+ .get(`api/public/doc/${workspaceId}`)
+ .arrayBuffer();
+ }
+ return await authClient
+ .get(`api/workspace/${workspaceId}/doc`)
+ .arrayBuffer();
+ } catch (error) {
+ sendMessage(messageCode.downloadWorkspaceFailed);
+ throw new RequestError('download workspace failed', error);
+ }
+ },
+ createBlockSuiteWorkspaceWithAuth: async (newWorkspaceId: string) => {
+ if (googleAuth.isExpired && googleAuth.isLogin) {
+ await googleAuth.refreshToken();
+ }
+ return _createBlocksuiteWorkspace(newWorkspaceId, {
+ blobOptionsGetter: (k: string) =>
+ // token could be expired
+ ({ api: '/api/workspace', token: googleAuth.token }[k]),
+ });
+ },
+ } as const;
}
diff --git a/packages/data-center/src/provider/affine/channel.ts b/packages/data-center/src/provider/affine/channel.ts
index ac779f6680..3f301e3736 100644
--- a/packages/data-center/src/provider/affine/channel.ts
+++ b/packages/data-center/src/provider/affine/channel.ts
@@ -2,7 +2,7 @@ import * as url from 'lib0/url';
import * as websocket from 'lib0/websocket';
import { Logger } from '../../types';
-import { auth } from './apis/auth';
+import { GoogleAuth } from './apis/google';
const RECONNECT_INTERVAL_TIME = 500;
const MAX_RECONNECT_TIMES = 50;
@@ -11,9 +11,11 @@ export class WebsocketClient extends websocket.WebsocketClient {
public shouldReconnect = false;
private _logger: Logger;
private _retryTimes = 0;
+ private _auth: GoogleAuth;
constructor(
serverUrl: string,
logger: Logger,
+ auth: GoogleAuth,
options?: ConstructorParameters[1] & {
params: Record;
}
@@ -27,6 +29,7 @@ export class WebsocketClient extends websocket.WebsocketClient {
const newUrl =
serverUrl + '/' + (encodedParams.length === 0 ? '' : '?' + encodedParams);
super(newUrl, options);
+ this._auth = auth;
this._logger = logger;
this._setupChannel();
}
@@ -41,7 +44,7 @@ export class WebsocketClient extends websocket.WebsocketClient {
this.on('disconnect', ({ error }: { error: Error }) => {
if (error) {
// Try reconnect if connect error has occurred
- if (this.shouldReconnect && auth.isLogin && !this.connected) {
+ if (this.shouldReconnect && this._auth.isLogin && !this.connected) {
try {
setTimeout(() => {
if (this._retryTimes <= MAX_RECONNECT_TIMES) {
diff --git a/packages/data-center/src/provider/affine/utils.ts b/packages/data-center/src/provider/affine/utils.ts
index 2c5f96f279..5a7c695cbf 100644
--- a/packages/data-center/src/provider/affine/utils.ts
+++ b/packages/data-center/src/provider/affine/utils.ts
@@ -1,29 +1,16 @@
-import { createBlocksuiteWorkspace as _createBlocksuiteWorkspace } from '../../utils';
import { applyUpdate } from '../../utils';
import type { WorkspaceUnitCtorParams } from '../../workspace-unit';
import { WorkspaceUnit } from '../../workspace-unit';
import { setDefaultAvatar } from '../utils';
import type { Apis } from './apis';
-import { auth } from './apis/auth';
import { getDatabase } from './idb-kv';
-export const createBlocksuiteWorkspaceWithAuth = async (id: string) => {
- if (auth.isExpired && auth.isLogin) {
- await auth.refreshToken();
- }
- return _createBlocksuiteWorkspace(id, {
- blobOptionsGetter: (k: string) =>
- // token could be expired
- ({ api: '/api/workspace', token: auth.token }[k]),
- });
-};
-
export const loadWorkspaceUnit = async (
params: WorkspaceUnitCtorParams,
apis: Apis
) => {
const workspaceUnit = new WorkspaceUnit(params);
- const blocksuiteWorkspace = await createBlocksuiteWorkspaceWithAuth(
+ const blocksuiteWorkspace = await apis.createBlockSuiteWorkspaceWithAuth(
workspaceUnit.id
);
@@ -54,10 +41,13 @@ export const loadWorkspaceUnit = async (
return workspaceUnit;
};
-export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
+export const createWorkspaceUnit = async (
+ params: WorkspaceUnitCtorParams,
+ apis: Apis
+) => {
const workspaceUnit = new WorkspaceUnit(params);
- const blocksuiteWorkspace = await createBlocksuiteWorkspaceWithAuth(
+ const blocksuiteWorkspace = await apis.createBlockSuiteWorkspaceWithAuth(
workspaceUnit.id
);