Skip to content

Commit

Permalink
feat(core): desktop multiple server support
Browse files Browse the repository at this point in the history
  • Loading branch information
EYHN committed Dec 2, 2024
1 parent f239857 commit 6ea92f2
Show file tree
Hide file tree
Showing 135 changed files with 1,923 additions and 1,549 deletions.
6 changes: 6 additions & 0 deletions packages/backend/server/src/core/config/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export class PasswordLimitsType {
export class CredentialsRequirementType {
@Field()
password!: PasswordLimitsType;

@Field()
email!: boolean;
}

registerEnumType(RuntimeConfigType, {
Expand Down Expand Up @@ -108,11 +111,14 @@ export class ServerConfigResolver {
'auth/password.min': true,
});

const isMailerAvailable = this.config.mailer.host !== undefined;

return {
password: {
minLength: config['auth/password.min'],
maxLength: config['auth/password.max'],
},
email: isMailerAvailable,
};
}

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { createApp } from './app';
import { URLHelper } from './fundamentals';

const app = await createApp();
const listeningHost = AFFiNE.deploy ? '0.0.0.0' : 'localhost';
const listeningHost = '0.0.0.0';
await app.listen(AFFiNE.server.port, listeningHost);
const url = app.get(URLHelper);

Expand Down
1 change: 1 addition & 0 deletions packages/backend/server/src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ input CreateUserInput {
}

type CredentialsRequirementType {
email: Boolean!
password: PasswordLimitsType!
}

Expand Down
7 changes: 0 additions & 7 deletions packages/common/env/src/workspace.ts

This file was deleted.

22 changes: 12 additions & 10 deletions packages/common/infra/src/framework/core/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,33 @@ export class EventBus {
});

for (const handler of handlers.values()) {
this.on(handler.event.id, handler.handler);
this.on(handler.event, handler.handler);
}
}

get root(): EventBus {
return this.parent?.root ?? this;
}

