Skip to content

Commit cd97f6f

Browse files
authored
Object restore tool (#8318)
Signed-off-by: Denis Bykhov <[email protected]>
1 parent d689cff commit cd97f6f

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

Diff for: dev/tool/src/index.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ import { buildStorageFromConfig, createStorageFromConfig, storageConfigFromEnv }
7878
import { program, type Command } from 'commander'
7979
import { addControlledDocumentRank } from './qms'
8080
import { clearTelegramHistory } from './telegram'
81-
import { backupRestore, diffWorkspace, updateField } from './workspace'
81+
import { backupRestore, diffWorkspace, restoreRemovedDoc, updateField } from './workspace'
8282

8383
import core, {
8484
AccountRole,
@@ -2296,6 +2296,15 @@ export function devTool (
22962296
})
22972297
})
22982298

2299+
program
2300+
.command('restore-removed-doc <workspace>')
2301+
.requiredOption('--ids <objectIds>', 'ids')
2302+
.action(async (workspace: string, cmd: { ids: string }) => {
2303+
const wsid = getWorkspaceId(workspace)
2304+
const endpoint = await getTransactorEndpoint(generateToken(systemAccountEmail, wsid), 'external')
2305+
await restoreRemovedDoc(toolCtx, wsid, endpoint, cmd.ids)
2306+
})
2307+
22992308
program
23002309
.command('add-controlled-doc-rank-mongo')
23012310
.description('add rank to controlled documents')

Diff for: dev/tool/src/workspace.ts

+64
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import contact from '@hcengineering/contact'
1818
import core, {
1919
DOMAIN_TX,
2020
getWorkspaceId,
21+
TxOperations,
2122
type BackupClient,
2223
type BaseWorkspaceInfo,
2324
type Class,
@@ -161,3 +162,66 @@ export async function backupRestore (
161162
await storageAdapter.close()
162163
}
163164
}
165+
166+
export async function restoreRemovedDoc (
167+
ctx: MeasureContext,
168+
workspaceId: WorkspaceId,
169+
transactorUrl: string,
170+
idsVal: string
171+
): Promise<void> {
172+
const ids = idsVal.split(';').map((it) => it.trim()) as Ref<Doc>[]
173+
const connection = (await connect(transactorUrl, workspaceId, undefined, {
174+
mode: 'backup',
175+
model: 'upgrade', // Required for force all clients reload after operation will be complete.
176+
admin: 'true'
177+
})) as unknown as CoreClient & BackupClient
178+
try {
179+
for (const id of ids) {
180+
try {
181+
ctx.info('start restoring', { id })
182+
const ops = new TxOperations(connection, core.account.System)
183+
const processed = new Set<Ref<Doc>>()
184+
const txes = await getObjectTxesAndRelatedTxes(ctx, ops, id, processed, true)
185+
txes.filter((p) => p._class !== core.class.TxRemoveDoc).sort((a, b) => a.modifiedOn - b.modifiedOn)
186+
for (const tx of txes) {
187+
tx.space = core.space.DerivedTx
188+
await ops.tx(tx)
189+
}
190+
ctx.info('success restored', { id })
191+
} catch (err) {
192+
ctx.error('error restoring', { id, err })
193+
}
194+
}
195+
} finally {
196+
await connection.sendForceClose()
197+
await connection.close()
198+
}
199+
}
200+
201+
async function getObjectTxesAndRelatedTxes (
202+
ctx: MeasureContext,
203+
client: TxOperations,
204+
objectId: Ref<Doc>,
205+
processed: Set<Ref<Doc>>,
206+
filterRemoved = false
207+
): Promise<Tx[]> {
208+
ctx.info('Find txes for', { objectId })
209+
const result: Tx[] = []
210+
if (processed.has(objectId)) {
211+
return result
212+
}
213+
processed.add(objectId)
214+
let txes = (await client.findAll(core.class.TxCUD, { objectId })) as Tx[]
215+
if (filterRemoved) {
216+
txes = txes.filter((it) => it._class !== core.class.TxRemoveDoc)
217+
}
218+
result.push(...txes)
219+
const relatedTxes = await client.findAll(core.class.TxCUD, { attachedTo: objectId })
220+
result.push(...relatedTxes)
221+
const relatedIds = new Set(relatedTxes.map((it) => it.objectId))
222+
for (const relatedId of relatedIds) {
223+
const rel = await getObjectTxesAndRelatedTxes(ctx, client, relatedId, processed)
224+
result.push(...rel)
225+
}
226+
return result
227+
}

0 commit comments

Comments
 (0)