Files
gitmost/apps/server/src/integrations/queue/constants/queue.interface.ts
T
agent_coder 6bf8361936 perf(comment): унести Yjs-обновление comment-mark с HTTP-критического пути (#399)
resolve/unresolve/delete comment-mark синхронно дёргали collab-gateway
(handleYjsEvent) прямо в HTTP-запросе — сетевой раунд-трип к collab на
горячем пути. Теперь:
- DB-строка пишется синхронно (источник истины) с общим таймстампом;
- сама mark-операция уходит идемпотентным COMMENT_MARK_UPDATE-джобом на
  GENERAL_QUEUE, воркер проигрывает тот же handleYjsEvent;
- resolve/unresolve — fire-and-forget (best-effort), delete — await энкью
  ДО необратимого hard-delete (durability split);
- race-guard: устаревшее ПРОТИВОПОЛОЖНОЕ событие (ts <= updatedAt строки и
  состояние расходится) пропускается, а не флипает mark в устаревшее;
- DI-цикл обойдён ленивым moduleRef.get(CollaborationGateway, strict:false).

Внутренний цикл: 2 прохода. Правки по внутреннему ревью: `<` → `<=` в
race-guard (безопасная обработка суб-миллисекундной ничьи двух
противоположных тогглов); задокументирован сознательный компромисс —
транзиентный page.updated-broadcast из воркера несёт только {id}, теряя
name/avatarUrl «кто редактировал» (lastUpdatedById выставляется верно,
косметика, самочинится на следующем реальном редактировании).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 05:15:11 +03:00

162 lines
4.1 KiB
TypeScript

import { MentionNode } from '../../../common/helpers/prosemirror/utils';
export interface IPageBacklinkJob {
pageId: string;
workspaceId: string;
mentions: MentionNode[];
internalLinkSlugIds?: string[];
}
export interface IAddPageWatchersJob {
userIds: string[];
pageId: string;
spaceId: string;
workspaceId: string;
}
export interface IStripeSeatsSyncJob {
workspaceId: string;
}
export interface IPageHistoryJob {
pageId: string;
}
/**
* AI_QUEUE payload for a content change that should trigger a RAG reindex
* (§6.7 stage D / §14[M1]). Produced by the collab persistence extension on
* `onStoreDocument` and by the page-delete path (the delete case carries the
* ids of pages whose embeddings must be purged).
*/
export interface IPageContentUpdatedJob {
pageIds: string[];
workspaceId: string;
}
/**
* AI_QUEUE payload for workspace-wide RAG embedding jobs
* (WORKSPACE_CREATE_EMBEDDINGS / WORKSPACE_DELETE_EMBEDDINGS).
*/
export interface IWorkspaceEmbeddingsJob {
workspaceId: string;
}
export interface INotificationCreateJob {
userId: string;
workspaceId: string;
type: string;
actorId?: string;
pageId?: string;
spaceId?: string;
commentId?: string;
data?: Record<string, unknown>;
}
export interface ICommentNotificationJob {
commentId: string;
parentCommentId?: string;
pageId: string;
spaceId: string;
workspaceId: string;
actorId: string;
mentionedUserIds: string[];
notifyWatchers: boolean;
}
/**
* GENERAL_QUEUE payload for the off-critical-path comment inline-mark mirror
* (#399). The comment DB row is the source of truth and is already updated
* synchronously (ms); this job flips/removes the inline `comment` mark in the
* collaborative Y.Doc for connected clients, OFF the HTTP response path, so
* `POST /api/comments/resolve` no longer waits the whole Y.Doc load + store
* pipeline (was ~4.5s p95). The mark op is idempotent, so BullMQ retries are
* safe.
*
* `action`:
* - 'resolve' / 'unresolve' → flip the mark's `resolved` attribute (exactly
* what the synchronous resolveCommentMark path did);
* - 'delete' → strip the anchor mark entirely (ephemeral suggestion #329).
* `ts` is the DB-mutation timestamp (ms). The worker's race-guard uses it (with
* the row's authoritative resolved state) to skip a resolve/unresolve event
* that a newer, opposite event has already superseded (out-of-order drain).
* `userId` supplies the connection-context user the store pipeline attributes
* the change to (persistence.extension reads context.user.id).
*/
export interface ICommentMarkUpdateJob {
documentName: string;
commentId: string;
action: 'resolve' | 'unresolve' | 'delete';
ts: number;
userId: string;
}
export interface ICommentResolvedNotificationJob {
commentId: string;
commentCreatorId: string;
pageId: string;
spaceId: string;
workspaceId: string;
actorId: string;
}
export interface IPageMentionNotificationJob {
userMentions: { userId: string; mentionId: string; creatorId: string }[];
oldMentionedUserIds: string[];
pageId: string;
spaceId: string;
workspaceId: string;
}
export interface IPageUpdateNotificationJob {
pageId: string;
spaceId: string;
workspaceId: string;
actorIds: string[];
}
export interface IPermissionGrantedNotificationJob {
userIds: string[];
pageId: string;
spaceId: string;
workspaceId: string;
actorId: string;
role: string;
}
export interface IVerificationExpiringNotificationJob {
verificationId: string;
}
export interface IVerificationExpiredNotificationJob {
verificationId: string;
}
export interface IVerificationReconcileJob {
// no payload
}
export interface IPageVerifiedNotificationJob {
pageId: string;
spaceId: string;
workspaceId: string;
actorId: string;
verifierIds: string[];
}
export interface IApprovalRequestedNotificationJob {
pageId: string;
spaceId: string;
workspaceId: string;
actorId: string;
verifierIds: string[];
}
export interface IApprovalRejectedNotificationJob {
pageId: string;
spaceId: string;
workspaceId: string;
actorId: string;
requestedById: string;
comment?: string;
}