auto-sync: 2026-05-29 21:11:09
This commit is contained in:
+79
-14
@@ -182,22 +182,83 @@ async def get_mail(mail_id: str):
|
||||
|
||||
@router.post("")
|
||||
async def send_mail(body: Dict[str, Any]):
|
||||
"""发送 Mail(创建 Task)"""
|
||||
"""发送 Mail(创建 Task)
|
||||
|
||||
API 层防御(A1-A10):
|
||||
A1: from 必填
|
||||
A9: from 必须是有效 Agent
|
||||
A5: in_reply_to 存在性校验
|
||||
A6/A7: 自动纠正 to
|
||||
A2: to 必填(非回复)
|
||||
A3: from != to 防自环
|
||||
A4: to 必须是有效 Agent
|
||||
A8: 回复权限校验(严格 1 对 1)
|
||||
A10: 正文非空
|
||||
"""
|
||||
bb = _bb()
|
||||
valid_agents = _get_valid_agents()
|
||||
auto_corrected = None
|
||||
|
||||
# --- A1: from 必填 ---
|
||||
from_agent = body.get("from", "").strip()
|
||||
if not from_agent:
|
||||
raise HTTPException(400, "`from` 必填")
|
||||
|
||||
# --- A9: from 必须是有效 Agent ---
|
||||
if from_agent not in valid_agents:
|
||||
raise HTTPException(400, f"`from` 不是有效的 Agent: {from_agent}")
|
||||
|
||||
# --- A5/A6/A7/A8: in_reply_to 处理 ---
|
||||
in_reply_to = body.get("in_reply_to")
|
||||
original = None
|
||||
if in_reply_to:
|
||||
original = bb.get_task(in_reply_to)
|
||||
# A5: 原邮件必须存在
|
||||
if not original:
|
||||
raise HTTPException(400, f"回复的邮件不存在: {in_reply_to}")
|
||||
|
||||
orig_from = original.assigned_by or ""
|
||||
orig_to = original.assignee or ""
|
||||
|
||||
# A8: 只有原邮件的双方能回复(严格 1 对 1)
|
||||
if from_agent not in (orig_from, orig_to):
|
||||
raise HTTPException(400, f"只有邮件的发送者或接收者可以回复")
|
||||
|
||||
# A6/A7: 自动纠正 to → 原邮件发件者
|
||||
to_agent = body.get("to", "").strip()
|
||||
corrected_to = orig_from # 回复方向固定: reply → original sender
|
||||
if to_agent and to_agent != corrected_to:
|
||||
auto_corrected = {"field": "to", "original": to_agent, "corrected": corrected_to}
|
||||
to_agent = corrected_to
|
||||
else:
|
||||
# --- A2: to 必填(非回复场景) ---
|
||||
to_agent = body.get("to", "").strip()
|
||||
if not to_agent:
|
||||
raise HTTPException(400, "`to` 必填")
|
||||
|
||||
# --- A3: from != to 防自环 ---
|
||||
if from_agent == to_agent:
|
||||
raise HTTPException(400, "不能给自己发邮件")
|
||||
|
||||
# --- A4: to 必须是有效 Agent ---
|
||||
if to_agent not in valid_agents:
|
||||
raise HTTPException(400, f"`to` 不是有效的 Agent: {to_agent}")
|
||||
|
||||
# --- A10: 正文非空 ---
|
||||
text = body.get("text", body.get("description", "")) or ""
|
||||
if not text.strip():
|
||||
raise HTTPException(400, "邮件正文不能为空")
|
||||
|
||||
mail_id = body.get("id", f"mail-{int(datetime.now().timestamp() * 1000)}")
|
||||
|
||||
# 自动处理 conversation_id:有 in_reply_to 时继承原邮件的 conversation_id
|
||||
conversation_id = body.get("conversation_id")
|
||||
in_reply_to = body.get("in_reply_to")
|
||||
if not conversation_id and in_reply_to:
|
||||
original = bb.get_task(in_reply_to)
|
||||
if original:
|
||||
try:
|
||||
orig_meta = json.loads(original.must_haves) if original.must_haves else {}
|
||||
conversation_id = orig_meta.get("conversation_id")
|
||||
except Exception:
|
||||
pass
|
||||
if not conversation_id and original:
|
||||
try:
|
||||
orig_meta = json.loads(original.must_haves) if original.must_haves else {}
|
||||
conversation_id = orig_meta.get("conversation_id")
|
||||
except Exception:
|
||||
pass
|
||||
if not conversation_id:
|
||||
conversation_id = f"conv-{int(datetime.now().timestamp() * 1000)}"
|
||||
|
||||
@@ -220,15 +281,19 @@ async def send_mail(body: Dict[str, Any]):
|
||||
task = Task(
|
||||
id=mail_id,
|
||||
title=body.get("title", ""),
|
||||
description=body.get("text", body.get("description", "")),
|
||||
assignee=body.get("to"),
|
||||
assigned_by=body.get("from", "user"),
|
||||
description=text,
|
||||
assignee=to_agent,
|
||||
assigned_by=from_agent,
|
||||
must_haves=json.dumps(meta),
|
||||
task_type="mail",
|
||||
status="pending",
|
||||
)
|
||||
bb.create_task(task)
|
||||
return {"ok": True, "mail_id": task.id}
|
||||
|
||||
result = {"ok": True, "mail_id": task.id}
|
||||
if auto_corrected:
|
||||
result["auto_corrected"] = auto_corrected
|
||||
return result
|
||||
|
||||
|
||||
@router.patch("/{mail_id}")
|
||||
|
||||
Reference in New Issue
Block a user