storage.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. * Data storage utilities for SaaS VOC Cloud Functions
  3. * Provides write operations for Parse objects
  4. */
  5. /**
  6. * Safely serialize a value, removing sensitive fields
  7. */
  8. function safeValue(value, depth) {
  9. if (depth > 5 || value === null || value === undefined) return value;
  10. if (value instanceof Date) return value;
  11. if (Array.isArray(value)) return value.slice(0, 100).map((item) => safeValue(item, depth + 1));
  12. if (typeof value !== 'object') return value;
  13. const output = {};
  14. Object.keys(value).slice(0, 100).forEach((key) => {
  15. if (/token|secret|password|credential|authorization|master/i.test(key)) return;
  16. output[key] = safeValue(value[key], depth + 1);
  17. });
  18. return output;
  19. }
  20. // Fields excluded from body data
  21. const EXCLUDED_BODY_FIELDS = [
  22. 'action', 'workspaceId', 'platform', 'payload', 'idempotencyKey',
  23. 'userId', 'jobId', 'productId', 'decisionId', 'analysisId',
  24. 'actionId', 'alertId', 'taskId', 'runId', 'versionId',
  25. ];
  26. // Fields excluded from storage data
  27. const EXCLUDED_STORAGE_FIELDS = ['action', 'workspaceId', 'userId'];
  28. /**
  29. * Get user ID from active request
  30. */
  31. function getUserId() {
  32. return Parse?.request?.user?.id || null;
  33. }
  34. /**
  35. * Extract body data from params (excluding system fields)
  36. */
  37. function bodyData(params, workspaceId) {
  38. const output = {};
  39. Object.keys(params).forEach((key) => {
  40. if (!EXCLUDED_BODY_FIELDS.includes(key)) {
  41. output[key] = safeValue(params[key], 0);
  42. }
  43. });
  44. output.workspaceId = workspaceId;
  45. return output;
  46. }
  47. /**
  48. * Extract storage data from params
  49. */
  50. function storageData(params, workspaceId) {
  51. const output = {};
  52. Object.keys(params).forEach((key) => {
  53. if (!EXCLUDED_STORAGE_FIELDS.includes(key)) {
  54. output[key] = safeValue(params[key], 0);
  55. }
  56. });
  57. output.workspaceId = workspaceId;
  58. return output;
  59. }
  60. /**
  61. * Generate a unique public ID
  62. */
  63. function publicId() {
  64. return 'cf-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10);
  65. }
  66. /**
  67. * Write a new object
  68. */
  69. async function writeObject(className, params, workspaceId) {
  70. const object = new Parse.Object(className);
  71. Object.entries(storageData(params, workspaceId)).forEach(([key, value]) => {
  72. object.set(key, value);
  73. });
  74. object.set('createdBy', getUserId());
  75. await object.save(null, { useMasterKey: true });
  76. return safeValue(object.toJSON(), 0);
  77. }
  78. /**
  79. * Update an existing object by public ID
  80. */
  81. async function updateByPublicId(className, workspaceId, id, params) {
  82. const object = await findObject(className, workspaceId, 'publicId', id);
  83. if (!object) {
  84. throw { status: 404, code: 'not_found', message: '记录不存在' };
  85. }
  86. Object.entries(bodyData(params, workspaceId)).forEach(([key, value]) => {
  87. object.set(key, value);
  88. });
  89. object.set('updatedBy', getUserId());
  90. await object.save(null, { useMasterKey: true });
  91. return safeValue(object.toJSON(), 0);
  92. }
  93. /**
  94. * Find an object by field value
  95. */
  96. async function findObject(className, workspaceId, field, value) {
  97. const query = new Parse.Query(className);
  98. query.equalTo('workspaceId', workspaceId);
  99. query.equalTo(field, value);
  100. return query.first({ useMasterKey: true });
  101. }
  102. /**
  103. * Create or return existing object (idempotent operation)
  104. */
  105. async function createIdempotent(className, workspaceId, keyField, keyValue, params) {
  106. if (keyValue) {
  107. const existing = await findObject(className, workspaceId, keyField, keyValue);
  108. if (existing) {
  109. return { value: safeValue(existing.toJSON(), 0), idempotent: true };
  110. }
  111. }
  112. const object = new Parse.Object(className);
  113. Object.entries(storageData(params, workspaceId)).forEach(([key, value]) => {
  114. object.set(key, value);
  115. });
  116. if (!object.get('publicId')) {
  117. object.set('publicId', publicId());
  118. }
  119. if (keyValue) {
  120. object.set(keyField, keyValue);
  121. }
  122. object.set('createdBy', getUserId());
  123. await object.save(null, { useMasterKey: true });
  124. return { value: safeValue(object.toJSON(), 0), idempotent: false };
  125. }
  126. /*
  127. // module.exports = {
  128. // safeValue,
  129. // getUserId,
  130. // bodyData,
  131. // storageData,
  132. // publicId,
  133. // writeObject,
  134. // updateByPublicId,
  135. // findObject,
  136. // createIdempotent,
  137. // };
  138. */