161 lines
6.4 KiB
Python
161 lines
6.4 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import urllib.parse
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from aiohttp import web
|
|
|
|
from .health import api_prompt_from_payload, build_health_payload, build_repro_lock, diagnose_workflow, resolve_models
|
|
|
|
_ROUTES_REGISTERED = False
|
|
|
|
|
|
def register_routes() -> None:
|
|
global _ROUTES_REGISTERED
|
|
if _ROUTES_REGISTERED:
|
|
return
|
|
try:
|
|
from server import PromptServer # type: ignore
|
|
except Exception:
|
|
return
|
|
|
|
routes = getattr(getattr(PromptServer, "instance", None), "routes", None)
|
|
if routes is None:
|
|
return
|
|
_ROUTES_REGISTERED = True
|
|
|
|
@routes.post("/genasset/setup/config")
|
|
async def genasset_setup_config(request: web.Request) -> web.Response:
|
|
blocked = _same_origin_block(request)
|
|
if blocked is not None:
|
|
return blocked
|
|
payload = await _json_payload(request)
|
|
try:
|
|
from .nodes import (
|
|
_genasset_config_paths,
|
|
_read_genasset_config,
|
|
genasset_manager_status,
|
|
request_json,
|
|
require_base_url,
|
|
)
|
|
|
|
base_url = require_base_url(str(payload.get("base_url") or "https://genasset.xyz"))
|
|
workspace_token = str(payload.get("workspace_token") or "").strip()
|
|
if not workspace_token:
|
|
raise RuntimeError("Paste a GenAsset workspace token.")
|
|
|
|
url = urllib.parse.urljoin(base_url + "/", "api/v1/workspace?lite=1&include_workspace_list=1")
|
|
request_json("GET", url, workspace_token)
|
|
|
|
try:
|
|
config, existing_path = _read_genasset_config()
|
|
except Exception:
|
|
config, existing_path = {}, ""
|
|
if not isinstance(config, dict):
|
|
config = {}
|
|
config.update({"base_url": base_url, "workspace_token": workspace_token})
|
|
|
|
config_path = Path(existing_path) if existing_path else next(iter(_genasset_config_paths()))
|
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8")
|
|
try:
|
|
os.chmod(config_path, 0o600)
|
|
except Exception:
|
|
pass
|
|
|
|
return web.json_response(
|
|
{
|
|
"ok": True,
|
|
"message": "GenAsset token saved.",
|
|
"config_path": str(config_path),
|
|
"status": genasset_manager_status(check=True),
|
|
}
|
|
)
|
|
except Exception as exc:
|
|
return web.json_response({"ok": False, "error": str(exc)}, status=400)
|
|
|
|
@routes.post("/genasset/health/resolve")
|
|
async def genasset_health_resolve(request: web.Request) -> web.Response:
|
|
payload = await _json_payload(request)
|
|
api_prompt = api_prompt_from_payload(payload)
|
|
return web.json_response(resolve_models(api_prompt))
|
|
|
|
@routes.post("/genasset/health/repro")
|
|
async def genasset_health_repro(request: web.Request) -> web.Response:
|
|
payload = await _json_payload(request)
|
|
api_prompt = api_prompt_from_payload(payload)
|
|
workflow = payload.get("workflow") if isinstance(payload.get("workflow"), dict) else {}
|
|
return web.json_response({"ok": True, "repro_lock": build_repro_lock(api_prompt, workflow)})
|
|
|
|
@routes.post("/genasset/health/diagnose")
|
|
async def genasset_health_diagnose(request: web.Request) -> web.Response:
|
|
payload = await _json_payload(request)
|
|
api_prompt = api_prompt_from_payload(payload)
|
|
known_node_types = payload.get("known_node_types")
|
|
if not isinstance(known_node_types, list):
|
|
known_node_types = []
|
|
return web.json_response(diagnose_workflow(api_prompt, known_node_types=[str(item) for item in known_node_types]))
|
|
|
|
@routes.post("/genasset/health/doctor")
|
|
async def genasset_health_doctor(request: web.Request) -> web.Response:
|
|
payload = await _json_payload(request)
|
|
try:
|
|
from .nodes import request_json, require_base_url, resolve_workspace_token
|
|
|
|
base_url = require_base_url(str(payload.get("base_url") or "https://genasset.xyz"))
|
|
token_text = str(payload.get("token") or "ComfyUI/user/genasset.json")
|
|
workspace_token, token_source, token_source_ref = resolve_workspace_token(token_text)
|
|
health_payload = build_health_payload(payload)
|
|
health_payload["client"] = {
|
|
"source": "comfyui-genasset",
|
|
"token_source": token_source,
|
|
"token_source_ref": token_source_ref,
|
|
}
|
|
url = urllib.parse.urljoin(base_url + "/", "api/v1/comfy/workflow-doctor")
|
|
data = request_json("POST", url, workspace_token, health_payload)
|
|
return web.json_response(
|
|
{
|
|
"ok": True,
|
|
"diagnostics": health_payload["diagnostics"],
|
|
"repro_lock": health_payload["repro_lock"],
|
|
"doctor": data,
|
|
}
|
|
)
|
|
except Exception as exc:
|
|
return web.json_response({"ok": False, "error": str(exc)}, status=400)
|
|
|
|
|
|
async def _json_payload(request: web.Request) -> dict[str, Any]:
|
|
try:
|
|
payload = await request.json()
|
|
except json.JSONDecodeError as exc:
|
|
raise web.HTTPBadRequest(text=f"Invalid JSON: {exc}") from exc
|
|
if not isinstance(payload, dict):
|
|
raise web.HTTPBadRequest(text="Expected a JSON object.")
|
|
return payload
|
|
|
|
|
|
def _same_origin_block(request: web.Request) -> web.Response | None:
|
|
origin = str(request.headers.get("Origin") or request.headers.get("Referer") or "").strip()
|
|
if not origin:
|
|
return None
|
|
try:
|
|
origin_parts = urllib.parse.urlparse(origin)
|
|
origin_host = origin_parts.netloc.lower()
|
|
origin_name = (origin_parts.hostname or "").lower()
|
|
except Exception:
|
|
origin_host = ""
|
|
origin_name = ""
|
|
request_host = str(request.headers.get("Host") or getattr(request, "host", "") or "").lower()
|
|
if origin_name in {"localhost", "127.0.0.1", "::1"}:
|
|
return None
|
|
if origin_host and request_host and origin_host != request_host:
|
|
return web.json_response(
|
|
{"ok": False, "error": "Cross-origin GenAsset setup requests are not allowed."},
|
|
status=403,
|
|
)
|
|
return None
|