Files

296 lines
10 KiB
Python

import json
from datetime import datetime
from time import monotonic
from aiohttp import web
from .logger import log
def _ok(**kwargs):
return web.json_response({"status": "success", **kwargs})
def _err(msg, status=400):
return web.json_response({"status": "error", "message": msg}, status=status)
class WorkflowManagerAPI:
def __init__(self, manager):
self.manager = manager
self._recent_auto_saves = {}
async def _body(self, request):
try:
return await request.json()
except Exception:
return {}
def _find_existing_workflow_id(self, name, group_id):
matches = []
for workflow_id, workflow in self.manager.get_all_workflows().items():
if workflow.get("name") == name and workflow.get("group") == group_id:
matches.append((workflow_id, workflow))
if not matches:
return None
matches.sort(
key=lambda match: (
match[1].get("modified_at") or match[1].get("created_at") or "",
match[0],
),
reverse=True,
)
if len(matches) > 1:
ids = ", ".join(workflow_id[:8] for workflow_id, _ in matches)
log(f"[yeban-WM] Detected duplicate workflow name/group while saving {name}: {ids}")
return matches[0][0]
async def list_workflows(self, request):
try:
return _ok(
workflows=self.manager.get_all_workflows(),
groups=self.manager.get_all_groups(),
)
except Exception as e:
return _err(str(e), 500)
async def save_workflow(self, request):
try:
d = await self._body(request)
wf_data = d.get("workflow_data")
if isinstance(wf_data, str):
wf_data = json.loads(wf_data)
name = d.get("workflow_name")
group_id = d.get("group_id") or None
group_label = group_id or "\u6839\u76ee\u5f55"
is_auto_save = d.get("auto_save") is not False
if not name:
return _err("workflow_name required", 400)
now = datetime.now().strftime("%H:%M:%S")
existing_id = self._find_existing_workflow_id(name, group_id)
if existing_id:
if is_auto_save:
last_saved_at = self._recent_auto_saves.get(existing_id, 0)
if monotonic() - last_saved_at < 10:
log(f"[yeban-WM] [{now}] \u5df2\u62e6\u622a\u91cd\u590d\u81ea\u52a8\u4fdd\u5b58: {name} -> {group_label} | id: {existing_id[:8]}...")
return _ok(workflow_id=existing_id, throttled=True)
current_data = self.manager.load_workflow(existing_id)
if current_data == wf_data:
if is_auto_save:
self._recent_auto_saves[existing_id] = monotonic()
log(f"[yeban-WM] [{now}] \u8df3\u8fc7\u672a\u53d8\u5316\u7684\u4fdd\u5b58: {name} -> {group_label} | id: {existing_id[:8]}...")
return _ok(workflow_id=existing_id, unchanged=True)
self.manager.save_version(existing_id)
log(f"[yeban-WM] [{now}] \u5df2\u521b\u5efa\u7248\u672c\u5feb\u7167: {name}")
wf_id = self.manager.save_workflow(wf_data, name, group_id)
if existing_id:
log(f"[yeban-WM] [{now}] \u5df2\u8986\u76d6: {name} -> {group_label} | id: {wf_id[:8]}...")
else:
log(f"[yeban-WM] [{now}] \u5df2\u4fdd\u5b58: {name} -> {group_label} | id: {wf_id[:8]}...")
if is_auto_save:
self._recent_auto_saves[wf_id] = monotonic()
return _ok(workflow_id=wf_id)
except Exception as e:
log(f"[yeban-WM] \u4fdd\u5b58\u5931\u8d25: {e}")
return _err(str(e), 500)
async def load_workflow(self, request):
try:
d = await self._body(request)
wf_id = d.get("workflow_id")
if not wf_id:
return _err("workflow_id required", 400)
data = self.manager.load_workflow(wf_id)
if data is None:
return _err("Workflow not found", 404)
return _ok(data=data)
except Exception as e:
return _err(str(e), 500)
async def delete_workflow(self, request):
try:
d = await self._body(request)
result = self.manager.delete_workflow(d.get("workflow_id"))
return _ok() if result else _err("Workflow not found", 404)
except Exception as e:
return _err(str(e), 500)
async def rename_workflow(self, request):
try:
d = await self._body(request)
result = self.manager.rename_workflow(d.get("workflow_id"), d.get("new_name"))
return _ok(workflow_id=result) if result else _err("Workflow not found", 404)
except Exception as e:
return _err(str(e), 500)
async def move_workflow(self, request):
try:
d = await self._body(request)
result = self.manager.move_workflow(
d.get("workflow_id"),
d.get("target_group_id") or None,
)
return _ok(workflow_id=result) if result else _err("Workflow not found", 404)
except Exception as e:
return _err(str(e), 500)
async def copy_workflow(self, request):
try:
d = await self._body(request)
result = self.manager.copy_workflow(
d.get("workflow_id"),
d.get("target_group_id") or None,
d.get("new_name") or None,
)
return _ok(workflow_id=result) if result else _err("Workflow not found", 404)
except Exception as e:
return _err(str(e), 500)
async def create_group(self, request):
try:
d = await self._body(request)
name = d.get("name")
if not name:
return _err("name required", 400)
group_id = self.manager.create_group(name, d.get("parent_group_id") or None)
return _ok(group_id=group_id)
except Exception as e:
return _err(str(e), 500)
async def delete_group(self, request):
try:
d = await self._body(request)
result = self.manager.delete_group(d.get("group_id"))
return _ok() if result else _err("Group not found", 404)
except Exception as e:
return _err(str(e), 500)
async def rename_group(self, request):
try:
d = await self._body(request)
result = self.manager.rename_group(d.get("group_id"), d.get("new_name"))
return _ok(group_id=result) if result else _err("Group not found", 404)
except Exception as e:
return _err(str(e), 500)
async def save_thumbnail(self, request):
try:
d = await self._body(request)
result = self.manager.save_thumbnail(d.get("workflow_id"), d.get("data_url"))
return _ok() if result else _err("Workflow not found", 404)
except Exception as e:
return _err(str(e), 500)
async def get_thumbnail(self, request):
try:
wf_id = request.match_info.get("workflow_id")
path = self.manager.get_thumbnail_path(wf_id)
if path is None:
return web.Response(status=404)
return web.FileResponse(path)
except Exception as e:
return _err(str(e), 500)
async def list_versions(self, request):
try:
d = await self._body(request)
versions = self.manager.list_versions(d.get("workflow_id"))
return _ok(versions=versions)
except Exception as e:
return _err(str(e), 500)
async def restore_version(self, request):
try:
d = await self._body(request)
result = self.manager.restore_version(
d.get("workflow_id"),
d.get("version_filename"),
)
return _ok() if result else _err("Version not found", 404)
except Exception as e:
return _err(str(e), 500)
async def search_workflows(self, request):
try:
d = await self._body(request)
results = self.manager.search_workflows(d.get("query", ""))
return _ok(results=results)
except Exception as e:
return _err(str(e), 500)
async def toggle_star(self, request):
try:
d = await self._body(request)
result = self.manager.toggle_star(d.get("workflow_id"))
if result is None:
return _err("Workflow not found", 404)
return _ok(starred=result)
except Exception as e:
return _err(str(e), 500)
async def update_tags(self, request):
try:
d = await self._body(request)
result = self.manager.update_tags(d.get("workflow_id"), d.get("tags", []))
return _ok() if result else _err("Workflow not found", 404)
except Exception as e:
return _err(str(e), 500)
async def backup_workflow(self, request):
try:
d = await self._body(request)
result = self.manager.backup_workflow(d.get("workflow_id"))
return _ok(backup_path=result) if result else _err("Workflow not found", 404)
except Exception as e:
return _err(str(e), 500)
async def import_workflow(self, request):
try:
d = await self._body(request)
result = self.manager.import_workflow(
d.get("import_path"),
d.get("workflow_name"),
d.get("group_id") or None,
)
return _ok(workflow_id=result) if result else _err("Import failed", 500)
except Exception as e:
return _err(str(e), 500)
async def get_settings(self, request):
try:
return _ok(settings=self.manager.get_settings())
except Exception as e:
return _err(str(e), 500)
async def update_settings(self, request):
try:
d = await self._body(request)
settings = self.manager.update_settings(
backend_logging_enabled=d.get("backend_logging_enabled")
)
return _ok(settings=settings)
except Exception as e:
return _err(str(e), 500)