mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 19:02:23 +08:00
fix: old workspace migration (#14403)
fix #14395 #### PR Dependency Tree * **PR #14403** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added ability to enumerate and list local workspaces. * Improved workspace ID persistence with Electron global-state storage, automatic fallback to legacy storage, and one-time migration to consolidate IDs. * **Tests** * Added unit test validating listing behavior (includes/excludes workspaces based on presence of workspace DB file). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -15,6 +15,7 @@ import { WorkspaceSQLiteDB } from '../nbstore/v1/workspace-db-adapter';
|
||||
import type { WorkspaceMeta } from '../type';
|
||||
import {
|
||||
getDeletedWorkspacesBasePath,
|
||||
getSpaceBasePath,
|
||||
getSpaceDBPath,
|
||||
getWorkspaceBasePathV1,
|
||||
getWorkspaceMeta,
|
||||
@@ -96,6 +97,33 @@ export async function storeWorkspaceMeta(
|
||||
}
|
||||
}
|
||||
|
||||
export async function listLocalWorkspaceIds(): Promise<string[]> {
|
||||
const localWorkspaceBasePath = path.join(
|
||||
await getSpaceBasePath('workspace'),
|
||||
'local'
|
||||
);
|
||||
if (!(await fs.pathExists(localWorkspaceBasePath))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const entries = await fs.readdir(localWorkspaceBasePath);
|
||||
const ids = await Promise.all(
|
||||
entries.map(async entry => {
|
||||
const workspacePath = path.join(localWorkspaceBasePath, entry);
|
||||
const stat = await fs.stat(workspacePath).catch(() => null);
|
||||
if (!stat?.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
if (!(await fs.pathExists(path.join(workspacePath, 'storage.db')))) {
|
||||
return null;
|
||||
}
|
||||
return entry;
|
||||
})
|
||||
);
|
||||
|
||||
return ids.filter((id): id is string => typeof id === 'string');
|
||||
}
|
||||
|
||||
type WorkspaceDocMeta = {
|
||||
id: string;
|
||||
name: string;
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
deleteBackupWorkspace,
|
||||
deleteWorkspace,
|
||||
getDeletedWorkspaces,
|
||||
listLocalWorkspaceIds,
|
||||
trashWorkspace,
|
||||
} from './handlers';
|
||||
|
||||
@@ -18,4 +19,5 @@ export const workspaceHandlers = {
|
||||
return getDeletedWorkspaces();
|
||||
},
|
||||
deleteBackupWorkspace: async (id: string) => deleteBackupWorkspace(id),
|
||||
listLocalWorkspaceIds: async () => listLocalWorkspaceIds(),
|
||||
};
|
||||
|
||||
@@ -33,6 +33,43 @@ afterAll(() => {
|
||||
});
|
||||
|
||||
describe('workspace db management', () => {
|
||||
test('list local workspace ids', async () => {
|
||||
const { listLocalWorkspaceIds } =
|
||||
await import('@affine/electron/helper/workspace/handlers');
|
||||
const validWorkspaceId = v4();
|
||||
const noDbWorkspaceId = v4();
|
||||
const fileEntry = 'README.txt';
|
||||
|
||||
const validWorkspacePath = path.join(
|
||||
appDataPath,
|
||||
'workspaces',
|
||||
'local',
|
||||
validWorkspaceId
|
||||
);
|
||||
const noDbWorkspacePath = path.join(
|
||||
appDataPath,
|
||||
'workspaces',
|
||||
'local',
|
||||
noDbWorkspaceId
|
||||
);
|
||||
const nonDirectoryPath = path.join(
|
||||
appDataPath,
|
||||
'workspaces',
|
||||
'local',
|
||||
fileEntry
|
||||
);
|
||||
|
||||
await fs.ensureDir(validWorkspacePath);
|
||||
await fs.ensureFile(path.join(validWorkspacePath, 'storage.db'));
|
||||
await fs.ensureDir(noDbWorkspacePath);
|
||||
await fs.outputFile(nonDirectoryPath, 'not-a-workspace');
|
||||
|
||||
const ids = await listLocalWorkspaceIds();
|
||||
expect(ids).toContain(validWorkspaceId);
|
||||
expect(ids).not.toContain(noDbWorkspaceId);
|
||||
expect(ids).not.toContain(fileEntry);
|
||||
});
|
||||
|
||||
test('trash workspace', async () => {
|
||||
const { trashWorkspace } =
|
||||
await import('@affine/electron/helper/workspace/handlers');
|
||||
|
||||
Reference in New Issue
Block a user