feat(core): new worker workspace engine (#9257)

This commit is contained in:
EYHN
2025-01-17 00:22:18 +08:00
committed by GitHub
parent 7dc470e7ea
commit a2ffdb4047
219 changed files with 4267 additions and 7194 deletions

View File

@@ -11,9 +11,7 @@ export { AccountChanged } from './events/account-changed';
export { AccountLoggedIn } from './events/account-logged-in';
export { AccountLoggedOut } from './events/account-logged-out';
export { ServerInitialized } from './events/server-initialized';
export { RawFetchProvider } from './provider/fetch';
export { ValidatorProvider } from './provider/validator';
export { WebSocketAuthProvider } from './provider/websocket-auth';
export { AuthService } from './services/auth';
export { CaptchaService } from './services/captcha';
export { DefaultServerService } from './services/default-server';
@@ -27,7 +25,6 @@ export { SubscriptionService } from './services/subscription';
export { UserCopilotQuotaService } from './services/user-copilot-quota';
export { UserFeatureService } from './services/user-feature';
export { UserQuotaService } from './services/user-quota';
export { WebSocketService } from './services/websocket';
export { WorkspaceInvoicesService } from './services/workspace-invoices';
export { WorkspaceServerService } from './services/workspace-server';
export { WorkspaceSubscriptionService } from './services/workspace-subscription';
@@ -51,9 +48,7 @@ import { UserFeature } from './entities/user-feature';
import { UserQuota } from './entities/user-quota';
import { WorkspaceInvoices } from './entities/workspace-invoices';
import { WorkspaceSubscription } from './entities/workspace-subscription';
import { DefaultRawFetchProvider, RawFetchProvider } from './provider/fetch';
import { ValidatorProvider } from './provider/validator';
import { WebSocketAuthProvider } from './provider/websocket-auth';
import { ServerScope } from './scopes/server';
import { AuthService } from './services/auth';
import { CaptchaService } from './services/captcha';
@@ -69,7 +64,6 @@ import { SubscriptionService } from './services/subscription';
import { UserCopilotQuotaService } from './services/user-copilot-quota';
import { UserFeatureService } from './services/user-feature';
import { UserQuotaService } from './services/user-quota';
import { WebSocketService } from './services/websocket';
import { WorkspaceInvoicesService } from './services/workspace-invoices';
import { WorkspaceServerService } from './services/workspace-server';
import { WorkspaceSubscriptionService } from './services/workspace-subscription';
@@ -85,26 +79,16 @@ import { UserQuotaStore } from './stores/user-quota';
export function configureCloudModule(framework: Framework) {
framework
.impl(RawFetchProvider, DefaultRawFetchProvider)
.service(ServersService, [ServerListStore, ServerConfigStore])
.service(DefaultServerService, [ServersService])
.store(ServerListStore, [GlobalStateService])
.store(ServerConfigStore, [RawFetchProvider])
.store(ServerConfigStore)
.entity(Server, [ServerListStore])
.scope(ServerScope)
.service(ServerService, [ServerScope])
.service(FetchService, [RawFetchProvider, ServerService])
.service(FetchService, [ServerService])
.service(EventSourceService, [ServerService])
.service(GraphQLService, [FetchService])
.service(
WebSocketService,
f =>
new WebSocketService(
f.get(ServerService),
f.get(AuthService),
f.getOptional(WebSocketAuthProvider)
)
)
.service(CaptchaService, f => {
return new CaptchaService(
f.get(ServerService),

View File

@@ -1,17 +0,0 @@
import { createIdentifier } from '@toeverything/infra';
import type { FetchInit } from '../services/fetch';
export interface RawFetchProvider {
/**
* standard fetch, in ios&android, we can use native fetch to implement this
*/
fetch: (input: string | URL, init?: FetchInit) => Promise<Response>;
}
export const RawFetchProvider =
createIdentifier<RawFetchProvider>('FetchProvider');
export const DefaultRawFetchProvider = {
fetch: globalThis.fetch.bind(globalThis),
};

View File

@@ -1,22 +0,0 @@
import { createIdentifier } from '@toeverything/infra';
export interface WebSocketAuthProvider {
/**
* Returns the token and userId for WebSocket authentication
*
* Useful when cookies are not available for WebSocket connections
*
* @param url - The URL of the WebSocket endpoint
*/
getAuthToken: (url: string) => Promise<
| {
token?: string;
userId?: string;
}
| undefined
>;
}
export const WebSocketAuthProvider = createIdentifier<WebSocketAuthProvider>(
'WebSocketAuthProvider'
);

View File

@@ -3,7 +3,6 @@ import { UserFriendlyError } from '@affine/graphql';
import { fromPromise, Service } from '@toeverything/infra';
import { BackendError, NetworkError } from '../error';
import type { RawFetchProvider } from '../provider/fetch';
import type { ServerService } from './server';
const logger = new DebugLogger('affine:fetch');
@@ -11,10 +10,7 @@ const logger = new DebugLogger('affine:fetch');
export type FetchInit = RequestInit & { timeout?: number };
export class FetchService extends Service {
constructor(
private readonly fetchProvider: RawFetchProvider,
private readonly serverService: ServerService
) {
constructor(private readonly serverService: ServerService) {
super();
}
rxFetch = (
@@ -50,7 +46,7 @@ export class FetchService extends Service {
abortController.abort('timeout');
}, timeout);
const res = await this.fetchProvider
const res = await globalThis
.fetch(new URL(input, this.serverService.server.serverMetadata.baseUrl), {
...init,
signal: abortController.signal,

View File

@@ -1,66 +0,0 @@
import { OnEvent, Service } from '@toeverything/infra';
import { Manager } from 'socket.io-client';
import { ApplicationStarted } from '../../lifecycle';
import { AccountChanged } from '../events/account-changed';
import type { WebSocketAuthProvider } from '../provider/websocket-auth';
import type { AuthService } from './auth';
import type { ServerService } from './server';
@OnEvent(AccountChanged, e => e.update)
@OnEvent(ApplicationStarted, e => e.update)
export class WebSocketService extends Service {
ioManager: Manager = new Manager(`${this.serverService.server.baseUrl}/`, {
autoConnect: false,
transports: ['websocket'],
secure: location.protocol === 'https:',
});
socket = this.ioManager.socket('/', {
auth: this.webSocketAuthProvider
? cb => {
this.webSocketAuthProvider
?.getAuthToken(`${this.serverService.server.baseUrl}/`)
.then(v => {
cb(v ?? {});
})
.catch(e => {
console.error('Failed to get auth token for websocket', e);
});
}
: undefined,
});
refCount = 0;
constructor(
private readonly serverService: ServerService,
private readonly authService: AuthService,
private readonly webSocketAuthProvider?: WebSocketAuthProvider
) {
super();
}
/**
* Connect socket, with automatic connect and reconnect logic.
* External code should not call `socket.connect()` or `socket.disconnect()` manually.
* When socket is no longer needed, call `dispose()` to clean up resources.
*/
connect() {
this.refCount++;
this.update();
return {
socket: this.socket,
dispose: () => {
this.refCount--;
this.update();
},
};
}
update(): void {
if (this.authService.session.account$.value && this.refCount > 0) {
this.socket.connect();
} else {
this.socket.disconnect();
}
}
}

View File

@@ -8,13 +8,11 @@ import {
} from '@affine/graphql';
import { Store } from '@toeverything/infra';
import type { RawFetchProvider } from '../provider/fetch';
export type ServerConfigType = ServerConfigQuery['serverConfig'] &
OauthProvidersQuery['serverConfig'];
export class ServerConfigStore extends Store {
constructor(private readonly fetcher: RawFetchProvider) {
constructor() {
super();
}
@@ -22,10 +20,7 @@ export class ServerConfigStore extends Store {
serverBaseUrl: string,
abortSignal?: AbortSignal
): Promise<ServerConfigType> {
const gql = gqlFetcherFactory(
`${serverBaseUrl}/graphql`,
this.fetcher.fetch
);
const gql = gqlFetcherFactory(`${serverBaseUrl}/graphql`, globalThis.fetch);
const serverConfigData = await gql({
query: serverConfigQuery,
context: {