on<T>(id: string, listener: (event: FrameworkEvent<T>) => void) {
if (!this.listeners[id]) {
this.listeners[id] = [];
on<T>(event: FrameworkEvent<T>, listener: (event: T) => void) {
if (!this.listeners[event.id]) {
this.listeners[event.id] = [];
}
this.listeners[id].push(listener);
const off = this.parent?.on(id, listener);
this.listeners[event.id].push(listener);
const off = this.parent?.on(event, listener);
return () => {
this.off(id, listener);
this.off(event, listener);
off?.();
};
}

off<T>(id: string, listener: (event: FrameworkEvent<T>) => void) {
if (!this.listeners[id]) {
off<T>(event: FrameworkEvent<T>, listener: (event: T) => void) {
if (!this.listeners[event.id]) {
return;
}
this.listeners[id] = this.listeners[id].filter(l => l !== listener);
this.listeners[event.id] = this.listeners[event.id].filter(
l => l !== listener
);
}

emit<T>(event: FrameworkEvent<T>, payload: T) {
Expand Down
9 changes: 9 additions & 0 deletions packages/common/infra/src/modules/feature-flag/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,15 @@ export const AFFINE_FLAGS = {
configurable: false,
defaultState: isMobile,
},
enable_multiple_cloud_servers: {
category: 'affine',
displayName:
'com.affine.settings.workspace.experimental-features.enable-multiple-cloud-servers.name',
description:
'com.affine.settings.workspace.experimental-features.enable-multiple-cloud-servers.description',
configurable: isDesktopEnvironment,
defaultState: false,
},
} satisfies { [key in string]: FlagInfo };

export type AFFINE_FLAGS = typeof AFFINE_FLAGS;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export class GlobalContext extends Entity {
memento = new MemoryMemento();

workspaceId = this.define<string>('workspaceId');
workspaceFlavour = this.define<string>('workspaceFlavour');

/**
* is in doc page
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { WorkspaceFlavour } from '@affine/env/workspace';
import { describe, expect, test } from 'vitest';

import { Framework } from '../../../framework';
Expand All @@ -22,7 +21,7 @@ describe('Workspace System', () => {
expect(workspaceService.list.workspaces$.value.length).toBe(0);

const workspace = workspaceService.open({
metadata: await workspaceService.create(WorkspaceFlavour.LOCAL),
metadata: await workspaceService.create('local'),
});

expect(workspace.workspace).toBeInstanceOf(Workspace);
Expand Down
45 changes: 29 additions & 16 deletions packages/common/infra/src/modules/workspace/entities/list.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
import { combineLatest, map, of, switchMap } from 'rxjs';

import { Entity } from '../../../framework';
import { LiveData } from '../../../livedata';
import type { WorkspaceFlavourProvider } from '../providers/flavour';
import type { WorkspaceMetadata } from '../metadata';
import type { WorkspaceFlavoursService } from '../services/flavours';

export class WorkspaceList extends Entity {
workspaces$ = new LiveData(this.providers.map(p => p.workspaces$))
.map(v => {
return v;
})
.flat()
.map(workspaces => {
return workspaces.flat();
});
isRevalidating$ = new LiveData(
this.providers.map(p => p.isRevalidating$ ?? new LiveData(false))
)
.flat()
.map(isLoadings => isLoadings.some(isLoading => isLoading));
workspaces$ = LiveData.from<WorkspaceMetadata[]>(
this.flavoursService.flavours$.pipe(
switchMap(flavours =>
combineLatest(flavours.map(flavour => flavour.workspaces$)).pipe(
map(workspaces => workspaces.flat())
)
)
),
[]
);

isRevalidating$ = LiveData.from<boolean>(
this.flavoursService.flavours$.pipe(
switchMap(flavours =>
combineLatest(
flavours.map(flavour => flavour.isRevalidating$ ?? of(false))
).pipe(map(isLoadings => isLoadings.some(isLoading => isLoading)))
)
),
false
);

workspace$(id: string) {
return this.workspaces$.map(workspaces =>
workspaces.find(workspace => workspace.id === id)
);
}

constructor(private readonly providers: WorkspaceFlavourProvider[]) {
constructor(private readonly flavoursService: WorkspaceFlavoursService) {
super();
}

revalidate() {
this.providers.forEach(provider => provider.revalidate?.());
this.flavoursService.flavours$.value.forEach(provider => {
provider.revalidate?.();
});
}

waitForRevalidation(signal?: AbortSignal) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '../../../livedata';
import type { WorkspaceMetadata } from '../metadata';
import type { WorkspaceFlavourProvider } from '../providers/flavour';
import type { WorkspaceFlavoursService } from '../services/flavours';
import type { WorkspaceProfileCacheStore } from '../stores/profile-cache';
import type { Workspace } from './workspace';

Expand Down Expand Up @@ -47,12 +48,14 @@ export class WorkspaceProfile extends Entity<{ metadata: WorkspaceMetadata }> {

constructor(
private readonly cache: WorkspaceProfileCacheStore,
providers: WorkspaceFlavourProvider[]
flavoursService: WorkspaceFlavoursService
) {
super();

this.provider =
providers.find(p => p.flavour === this.props.metadata.flavour) ?? null;
flavoursService.flavours$.value.find(
p => p.flavour === this.props.metadata.flavour
) ?? null;
}

private setProfile(info: WorkspaceProfileInfo) {
Expand Down
25 changes: 14 additions & 11 deletions packages/common/infra/src/modules/workspace/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export { getAFFiNEWorkspaceSchema } from './global-schema';
export type { WorkspaceMetadata } from './metadata';
export type { WorkspaceOpenOptions } from './open-options';
export type { WorkspaceEngineProvider } from './providers/flavour';
export { WorkspaceFlavourProvider } from './providers/flavour';
export type { WorkspaceFlavourProvider } from './providers/flavour';
export { WorkspaceFlavoursProvider } from './providers/flavour';
export { WorkspaceLocalCache, WorkspaceLocalState } from './providers/storage';
export { WorkspaceScope } from './scopes/workspace';
export { WorkspaceService } from './services/workspace';
Expand All @@ -21,48 +22,50 @@ import {
WorkspaceLocalCacheImpl,
WorkspaceLocalStateImpl,
} from './impls/storage';
import { WorkspaceFlavourProvider } from './providers/flavour';
import { WorkspaceFlavoursProvider } from './providers/flavour';
import { WorkspaceLocalCache, WorkspaceLocalState } from './providers/storage';
import { WorkspaceScope } from './scopes/workspace';
import { WorkspaceDestroyService } from './services/destroy';
import { WorkspaceEngineService } from './services/engine';
import { WorkspaceFactoryService } from './services/factory';
import { WorkspaceFlavoursService } from './services/flavours';
import { WorkspaceListService } from './services/list';
import { WorkspaceProfileService } from './services/profile';
import { WorkspaceRepositoryService } from './services/repo';
import { WorkspaceTransformService } from './services/transform';
import { WorkspaceService } from './services/workspace';
import { WorkspacesService } from './services/workspaces';
import { WorkspaceProfileCacheStore } from './stores/profile-cache';
import { TestingWorkspaceLocalProvider } from './testing/testing-provider';
import { TestingWorkspaceFlavoursProvider } from './testing/testing-provider';

export function configureWorkspaceModule(framework: Framework) {
framework
.service(WorkspacesService, [
[WorkspaceFlavourProvider],
WorkspaceFlavoursService,
WorkspaceListService,
WorkspaceProfileService,
WorkspaceTransformService,
WorkspaceRepositoryService,
WorkspaceFactoryService,
WorkspaceDestroyService,
])
.service(WorkspaceDestroyService, [[WorkspaceFlavourProvider]])
.service(WorkspaceFlavoursService, [[WorkspaceFlavoursProvider]])
.service(WorkspaceDestroyService, [WorkspaceFlavoursService])
.service(WorkspaceListService)
.entity(WorkspaceList, [[WorkspaceFlavourProvider]])
.entity(WorkspaceList, [WorkspaceFlavoursService])
.service(WorkspaceProfileService)
.store(WorkspaceProfileCacheStore, [GlobalCache])
.entity(WorkspaceProfile, [
WorkspaceProfileCacheStore,
[WorkspaceFlavourProvider],
WorkspaceFlavoursService,
])
.service(WorkspaceFactoryService, [[WorkspaceFlavourProvider]])
.service(WorkspaceFactoryService, [WorkspaceFlavoursService])
.service(WorkspaceTransformService, [
WorkspaceFactoryService,
WorkspaceDestroyService,
])
.service(WorkspaceRepositoryService, [
[WorkspaceFlavourProvider],
WorkspaceFlavoursService,
WorkspaceProfileService,
])
.scope(WorkspaceScope)
Expand All @@ -82,8 +85,8 @@ export function configureWorkspaceModule(framework: Framework) {

export function configureTestingWorkspaceProvider(framework: Framework) {
framework.impl(
WorkspaceFlavourProvider('LOCAL'),
TestingWorkspaceLocalProvider,
WorkspaceFlavoursProvider('LOCAL'),
TestingWorkspaceFlavoursProvider,
[GlobalState]
);
}
4 changes: 1 addition & 3 deletions packages/common/infra/src/modules/workspace/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { WorkspaceFlavour } from '@affine/env/workspace';

export type WorkspaceMetadata = {
id: string;
flavour: WorkspaceFlavour;
flavour: string;
initialized?: boolean;
};
11 changes: 7 additions & 4 deletions packages/common/infra/src/modules/workspace/providers/flavour.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { WorkspaceFlavour } from '@affine/env/workspace';
import type { DocCollection } from '@blocksuite/affine/store';

import { createIdentifier } from '../../../framework';
Expand All @@ -22,7 +21,7 @@ export interface WorkspaceEngineProvider {
}

export interface WorkspaceFlavourProvider {
flavour: WorkspaceFlavour;
flavour: string;

deleteWorkspace(id: string): Promise<void>;

Expand Down Expand Up @@ -60,5 +59,9 @@ export interface WorkspaceFlavourProvider {
onWorkspaceInitialized?(workspace: Workspace): void;
}

export const WorkspaceFlavourProvider =
createIdentifier<WorkspaceFlavourProvider>('WorkspaceFlavourProvider');
export interface WorkspaceFlavoursProvider {
workspaceFlavours$: LiveData<WorkspaceFlavourProvider[]>;
}

export const WorkspaceFlavoursProvider =
createIdentifier<WorkspaceFlavoursProvider>('WorkspaceFlavoursProvider');
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Service } from '../../../framework';
import type { WorkspaceMetadata } from '../metadata';
import type { WorkspaceFlavourProvider } from '../providers/flavour';
import type { WorkspaceFlavoursService } from './flavours';

export class WorkspaceDestroyService extends Service {
constructor(private readonly providers: WorkspaceFlavourProvider[]) {
constructor(private readonly flavoursService: WorkspaceFlavoursService) {
super();
}

deleteWorkspace = async (metadata: WorkspaceMetadata) => {
const provider = this.providers.find(p => p.flavour === metadata.flavour);
const provider = this.flavoursService.flavours$.value.find(
p => p.flavour === metadata.flavour
);
if (!provider) {
throw new Error(`Unknown workspace flavour: ${metadata.flavour}`);
}
Expand Down
11 changes: 6 additions & 5 deletions packages/common/infra/src/modules/workspace/services/factory.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { WorkspaceFlavour } from '@affine/env/workspace';
import type { DocCollection } from '@blocksuite/affine/store';

import { Service } from '../../../framework';
import type { BlobStorage, DocStorage } from '../../../sync';
import type { WorkspaceFlavourProvider } from '../providers/flavour';
import type { WorkspaceFlavoursService } from './flavours';

export class WorkspaceFactoryService extends Service {
constructor(private readonly providers: WorkspaceFlavourProvider[]) {
constructor(private readonly flavoursService: WorkspaceFlavoursService) {
super();
}

Expand All @@ -17,14 +16,16 @@ export class WorkspaceFactoryService extends Service {
* @returns workspace id
*/
create = async (
flavour: WorkspaceFlavour,
flavour: string,
initial: (
docCollection: DocCollection,
blobStorage: BlobStorage,
docStorage: DocStorage
) => Promise<void> = () => Promise.resolve()
) => {
const provider = this.providers.find(x => x.flavour === flavour);
const provider = this.flavoursService.flavours$.value.find(
x => x.flavour === flavour
);
if (!provider) {
throw new Error(`Unknown workspace flavour: ${flavour}`);
}
Expand Down
Loading

0 comments on commit 6ea92f2

Please sign in to comment.