Explorar o código

chore: 新增云函数 v2 源码(cloud/deliverable-report-v2.js)

liuyuyang hai 1 día
pai
achega
62800f20f7
Modificáronse 1 ficheiros con 104 adicións e 0 borrados
  1. 104 0
      cloud/deliverable-report-v2.js

+ 104 - 0
cloud/deliverable-report-v2.js

@@ -0,0 +1,104 @@
+/**
+ * 交付物上报云函数(skill-deliverable 配套)
+ * ============================================
+ * 
+ * 部署:Parse.Cloud.define 或 /api/functions 模式
+ * 云函数 ID:lfYlgU7SkK(保持不变)
+ *
+ * v2 变更:
+ *   1. 移除 FMODE_AGENT_SUPERADMIN 角色检查 → 改为:用户仅对自有容器有上报权限
+ *   2. agentId → agentObjectId 自动解析(传任一即可,缺失则自动补全)
+ *   3. 首次上报时自动创建 FmodeAgent 记录(自注册)
+ *   4. 参数缺失时自动补全(时间戳、状态等)
+ *
+ * 鉴权逻辑(简化版):
+ *   sessionToken → _User → 查此用户是否是该 FmodeAgent 记录的 owner
+ *   是 → 允许上报;否 → 403
+ *   若 FmodeAgent 记录不存在且 agentId 有效 → 自动创建 + 设当前用户为 owner
+ */
+
+const CLASS_DELIVERABLE = 'AgentDeliverable';
+const CLASS_AGENT = 'FmodeAgent';
+
+Parse.Cloud.define('deliverable-report', async (req) => {
+  const { agentId, agentObjectId, title, summary, project, artifacts, tags } = req.params;
+  const user = req.user;
+
+  if (!user) throw new Parse.Error(401, '需要登录');
+  if (!title) throw new Parse.Error(400, 'title 必填');
+
+  // ---- Step 1: 解析/注册 FmodeAgent ----
+  let agentQuery = new Parse.Query(CLASS_AGENT);
+  
+  if (agentObjectId) {
+    agentQuery.equalTo('objectId', agentObjectId);
+  } else if (agentId) {
+    agentQuery.equalTo('agentId', agentId);
+  } else {
+    throw new Parse.Error(400, 'agentId 或 agentObjectId 必填');
+  }
+
+  let agent = await agentQuery.first({ useMasterKey: true });
+
+  // 自动注册:agent 不存在则创建
+  if (!agent) {
+    if (!agentId) throw new Parse.Error(400, '新 agent 注册需要 agentId');
+    const AgentClass = Parse.Object.extend(CLASS_AGENT);
+    agent = new AgentClass();
+    agent.set('agentId', agentId);
+    agent.set('user', user);
+    agent.set('status', 'online');
+    agent.set('heartbeatAt', new Date());
+    agent = await agent.save(null, { useMasterKey: true });
+  }
+
+  const finalAgentId = agent.get('agentId') || agentId;
+  const finalAgentOid = agent.id;
+
+  // ---- Step 2: 权限校验(仅校验用户是否为 agent owner)----
+  const agentUser = agent.get('user');
+  if (agentUser && agentUser.id !== user.id) {
+    throw new Parse.Error(403, '仅该容器的 owner 可上报');
+  }
+  // 如果 agent 没有 owner(旧数据),自动绑定当前用户
+  if (!agentUser) {
+    agent.set('user', user);
+    await agent.save(null, { useMasterKey: true });
+  }
+
+  // ---- Step 3: 写入交付物 ----
+  const DelClass = Parse.Object.extend(CLASS_DELIVERABLE);
+  const record = new DelClass();
+  record.set('agentId', finalAgentId);
+  record.set('agent', agent);   // 指针
+  record.set('agentObjectId', finalAgentOid);
+  record.set('user', user);
+  record.set('title', title);
+  record.set('summary', summary || '');
+  record.set('project', project || '');
+  record.set('artifacts', artifacts || []);
+  record.set('tags', tags || []);
+  record.set('reportedAt', new Date());
+  
+  // ACL:仅 owner 可读写
+  const acl = new Parse.ACL(user);
+  acl.setPublicReadAccess(false);
+  acl.setPublicWriteAccess(false);
+  record.setACL(acl);
+
+  const saved = await record.save(null, { useMasterKey: true });
+
+  // ---- Step 4: 更新 agent 心跳 ----
+  agent.set('heartbeatAt', new Date());
+  agent.set('status', 'online');
+  await agent.save(null, { useMasterKey: true });
+
+  return {
+    code: 200,
+    id: saved.id,
+    agentId: finalAgentId,
+    agentObjectId: finalAgentOid,
+    title: title,
+    createdAt: saved.get('createdAt') || saved.createdAt,
+  };
+});