mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: Adds an API to update attachment metadata, supporting the repair of encrypted information of old attachments
This commit is contained in:
@@ -279,6 +279,64 @@ export async function handleGetAttachment(
|
||||
});
|
||||
}
|
||||
|
||||
// PUT /api/ciphers/{cipherId}/attachment/{attachmentId}/metadata
|
||||
// 修正旧附件的加密元数据,供官方客户端按当前 Bitwarden 契约解密。
|
||||
export async function handleUpdateAttachmentMetadata(
|
||||
request: Request,
|
||||
env: Env,
|
||||
userId: string,
|
||||
cipherId: string,
|
||||
attachmentId: string
|
||||
): Promise<Response> {
|
||||
const storage = new StorageService(env.DB);
|
||||
|
||||
const cipher = await storage.getCipher(cipherId);
|
||||
if (!cipher || cipher.userId !== userId) {
|
||||
return errorResponse('Cipher not found', 404);
|
||||
}
|
||||
|
||||
const attachment = await storage.getAttachment(attachmentId);
|
||||
if (!attachment || attachment.cipherId !== cipherId) {
|
||||
return errorResponse('Attachment not found', 404);
|
||||
}
|
||||
|
||||
let body: { fileName?: string | null; key?: string | null };
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return errorResponse('Invalid JSON', 400);
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(body, 'fileName') && !Object.prototype.hasOwnProperty.call(body, 'key')) {
|
||||
return errorResponse('No metadata fields supplied', 400);
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(body, 'fileName')) {
|
||||
const fileName = String(body.fileName || '').trim();
|
||||
if (!fileName) return errorResponse('fileName is required', 400);
|
||||
attachment.fileName = fileName;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(body, 'key')) {
|
||||
const key = body.key == null ? null : String(body.key || '').trim();
|
||||
attachment.key = key || null;
|
||||
}
|
||||
|
||||
await storage.saveAttachment(attachment);
|
||||
const revisionInfo = await storage.updateCipherRevisionDate(cipherId);
|
||||
if (revisionInfo) {
|
||||
await notifyVaultSyncForRequest(request, env, revisionInfo.userId, revisionInfo.revisionDate);
|
||||
}
|
||||
|
||||
return jsonResponse({
|
||||
object: 'attachment',
|
||||
id: attachment.id,
|
||||
fileName: attachment.fileName,
|
||||
key: attachment.key,
|
||||
size: String(Number(attachment.size) || 0),
|
||||
sizeName: attachment.sizeName,
|
||||
});
|
||||
}
|
||||
|
||||
// GET /api/attachments/{cipherId}/{attachmentId}?token=xxx
|
||||
// Public download endpoint (uses token for auth instead of header)
|
||||
export async function handlePublicDownloadAttachment(
|
||||
|
||||
@@ -60,6 +60,7 @@ import {
|
||||
handleCreateAttachment,
|
||||
handleUploadAttachment,
|
||||
handleGetAttachment,
|
||||
handleUpdateAttachmentMetadata,
|
||||
handleDeleteAttachment,
|
||||
} from './handlers/attachments';
|
||||
import { handleAuthenticatedDeviceRoute } from './router-devices';
|
||||
@@ -201,6 +202,11 @@ export async function handleAuthenticatedRoute(
|
||||
if (method === 'DELETE') return handleDeleteAttachment(request, env, userId, cipherId, attachmentId);
|
||||
}
|
||||
|
||||
const attachmentMetadataMatch = subPath.match(/^\/attachment\/([a-f0-9-]+)\/metadata$/i);
|
||||
if (attachmentMetadataMatch && (method === 'POST' || method === 'PUT')) {
|
||||
return handleUpdateAttachmentMetadata(request, env, userId, cipherId, attachmentMetadataMatch[1]);
|
||||
}
|
||||
|
||||
const attachmentDeleteMatch = subPath.match(/^\/attachment\/([a-f0-9-]+)\/delete$/i);
|
||||
if (attachmentDeleteMatch && method === 'POST') {
|
||||
return handleDeleteAttachment(request, env, userId, cipherId, attachmentDeleteMatch[1]);
|
||||
|
||||
Reference in New Issue
Block a user