const db = require('../db'); const { requireAuth } = require('../auth'); function normalizeVaultItem(item) { if (!item || typeof item !== 'object') { return null; } var itemType = String(item.itemType || '').trim().slice(0, 64); var itemKey = String(item.itemKey || '').trim().slice(0, 191); var payloadCiphertext = String(item.payloadCiphertext || '').trim(); var payloadIv = String(item.payloadIv || '').trim().slice(0, 128); var payloadTag = String(item.payloadTag || '').trim().slice(0, 128); var payloadHash = String(item.payloadHash || '').trim().slice(0, 64); var keyVersion = Math.max(1, Number(item.keyVersion) || 1); if (!itemType || !itemKey || !payloadCiphertext || !payloadIv || !payloadTag || !payloadHash) { return null; } return { itemType: itemType, itemKey: itemKey, payloadCiphertext: payloadCiphertext, payloadIv: payloadIv, payloadTag: payloadTag, payloadHash: payloadHash, keyVersion: keyVersion }; } async function routes(fastify) { fastify.addHook('preHandler', requireAuth); fastify.post('/api/vault/push', async function (request, reply) { var items = Array.isArray(request.body && request.body.items) ? request.body.items : []; var normalized = items.map(normalizeVaultItem).filter(Boolean); var index = 0; var item = null; if (normalized.length === 0) { reply.code(400); return { ok: false, error: '没有可保存的保险柜项目' }; } for (index = 0; index < normalized.length; index++) { item = normalized[index]; await db.execute( 'INSERT INTO vault_items (user_id, item_type, item_key, payload_ciphertext, payload_iv, payload_tag, payload_hash, key_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE payload_ciphertext = VALUES(payload_ciphertext), payload_iv = VALUES(payload_iv), payload_tag = VALUES(payload_tag), payload_hash = VALUES(payload_hash), key_version = VALUES(key_version), updated_at = CURRENT_TIMESTAMP', [ request.authContext.user.id, item.itemType, item.itemKey, item.payloadCiphertext, item.payloadIv, item.payloadTag, item.payloadHash, item.keyVersion ] ); } return { ok: true, savedCount: normalized.length }; }); fastify.post('/api/vault/pull', async function (request) { var itemTypes = Array.isArray(request.body && request.body.itemTypes) ? request.body.itemTypes.map(function (itemType) { return String(itemType || '').trim().slice(0, 64); }).filter(Boolean) : []; var sql = 'SELECT item_type, item_key, payload_ciphertext, payload_iv, payload_tag, payload_hash, key_version, updated_at FROM vault_items WHERE user_id = ?'; var params = [request.authContext.user.id]; if (itemTypes.length > 0) { sql += ' AND item_type IN (' + itemTypes.map(function () { return '?'; }).join(',') + ')'; params = params.concat(itemTypes); } sql += ' ORDER BY updated_at DESC'; var rows = await db.query(sql, params); return { ok: true, items: rows.map(function (row) { return { itemType: row.item_type, itemKey: row.item_key, payloadCiphertext: row.payload_ciphertext, payloadIv: row.payload_iv, payloadTag: row.payload_tag, payloadHash: row.payload_hash, keyVersion: Number(row.key_version || 1), updatedAt: row.updated_at }; }) }; }); } module.exports = routes;