deliverable-report-v2.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /**
  2. * 交付物上报云函数(skill-deliverable 配套)
  3. * ============================================
  4. *
  5. * 部署:Parse.Cloud.define 或 /api/functions 模式
  6. * 云函数 ID:lfYlgU7SkK(保持不变)
  7. *
  8. * v2 变更:
  9. * 1. 移除 FMODE_AGENT_SUPERADMIN 角色检查 → 改为:用户仅对自有容器有上报权限
  10. * 2. agentId → agentObjectId 自动解析(传任一即可,缺失则自动补全)
  11. * 3. 首次上报时自动创建 FmodeAgent 记录(自注册)
  12. * 4. 参数缺失时自动补全(时间戳、状态等)
  13. *
  14. * 鉴权逻辑(简化版):
  15. * sessionToken → _User → 查此用户是否是该 FmodeAgent 记录的 owner
  16. * 是 → 允许上报;否 → 403
  17. * 若 FmodeAgent 记录不存在且 agentId 有效 → 自动创建 + 设当前用户为 owner
  18. */
  19. const CLASS_DELIVERABLE = 'AgentDeliverable';
  20. const CLASS_AGENT = 'FmodeAgent';
  21. Parse.Cloud.define('deliverable-report', async (req) => {
  22. const { agentId, agentObjectId, title, summary, project, artifacts, tags } = req.params;
  23. const user = req.user;
  24. if (!user) throw new Parse.Error(401, '需要登录');
  25. if (!title) throw new Parse.Error(400, 'title 必填');
  26. // ---- Step 1: 解析/注册 FmodeAgent ----
  27. let agentQuery = new Parse.Query(CLASS_AGENT);
  28. if (agentObjectId) {
  29. agentQuery.equalTo('objectId', agentObjectId);
  30. } else if (agentId) {
  31. agentQuery.equalTo('agentId', agentId);
  32. } else {
  33. throw new Parse.Error(400, 'agentId 或 agentObjectId 必填');
  34. }
  35. let agent = await agentQuery.first({ useMasterKey: true });
  36. // 自动注册:agent 不存在则创建
  37. if (!agent) {
  38. if (!agentId) throw new Parse.Error(400, '新 agent 注册需要 agentId');
  39. const AgentClass = Parse.Object.extend(CLASS_AGENT);
  40. agent = new AgentClass();
  41. agent.set('agentId', agentId);
  42. agent.set('user', user);
  43. agent.set('status', 'online');
  44. agent.set('heartbeatAt', new Date());
  45. agent = await agent.save(null, { useMasterKey: true });
  46. }
  47. const finalAgentId = agent.get('agentId') || agentId;
  48. const finalAgentOid = agent.id;
  49. // ---- Step 2: 权限校验(仅校验用户是否为 agent owner)----
  50. const agentUser = agent.get('user');
  51. if (agentUser && agentUser.id !== user.id) {
  52. throw new Parse.Error(403, '仅该容器的 owner 可上报');
  53. }
  54. // 如果 agent 没有 owner(旧数据),自动绑定当前用户
  55. if (!agentUser) {
  56. agent.set('user', user);
  57. await agent.save(null, { useMasterKey: true });
  58. }
  59. // ---- Step 3: 写入交付物 ----
  60. const DelClass = Parse.Object.extend(CLASS_DELIVERABLE);
  61. const record = new DelClass();
  62. record.set('agentId', finalAgentId);
  63. record.set('agent', agent); // 指针
  64. record.set('agentObjectId', finalAgentOid);
  65. record.set('user', user);
  66. record.set('title', title);
  67. record.set('summary', summary || '');
  68. record.set('project', project || '');
  69. record.set('artifacts', artifacts || []);
  70. record.set('tags', tags || []);
  71. record.set('reportedAt', new Date());
  72. // ACL:仅 owner 可读写
  73. const acl = new Parse.ACL(user);
  74. acl.setPublicReadAccess(false);
  75. acl.setPublicWriteAccess(false);
  76. record.setACL(acl);
  77. const saved = await record.save(null, { useMasterKey: true });
  78. // ---- Step 4: 更新 agent 心跳 ----
  79. agent.set('heartbeatAt', new Date());
  80. agent.set('status', 'online');
  81. await agent.save(null, { useMasterKey: true });
  82. return {
  83. code: 200,
  84. id: saved.id,
  85. agentId: finalAgentId,
  86. agentObjectId: finalAgentOid,
  87. title: title,
  88. createdAt: saved.get('createdAt') || saved.createdAt,
  89. };
  90. });