diff --git a/server.py b/server.py index dc37ae7..f436752 100644 --- a/server.py +++ b/server.py @@ -19,7 +19,7 @@ from typing import Optional from urllib.parse import urlparse from fastapi import FastAPI, HTTPException, Request, Response, Depends, Cookie -from fastapi.responses import JSONResponse, HTMLResponse +from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from pydantic import BaseModel import requests @@ -243,16 +243,15 @@ def base_html(title: str, content: str, username: str = None) -> str: user_section = f'''
Logged in as {username} - +
''' if username else '''
- Login + Login | - Register + Register
''' @@ -268,14 +267,13 @@ def base_html(title: str, content: str, username: str = None) -> str:

- Art DAG L2 + Art DAG L2

{user_section}
@@ -345,7 +343,7 @@ async def ui_home(request: Request): return HTMLResponse(base_html("Home", content, username)) -@app.get("/ui/login", response_class=HTMLResponse) +@app.get("/login", response_class=HTMLResponse) async def ui_login_page(request: Request): """Login page.""" username = get_user_from_cookie(request) @@ -354,13 +352,13 @@ async def ui_login_page(request: Request):
You are already logged in as {username}
-

Go to home page

+

Go to home page

''', username)) content = '''

Login

-
+
-

Don't have an account? Register

+

Don't have an account? Register

