fix: restore wiki content fixes (#7474)

This commit is contained in:
Alexander Onnikov 2024-12-16 16:57:58 +07:00 committed by GitHub
parent 9994ffedc2
commit 413da21b50
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 96 additions and 11 deletions

View File

@ -20,8 +20,10 @@ import core, {
type MeasureContext,
type Ref,
type TxCreateDoc,
type TxUpdateDoc,
type WorkspaceId,
DOMAIN_TX,
SortingOrder,
makeCollabYdocId,
makeDocCollabId
} from '@hcengineering/core'
@ -63,13 +65,18 @@ export async function restoreWikiContentMongo (
}
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))
const wrongYdocId = await findWikiDocYdocName(ctx, db, workspaceId, doc._id)
if (wrongYdocId === undefined) {
console.log('current ydoc not found', doc._id)
continue
}
const stat = storageAdapter.stat(ctx, workspaceId, wrongYdocId)
if (stat === undefined) continue
const ydoc1 = await loadCollabYdoc(ctx, storageAdapter, workspaceId, correctCollabId)
const ydoc2 = await loadCollabYdoc(ctx, storageAdapter, workspaceId, wrongCollabId)
const ydoc2 = await loadCollabYdoc(ctx, storageAdapter, workspaceId, wrongYdocId)
if (ydoc1 !== undefined && ydoc1.share.has('content')) {
// There already is content, we should skip the document
@ -101,6 +108,81 @@ export async function restoreWikiContentMongo (
}
}
export async function findWikiDocYdocName (
ctx: MeasureContext,
db: Db,
workspaceId: WorkspaceId,
doc: Ref<Document>
): Promise<Ref<Blob> | undefined> {
const updateContentTx = await db.collection<TxUpdateDoc<Document & { content: string }>>(DOMAIN_TX).findOne(
{
_class: core.class.TxUpdateDoc,
objectId: doc,
objectClass: document.class.Document,
'operations.content': { $exists: true }
},
{
sort: { modifiedOn: SortingOrder.Descending }
}
)
if (updateContentTx?.operations?.content != null) {
const value = updateContentTx.operations.content as string
if (value.includes(':')) {
console.log('found update content tx', doc, value)
return value.split(':')[0] as Ref<Blob>
}
}
const updateDescriptionTx = await db.collection<TxUpdateDoc<Document & { description: string }>>(DOMAIN_TX).findOne(
{
_class: core.class.TxUpdateDoc,
objectId: doc,
objectClass: document.class.Document,
'operations.description': { $exists: true }
},
{
sort: { modifiedOn: SortingOrder.Descending }
}
)
if (updateDescriptionTx?.operations?.description != null) {
const value = updateDescriptionTx.operations.description
if (value.includes(':')) {
console.log('found update description tx', doc, value)
return value.split(':')[0] as Ref<Blob>
}
}
const createContentTx = await db.collection<TxCreateDoc<Document & { content: string }>>(DOMAIN_TX).findOne({
_class: core.class.TxCreateDoc,
objectId: doc,
objectClass: document.class.Document,
'attributes.content': { $exists: true }
})
if (createContentTx?.attributes?.content != null) {
const value = createContentTx.attributes.content
if (value.includes(':')) {
console.log('found create content tx', doc, value)
return value.split(':')[0] as Ref<Blob>
}
}
const createContentIdTx = await db.collection<TxCreateDoc<Document & { contentId: Ref<Blob> }>>(DOMAIN_TX).findOne({
_class: core.class.TxCreateDoc,
objectId: doc,
objectClass: document.class.Document,
'attributes.contentId': { $exists: true }
})
if (createContentIdTx?.attributes?.contentId != null) {
const value = createContentIdTx.attributes.contentId
console.log('found create contentId tx', doc, value)
return value
}
}
export interface RestoreControlledDocContentParams {
dryRun: boolean
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import type { Class, MarkupBlobRef, Doc, Ref } from './classes'
import type { Blob, Class, Doc, MarkupBlobRef, Ref } from './classes'
/** @public */
export interface CollaborativeDoc {
@ -40,9 +40,9 @@ export function makeDocCollabId<T extends Doc, U extends keyof T> (
}
/** @public */
export function makeCollabYdocId (doc: CollaborativeDoc): MarkupBlobRef {
export function makeCollabYdocId (doc: CollaborativeDoc): Ref<Blob> {
const { objectId, objectAttr } = doc
return `${objectId}%${objectAttr}` as MarkupBlobRef
return `${objectId}%${objectAttr}` as Ref<Blob>
}
/** @public */

View File

@ -19,6 +19,7 @@ import {
type Ref,
type WorkspaceId,
Markup,
MarkupBlobRef,
MeasureContext,
generateId,
makeCollabJsonId,
@ -35,9 +36,9 @@ export async function loadCollabYdoc (
ctx: MeasureContext,
storageAdapter: StorageAdapter,
workspace: WorkspaceId,
doc: CollaborativeDoc
doc: CollaborativeDoc | MarkupBlobRef
): Promise<YDoc | undefined> {
const blobId = makeCollabYdocId(doc)
const blobId = typeof doc === 'string' ? doc : makeCollabYdocId(doc)
const blob = await storageAdapter.stat(ctx, workspace, blobId)
if (blob === undefined) {
@ -61,10 +62,10 @@ export async function saveCollabYdoc (
ctx: MeasureContext,
storageAdapter: StorageAdapter,
workspace: WorkspaceId,
doc: CollaborativeDoc,
doc: CollaborativeDoc | MarkupBlobRef,
ydoc: YDoc
): Promise<Ref<Blob>> {
const blobId = makeCollabYdocId(doc)
const blobId = typeof doc === 'string' ? doc : makeCollabYdocId(doc)
const buffer = yDocToBuffer(ydoc)
await storageAdapter.put(ctx, workspace, blobId, buffer, 'application/ydoc', buffer.length)

View File

@ -307,7 +307,9 @@ export class S3Service implements StorageAdapter {
version: result.VersionId ?? null
}
} catch (err: any) {
ctx.warn('no object found', { error: err, objectName, workspaceId: workspaceId.name })
if (err?.$metadata?.httpStatusCode !== 404) {
ctx.warn('no object found', { error: err, objectName, workspaceId: workspaceId.name })
}
}
}