feat: add personal canvas workflows

This commit is contained in:
2026-05-26 11:18:28 +08:00
parent bbd1f08f7c
commit 5290812353
7 changed files with 698 additions and 33 deletions

View File

@@ -2189,6 +2189,18 @@ class CanvasProjectImportReq(BaseModel):
projects: list[CanvasProjectWriteReq] = Field(default_factory=list)
class CanvasWorkflowWriteReq(BaseModel):
id: str = ""
name: str = "未命名工作流"
description: str = ""
thumbnail: str = ""
workflow_data: dict = Field(default_factory=dict)
created_at: float = 0.0
updated_at: float = 0.0
source: str = "canvas"
source_project_id: str = ""
def _ts(value) -> float:
if hasattr(value, "timestamp"):
return float(value.timestamp())
@@ -2220,6 +2232,23 @@ def _canvas_project_public(row: dict) -> dict:
}
def _canvas_workflow_public(row: dict) -> dict:
return {
"id": str(row.get("id") or ""),
"name": str(row.get("name") or ""),
"description": str(row.get("description") or ""),
"thumbnail": str(row.get("thumbnail") or ""),
"workflow_data": row.get("workflow_data") or {},
"created_at": _ts(row.get("created_at")),
"updated_at": _ts(row.get("updated_at")),
"version": int(row.get("version") or 1),
"owner_id": str(row.get("owner_id") or ""),
"owner_name": str(row.get("owner_name") or ""),
"owner_email": str(row.get("owner_email") or ""),
"owner_provider": str(row.get("owner_provider") or ""),
}
@app.get("/canvas-projects")
def list_canvas_projects(request: Request) -> dict:
_require_db()
@@ -2239,6 +2268,8 @@ def create_canvas_project(req: CanvasProjectWriteReq, request: Request) -> dict:
row = db.upsert_canvas_project(user, req.model_dump())
if not row:
raise HTTPException(500, "canvas project save failed")
if str(row.get("owner_id") or "") != _session_user_id(user):
raise HTTPException(403, "canvas project belongs to another user")
db.audit(user, "canvas_project.create", "canvas_project", str(row.get("id") or ""), req.model_dump(exclude={"canvas_data"}), request, str(row.get("visibility") or "private"))
return {"ok": True, "item": _canvas_project_public(row)}
@@ -2296,6 +2327,58 @@ def import_canvas_projects(req: CanvasProjectImportReq, request: Request) -> dic
return {"ok": True, "items": imported}
@app.get("/canvas-workflows")
def list_canvas_workflows(request: Request) -> dict:
_require_db()
user = data_user_from_request(request)
db.upsert_user(user, request)
return {
"ok": True,
"items": [_canvas_workflow_public(row) for row in db.list_canvas_workflows(user)],
}
@app.post("/canvas-workflows")
def create_canvas_workflow(req: CanvasWorkflowWriteReq, request: Request) -> dict:
_require_db()
user = data_user_from_request(request)
db.upsert_user(user, request)
row = db.upsert_canvas_workflow(user, req.model_dump())
if not row:
raise HTTPException(500, "canvas workflow save failed")
if str(row.get("owner_id") or "") != _session_user_id(user):
raise HTTPException(403, "canvas workflow belongs to another user")
db.audit(user, "canvas_workflow.create", "canvas_workflow", str(row.get("id") or ""), {"name": req.name, "source_project_id": req.source_project_id}, request, "private")
return {"ok": True, "item": _canvas_workflow_public(row)}
@app.put("/canvas-workflows/{workflow_id}")
def put_canvas_workflow(workflow_id: str, req: CanvasWorkflowWriteReq, request: Request) -> dict:
_require_db()
user = data_user_from_request(request)
db.upsert_user(user, request)
payload = req.model_dump()
payload["id"] = workflow_id
row = db.upsert_canvas_workflow(user, payload)
if not row:
raise HTTPException(500, "canvas workflow save failed")
if str(row.get("owner_id") or "") != _session_user_id(user):
raise HTTPException(403, "canvas workflow belongs to another user")
db.audit(user, "canvas_workflow.save", "canvas_workflow", workflow_id, {"name": req.name}, request, "private")
return {"ok": True, "item": _canvas_workflow_public(row)}
@app.delete("/canvas-workflows/{workflow_id}")
def delete_canvas_workflow(workflow_id: str, request: Request) -> dict:
_require_db()
user = data_user_from_request(request)
ok = db.soft_delete_canvas_workflow(user, workflow_id)
if not ok:
raise HTTPException(404, "canvas workflow not found")
db.audit(user, "canvas_workflow.delete", "canvas_workflow", workflow_id, request=request, visibility="private")
return {"ok": True, "id": workflow_id}
def _parse_library_metadata(raw: str) -> dict:
if not raw.strip():
return {}