mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-22 21:50:13 +00:00
Add official Bitwarden resource sync notifications
This commit is contained in:
@@ -3,11 +3,21 @@ import type { Env } from '../types';
|
|||||||
|
|
||||||
const SIGNALR_RECORD_SEPARATOR = 0x1e;
|
const SIGNALR_RECORD_SEPARATOR = 0x1e;
|
||||||
const SIGNALR_HANDSHAKE_ACK = new Uint8Array([0x7b, 0x7d, SIGNALR_RECORD_SEPARATOR]);
|
const SIGNALR_HANDSHAKE_ACK = new Uint8Array([0x7b, 0x7d, SIGNALR_RECORD_SEPARATOR]);
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_CIPHER_UPDATE = 0;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_CIPHER_CREATE = 1;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_FOLDER_DELETE = 3;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_CIPHERS = 4;
|
||||||
const SIGNALR_UPDATE_TYPE_SYNC_VAULT = 5;
|
const SIGNALR_UPDATE_TYPE_SYNC_VAULT = 5;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_FOLDER_CREATE = 7;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_FOLDER_UPDATE = 8;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_CIPHER_DELETE = 9;
|
||||||
const SIGNALR_UPDATE_TYPE_LOG_OUT = 11;
|
const SIGNALR_UPDATE_TYPE_LOG_OUT = 11;
|
||||||
const SIGNALR_UPDATE_TYPE_BACKUP_RESTORE_PROGRESS = 13;
|
const SIGNALR_UPDATE_TYPE_SYNC_SEND_CREATE = 12;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_SEND_UPDATE = 13;
|
||||||
|
const SIGNALR_UPDATE_TYPE_SYNC_SEND_DELETE = 14;
|
||||||
const SIGNALR_UPDATE_TYPE_AUTH_REQUEST = 15;
|
const SIGNALR_UPDATE_TYPE_AUTH_REQUEST = 15;
|
||||||
const SIGNALR_UPDATE_TYPE_AUTH_REQUEST_RESPONSE = 16;
|
const SIGNALR_UPDATE_TYPE_AUTH_REQUEST_RESPONSE = 16;
|
||||||
|
const SIGNALR_UPDATE_TYPE_BACKUP_RESTORE_PROGRESS = 102;
|
||||||
|
|
||||||
type HubProtocol = 'json' | 'messagepack';
|
type HubProtocol = 'json' | 'messagepack';
|
||||||
type HubKind = 'user' | 'anonymous-auth-request';
|
type HubKind = 'user' | 'anonymous-auth-request';
|
||||||
@@ -422,6 +432,243 @@ export function notifyUserVaultSync(
|
|||||||
waitUntil(notifyUserUpdate(env, userId, SIGNALR_UPDATE_TYPE_SYNC_VAULT, revisionDate, contextId ?? null, null));
|
waitUntil(notifyUserUpdate(env, userId, SIGNALR_UPDATE_TYPE_SYNC_VAULT, revisionDate, contextId ?? null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function notifyUserCiphersSync(
|
||||||
|
env: Env,
|
||||||
|
userId: string,
|
||||||
|
revisionDate: string,
|
||||||
|
contextId?: string | null
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(env, userId, SIGNALR_UPDATE_TYPE_SYNC_CIPHERS, revisionDate, contextId ?? null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserCipherCreate(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
cipherId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
organizationId?: string | null;
|
||||||
|
collectionIds?: string[] | null;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_CIPHER_CREATE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.cipherId,
|
||||||
|
OrganizationId: payload.organizationId ?? null,
|
||||||
|
CollectionIds: Array.isArray(payload.collectionIds) ? payload.collectionIds : null,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserCipherUpdate(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
cipherId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
organizationId?: string | null;
|
||||||
|
collectionIds?: string[] | null;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_CIPHER_UPDATE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.cipherId,
|
||||||
|
OrganizationId: payload.organizationId ?? null,
|
||||||
|
CollectionIds: Array.isArray(payload.collectionIds) ? payload.collectionIds : null,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserCipherDelete(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
cipherId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
organizationId?: string | null;
|
||||||
|
collectionIds?: string[] | null;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_CIPHER_DELETE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.cipherId,
|
||||||
|
OrganizationId: payload.organizationId ?? null,
|
||||||
|
CollectionIds: Array.isArray(payload.collectionIds) ? payload.collectionIds : null,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserFolderCreate(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
folderId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_FOLDER_CREATE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.folderId,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserFolderUpdate(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
folderId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_FOLDER_UPDATE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.folderId,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserFolderDelete(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
folderId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_FOLDER_DELETE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.folderId,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserSendCreate(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
sendId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_SEND_CREATE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.sendId,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserSendUpdate(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
sendId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_SEND_UPDATE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.sendId,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifyUserSendDelete(
|
||||||
|
env: Env,
|
||||||
|
payload: {
|
||||||
|
userId: string;
|
||||||
|
sendId: string;
|
||||||
|
revisionDate: string;
|
||||||
|
contextId?: string | null;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
waitUntil(notifyUserUpdate(
|
||||||
|
env,
|
||||||
|
payload.userId,
|
||||||
|
SIGNALR_UPDATE_TYPE_SYNC_SEND_DELETE,
|
||||||
|
payload.revisionDate,
|
||||||
|
payload.contextId ?? null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
UserId: payload.userId,
|
||||||
|
Id: payload.sendId,
|
||||||
|
RevisionDate: payload.revisionDate,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
export function notifyUserLogout(
|
export function notifyUserLogout(
|
||||||
env: Env,
|
env: Env,
|
||||||
userId: string,
|
userId: string,
|
||||||
|
|||||||
+74
-1
@@ -11,7 +11,13 @@ import {
|
|||||||
PasswordHistory,
|
PasswordHistory,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { StorageService } from '../services/storage';
|
import { StorageService } from '../services/storage';
|
||||||
import { notifyUserVaultSync } from '../durable/notifications-hub';
|
import {
|
||||||
|
notifyUserCipherCreate,
|
||||||
|
notifyUserCipherDelete,
|
||||||
|
notifyUserCipherUpdate,
|
||||||
|
notifyUserCiphersSync,
|
||||||
|
notifyUserVaultSync,
|
||||||
|
} from '../durable/notifications-hub';
|
||||||
import { jsonResponse, errorResponse } from '../utils/response';
|
import { jsonResponse, errorResponse } from '../utils/response';
|
||||||
import { generateUUID } from '../utils/uuid';
|
import { generateUUID } from '../utils/uuid';
|
||||||
import { deleteAllAttachmentsForCipher, deleteAllAttachmentsForCiphers } from './attachments';
|
import { deleteAllAttachmentsForCipher, deleteAllAttachmentsForCiphers } from './attachments';
|
||||||
@@ -51,6 +57,60 @@ function notifyVaultSyncForRequest(
|
|||||||
notifyUserVaultSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
notifyUserVaultSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notifyCipherCreateForRequest(
|
||||||
|
request: Request,
|
||||||
|
env: Env,
|
||||||
|
cipher: Cipher,
|
||||||
|
revisionDate: string
|
||||||
|
): void {
|
||||||
|
notifyUserCipherCreate(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 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 notifyCipherDeleteForRequest(
|
||||||
|
request: Request,
|
||||||
|
env: Env,
|
||||||
|
cipher: Cipher,
|
||||||
|
revisionDate: string
|
||||||
|
): void {
|
||||||
|
notifyUserCipherDelete(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 getAliasedProp(source: any, aliases: string[]): { present: boolean; value: any } {
|
function getAliasedProp(source: any, aliases: string[]): { present: boolean; value: any } {
|
||||||
if (!source || typeof source !== 'object') return { present: false, value: undefined };
|
if (!source || typeof source !== 'object') return { present: false, value: undefined };
|
||||||
for (const key of aliases) {
|
for (const key of aliases) {
|
||||||
@@ -815,6 +875,7 @@ export async function handleCreateCipher(request: Request, env: Env, userId: str
|
|||||||
await storage.saveCipher(cipher);
|
await storage.saveCipher(cipher);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherCreateForRequest(request, env, cipher, revisionDate);
|
||||||
const responseOptions = cipherResponseOptionsForRequest(request);
|
const responseOptions = cipherResponseOptionsForRequest(request);
|
||||||
|
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
@@ -925,6 +986,7 @@ export async function handleUpdateCipher(request: Request, env: Env, userId: str
|
|||||||
await storage.saveCipher(cipher);
|
await storage.saveCipher(cipher);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherUpdateForRequest(request, env, cipher, revisionDate);
|
||||||
const attachments = await storage.getAttachmentsByCipher(cipher.id);
|
const attachments = await storage.getAttachmentsByCipher(cipher.id);
|
||||||
const responseOptions = cipherResponseOptionsForRequest(request);
|
const responseOptions = cipherResponseOptionsForRequest(request);
|
||||||
|
|
||||||
@@ -949,6 +1011,7 @@ export async function handleDeleteCipher(request: Request, env: Env, userId: str
|
|||||||
await storage.saveCipher(cipher);
|
await storage.saveCipher(cipher);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherDeleteForRequest(request, env, cipher, revisionDate);
|
||||||
await writeCipherAudit(storage, request, userId, 'cipher.delete.soft', {
|
await writeCipherAudit(storage, request, userId, 'cipher.delete.soft', {
|
||||||
id: cipher.id,
|
id: cipher.id,
|
||||||
type: cipher.type,
|
type: cipher.type,
|
||||||
@@ -978,6 +1041,7 @@ export async function handleDeleteCipherCompat(request: Request, env: Env, userI
|
|||||||
await storage.deleteCipher(id, userId);
|
await storage.deleteCipher(id, userId);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherDeleteForRequest(request, env, cipher, revisionDate);
|
||||||
await writeCipherAudit(storage, request, userId, 'cipher.delete.permanent', {
|
await writeCipherAudit(storage, request, userId, 'cipher.delete.permanent', {
|
||||||
id,
|
id,
|
||||||
type: cipher.type,
|
type: cipher.type,
|
||||||
@@ -1005,6 +1069,7 @@ export async function handlePermanentDeleteCipher(request: Request, env: Env, us
|
|||||||
await storage.deleteCipher(id, userId);
|
await storage.deleteCipher(id, userId);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherDeleteForRequest(request, env, cipher, revisionDate);
|
||||||
await writeCipherAudit(storage, request, userId, 'cipher.delete.permanent', {
|
await writeCipherAudit(storage, request, userId, 'cipher.delete.permanent', {
|
||||||
id,
|
id,
|
||||||
type: cipher.type,
|
type: cipher.type,
|
||||||
@@ -1029,6 +1094,7 @@ export async function handleRestoreCipher(request: Request, env: Env, userId: st
|
|||||||
await storage.saveCipher(cipher);
|
await storage.saveCipher(cipher);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherUpdateForRequest(request, env, cipher, revisionDate);
|
||||||
|
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
cipherToResponse(cipher, [], cipherResponseOptionsForRequest(request))
|
cipherToResponse(cipher, [], cipherResponseOptionsForRequest(request))
|
||||||
@@ -1068,6 +1134,7 @@ export async function handlePartialUpdateCipher(request: Request, env: Env, user
|
|||||||
await storage.saveCipher(cipher);
|
await storage.saveCipher(cipher);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherUpdateForRequest(request, env, cipher, revisionDate);
|
||||||
|
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
cipherToResponse(cipher, [], cipherResponseOptionsForRequest(request))
|
cipherToResponse(cipher, [], cipherResponseOptionsForRequest(request))
|
||||||
@@ -1144,6 +1211,7 @@ export async function handleArchiveCipher(request: Request, env: Env, userId: st
|
|||||||
await storage.saveCipher(cipher);
|
await storage.saveCipher(cipher);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyCipherUpdateForRequest(request, env, cipher, revisionDate);
|
||||||
|
|
||||||
const attachments = await storage.getAttachmentsByCipher(cipher.id);
|
const attachments = await storage.getAttachmentsByCipher(cipher.id);
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
@@ -1192,6 +1260,7 @@ export async function handleBulkArchiveCiphers(request: Request, env: Env, userI
|
|||||||
const revisionDate = await storage.bulkArchiveCiphers(ids, userId);
|
const revisionDate = await storage.bulkArchiveCiphers(ids, userId);
|
||||||
if (revisionDate) {
|
if (revisionDate) {
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserCiphersSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildCipherListResponse(request, storage, userId, ids);
|
return buildCipherListResponse(request, storage, userId, ids);
|
||||||
@@ -1216,6 +1285,7 @@ export async function handleBulkUnarchiveCiphers(request: Request, env: Env, use
|
|||||||
const revisionDate = await storage.bulkUnarchiveCiphers(ids, userId);
|
const revisionDate = await storage.bulkUnarchiveCiphers(ids, userId);
|
||||||
if (revisionDate) {
|
if (revisionDate) {
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserCiphersSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildCipherListResponse(request, storage, userId, ids);
|
return buildCipherListResponse(request, storage, userId, ids);
|
||||||
@@ -1239,6 +1309,7 @@ export async function handleBulkDeleteCiphers(request: Request, env: Env, userId
|
|||||||
const revisionDate = await storage.bulkSoftDeleteCiphers(body.ids, userId);
|
const revisionDate = await storage.bulkSoftDeleteCiphers(body.ids, userId);
|
||||||
if (revisionDate) {
|
if (revisionDate) {
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserCiphersSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
||||||
await writeCipherAudit(storage, request, userId, 'cipher.delete.soft.bulk', {
|
await writeCipherAudit(storage, request, userId, 'cipher.delete.soft.bulk', {
|
||||||
count: body.ids.length,
|
count: body.ids.length,
|
||||||
});
|
});
|
||||||
@@ -1265,6 +1336,7 @@ export async function handleBulkRestoreCiphers(request: Request, env: Env, userI
|
|||||||
const revisionDate = await storage.bulkRestoreCiphers(body.ids, userId);
|
const revisionDate = await storage.bulkRestoreCiphers(body.ids, userId);
|
||||||
if (revisionDate) {
|
if (revisionDate) {
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserCiphersSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(null, { status: 204 });
|
return new Response(null, { status: 204 });
|
||||||
@@ -1301,6 +1373,7 @@ export async function handleBulkPermanentDeleteCiphers(request: Request, env: En
|
|||||||
const revisionDate = await storage.bulkDeleteCiphers(ownedIds, userId);
|
const revisionDate = await storage.bulkDeleteCiphers(ownedIds, userId);
|
||||||
if (revisionDate) {
|
if (revisionDate) {
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserCiphersSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
||||||
await writeCipherAudit(storage, request, userId, 'cipher.delete.permanent.bulk', {
|
await writeCipherAudit(storage, request, userId, 'cipher.delete.permanent.bulk', {
|
||||||
count: ownedIds.length,
|
count: ownedIds.length,
|
||||||
requestedCount: ids.length,
|
requestedCount: ids.length,
|
||||||
|
|||||||
+24
-1
@@ -1,5 +1,10 @@
|
|||||||
import { Env, Folder, FolderResponse } from '../types';
|
import { Env, Folder, FolderResponse } from '../types';
|
||||||
import { notifyUserVaultSync } from '../durable/notifications-hub';
|
import {
|
||||||
|
notifyUserFolderCreate,
|
||||||
|
notifyUserFolderDelete,
|
||||||
|
notifyUserFolderUpdate,
|
||||||
|
notifyUserVaultSync,
|
||||||
|
} from '../durable/notifications-hub';
|
||||||
import { StorageService } from '../services/storage';
|
import { StorageService } from '../services/storage';
|
||||||
import { jsonResponse, errorResponse } from '../utils/response';
|
import { jsonResponse, errorResponse } from '../utils/response';
|
||||||
import { readActingDeviceIdentifier } from '../utils/device';
|
import { readActingDeviceIdentifier } from '../utils/device';
|
||||||
@@ -111,6 +116,12 @@ export async function handleCreateFolder(request: Request, env: Env, userId: str
|
|||||||
await storage.saveFolder(folder);
|
await storage.saveFolder(folder);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserFolderCreate(env, {
|
||||||
|
userId,
|
||||||
|
folderId: folder.id,
|
||||||
|
revisionDate,
|
||||||
|
contextId: readActingDeviceIdentifier(request),
|
||||||
|
});
|
||||||
|
|
||||||
return jsonResponse(folderToResponse(folder), 200);
|
return jsonResponse(folderToResponse(folder), 200);
|
||||||
}
|
}
|
||||||
@@ -139,6 +150,12 @@ export async function handleUpdateFolder(request: Request, env: Env, userId: str
|
|||||||
await storage.saveFolder(folder);
|
await storage.saveFolder(folder);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserFolderUpdate(env, {
|
||||||
|
userId,
|
||||||
|
folderId: folder.id,
|
||||||
|
revisionDate,
|
||||||
|
contextId: readActingDeviceIdentifier(request),
|
||||||
|
});
|
||||||
|
|
||||||
return jsonResponse(folderToResponse(folder));
|
return jsonResponse(folderToResponse(folder));
|
||||||
}
|
}
|
||||||
@@ -156,6 +173,12 @@ export async function handleDeleteFolder(request: Request, env: Env, userId: str
|
|||||||
await storage.deleteFolder(id, userId);
|
await storage.deleteFolder(id, userId);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifyUserFolderDelete(env, {
|
||||||
|
userId,
|
||||||
|
folderId: id,
|
||||||
|
revisionDate,
|
||||||
|
contextId: readActingDeviceIdentifier(request),
|
||||||
|
});
|
||||||
await writeFolderAudit(storage, request, userId, 'folder.delete', {
|
await writeFolderAudit(storage, request, userId, 'folder.delete', {
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import {
|
|||||||
formatSize,
|
formatSize,
|
||||||
getAliasedProp,
|
getAliasedProp,
|
||||||
normalizeEmails,
|
normalizeEmails,
|
||||||
|
notifySendCreateForRequest,
|
||||||
|
notifySendDeleteForRequest,
|
||||||
|
notifySendUpdateForRequest,
|
||||||
notifyVaultSyncForRequest,
|
notifyVaultSyncForRequest,
|
||||||
parseDate,
|
parseDate,
|
||||||
parseFileLength,
|
parseFileLength,
|
||||||
@@ -249,6 +252,7 @@ export async function handleCreateSend(request: Request, env: Env, userId: strin
|
|||||||
await storage.saveSend(send);
|
await storage.saveSend(send);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifySendCreateForRequest(request, env, send.id, userId, revisionDate);
|
||||||
|
|
||||||
return jsonResponse(sendToResponse(send));
|
return jsonResponse(sendToResponse(send));
|
||||||
}
|
}
|
||||||
@@ -372,6 +376,7 @@ export async function handleCreateFileSendV2(request: Request, env: Env, userId:
|
|||||||
await storage.saveSend(send);
|
await storage.saveSend(send);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifySendCreateForRequest(request, env, send.id, userId, revisionDate);
|
||||||
const jwtSecret = getSafeJwtSecret(env);
|
const jwtSecret = getSafeJwtSecret(env);
|
||||||
if (!jwtSecret) {
|
if (!jwtSecret) {
|
||||||
return errorResponse('Server configuration error', 500);
|
return errorResponse('Server configuration error', 500);
|
||||||
@@ -619,6 +624,7 @@ export async function handleUpdateSend(request: Request, env: Env, userId: strin
|
|||||||
await storage.saveSend(send);
|
await storage.saveSend(send);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifySendUpdateForRequest(request, env, send.id, userId, revisionDate);
|
||||||
|
|
||||||
return jsonResponse(sendToResponse(send));
|
return jsonResponse(sendToResponse(send));
|
||||||
}
|
}
|
||||||
@@ -641,6 +647,7 @@ export async function handleDeleteSend(request: Request, env: Env, userId: strin
|
|||||||
await storage.deleteSend(sendId, userId);
|
await storage.deleteSend(sendId, userId);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifySendDeleteForRequest(request, env, sendId, userId, revisionDate);
|
||||||
await writeSendAudit(storage, request, userId, 'send.delete', {
|
await writeSendAudit(storage, request, userId, 'send.delete', {
|
||||||
id: sendId,
|
id: sendId,
|
||||||
type: send.type,
|
type: send.type,
|
||||||
@@ -697,6 +704,7 @@ export async function handleRemoveSendPassword(request: Request, env: Env, userI
|
|||||||
await storage.saveSend(send);
|
await storage.saveSend(send);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifySendUpdateForRequest(request, env, send.id, userId, revisionDate);
|
||||||
await writeSendAudit(storage, request, userId, 'send.password.remove', {
|
await writeSendAudit(storage, request, userId, 'send.password.remove', {
|
||||||
id: send.id,
|
id: send.id,
|
||||||
type: send.type,
|
type: send.type,
|
||||||
@@ -718,6 +726,7 @@ export async function handleRemoveSendAuth(request: Request, env: Env, userId: s
|
|||||||
await storage.saveSend(send);
|
await storage.saveSend(send);
|
||||||
const revisionDate = await storage.updateRevisionDate(userId);
|
const revisionDate = await storage.updateRevisionDate(userId);
|
||||||
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
notifyVaultSyncForRequest(request, env, userId, revisionDate);
|
||||||
|
notifySendUpdateForRequest(request, env, send.id, userId, revisionDate);
|
||||||
await writeSendAudit(storage, request, userId, 'send.auth.remove', {
|
await writeSendAudit(storage, request, userId, 'send.auth.remove', {
|
||||||
id: send.id,
|
id: send.id,
|
||||||
type: send.type,
|
type: send.type,
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { Env, Send, SendAuthType, SendResponse, SendType, DEFAULT_DEV_SECRET } from '../types';
|
import { Env, Send, SendAuthType, SendResponse, SendType, DEFAULT_DEV_SECRET } from '../types';
|
||||||
import { notifyUserVaultSync } from '../durable/notifications-hub';
|
import {
|
||||||
|
notifyUserSendCreate,
|
||||||
|
notifyUserSendDelete,
|
||||||
|
notifyUserSendUpdate,
|
||||||
|
notifyUserVaultSync,
|
||||||
|
} from '../durable/notifications-hub';
|
||||||
import { StorageService } from '../services/storage';
|
import { StorageService } from '../services/storage';
|
||||||
import { jsonResponse, errorResponse } from '../utils/response';
|
import { jsonResponse, errorResponse } from '../utils/response';
|
||||||
import { readActingDeviceIdentifier } from '../utils/device';
|
import { readActingDeviceIdentifier } from '../utils/device';
|
||||||
@@ -18,6 +23,51 @@ export function notifyVaultSyncForRequest(
|
|||||||
notifyUserVaultSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
notifyUserVaultSync(env, userId, revisionDate, readActingDeviceIdentifier(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function notifySendCreateForRequest(
|
||||||
|
request: Request,
|
||||||
|
env: Env,
|
||||||
|
sendId: string,
|
||||||
|
userId: string,
|
||||||
|
revisionDate: string
|
||||||
|
): void {
|
||||||
|
notifyUserSendCreate(env, {
|
||||||
|
userId,
|
||||||
|
sendId,
|
||||||
|
revisionDate,
|
||||||
|
contextId: readActingDeviceIdentifier(request),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifySendUpdateForRequest(
|
||||||
|
request: Request,
|
||||||
|
env: Env,
|
||||||
|
sendId: string,
|
||||||
|
userId: string,
|
||||||
|
revisionDate: string
|
||||||
|
): void {
|
||||||
|
notifyUserSendUpdate(env, {
|
||||||
|
userId,
|
||||||
|
sendId,
|
||||||
|
revisionDate,
|
||||||
|
contextId: readActingDeviceIdentifier(request),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function notifySendDeleteForRequest(
|
||||||
|
request: Request,
|
||||||
|
env: Env,
|
||||||
|
sendId: string,
|
||||||
|
userId: string,
|
||||||
|
revisionDate: string
|
||||||
|
): void {
|
||||||
|
notifyUserSendDelete(env, {
|
||||||
|
userId,
|
||||||
|
sendId,
|
||||||
|
revisionDate,
|
||||||
|
contextId: readActingDeviceIdentifier(request),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getAliasedProp(source: unknown, aliases: string[]): { present: boolean; value: unknown } {
|
export function getAliasedProp(source: unknown, aliases: string[]): { present: boolean; value: unknown } {
|
||||||
if (!source || typeof source !== 'object') return { present: false, value: undefined };
|
if (!source || typeof source !== 'object') return { present: false, value: undefined };
|
||||||
for (const key of aliases) {
|
for (const key of aliases) {
|
||||||
|
|||||||
+2
-2
@@ -136,8 +136,8 @@ const THEME_STORAGE_KEY = 'nodewarden.theme.preference.v1';
|
|||||||
const SIGNALR_RECORD_SEPARATOR = String.fromCharCode(0x1e);
|
const SIGNALR_RECORD_SEPARATOR = String.fromCharCode(0x1e);
|
||||||
const SIGNALR_UPDATE_TYPE_SYNC_VAULT = 5;
|
const SIGNALR_UPDATE_TYPE_SYNC_VAULT = 5;
|
||||||
const SIGNALR_UPDATE_TYPE_LOG_OUT = 11;
|
const SIGNALR_UPDATE_TYPE_LOG_OUT = 11;
|
||||||
const SIGNALR_UPDATE_TYPE_DEVICE_STATUS = 12;
|
const SIGNALR_UPDATE_TYPE_DEVICE_STATUS = 101;
|
||||||
const SIGNALR_UPDATE_TYPE_BACKUP_RESTORE_PROGRESS = 13;
|
const SIGNALR_UPDATE_TYPE_BACKUP_RESTORE_PROGRESS = 102;
|
||||||
|
|
||||||
type ThemePreference = 'system' | 'light' | 'dark';
|
type ThemePreference = 'system' | 'light' | 'dark';
|
||||||
type LockTimeoutMinutes = 0 | 1 | 5 | 15 | 30;
|
type LockTimeoutMinutes = 0 | 1 | 5 | 15 | 30;
|
||||||
|
|||||||
Reference in New Issue
Block a user