feat: implement NotificationsHub for real-time vault sync notifications

- Added NotificationsHub durable object to handle WebSocket connections for vault sync notifications.
- Integrated SignalR protocol for message framing and communication.
- Updated storage service methods to return revision date and user ID for vault sync notifications.
- Enhanced existing handlers (attachments, ciphers, folders, sends, and import) to notify users of vault sync events.
- Created new notifications handler for WebSocket negotiation and binding user IDs.
- Updated frontend to establish WebSocket connection for receiving vault sync notifications.
- Improved CORS headers to support new notification endpoints.
- Bumped wrangler version in package.json to 4.71.0.
This commit is contained in:
shuaiplus
2026-03-09 00:25:34 +08:00
parent 54cf1ff718
commit 899f1004a3
18 changed files with 779 additions and 76 deletions
+7 -6
View File
@@ -504,8 +504,8 @@ export class StorageService {
});
}
async bulkMoveCiphers(ids: string[], folderId: string | null, userId: string): Promise<void> {
if (ids.length === 0) return;
async bulkMoveCiphers(ids: string[], folderId: string | null, userId: string): Promise<string | null> {
if (ids.length === 0) return null;
const now = new Date().toISOString();
const uniqueIds = Array.from(new Set(ids));
const patch = JSON.stringify({
@@ -528,7 +528,7 @@ export class StorageService {
.run();
}
await this.updateRevisionDate(userId);
return this.updateRevisionDate(userId);
}
// --- Folders ---
@@ -744,12 +744,13 @@ export class StorageService {
await this.db.prepare('DELETE FROM attachments WHERE cipher_id = ?').bind(cipherId).run();
}
async updateCipherRevisionDate(cipherId: string): Promise<void> {
async updateCipherRevisionDate(cipherId: string): Promise<{ userId: string; revisionDate: string } | null> {
const cipher = await this.getCipher(cipherId);
if (!cipher) return;
if (!cipher) return null;
cipher.updatedAt = new Date().toISOString();
await this.saveCipher(cipher);
await this.updateRevisionDate(cipher.userId);
const revisionDate = await this.updateRevisionDate(cipher.userId);
return { userId: cipher.userId, revisionDate };
}
// --- Refresh tokens ---