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>
|
<!DOCTYPE html>
|
||||||
<html class="dark">
|
<html class="dark">
|
||||||
<head>
|
<head>
|
||||||
@@ -428,10 +442,10 @@ HOME_HTML = """
|
|||||||
<title>Art DAG L1 Server</title>
|
<title>Art DAG L1 Server</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<script>
|
<script>
|
||||||
tailwind.config = {
|
tailwind.config = {{
|
||||||
darkMode: 'class',
|
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>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-dark-900 text-gray-100 min-h-screen">
|
<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="/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="/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="/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>
|
</nav>
|
||||||
|
|
||||||
<h1 class="text-3xl font-bold text-white border-b border-dark-500 pb-4 mb-6">Art DAG L1 Server</h1>
|
<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>
|
<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 \\
|
<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" \\
|
-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>
|
<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>
|
<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">{
|
<pre class="bg-dark-700 p-4 rounded-lg overflow-x-auto border border-dark-500"><code class="text-green-300">{{
|
||||||
"output": {"content_hash": "..."},
|
"output": {{"content_hash": "..."}},
|
||||||
"inputs": [...],
|
"inputs": [...],
|
||||||
"effects": [...],
|
"effects": [...],
|
||||||
"infrastructure": {...}
|
"infrastructure": {{...}}
|
||||||
}</code></pre>
|
}}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -498,9 +512,11 @@ HOME_HTML = """
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/", response_class=HTMLResponse)
|
@app.get("/", response_class=HTMLResponse)
|
||||||
async def root():
|
async def root(request: Request):
|
||||||
"""Home page."""
|
"""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)
|
@app.post("/runs", response_model=RunStatus)
|
||||||
@@ -729,7 +745,7 @@ async def run_detail(run_id: str, request: Request):
|
|||||||
if wants_html(request):
|
if wants_html(request):
|
||||||
ctx = get_user_context_from_cookie(request)
|
ctx = get_user_context_from_cookie(request)
|
||||||
if not ctx:
|
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)
|
return HTMLResponse(render_page("Login Required", content, None, active_tab="runs"), status_code=401)
|
||||||
|
|
||||||
# Check user owns this run
|
# 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 wants_html(request):
|
||||||
if not ctx:
|
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"))
|
return HTMLResponse(render_page("Runs", content, None, active_tab="runs"))
|
||||||
|
|
||||||
if not runs_page:
|
if not runs_page:
|
||||||
@@ -1147,7 +1163,7 @@ async def list_recipes_api(request: Request, page: int = 1, limit: int = 20):
|
|||||||
if not ctx:
|
if not ctx:
|
||||||
return HTMLResponse(render_page(
|
return HTMLResponse(render_page(
|
||||||
"Recipes",
|
"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,
|
None,
|
||||||
active_tab="recipes"
|
active_tab="recipes"
|
||||||
))
|
))
|
||||||
@@ -1520,7 +1536,7 @@ async def ui_recipes_list(request: Request):
|
|||||||
ctx = get_user_context_from_cookie(request)
|
ctx = get_user_context_from_cookie(request)
|
||||||
|
|
||||||
if not ctx:
|
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()
|
all_recipes = list_all_recipes()
|
||||||
|
|
||||||
@@ -1681,7 +1697,7 @@ async def cache_detail(content_hash: str, request: Request):
|
|||||||
|
|
||||||
if wants_html(request):
|
if wants_html(request):
|
||||||
if not ctx:
|
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)
|
return HTMLResponse(render_page("Login Required", content, None, active_tab="media"), status_code=401)
|
||||||
|
|
||||||
# Check user has access
|
# Check user has access
|
||||||
@@ -2190,7 +2206,7 @@ async def list_media(
|
|||||||
if wants_html(request):
|
if wants_html(request):
|
||||||
# Require login for HTML media view
|
# Require login for HTML media view
|
||||||
if not ctx:
|
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"))
|
return HTMLResponse(render_page("Media", content, None, active_tab="media"))
|
||||||
|
|
||||||
# Get hashes owned by/associated with this user
|
# 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 = ""
|
user_info = ""
|
||||||
if actor_id:
|
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'''
|
user_info = f'''
|
||||||
<div class="flex items-center gap-4 text-sm text-gray-400">
|
<div class="flex items-center gap-4 text-sm text-gray-400">
|
||||||
Logged in as <strong class="text-white">{actor_id}</strong>
|
Logged in as <a href="{l2_user_url}" class="text-white hover:text-blue-300">{actor_id}</a>
|
||||||
<a href="/logout" class="text-blue-400 hover:text-blue-300">Logout</a>
|
|
||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
else:
|
else:
|
||||||
user_info = '''
|
user_info = '''
|
||||||
<div class="text-sm">
|
<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>
|
</div>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@@ -3135,16 +3155,20 @@ def render_ui_html(actor_id: Optional[str] = None, tab: str = "runs") -> str:
|
|||||||
"""
|
"""
|
||||||
user_info = ""
|
user_info = ""
|
||||||
if actor_id:
|
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'''
|
user_info = f'''
|
||||||
<div class="flex items-center gap-4 text-sm text-gray-400">
|
<div class="flex items-center gap-4 text-sm text-gray-400">
|
||||||
Logged in as <strong class="text-white">{actor_id}</strong>
|
Logged in as <a href="{l2_user_url}" class="text-white hover:text-blue-300">{actor_id}</a>
|
||||||
<a href="/logout" class="text-blue-400 hover:text-blue-300">Logout</a>
|
|
||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
else:
|
else:
|
||||||
user_info = '''
|
user_info = '''
|
||||||
<div class="text-sm">
|
<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>
|
</div>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@@ -3327,7 +3351,7 @@ async def ui_runs(request: Request):
|
|||||||
|
|
||||||
# Require login to see runs
|
# Require login to see runs
|
||||||
if not ctx:
|
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)
|
# 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)]
|
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
|
# Require login to see media
|
||||||
if not ctx:
|
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
|
# Get hashes owned by/associated with this user
|
||||||
user_hashes = await get_user_cache_hashes(ctx.username, ctx.actor_id)
|
user_hashes = await get_user_cache_hashes(ctx.username, ctx.actor_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user