From 46ba8b9950dcf9c36b746bab607cadfebc84eab2 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Sun, 21 Jun 2026 15:42:09 +0800 Subject: [PATCH] Emit cipher update notifications for attachment changes --- src/handlers/attachments.ts | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/handlers/attachments.ts b/src/handlers/attachments.ts index 854abaa..890ded3 100644 --- a/src/handlers/attachments.ts +++ b/src/handlers/attachments.ts @@ -1,5 +1,5 @@ -import { Env, Attachment, DEFAULT_DEV_SECRET } from '../types'; -import { notifyUserVaultSync } from '../durable/notifications-hub'; +import { Env, Attachment, Cipher, DEFAULT_DEV_SECRET } from '../types'; +import { notifyUserCipherUpdate, notifyUserVaultSync } from '../durable/notifications-hub'; import { StorageService } from '../services/storage'; import { jsonResponse, errorResponse } from '../utils/response'; import { buildDirectUploadUrl, getSafeJwtSecret, parseDirectUploadPayload } from '../utils/direct-upload'; @@ -31,6 +31,30 @@ function notifyVaultSyncForRequest( notifyUserVaultSync(env, userId, revisionDate, readActingDeviceIdentifier(request)); } +function normalizeOptionalId(value: unknown): string | null { + if (value == null) return null; + const normalized = String(value).trim(); + return normalized ? normalized : null; +} + +function notifyCipherUpdateForRequest( + request: Request, + env: Env, + cipher: Cipher, + revisionDate: string +): void { + notifyUserCipherUpdate(env, { + userId: cipher.userId, + cipherId: cipher.id, + revisionDate, + organizationId: normalizeOptionalId((cipher as any).organizationId ?? null), + collectionIds: Array.isArray((cipher as any).collectionIds) + ? (cipher as any).collectionIds.map((id: unknown) => String(id || '').trim()).filter(Boolean) + : null, + contextId: readActingDeviceIdentifier(request), + }); +} + function contentDispositionAttachment(fileName: string | null | undefined): string { const fallback = 'attachment'; const value = String(fileName || fallback) @@ -83,6 +107,7 @@ async function runWithConcurrency( async function processAttachmentUpload( request: Request, env: Env, + cipher: Cipher, attachment: Attachment, cipherId: string ): Promise { @@ -124,6 +149,7 @@ async function processAttachmentUpload( const revisionInfo = await storage.updateCipherRevisionDate(cipherId); if (revisionInfo) { notifyVaultSyncForRequest(request, env, revisionInfo.userId, revisionInfo.revisionDate); + notifyCipherUpdateForRequest(request, env, cipher, revisionInfo.revisionDate); } return new Response(null, { status: 201 }); @@ -184,6 +210,7 @@ export async function handleCreateAttachment( const revisionInfo = await storage.updateCipherRevisionDate(cipherId); if (revisionInfo) { notifyVaultSyncForRequest(request, env, revisionInfo.userId, revisionInfo.revisionDate); + notifyCipherUpdateForRequest(request, env, cipher, revisionInfo.revisionDate); } // Get updated cipher for response @@ -227,7 +254,7 @@ export async function handleUploadAttachment( return errorResponse('Attachment not found', 404); } - return processAttachmentUpload(request, env, attachment, cipherId); + return processAttachmentUpload(request, env, cipher, attachment, cipherId); } export async function handlePublicUploadAttachment( @@ -265,7 +292,7 @@ export async function handlePublicUploadAttachment( return errorResponse('Attachment not found', 404); } - return processAttachmentUpload(request, env, attachment, cipherId); + return processAttachmentUpload(request, env, cipher, attachment, cipherId); } // GET /api/ciphers/{cipherId}/attachment/{attachmentId} @@ -356,6 +383,7 @@ export async function handleUpdateAttachmentMetadata( const revisionInfo = await storage.updateCipherRevisionDate(cipherId); if (revisionInfo) { notifyVaultSyncForRequest(request, env, revisionInfo.userId, revisionInfo.revisionDate); + notifyCipherUpdateForRequest(request, env, cipher, revisionInfo.revisionDate); } return jsonResponse({ @@ -463,6 +491,7 @@ export async function handleDeleteAttachment( const revisionInfo = await storage.updateCipherRevisionDate(cipherId); if (revisionInfo) { notifyVaultSyncForRequest(request, env, revisionInfo.userId, revisionInfo.revisionDate); + notifyCipherUpdateForRequest(request, env, cipher, revisionInfo.revisionDate); await writeAttachmentAudit(storage, request, revisionInfo.userId, 'attachment.delete', { id: attachmentId, cipherId,