/* * Data storage utilities for SaaS VOC Cloud Functions * Provides write operations for Parse objects */ /** * Safely serialize a value, removing sensitive fields */ function safeValue(value, depth) { if (depth > 5 || value === null || value === undefined) return value; if (value instanceof Date) return value; if (Array.isArray(value)) return value.slice(0, 100).map((item) => safeValue(item, depth + 1)); if (typeof value !== 'object') return value; const output = {}; Object.keys(value).slice(0, 100).forEach((key) => { if (/token|secret|password|credential|authorization|master/i.test(key)) return; output[key] = safeValue(value[key], depth + 1); }); return output; } // Fields excluded from body data const EXCLUDED_BODY_FIELDS = [ 'action', 'workspaceId', 'platform', 'payload', 'idempotencyKey', 'userId', 'jobId', 'productId', 'decisionId', 'analysisId', 'actionId', 'alertId', 'taskId', 'runId', 'versionId', ]; // Fields excluded from storage data const EXCLUDED_STORAGE_FIELDS = ['action', 'workspaceId', 'userId']; /** * Get user ID from active request */ function getUserId() { return Parse?.request?.user?.id || null; } /** * Extract body data from params (excluding system fields) */ function bodyData(params, workspaceId) { const output = {}; Object.keys(params).forEach((key) => { if (!EXCLUDED_BODY_FIELDS.includes(key)) { output[key] = safeValue(params[key], 0); } }); output.workspaceId = workspaceId; return output; } /** * Extract storage data from params */ function storageData(params, workspaceId) { const output = {}; Object.keys(params).forEach((key) => { if (!EXCLUDED_STORAGE_FIELDS.includes(key)) { output[key] = safeValue(params[key], 0); } }); output.workspaceId = workspaceId; return output; } /** * Generate a unique public ID */ function publicId() { return 'cf-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10); } /** * Write a new object */ async function writeObject(className, params, workspaceId) { const object = new Parse.Object(className); Object.entries(storageData(params, workspaceId)).forEach(([key, value]) => { object.set(key, value); }); object.set('createdBy', getUserId()); await object.save(null, { useMasterKey: true }); return safeValue(object.toJSON(), 0); } /** * Update an existing object by public ID */ async function updateByPublicId(className, workspaceId, id, params) { const object = await findObject(className, workspaceId, 'publicId', id); if (!object) { throw { status: 404, code: 'not_found', message: '记录不存在' }; } Object.entries(bodyData(params, workspaceId)).forEach(([key, value]) => { object.set(key, value); }); object.set('updatedBy', getUserId()); await object.save(null, { useMasterKey: true }); return safeValue(object.toJSON(), 0); } /** * Find an object by field value */ async function findObject(className, workspaceId, field, value) { const query = new Parse.Query(className); query.equalTo('workspaceId', workspaceId); query.equalTo(field, value); return query.first({ useMasterKey: true }); } /** * Create or return existing object (idempotent operation) */ async function createIdempotent(className, workspaceId, keyField, keyValue, params) { if (keyValue) { const existing = await findObject(className, workspaceId, keyField, keyValue); if (existing) { return { value: safeValue(existing.toJSON(), 0), idempotent: true }; } } const object = new Parse.Object(className); Object.entries(storageData(params, workspaceId)).forEach(([key, value]) => { object.set(key, value); }); if (!object.get('publicId')) { object.set('publicId', publicId()); } if (keyValue) { object.set(keyField, keyValue); } object.set('createdBy', getUserId()); await object.save(null, { useMasterKey: true }); return { value: safeValue(object.toJSON(), 0), idempotent: false }; } /* // module.exports = { // safeValue, // getUserId, // bodyData, // storageData, // publicId, // writeObject, // updateByPublicId, // findObject, // createIdempotent, // }; */