Remove /ui redirect routes, add 404 page, update README
- Remove obsolete /ui, /ui/login, /ui/register, /ui/logout redirects - Fix /ui/login links to use /login directly - Add styled 404 page for HTML requests - Add Web UI section to README documenting routes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
14
README.md
14
README.md
@@ -24,7 +24,19 @@ celery -A celery_app worker --loglevel=info
|
||||
python server.py
|
||||
```
|
||||
|
||||
## L1 Server API
|
||||
## Web UI
|
||||
|
||||
The server provides a web interface at the root URL:
|
||||
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `/` | Home page with server info |
|
||||
| `/runs` | View and manage rendering runs |
|
||||
| `/recipes` | Browse and run available recipes |
|
||||
| `/media` | Browse cached media files |
|
||||
| `/run/{id}` | Run detail page |
|
||||
|
||||
## API
|
||||
|
||||
Interactive docs: http://localhost:8100/docs
|
||||
|
||||
|
||||
80
server.py
80
server.py
@@ -129,6 +129,57 @@ app = FastAPI(
|
||||
)
|
||||
|
||||
|
||||
@app.exception_handler(404)
|
||||
async def not_found_handler(request: Request, exc):
|
||||
"""Custom 404 page."""
|
||||
from fastapi.responses import JSONResponse
|
||||
accept = request.headers.get("accept", "")
|
||||
if "text/html" in accept:
|
||||
content = '''
|
||||
<div class="text-center py-16">
|
||||
<h2 class="text-6xl font-bold text-gray-600 mb-4">404</h2>
|
||||
<p class="text-xl text-gray-400 mb-8">Page not found</p>
|
||||
<a href="/" class="text-blue-400 hover:text-blue-300">Go to home page</a>
|
||||
</div>
|
||||
'''
|
||||
# Import render_page at runtime to avoid circular dependency
|
||||
html = f"""<!DOCTYPE html>
|
||||
<html class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Not Found | Art DAG L1 Server</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {{
|
||||
darkMode: 'class',
|
||||
theme: {{
|
||||
extend: {{
|
||||
colors: {{
|
||||
dark: {{ 900: '#0a0a0a', 800: '#111', 700: '#1a1a1a', 600: '#222', 500: '#333' }}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-dark-900 text-gray-100 min-h-screen">
|
||||
<div class="max-w-6xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
|
||||
<header class="flex flex-wrap items-center justify-between gap-4 mb-6">
|
||||
<h1 class="text-2xl font-bold">
|
||||
<a href="/" class="text-white hover:text-gray-200">Art DAG L1 Server</a>
|
||||
</h1>
|
||||
</header>
|
||||
<main class="bg-dark-700 rounded-lg p-6">
|
||||
{content}
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
return HTMLResponse(html, status_code=404)
|
||||
return JSONResponse({"detail": "Not found"}, status_code=404)
|
||||
|
||||
|
||||
class RunRequest(BaseModel):
|
||||
"""Request to start a run."""
|
||||
recipe: str # Recipe name (e.g., "dog", "identity") or "dag" for custom DAG
|
||||
@@ -1645,7 +1696,7 @@ async def ui_recipes_list(request: Request):
|
||||
ctx = await get_user_context_from_cookie(request)
|
||||
|
||||
if not ctx:
|
||||
return '<p class="text-gray-400 py-8 text-center"><a href="/ui/login" class="text-blue-400 hover:text-blue-300">Login via L2</a> to see recipes.</p>'
|
||||
return '<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login via L2</a> to see recipes.</p>'
|
||||
|
||||
all_recipes = list_all_recipes()
|
||||
|
||||
@@ -3595,31 +3646,6 @@ async def logout():
|
||||
return response
|
||||
|
||||
|
||||
@app.get("/ui")
|
||||
async def ui_index(tab: str = "runs"):
|
||||
"""Redirect /ui to clean URLs."""
|
||||
if tab == "cache":
|
||||
return RedirectResponse(url="/media", status_code=302)
|
||||
return RedirectResponse(url="/runs", status_code=302)
|
||||
|
||||
|
||||
@app.get("/ui/login")
|
||||
async def ui_login_page():
|
||||
"""Redirect to L2 login."""
|
||||
return RedirectResponse(url="/login", status_code=302)
|
||||
|
||||
|
||||
@app.get("/ui/register")
|
||||
async def ui_register_page():
|
||||
"""Redirect to L2 register."""
|
||||
return RedirectResponse(url="/register", status_code=302)
|
||||
|
||||
|
||||
@app.get("/ui/logout")
|
||||
async def ui_logout():
|
||||
"""Redirect to logout."""
|
||||
return RedirectResponse(url="/logout", status_code=302)
|
||||
|
||||
|
||||
@app.post("/ui/publish-run/{run_id}", response_class=HTMLResponse)
|
||||
async def ui_publish_run(run_id: str, request: Request, output_name: str = Form(...)):
|
||||
@@ -3712,7 +3738,7 @@ async def ui_runs(request: Request):
|
||||
|
||||
# Require login to see runs
|
||||
if not ctx:
|
||||
return '<p class="text-gray-400 py-8 text-center"><a href="/ui/login" class="text-blue-400 hover:text-blue-300">Login via L2</a> to see your runs.</p>'
|
||||
return '<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login via L2</a> to see your runs.</p>'
|
||||
|
||||
# Filter runs by user - match both plain username and ActivityPub format (@user@domain)
|
||||
runs = [r for r in runs if r.username in (ctx.username, ctx.actor_id)]
|
||||
|
||||
Reference in New Issue
Block a user