''' return HTMLResponse(base_html("Login", content)) -@app.post("/ui/login", response_class=HTMLResponse) +@app.post("/login", response_class=HTMLResponse) async def ui_login_submit(request: Request): """Handle login form submission.""" form = await request.form() @@ -398,7 +396,7 @@ async def ui_login_submit(request: Request): response = HTMLResponse(f'''
Login successful! Redirecting...
- + ''') response.set_cookie( key="auth_token", @@ -410,7 +408,7 @@ async def ui_login_submit(request: Request): return response -@app.get("/ui/register", response_class=HTMLResponse) +@app.get("/register", response_class=HTMLResponse) async def ui_register_page(request: Request): """Register page.""" username = get_user_from_cookie(request) @@ -419,13 +417,13 @@ async def ui_register_page(request: Request):
You are already logged in as {username}
-

Go to home page

+

Go to home page

''', username)) content = '''

Register

-
+
-

Already have an account? Login

+

Already have an account? Login

''' return HTMLResponse(base_html("Register", content)) -@app.post("/ui/register", response_class=HTMLResponse) +@app.post("/register", response_class=HTMLResponse) async def ui_register_submit(request: Request): """Handle register form submission.""" form = await request.form() @@ -482,7 +480,7 @@ async def ui_register_submit(request: Request): response = HTMLResponse(f'''
Registration successful! Redirecting...
- + ''') response.set_cookie( key="auth_token", @@ -494,17 +492,15 @@ async def ui_register_submit(request: Request): return response -@app.post("/ui/logout", response_class=HTMLResponse) -async def ui_logout(): - """Handle logout.""" - response = HTMLResponse(''' - - ''') +@app.get("/logout") +async def logout(): + """Handle logout - clear cookie and redirect to home.""" + response = RedirectResponse(url="/", status_code=302) response.delete_cookie("auth_token") return response -@app.get("/ui/registry", response_class=HTMLResponse) +@app.get("/ui/assets", response_class=HTMLResponse) async def ui_registry_page(request: Request): """Registry page showing all assets.""" username = get_user_from_cookie(request) @@ -927,7 +923,7 @@ async def ui_asset_detail(name: str, request: Request): content = f'''

Asset Not Found

No asset named "{name}" exists.

-

← Back to Registry

+

← Back to Assets

''' return HTMLResponse(base_html("Asset Not Found", content, username)) @@ -1099,7 +1095,7 @@ async def ui_asset_detail(name: str, request: Request): ''' content = f''' -

← Back to Registry

+

← Back to Assets

{name}

@@ -1264,22 +1260,42 @@ async def ui_user_detail(username: str, request: Request): @app.get("/") async def root(request: Request): - """Server info or redirect to UI.""" - # If browser, redirect to UI - accept = request.headers.get("accept", "") - if "text/html" in accept and "application/json" not in accept: - return HTMLResponse(status_code=302, headers={"Location": "/ui"}) - + """Server info. HTML shows home page with counts, JSON returns stats.""" registry = load_registry() activities = load_activities() users = load_users(DATA_DIR) + + assets_count = len(registry.get("assets", {})) + activities_count = len(activities) + users_count = len(users) + + if wants_html(request): + username = get_user_from_cookie(request) + content = f''' + + ''' + return HTMLResponse(base_html("Home", content, username)) + return { "name": "Art DAG L2 Server", "version": "0.1.0", "domain": DOMAIN, - "assets_count": len(registry.get("assets", {})), - "activities_count": len(activities), - "users_count": len(users) + "assets_count": assets_count, + "activities_count": activities_count, + "users_count": users_count } @@ -1593,9 +1609,9 @@ async def get_followers(username: str): ) -# ============ Registry Endpoints ============ +# ============ Assets Endpoints ============ -@app.get("/registry") +@app.get("/assets") async def get_registry(request: Request, page: int = 1, limit: int = 20): """Get registry. HTML for browsers (with infinite scroll), JSON for APIs (with pagination).""" registry = load_registry() @@ -1647,7 +1663,7 @@ async def get_registry(request: Request, page: int = 1, limit: int = 20): if page > 1: if has_more: rows += f''' - + Loading more... ''' @@ -1657,7 +1673,7 @@ async def get_registry(request: Request, page: int = 1, limit: int = 20): infinite_scroll_trigger = "" if has_more: infinite_scroll_trigger = f''' - + Loading more... ''' @@ -1706,7 +1722,7 @@ async def get_asset_by_name(name: str, request: Request): content = f'''

Asset Not Found

No asset named "{name}" exists.

-

← Back to Registry

+

← Back to Assets

''' return HTMLResponse(base_html("Asset Not Found", content, get_user_from_cookie(request))) raise HTTPException(404, f"Asset not found: {name}") @@ -1717,7 +1733,7 @@ async def get_asset_by_name(name: str, request: Request): return registry["assets"][name] -@app.get("/registry/{name}") +@app.get("/assets/{name}") async def get_asset(name: str): """Get a specific asset (API only, use /asset/{name} for content negotiation).""" registry = load_registry() @@ -1726,7 +1742,7 @@ async def get_asset(name: str): return registry["assets"][name] -@app.patch("/registry/{name}") +@app.patch("/assets/{name}") async def update_asset(name: str, req: UpdateAssetRequest, user: User = Depends(get_required_user)): """Update an existing asset's metadata. Creates an Update activity.""" registry = load_registry() @@ -1849,13 +1865,13 @@ def _register_asset_impl(req: RegisterRequest, owner: str): return {"asset": asset, "activity": activity} -@app.post("/registry") +@app.post("/assets") async def register_asset(req: RegisterRequest, user: User = Depends(get_required_user)): """Register a new asset and create ownership activity. Requires authentication.""" return _register_asset_impl(req, user.username) -@app.post("/registry/record-run") +@app.post("/assets/record-run") async def record_run(req: RecordRunRequest, user: User = Depends(get_required_user)): """Record an L1 run and register the output. Requires authentication.""" # Fetch run from the specified L1 server @@ -1897,7 +1913,7 @@ async def record_run(req: RecordRunRequest, user: User = Depends(get_required_us ), user.username) -@app.post("/registry/publish-cache") +@app.post("/assets/publish-cache") async def publish_cache(req: PublishCacheRequest, user: User = Depends(get_required_user)): """ Publish a cache item from L1 with metadata. @@ -2132,9 +2148,8 @@ async def get_object(content_hash: str, request: Request): wants_html = "text/html" in accept and "application/json" not in accept and "application/activity+json" not in accept if wants_html: - # Redirect to UI page for browsers - from fastapi.responses import RedirectResponse - return RedirectResponse(url=f"/ui/asset/{name}", status_code=303) + # Redirect to detail page for browsers + return RedirectResponse(url=f"/asset/{name}", status_code=303) owner = asset.get("owner", "unknown") return JSONResponse(