mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-18 16:31:57 +03:00
tool: restore wiki content (#7415)
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
parent
417a6ac084
commit
49a55ae7d6
@ -75,6 +75,7 @@
|
||||
"@hcengineering/model-all": "^0.6.0",
|
||||
"@hcengineering/model-attachment": "^0.6.0",
|
||||
"@hcengineering/model-contact": "^0.6.1",
|
||||
"@hcengineering/model-document": "^0.6.0",
|
||||
"@hcengineering/model-recruit": "^0.6.0",
|
||||
"@hcengineering/model-telegram": "^0.6.0",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
|
@ -122,6 +122,7 @@ import { copyToDatalake, moveFiles, showLostFiles } from './storage'
|
||||
import { getModelVersion } from '@hcengineering/model-all'
|
||||
import { type DatalakeConfig, DatalakeService, createDatalakeClient } from '@hcengineering/datalake'
|
||||
import { S3Service, type S3Config } from '@hcengineering/s3'
|
||||
import { restoreWikiContentMongo } from './markup'
|
||||
|
||||
const colorConstants = {
|
||||
colorRed: '\u001b[31m',
|
||||
@ -1240,6 +1241,58 @@ export function devTool (
|
||||
}
|
||||
})
|
||||
|
||||
program
|
||||
.command('restore-wiki-content-mongo')
|
||||
.description('restore wiki document contents')
|
||||
.option('-w, --workspace <workspace>', 'Selected workspace only', '')
|
||||
.option('-d, --dryrun', 'Dry run', false)
|
||||
.action(async (cmd: { workspace: string, dryrun: boolean }) => {
|
||||
const params = {
|
||||
dryRun: cmd.dryrun
|
||||
}
|
||||
|
||||
const { dbUrl, version } = prepareTools()
|
||||
|
||||
let workspaces: Workspace[] = []
|
||||
const accountUrl = getAccountDBUrl()
|
||||
await withDatabase(accountUrl, async (db) => {
|
||||
workspaces = await listWorkspacesPure(db)
|
||||
workspaces = workspaces
|
||||
.filter((p) => p.mode !== 'archived')
|
||||
.filter((p) => cmd.workspace === '' || p.workspace === cmd.workspace)
|
||||
.sort((a, b) => b.lastVisit - a.lastVisit)
|
||||
})
|
||||
|
||||
await withStorage(async (storageAdapter) => {
|
||||
await withDatabase(dbUrl, async (db) => {
|
||||
const mongodbUri = getMongoDBUrl()
|
||||
const client = getMongoClient(mongodbUri)
|
||||
const _client = await client.getClient()
|
||||
|
||||
try {
|
||||
const count = workspaces.length
|
||||
let index = 0
|
||||
for (const workspace of workspaces) {
|
||||
index++
|
||||
|
||||
toolCtx.info('processing workspace', { workspace: workspace.workspace, index, count })
|
||||
if (workspace.version === undefined || !deepEqual(workspace.version, version)) {
|
||||
console.log(`upgrade to ${versionToString(version)} is required`)
|
||||
continue
|
||||
}
|
||||
|
||||
const workspaceId = getWorkspaceId(workspace.workspace)
|
||||
const wsDb = getWorkspaceMongoDB(_client, { name: workspace.workspace })
|
||||
|
||||
await restoreWikiContentMongo(toolCtx, wsDb, workspaceId, storageAdapter, params)
|
||||
}
|
||||
} finally {
|
||||
client.close()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command('confirm-email <email>')
|
||||
.description('confirm user email')
|
||||
|
90
dev/tool/src/markup.ts
Normal file
90
dev/tool/src/markup.ts
Normal file
@ -0,0 +1,90 @@
|
||||
//
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { loadCollabYdoc, saveCollabYdoc, yDocCopyXmlField } from '@hcengineering/collaboration'
|
||||
import { type MeasureContext, type WorkspaceId, makeCollabYdocId } from '@hcengineering/core'
|
||||
import document, { type Document } from '@hcengineering/document'
|
||||
import { DOMAIN_DOCUMENT } from '@hcengineering/model-document'
|
||||
import { type StorageAdapter } from '@hcengineering/server-core'
|
||||
import { type Db } from 'mongodb'
|
||||
|
||||
export interface RestoreWikiContentParams {
|
||||
dryRun: boolean
|
||||
}
|
||||
|
||||
export async function restoreWikiContentMongo (
|
||||
ctx: MeasureContext,
|
||||
db: Db,
|
||||
workspaceId: WorkspaceId,
|
||||
storageAdapter: StorageAdapter,
|
||||
params: RestoreWikiContentParams
|
||||
): Promise<void> {
|
||||
const iterator = db.collection<Document>(DOMAIN_DOCUMENT).find({ _class: document.class.Document })
|
||||
|
||||
let processedCnt = 0
|
||||
let restoredCnt = 0
|
||||
|
||||
function printStats (): void {
|
||||
console.log('...processed', processedCnt, 'restored', restoredCnt)
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const doc = await iterator.next()
|
||||
if (doc === null) return
|
||||
|
||||
processedCnt++
|
||||
if (processedCnt % 100 === 0) {
|
||||
printStats()
|
||||
}
|
||||
|
||||
const correctCollabId = { objectClass: doc._class, objectId: doc._id, objectAttr: 'content' }
|
||||
const wrongCollabId = { objectClass: doc._class, objectId: doc._id, objectAttr: 'description' }
|
||||
|
||||
const stat = storageAdapter.stat(ctx, workspaceId, makeCollabYdocId(wrongCollabId))
|
||||
if (stat === undefined) continue
|
||||
|
||||
const ydoc1 = await loadCollabYdoc(ctx, storageAdapter, workspaceId, correctCollabId)
|
||||
const ydoc2 = await loadCollabYdoc(ctx, storageAdapter, workspaceId, wrongCollabId)
|
||||
|
||||
if (ydoc1 !== undefined && ydoc1.share.has('content')) {
|
||||
// There already is content, we should skip the document
|
||||
continue
|
||||
}
|
||||
|
||||
if (ydoc2 === undefined) {
|
||||
// There are no content to restore
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('restoring content for', doc._id)
|
||||
if (!params.dryRun) {
|
||||
if (ydoc2.share.has('description') && !ydoc2.share.has('content')) {
|
||||
yDocCopyXmlField(ydoc2, 'description', 'content')
|
||||
}
|
||||
|
||||
await saveCollabYdoc(ctx, storageAdapter, workspaceId, correctCollabId, ydoc2)
|
||||
}
|
||||
restoredCnt++
|
||||
} catch (err: any) {
|
||||
console.error('failed to restore content for', doc._id, err)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
printStats()
|
||||
await iterator.close()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user