mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-23 05:13:12 +03:00
feat: workspace list by provider
This commit is contained in:
parent
4b5e209ecc
commit
43e52fffe7
@ -46,8 +46,11 @@ export class DataCenter {
|
||||
this._providers.set(provider.id, provider);
|
||||
}
|
||||
|
||||
private async _getProvider(id: string, providerId: string): Promise<string> {
|
||||
const providerKey = `workspace:${id}:provider`;
|
||||
private async _getProvider(
|
||||
id: string,
|
||||
providerId = 'local'
|
||||
): Promise<string> {
|
||||
const providerKey = `${id}:provider`;
|
||||
if (this._providers.has(providerId)) {
|
||||
await this._config.set(providerKey, providerId);
|
||||
return providerId;
|
||||
@ -58,21 +61,32 @@ export class DataCenter {
|
||||
throw Error(`Provider ${providerId} not found`);
|
||||
}
|
||||
|
||||
private async _getWorkspace(id: string, pid: string): Promise<BaseProvider> {
|
||||
this._logger(`Init workspace ${id} with ${pid}`);
|
||||
private async _getWorkspace(
|
||||
id: string,
|
||||
params: LoadConfig
|
||||
): Promise<BaseProvider> {
|
||||
this._logger(`Init workspace ${id} with ${params.providerId}`);
|
||||
|
||||
const providerId = await this._getProvider(id, pid);
|
||||
const providerId = await this._getProvider(id, params.providerId);
|
||||
|
||||
// init workspace & register block schema
|
||||
const workspace = new Workspace({ room: id }).register(BlockSchema);
|
||||
|
||||
const Provider = this._providers.get(providerId);
|
||||
assert(Provider);
|
||||
const provider = new Provider();
|
||||
|
||||
// initial configurator
|
||||
const config = getKVConfigure(`workspace:${id}`);
|
||||
// set workspace configs
|
||||
const values = Object.entries(params.config || {});
|
||||
if (values.length) await config.setMany(values);
|
||||
|
||||
// init data by provider
|
||||
const provider = new Provider();
|
||||
await provider.init({
|
||||
apis: this._apis,
|
||||
config: getKVConfigure(id),
|
||||
config,
|
||||
globalConfig: getKVConfigure(`provider:${providerId}`),
|
||||
debug: this._logger.enabled,
|
||||
logger: this._logger.extend(`${Provider.id}:${id}`),
|
||||
workspace,
|
||||
@ -83,27 +97,22 @@ export class DataCenter {
|
||||
return provider;
|
||||
}
|
||||
|
||||
private async _setConfig(workspace: string, config: Record<string, any>) {
|
||||
const values = Object.entries(config);
|
||||
if (values.length) {
|
||||
const configure = getKVConfigure(workspace);
|
||||
await configure.setMany(values);
|
||||
}
|
||||
}
|
||||
|
||||
// load workspace data to memory
|
||||
/**
|
||||
* load workspace data to memory
|
||||
* @param workspaceId workspace id
|
||||
* @param config.providerId provider id
|
||||
* @param config.config provider config
|
||||
* @returns Workspace instance
|
||||
*/
|
||||
async load(
|
||||
workspaceId: string,
|
||||
params: LoadConfig = {}
|
||||
): Promise<Workspace | null> {
|
||||
const { providerId = 'local', config = {} } = params;
|
||||
if (workspaceId) {
|
||||
if (!this._workspaces.has(workspaceId)) {
|
||||
this._workspaces.set(
|
||||
workspaceId,
|
||||
this._setConfig(workspaceId, config).then(() =>
|
||||
this._getWorkspace(workspaceId, providerId)
|
||||
)
|
||||
this._getWorkspace(workspaceId, params)
|
||||
);
|
||||
}
|
||||
const workspace = this._workspaces.get(workspaceId);
|
||||
@ -113,7 +122,10 @@ export class DataCenter {
|
||||
return null;
|
||||
}
|
||||
|
||||
// destroy workspace's instance in memory
|
||||
/**
|
||||
* destroy workspace's instance in memory
|
||||
* @param workspaceId workspace id
|
||||
*/
|
||||
async destroy(workspaceId: string) {
|
||||
const provider = await this._workspaces.get(workspaceId);
|
||||
if (provider) {
|
||||
@ -122,7 +134,13 @@ export class DataCenter {
|
||||
}
|
||||
}
|
||||
|
||||
// reload new workspace instance to memory to refresh config
|
||||
/**
|
||||
* reload new workspace instance to memory to refresh config
|
||||
* @param workspaceId workspace id
|
||||
* @param config.providerId provider id
|
||||
* @param config.config provider config
|
||||
* @returns Workspace instance
|
||||
*/
|
||||
async reload(
|
||||
workspaceId: string,
|
||||
config: LoadConfig = {}
|
||||
@ -131,16 +149,34 @@ export class DataCenter {
|
||||
return this.load(workspaceId, config);
|
||||
}
|
||||
|
||||
async list() {
|
||||
const keys = await this._config.keys();
|
||||
return keys
|
||||
.filter(k => k.startsWith('workspace:'))
|
||||
.map(k => k.split(':')[1]);
|
||||
/**
|
||||
* get workspace list
|
||||
*/
|
||||
async list(): Promise<Record<string, Record<string, boolean>>> {
|
||||
const lists = await Promise.all(
|
||||
Array.from(this._providers.entries()).map(([providerId, provider]) =>
|
||||
provider
|
||||
.list(getKVConfigure(`provider:${providerId}`))
|
||||
.then(list => [providerId, list || []] as const)
|
||||
)
|
||||
);
|
||||
|
||||
return lists.reduce((ret, [providerId, list]) => {
|
||||
for (const [item, isLocal] of list) {
|
||||
const workspace = ret[item] || {};
|
||||
workspace[providerId] = isLocal;
|
||||
ret[item] = workspace;
|
||||
}
|
||||
return ret;
|
||||
}, {} as Record<string, Record<string, boolean>>);
|
||||
}
|
||||
|
||||
// delete local workspace's data
|
||||
/**
|
||||
* delete local workspace's data
|
||||
* @param workspaceId workspace id
|
||||
*/
|
||||
async delete(workspaceId: string) {
|
||||
await this._config.delete(`workspace:${workspaceId}:provider`);
|
||||
await this._config.delete(`${workspaceId}:provider`);
|
||||
const provider = await this._workspaces.get(workspaceId);
|
||||
if (provider) {
|
||||
this._workspaces.delete(workspaceId);
|
||||
@ -149,9 +185,11 @@ export class DataCenter {
|
||||
}
|
||||
}
|
||||
|
||||
// clear all local workspace's data
|
||||
/**
|
||||
* clear all local workspace's data
|
||||
*/
|
||||
async clear() {
|
||||
const workspaces = await this.list();
|
||||
await Promise.all(workspaces.map(id => this.delete(id)));
|
||||
await Promise.all(Object.keys(workspaces).map(id => this.delete(id)));
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ export class BaseProvider {
|
||||
static id = 'base';
|
||||
protected _apis!: Readonly<Apis>;
|
||||
protected _config!: Readonly<ConfigStore>;
|
||||
protected _globalConfig!: Readonly<ConfigStore>;
|
||||
protected _logger!: Logger;
|
||||
protected _workspace!: Workspace;
|
||||
|
||||
@ -14,9 +15,14 @@ export class BaseProvider {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return (this.constructor as any).id;
|
||||
}
|
||||
|
||||
async init(params: InitialParams) {
|
||||
this._apis = params.apis;
|
||||
this._config = params.config;
|
||||
this._globalConfig = params.globalConfig;
|
||||
this._logger = params.logger;
|
||||
this._workspace = params.workspace;
|
||||
this._logger.enabled = params.debug;
|
||||
@ -48,4 +54,12 @@ export class BaseProvider {
|
||||
get workspace() {
|
||||
return this._workspace;
|
||||
}
|
||||
|
||||
// get workspace list,return a map of workspace id and boolean
|
||||
// if value is true, it exists locally, otherwise it does not exist locally
|
||||
static async list(
|
||||
_config: Readonly<ConfigStore>
|
||||
): Promise<Map<string, boolean> | undefined> {
|
||||
throw Error('Not implemented: list');
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ export type Logger = ReturnType<typeof getLogger>;
|
||||
|
||||
export type InitialParams = {
|
||||
apis: Apis;
|
||||
config: ConfigStore;
|
||||
config: Readonly<ConfigStore>;
|
||||
globalConfig: Readonly<ConfigStore>;
|
||||
debug: boolean;
|
||||
logger: Logger;
|
||||
workspace: Workspace;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { BlobStorage } from '@blocksuite/store';
|
||||
import assert from 'assert';
|
||||
|
||||
import type { InitialParams } from '../index.js';
|
||||
import type { ConfigStore, InitialParams } from '../index.js';
|
||||
import { BaseProvider } from '../base.js';
|
||||
import { IndexedDBProvider } from './indexeddb.js';
|
||||
|
||||
@ -31,12 +31,15 @@ export class LocalProvider extends BaseProvider {
|
||||
|
||||
await this._idb.whenSynced;
|
||||
this._logger('Local data loaded');
|
||||
|
||||
await this._globalConfig.set(this._workspace.room, true);
|
||||
}
|
||||
|
||||
async clear() {
|
||||
await super.clear();
|
||||
await this._blobs.clear();
|
||||
await this._idb?.clearData();
|
||||
await this._globalConfig.delete(this._workspace.room!);
|
||||
}
|
||||
|
||||
async destroy(): Promise<void> {
|
||||
@ -51,4 +54,11 @@ export class LocalProvider extends BaseProvider {
|
||||
async setBlob(blob: Blob): Promise<string> {
|
||||
return this._blobs.set(blob);
|
||||
}
|
||||
|
||||
static async list(
|
||||
config: Readonly<ConfigStore<boolean>>
|
||||
): Promise<Map<string, boolean> | undefined> {
|
||||
const entries = await config.entries();
|
||||
return new Map(entries);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,20 @@
|
||||
import { createStore, del, get, keys, set, setMany, clear } from 'idb-keyval';
|
||||
import {
|
||||
createStore,
|
||||
del,
|
||||
get,
|
||||
keys,
|
||||
set,
|
||||
setMany,
|
||||
clear,
|
||||
entries,
|
||||
} from 'idb-keyval';
|
||||
|
||||
export type ConfigStore<T = any> = {
|
||||
get: (key: string) => Promise<T | undefined>;
|
||||
set: (key: string, value: T) => Promise<void>;
|
||||
setMany: (values: [string, T][]) => Promise<void>;
|
||||
keys: () => Promise<string[]>;
|
||||
entries: () => Promise<[string, T][]>;
|
||||
delete: (key: string) => Promise<void>;
|
||||
clear: () => Promise<void>;
|
||||
};
|
||||
@ -16,6 +26,7 @@ const initialIndexedDB = <T = any>(database: string): ConfigStore<T> => {
|
||||
set: (key: string, value: T) => set(key, value, store),
|
||||
setMany: (values: [string, T][]) => setMany(values, store),
|
||||
keys: () => keys(store),
|
||||
entries: () => entries(store),
|
||||
delete: (key: string) => del(key, store),
|
||||
clear: () => clear(store),
|
||||
};
|
||||
@ -27,9 +38,10 @@ const scopedIndexedDB = () => {
|
||||
|
||||
return <T = any>(scope: string): Readonly<ConfigStore<T>> => {
|
||||
if (!storeCache.has(scope)) {
|
||||
const prefix = `${scope}:`;
|
||||
const store = {
|
||||
get: async (key: string) => idb.get(`${scope}:${key}`),
|
||||
set: (key: string, value: T) => idb.set(`${scope}:${key}`, value),
|
||||
get: async (key: string) => idb.get(prefix + key),
|
||||
set: (key: string, value: T) => idb.set(prefix + key, value),
|
||||
setMany: (values: [string, T][]) =>
|
||||
idb.setMany(values.map(([k, v]) => [`${scope}:${k}`, v])),
|
||||
keys: () =>
|
||||
@ -37,16 +49,24 @@ const scopedIndexedDB = () => {
|
||||
.keys()
|
||||
.then(keys =>
|
||||
keys
|
||||
.filter(k => k.startsWith(`${scope}:`))
|
||||
.map(k => k.replace(`${scope}:`, ''))
|
||||
.filter(k => k.startsWith(prefix))
|
||||
.map(k => k.slice(prefix.length))
|
||||
),
|
||||
delete: (key: string) => idb.delete(`${scope}:${key}`),
|
||||
entries: () =>
|
||||
idb
|
||||
.entries()
|
||||
.then(entries =>
|
||||
entries
|
||||
.filter(([k]) => k.startsWith(prefix))
|
||||
.map(([k, v]) => [k.slice(prefix.length), v] as [string, T])
|
||||
),
|
||||
delete: (key: string) => idb.delete(prefix + key),
|
||||
clear: async () => {
|
||||
await idb
|
||||
.keys()
|
||||
.then(keys =>
|
||||
Promise.all(
|
||||
keys.filter(k => k.startsWith(`${scope}:`)).map(k => del(k))
|
||||
keys.filter(k => k.startsWith(prefix)).map(k => del(k))
|
||||
)
|
||||
);
|
||||
},
|
||||
|
@ -56,12 +56,20 @@ test('list workspaces', async () => {
|
||||
dataCenter.load('test6'),
|
||||
]);
|
||||
|
||||
expect(await dataCenter.list()).toStrictEqual([
|
||||
'test3',
|
||||
'test4',
|
||||
'test5',
|
||||
'test6',
|
||||
]);
|
||||
expect(await dataCenter.list()).toStrictEqual({
|
||||
test3: { local: true },
|
||||
test4: { local: true },
|
||||
test5: { local: true },
|
||||
test6: { local: true },
|
||||
});
|
||||
|
||||
await dataCenter.reload('test3', { providerId: 'affine' });
|
||||
expect(await dataCenter.list()).toStrictEqual({
|
||||
test3: { affine: true, local: true },
|
||||
test4: { local: true },
|
||||
test5: { local: true },
|
||||
test6: { local: true },
|
||||
});
|
||||
});
|
||||
|
||||
test('destroy workspaces', async () => {
|
||||
@ -87,5 +95,5 @@ test('remove workspaces', async () => {
|
||||
// remove workspace will remove workspace data
|
||||
await Promise.all([dataCenter.load('test9'), dataCenter.load('test10')]);
|
||||
await dataCenter.delete('test9');
|
||||
expect(await dataCenter.list()).toStrictEqual(['test10']);
|
||||
expect(await dataCenter.list()).toStrictEqual({ test10: { local: true } });
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user