mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-08 20:57:08 +08:00
feat(server): impl doc gc (#15282)
#### PR Dependency Tree * **PR #15282** 👈 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 automated document cleanup to reconcile missing workspace docs, delete related stored data, and recover if the doc returns. * Added effect-based follow-up reconciliation for search indexing, Copilot embeddings, and comment attachment cleanup with explicit acknowledgements. * **Bug Fixes** * Deleted-document references now persist as dangling references rather than disappearing. * Improved document deletion flow to enforce permissions and ensure authorized deletions succeed. * **Tests** * Expanded coverage for cleanup recovery, indexing/embedding reconciliation, permissions, and reference semantics. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -205,20 +205,49 @@ export class CopilotEmbeddingJob {
|
||||
);
|
||||
}
|
||||
|
||||
@OnJob('copilot.embedding.deleteDoc')
|
||||
async deleteDocEmbeddingQueueFromEvent(
|
||||
doc: Jobs['copilot.embedding.deleteDoc']
|
||||
) {
|
||||
private async deleteDocEmbedding(doc: {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
}) {
|
||||
await this.queue.remove(
|
||||
`workspace:embedding:${doc.workspaceId}:${doc.docId}`,
|
||||
'copilot.embedding.docs'
|
||||
);
|
||||
await this.models.copilotContext.deleteWorkspaceEmbedding(
|
||||
await this.models.copilotContext.purgeWorkspaceEmbedding(
|
||||
doc.workspaceId,
|
||||
doc.docId
|
||||
);
|
||||
}
|
||||
|
||||
@OnJob('copilot.embedding.reconcileDocumentCleanup')
|
||||
async reconcileDocumentCleanup({
|
||||
workspaceId,
|
||||
docId,
|
||||
cleanupVersion,
|
||||
}: Jobs['copilot.embedding.reconcileDocumentCleanup']) {
|
||||
const root = await this.doc.getDoc(workspaceId, workspaceId);
|
||||
if (!root) {
|
||||
throw new Error(`workspace root ${workspaceId} not found`);
|
||||
}
|
||||
const live = readAllDocIdsFromWorkspaceSnapshot(root.bin, true).includes(
|
||||
docId
|
||||
);
|
||||
if (live) {
|
||||
if (!(await this.doc.getDoc(workspaceId, docId))) {
|
||||
throw new Error(`restored document ${workspaceId}/${docId} not found`);
|
||||
}
|
||||
await this.addDocEmbeddingQueueFromEvent({ workspaceId, docId });
|
||||
} else {
|
||||
await this.deleteDocEmbedding({ workspaceId, docId });
|
||||
}
|
||||
await this.queue.add('backendRuntime.ackDocumentCleanupEffect', {
|
||||
workspaceId,
|
||||
docId,
|
||||
cleanupVersion,
|
||||
effect: 'copilot',
|
||||
});
|
||||
}
|
||||
|
||||
private async readCopilotBlob(
|
||||
userId: string,
|
||||
workspaceId: string,
|
||||
|
||||
@@ -68,9 +68,10 @@ declare global {
|
||||
docId: string;
|
||||
};
|
||||
|
||||
'copilot.embedding.deleteDoc': {
|
||||
'copilot.embedding.reconcileDocumentCleanup': {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
cleanupVersion: string;
|
||||
};
|
||||
|
||||
'copilot.embedding.files': {
|
||||
|
||||
@@ -39,10 +39,6 @@ declare global {
|
||||
'copilot.session.generateTitle': {
|
||||
sessionId: string;
|
||||
};
|
||||
'copilot.session.deleteDoc': {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,23 +508,6 @@ export class ChatSessionService {
|
||||
return null;
|
||||
}
|
||||
|
||||
@OnJob('copilot.session.deleteDoc')
|
||||
async deleteDocSessions(doc: Jobs['copilot.session.deleteDoc']) {
|
||||
const sessionIds = await this.models.copilotSession
|
||||
.list({
|
||||
userId: undefined,
|
||||
workspaceId: doc.workspaceId,
|
||||
docId: doc.docId,
|
||||
})
|
||||
.then(s => s.map(s => [s.userId, s.id]));
|
||||
for (const [userId, sessionId] of sessionIds) {
|
||||
await this.models.copilotSession.update(
|
||||
{ userId, sessionId, docId: null },
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@OnJob('copilot.session.generateTitle')
|
||||
async generateSessionTitle(job: Jobs['copilot.session.generateTitle']) {
|
||||
const { sessionId } = job;
|
||||
|
||||
Reference in New Issue
Block a user