Show logged-in user on home page instead of login button
Convert static HOME_HTML to render_home_html() function that dynamically shows user info with link to L2 profile when authenticated, or login button when not logged in. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
76
server.py
76
server.py
@@ -419,7 +419,21 @@ async def api_info():
|
||||
}
|
||||
|
||||
|
||||
HOME_HTML = """
|
||||
def render_home_html(actor_id: Optional[str] = None) -> str:
|
||||
"""Render the home page HTML with optional user info."""
|
||||
if actor_id:
|
||||
# Extract username and domain from @username@domain format
|
||||
parts = actor_id.lstrip("@").split("@")
|
||||
username = parts[0] if parts else actor_id
|
||||
domain = parts[1] if len(parts) > 1 else ""
|
||||
l2_user_url = f"https://{domain}/users/{username}" if domain else "#"
|
||||
user_section = f'''<div class="ml-auto flex items-center gap-2 text-sm text-gray-300">
|
||||
Logged in as <a href="{l2_user_url}" class="text-blue-400 hover:text-blue-300">{actor_id}</a>
|
||||
</div>'''
|
||||
else:
|
||||
user_section = '''<a href="/login" class="ml-auto px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-md text-white font-medium transition-colors">Login via L2</a>'''
|
||||
|
||||
return f"""
|
||||
<!DOCTYPE html>
|
||||
<html class="dark">
|
||||
<head>
|
||||
@@ -428,10 +442,10 @@ HOME_HTML = """
|
||||
<title>Art DAG L1 Server</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
tailwind.config = {{
|
||||
darkMode: 'class',
|
||||
theme: { extend: { colors: { dark: { 900: '#0a0a0a', 800: '#111', 700: '#1a1a1a', 600: '#222', 500: '#333' } } } }
|
||||
}
|
||||
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">
|
||||
@@ -441,7 +455,7 @@ HOME_HTML = """
|
||||
<a href="/recipes" class="px-4 py-2 bg-dark-500 hover:bg-dark-600 rounded-md text-blue-400 hover:text-blue-300 font-medium transition-colors">Recipes</a>
|
||||
<a href="/media" class="px-4 py-2 bg-dark-500 hover:bg-dark-600 rounded-md text-blue-400 hover:text-blue-300 font-medium transition-colors">Media</a>
|
||||
<a href="/docs" class="px-4 py-2 bg-dark-500 hover:bg-dark-600 rounded-md text-blue-400 hover:text-blue-300 font-medium transition-colors">API Docs</a>
|
||||
<a href="/login" class="ml-auto px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-md text-white font-medium transition-colors">Login</a>
|
||||
{user_section}
|
||||
</nav>
|
||||
|
||||
<h1 class="text-3xl font-bold text-white border-b border-dark-500 pb-4 mb-6">Art DAG L1 Server</h1>
|
||||
@@ -481,16 +495,16 @@ HOME_HTML = """
|
||||
<h2 class="text-xl font-semibold text-gray-200 mt-8 mb-4">Start a Run</h2>
|
||||
<pre class="bg-dark-700 p-4 rounded-lg overflow-x-auto border border-dark-500 mb-8"><code class="text-green-300">curl -X POST /runs \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{"recipe": "dog", "inputs": ["33268b6e..."]}'</code></pre>
|
||||
-d '{{"recipe": "dog", "inputs": ["33268b6e..."]}}'</code></pre>
|
||||
|
||||
<h2 class="text-xl font-semibold text-gray-200 mt-8 mb-4">Provenance</h2>
|
||||
<p class="text-gray-300 mb-4">Every render produces a provenance record linking inputs, effects, and infrastructure:</p>
|
||||
<pre class="bg-dark-700 p-4 rounded-lg overflow-x-auto border border-dark-500"><code class="text-green-300">{
|
||||
"output": {"content_hash": "..."},
|
||||
<pre class="bg-dark-700 p-4 rounded-lg overflow-x-auto border border-dark-500"><code class="text-green-300">{{
|
||||
"output": {{"content_hash": "..."}},
|
||||
"inputs": [...],
|
||||
"effects": [...],
|
||||
"infrastructure": {...}
|
||||
}</code></pre>
|
||||
"infrastructure": {{...}}
|
||||
}}</code></pre>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -498,9 +512,11 @@ HOME_HTML = """
|
||||
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def root():
|
||||
async def root(request: Request):
|
||||
"""Home page."""
|
||||
return HOME_HTML
|
||||
ctx = get_user_context_from_cookie(request)
|
||||
actor_id = ctx.actor_id if ctx else None
|
||||
return render_home_html(actor_id)
|
||||
|
||||
|
||||
@app.post("/runs", response_model=RunStatus)
|
||||
@@ -729,7 +745,7 @@ async def run_detail(run_id: str, request: Request):
|
||||
if wants_html(request):
|
||||
ctx = get_user_context_from_cookie(request)
|
||||
if not ctx:
|
||||
content = '<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to view run details.</p>'
|
||||
content = '<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 view run details.</p>'
|
||||
return HTMLResponse(render_page("Login Required", content, None, active_tab="runs"), status_code=401)
|
||||
|
||||
# Check user owns this run
|
||||
@@ -964,7 +980,7 @@ async def list_runs(request: Request, page: int = 1, limit: int = 20):
|
||||
|
||||
if wants_html(request):
|
||||
if not ctx:
|
||||
content = '<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to see your runs.</p>'
|
||||
content = '<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>'
|
||||
return HTMLResponse(render_page("Runs", content, None, active_tab="runs"))
|
||||
|
||||
if not runs_page:
|
||||
@@ -1147,7 +1163,7 @@ async def list_recipes_api(request: Request, page: int = 1, limit: int = 20):
|
||||
if not ctx:
|
||||
return HTMLResponse(render_page(
|
||||
"Recipes",
|
||||
'<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to see recipes.</p>',
|
||||
'<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>',
|
||||
None,
|
||||
active_tab="recipes"
|
||||
))
|
||||
@@ -1520,7 +1536,7 @@ async def ui_recipes_list(request: Request):
|
||||
ctx = 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</a> to see recipes.</p>'
|
||||
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>'
|
||||
|
||||
all_recipes = list_all_recipes()
|
||||
|
||||
@@ -1681,7 +1697,7 @@ async def cache_detail(content_hash: str, request: Request):
|
||||
|
||||
if wants_html(request):
|
||||
if not ctx:
|
||||
content = '<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to view cached content.</p>'
|
||||
content = '<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 view cached content.</p>'
|
||||
return HTMLResponse(render_page("Login Required", content, None, active_tab="media"), status_code=401)
|
||||
|
||||
# Check user has access
|
||||
@@ -2190,7 +2206,7 @@ async def list_media(
|
||||
if wants_html(request):
|
||||
# Require login for HTML media view
|
||||
if not ctx:
|
||||
content = '<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to see media.</p>'
|
||||
content = '<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 media.</p>'
|
||||
return HTMLResponse(render_page("Media", content, None, active_tab="media"))
|
||||
|
||||
# Get hashes owned by/associated with this user
|
||||
@@ -3078,16 +3094,20 @@ def render_page(title: str, content: str, actor_id: Optional[str] = None, active
|
||||
"""
|
||||
user_info = ""
|
||||
if actor_id:
|
||||
# Extract username and domain from @username@domain format
|
||||
parts = actor_id.lstrip("@").split("@")
|
||||
username = parts[0] if parts else actor_id
|
||||
domain = parts[1] if len(parts) > 1 else ""
|
||||
l2_user_url = f"https://{domain}/users/{username}" if domain else "#"
|
||||
user_info = f'''
|
||||
<div class="flex items-center gap-4 text-sm text-gray-400">
|
||||
Logged in as <strong class="text-white">{actor_id}</strong>
|
||||
<a href="/logout" class="text-blue-400 hover:text-blue-300">Logout</a>
|
||||
Logged in as <a href="{l2_user_url}" class="text-white hover:text-blue-300">{actor_id}</a>
|
||||
</div>
|
||||
'''
|
||||
else:
|
||||
user_info = '''
|
||||
<div class="text-sm">
|
||||
<a href="/login" class="text-blue-400 hover:text-blue-300">Login</a>
|
||||
<a href="/login" class="text-blue-400 hover:text-blue-300">Login via L2</a>
|
||||
</div>
|
||||
'''
|
||||
|
||||
@@ -3135,16 +3155,20 @@ def render_ui_html(actor_id: Optional[str] = None, tab: str = "runs") -> str:
|
||||
"""
|
||||
user_info = ""
|
||||
if actor_id:
|
||||
# Extract username and domain from @username@domain format
|
||||
parts = actor_id.lstrip("@").split("@")
|
||||
username = parts[0] if parts else actor_id
|
||||
domain = parts[1] if len(parts) > 1 else ""
|
||||
l2_user_url = f"https://{domain}/users/{username}" if domain else "#"
|
||||
user_info = f'''
|
||||
<div class="flex items-center gap-4 text-sm text-gray-400">
|
||||
Logged in as <strong class="text-white">{actor_id}</strong>
|
||||
<a href="/logout" class="text-blue-400 hover:text-blue-300">Logout</a>
|
||||
Logged in as <a href="{l2_user_url}" class="text-white hover:text-blue-300">{actor_id}</a>
|
||||
</div>
|
||||
'''
|
||||
else:
|
||||
user_info = '''
|
||||
<div class="text-sm">
|
||||
<a href="/login" class="text-blue-400 hover:text-blue-300">Login</a>
|
||||
<a href="/login" class="text-blue-400 hover:text-blue-300">Login via L2</a>
|
||||
</div>
|
||||
'''
|
||||
|
||||
@@ -3327,7 +3351,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</a> to see your runs.</p>'
|
||||
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>'
|
||||
|
||||
# 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)]
|
||||
@@ -3426,7 +3450,7 @@ async def ui_media_list(
|
||||
|
||||
# Require login to see media
|
||||
if not ctx:
|
||||
return '<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to see media.</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 media.</p>'
|
||||
|
||||
# Get hashes owned by/associated with this user
|
||||
user_hashes = await get_user_cache_hashes(ctx.username, ctx.actor_id)
|
||||
|
||||
Reference in New Issue
Block a user