From 94005da7c6f264ddc1dd11acc3c97abe84ce83a4 Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Wed, 25 Sep 2024 19:05:11 +0700 Subject: [PATCH] feat: show lost files tool (#6721) --- dev/tool/src/index.ts | 45 ++++++++++++++++++++++++++++++++++++++++- dev/tool/src/storage.ts | 28 +++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index 6f98eac8e7..314ba72f37 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -116,7 +116,7 @@ import { fixJsonMarkup, migrateMarkup } from './markup' import { fixMixinForeignAttributes, showMixinForeignAttributes } from './mixin' import { importNotion } from './notion' import { fixAccountEmails, renameAccount } from './renameAccount' -import { moveFiles, syncFiles } from './storage' +import { moveFiles, showLostFiles, syncFiles } from './storage' const colorConstants = { colorRed: '\u001b[31m', @@ -1230,6 +1230,49 @@ export function devTool ( }) }) + program + .command('show-lost-files') + .option('-w, --workspace ', 'Selected workspace only', '') + .option('--disabled', 'Include disabled workspaces', false) + .option('--all', 'Show all files', false) + .action(async (cmd: { workspace: string, disabled: boolean, all: boolean }) => { + const { mongodbUri } = prepareTools() + await withDatabase(mongodbUri, async (db, client) => { + await withStorage(mongodbUri, async (adapter) => { + try { + let index = 1 + const workspaces = await listWorkspacesPure(db) + workspaces.sort((a, b) => b.lastVisit - a.lastVisit) + + for (const workspace of workspaces) { + if (workspace.disabled === true && !cmd.disabled) { + console.log('ignore disabled workspace', workspace.workspace) + continue + } + + if (cmd.workspace !== '' && workspace.workspace !== cmd.workspace) { + continue + } + + try { + console.log('start', workspace.workspace, index, '/', workspaces.length) + const workspaceId = getWorkspaceId(workspace.workspace) + const wsDb = getWorkspaceDB(client, { name: workspace.workspace }) + await showLostFiles(toolCtx, workspaceId, wsDb, adapter, { showAll: cmd.all }) + console.log('done', workspace.workspace) + } catch (err) { + console.error(err) + } + + index += 1 + } + } catch (err: any) { + console.error(err) + } + }) + }) + }) + program.command('fix-bw-workspace ').action(async (workspace: string) => { const { mongodbUri } = prepareTools() await withStorage(mongodbUri, async (adapter) => { diff --git a/dev/tool/src/storage.ts b/dev/tool/src/storage.ts index e5a305a910..c70a2d14e0 100644 --- a/dev/tool/src/storage.ts +++ b/dev/tool/src/storage.ts @@ -13,8 +13,11 @@ // limitations under the License. // +import { type Attachment } from '@hcengineering/attachment' import { type Blob, type MeasureContext, type WorkspaceId, RateLimiter } from '@hcengineering/core' +import { DOMAIN_ATTACHMENT } from '@hcengineering/model-attachment' import { type StorageAdapter, type StorageAdapterEx } from '@hcengineering/server-core' +import { type Db } from 'mongodb' import { PassThrough } from 'stream' export interface MoveFilesParams { @@ -93,6 +96,31 @@ export async function moveFiles ( } } +export async function showLostFiles ( + ctx: MeasureContext, + workspaceId: WorkspaceId, + db: Db, + storageAdapter: StorageAdapter, + { showAll }: { showAll: boolean } +): Promise { + const iterator = db.collection(DOMAIN_ATTACHMENT).find({}) + + while (true) { + const attachment = await iterator.next() + if (attachment === null) break + + const { _id, _class, file, name, modifiedOn } = attachment + const date = new Date(modifiedOn).toISOString() + + const stat = await storageAdapter.stat(ctx, workspaceId, file) + if (stat === undefined) { + console.warn('-', date, _class, _id, file, name) + } else if (showAll) { + console.log('+', date, _class, _id, file, name) + } + } +} + async function processAdapter ( ctx: MeasureContext, exAdapter: StorageAdapterEx,