Full source for the-third-rev: Discord bot (discord.py), FastAPI web UI (React/TS/Vite/Tailwind), ComfyUI integration, generation history DB, preset manager, workflow inspector, and all supporting modules. Excluded from tracking: .env, invite_tokens.json, *.db (SQLite), current-workflow-changes.json, user_settings/, presets/, logs/, web-static/ (build output), frontend/node_modules/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
154 lines
4.7 KiB
Python
154 lines
4.7 KiB
Python
"""CRUD for workflow presets via /api/presets"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from web.auth import require_auth
|
|
from web.deps import get_comfy, get_user_registry
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class SavePresetRequest(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
|
|
|
|
class SaveFromHistoryRequest(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
|
|
|
|
def _get_pm():
|
|
from web.deps import get_bot
|
|
bot = get_bot()
|
|
pm = getattr(bot, "preset_manager", None) if bot else None
|
|
if pm is None:
|
|
from preset_manager import PresetManager
|
|
pm = PresetManager()
|
|
return pm
|
|
|
|
|
|
@router.get("")
|
|
async def list_presets(_: dict = Depends(require_auth)):
|
|
pm = _get_pm()
|
|
return {"presets": pm.list_preset_details()}
|
|
|
|
|
|
@router.post("")
|
|
async def save_preset(body: SavePresetRequest, user: dict = Depends(require_auth)):
|
|
"""Capture the user's overrides + workflow template as a named preset."""
|
|
user_label: str = user["sub"]
|
|
registry = get_user_registry()
|
|
pm = _get_pm()
|
|
|
|
if registry:
|
|
workflow_template = registry.get_workflow_template(user_label)
|
|
overrides = registry.get_state_manager(user_label).get_overrides()
|
|
else:
|
|
comfy = get_comfy()
|
|
if comfy is None:
|
|
raise HTTPException(503, "ComfyUI not available")
|
|
workflow_template = comfy.get_workflow_template()
|
|
overrides = comfy.state_manager.get_overrides()
|
|
|
|
try:
|
|
pm.save(body.name, workflow_template, overrides, owner=user_label, description=body.description)
|
|
except ValueError as exc:
|
|
raise HTTPException(400, str(exc))
|
|
return {"ok": True, "name": body.name}
|
|
|
|
|
|
@router.get("/{name}")
|
|
async def get_preset(name: str, _: dict = Depends(require_auth)):
|
|
pm = _get_pm()
|
|
data = pm.load(name)
|
|
if data is None:
|
|
raise HTTPException(404, "Preset not found")
|
|
return data
|
|
|
|
|
|
@router.post("/{name}/load")
|
|
async def load_preset(name: str, user: dict = Depends(require_auth)):
|
|
"""Restore overrides (and optionally workflow template) from a preset into the user's state."""
|
|
pm = _get_pm()
|
|
data = pm.load(name)
|
|
if data is None:
|
|
raise HTTPException(404, "Preset not found")
|
|
|
|
user_label: str = user["sub"]
|
|
registry = get_user_registry()
|
|
|
|
if registry:
|
|
wf = data.get("workflow")
|
|
if wf:
|
|
registry.set_workflow(user_label, wf, name)
|
|
else:
|
|
# No workflow in preset — just clear overrides and restore state
|
|
registry.get_state_manager(user_label).clear_overrides()
|
|
state = data.get("state", {})
|
|
sm = registry.get_state_manager(user_label)
|
|
for k, v in state.items():
|
|
if v is not None:
|
|
sm.set_override(k, v)
|
|
else:
|
|
comfy = get_comfy()
|
|
if comfy is None:
|
|
raise HTTPException(503, "ComfyUI not available")
|
|
comfy.state_manager.clear_overrides()
|
|
state = data.get("state", {})
|
|
for k, v in state.items():
|
|
if v is not None:
|
|
comfy.state_manager.set_override(k, v)
|
|
wf = data.get("workflow")
|
|
if wf:
|
|
comfy.workflow_manager.set_workflow_template(wf)
|
|
|
|
return {"ok": True, "name": name, "overrides_restored": list(data.get("state", {}).keys())}
|
|
|
|
|
|
@router.delete("/{name}")
|
|
async def delete_preset(name: str, user: dict = Depends(require_auth)):
|
|
pm = _get_pm()
|
|
data = pm.load(name)
|
|
if data is None:
|
|
raise HTTPException(404, "Preset not found")
|
|
|
|
user_label: str = user["sub"]
|
|
is_admin = user.get("admin") is True
|
|
owner = data.get("owner")
|
|
if owner is not None and owner != user_label and not is_admin:
|
|
raise HTTPException(403, "You do not have permission to delete this preset")
|
|
|
|
pm.delete(name)
|
|
return {"ok": True}
|
|
|
|
|
|
@router.post("/from-history/{prompt_id}")
|
|
async def save_preset_from_history(
|
|
prompt_id: str,
|
|
body: SaveFromHistoryRequest,
|
|
user: dict = Depends(require_auth),
|
|
):
|
|
"""Create a preset from a past generation's overrides."""
|
|
from generation_db import get_generation_full
|
|
|
|
gen = get_generation_full(prompt_id)
|
|
if gen is None:
|
|
raise HTTPException(404, "Generation not found")
|
|
|
|
user_label: str = user["sub"]
|
|
is_admin = user.get("admin") is True
|
|
if not is_admin and gen.get("user_label") != user_label:
|
|
raise HTTPException(404, "Generation not found")
|
|
|
|
pm = _get_pm()
|
|
try:
|
|
pm.save(body.name, None, gen["overrides"], owner=user_label, description=body.description)
|
|
except ValueError as exc:
|
|
raise HTTPException(400, str(exc))
|
|
return {"ok": True, "name": body.name}
|