mirror of
https://git.datalinker.icu/comfyanonymous/ComfyUI
synced 2025-12-16 09:35:12 +08:00
111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
import json
|
|
from typing import Sequence
|
|
from aiohttp import web
|
|
|
|
from app import assets_manager
|
|
|
|
|
|
ROUTES = web.RouteTableDef()
|
|
|
|
|
|
@ROUTES.get("/api/assets")
|
|
async def list_assets(request: web.Request) -> web.Response:
|
|
q = request.rel_url.query
|
|
|
|
include_tags: Sequence[str] = _parse_csv_tags(q.get("include_tags"))
|
|
exclude_tags: Sequence[str] = _parse_csv_tags(q.get("exclude_tags"))
|
|
name_contains = q.get("name_contains")
|
|
|
|
# Optional JSON metadata filter (top-level key equality only for now)
|
|
metadata_filter = None
|
|
raw_meta = q.get("metadata_filter")
|
|
if raw_meta:
|
|
try:
|
|
metadata_filter = json.loads(raw_meta)
|
|
if not isinstance(metadata_filter, dict):
|
|
metadata_filter = None
|
|
except Exception:
|
|
# Silently ignore malformed JSON for first iteration; could 400 in future
|
|
metadata_filter = None
|
|
|
|
limit = _parse_int(q.get("limit"), default=20, lo=1, hi=100)
|
|
offset = _parse_int(q.get("offset"), default=0, lo=0, hi=10_000_000)
|
|
sort = q.get("sort", "created_at")
|
|
order = q.get("order", "desc")
|
|
|
|
payload = await assets_manager.list_assets(
|
|
include_tags=include_tags,
|
|
exclude_tags=exclude_tags,
|
|
name_contains=name_contains,
|
|
metadata_filter=metadata_filter,
|
|
limit=limit,
|
|
offset=offset,
|
|
sort=sort,
|
|
order=order,
|
|
)
|
|
return web.json_response(payload)
|
|
|
|
|
|
@ROUTES.put("/api/assets/{id}")
|
|
async def update_asset(request: web.Request) -> web.Response:
|
|
asset_info_id_raw = request.match_info.get("id")
|
|
try:
|
|
asset_info_id = int(asset_info_id_raw)
|
|
except Exception:
|
|
return _error_response(400, "INVALID_ID", f"AssetInfo id '{asset_info_id_raw}' is not a valid integer.")
|
|
|
|
try:
|
|
payload = await request.json()
|
|
except Exception:
|
|
return _error_response(400, "INVALID_JSON", "Request body must be valid JSON.")
|
|
|
|
name = payload.get("name", None)
|
|
tags = payload.get("tags", None)
|
|
user_metadata = payload.get("user_metadata", None)
|
|
|
|
if name is None and tags is None and user_metadata is None:
|
|
return _error_response(400, "NO_FIELDS", "Provide at least one of: name, tags, user_metadata.")
|
|
|
|
if tags is not None and (not isinstance(tags, list) or not all(isinstance(t, str) for t in tags)):
|
|
return _error_response(400, "INVALID_TAGS", "Field 'tags' must be an array of strings.")
|
|
|
|
if user_metadata is not None and not isinstance(user_metadata, dict):
|
|
return _error_response(400, "INVALID_METADATA", "Field 'user_metadata' must be an object.")
|
|
|
|
try:
|
|
result = await assets_manager.update_asset(
|
|
asset_info_id=asset_info_id,
|
|
name=name,
|
|
tags=tags,
|
|
user_metadata=user_metadata,
|
|
)
|
|
except ValueError as ve:
|
|
return _error_response(404, "ASSET_NOT_FOUND", str(ve), {"id": asset_info_id})
|
|
except Exception:
|
|
return _error_response(500, "INTERNAL", "Unexpected server error.")
|
|
return web.json_response(result, status=200)
|
|
|
|
|
|
def register_assets_routes(app: web.Application) -> None:
|
|
app.add_routes(ROUTES)
|
|
|
|
|
|
def _parse_csv_tags(raw: str | None) -> list[str]:
|
|
if not raw:
|
|
return []
|
|
return [t.strip() for t in raw.split(",") if t.strip()]
|
|
|
|
|
|
def _parse_int(qval: str | None, default: int, lo: int, hi: int) -> int:
|
|
if not qval:
|
|
return default
|
|
try:
|
|
v = int(qval)
|
|
except Exception:
|
|
return default
|
|
return max(lo, min(hi, v))
|
|
|
|
|
|
def _error_response(status: int, code: str, message: str, details: dict | None = None) -> web.Response:
|
|
return web.json_response({"error": {"code": code, "message": message, "details": details or {}}}, status=status)
|