refactor: move webapp from /user/page to /app
Simplified the webapp routing structure by consolidating the admin UI
to a single clean endpoint.
Changes:
- Moved webapp from /user/page to /app (root of mount)
- Removed /user JSON endpoint (no longer needed)
- Updated mount point from /user to /app in app.py
- Updated all route path checks (3 locations)
- Updated OAuth redirects to point to /app
- Updated all HTMX endpoint references
- Updated documentation (ADR-007, CHANGELOG)
- Added redirect from /app to /app/ for trailing slash handling
New Route Structure:
- /app - Main webapp (HTML UI with tabs)
- /app/revoke - Revoke background access
- /app/webhooks - Webhook management UI
- /app/webhooks/enable/{preset_id} - Enable webhook preset
- /app/webhooks/disable/{preset_id} - Disable webhook preset
Breaking Change: Existing bookmarks to /user or /user/page will no longer work.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Browser-based OAuth login routes for admin UI.
|
||||
|
||||
Separate from MCP OAuth flow - these routes establish browser sessions
|
||||
for accessing admin UI endpoints like /user/page.
|
||||
for accessing admin UI endpoints like /app.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
@@ -38,8 +38,8 @@ async def oauth_login(request: Request) -> RedirectResponse | JSONResponse:
|
||||
"""
|
||||
oauth_ctx = request.app.state.oauth_context
|
||||
if not oauth_ctx:
|
||||
# BasicAuth mode - no login needed, redirect to user page
|
||||
return RedirectResponse("/user/page", status_code=302)
|
||||
# BasicAuth mode - no login needed, redirect to app
|
||||
return RedirectResponse("/app", status_code=302)
|
||||
|
||||
storage = oauth_ctx["storage"]
|
||||
oauth_client = oauth_ctx["oauth_client"]
|
||||
@@ -71,7 +71,7 @@ async def oauth_login(request: Request) -> RedirectResponse | JSONResponse:
|
||||
await storage.store_oauth_session(
|
||||
session_id=state, # Use state as session ID
|
||||
client_id="browser-ui",
|
||||
client_redirect_uri="/user/page",
|
||||
client_redirect_uri="/app",
|
||||
state=state,
|
||||
code_challenge=code_challenge,
|
||||
code_challenge_method="S256",
|
||||
@@ -383,7 +383,7 @@ async def oauth_login_callback(request: Request) -> RedirectResponse | HTMLRespo
|
||||
# Continue anyway - profile cache is optional for browser UI
|
||||
|
||||
# Create response and set session cookie
|
||||
response = RedirectResponse("/user/page", status_code=302)
|
||||
response = RedirectResponse("/app", status_code=302)
|
||||
response.set_cookie(
|
||||
key="mcp_session",
|
||||
value=user_id,
|
||||
|
||||
@@ -602,7 +602,7 @@ async def user_info_html(request: Request) -> HTMLResponse:
|
||||
webhooks_tab_html = ""
|
||||
if show_webhooks_tab:
|
||||
webhooks_tab_html = """
|
||||
<div hx-get="/user/webhooks" hx-trigger="load" hx-swap="outerHTML">
|
||||
<div hx-get="/app/webhooks" hx-trigger="load" hx-swap="outerHTML">
|
||||
<p style="color: #999;">Loading webhook management...</p>
|
||||
</div>
|
||||
"""
|
||||
|
||||
@@ -32,7 +32,7 @@ def _get_storage(request: Request):
|
||||
Returns:
|
||||
RefreshTokenStorage instance or None
|
||||
"""
|
||||
# Try browser_app state first (for /user routes)
|
||||
# Try browser_app state first (for /app routes)
|
||||
storage = getattr(request.app.state, "storage", None)
|
||||
|
||||
# Try oauth_context if in OAuth mode
|
||||
@@ -289,7 +289,7 @@ async def webhook_management_pane(request: Request) -> HTMLResponse:
|
||||
status_badge = f'<span style="color: #4caf50; font-weight: bold;">✓ Enabled ({num_webhooks} webhooks)</span>'
|
||||
action_button = f"""
|
||||
<button
|
||||
hx-delete="/user/webhooks/disable/{preset_id}"
|
||||
hx-delete="/app/webhooks/disable/{preset_id}"
|
||||
hx-target="#preset-{preset_id}"
|
||||
hx-swap="outerHTML"
|
||||
class="button"
|
||||
@@ -301,7 +301,7 @@ async def webhook_management_pane(request: Request) -> HTMLResponse:
|
||||
status_badge = '<span style="color: #999;">Not Enabled</span>'
|
||||
action_button = f"""
|
||||
<button
|
||||
hx-post="/user/webhooks/enable/{preset_id}"
|
||||
hx-post="/app/webhooks/enable/{preset_id}"
|
||||
hx-target="#preset-{preset_id}"
|
||||
hx-swap="outerHTML"
|
||||
class="button button-primary">
|
||||
@@ -429,7 +429,7 @@ async def enable_webhook_preset(request: Request) -> HTMLResponse:
|
||||
<div><span style="color: #4caf50; font-weight: bold;">✓ Enabled ({num_webhooks} webhooks)</span></div>
|
||||
<div>
|
||||
<button
|
||||
hx-delete="/user/webhooks/disable/{preset_id}"
|
||||
hx-delete="/app/webhooks/disable/{preset_id}"
|
||||
hx-target="#preset-{preset_id}"
|
||||
hx-swap="outerHTML"
|
||||
class="button"
|
||||
@@ -520,7 +520,7 @@ async def disable_webhook_preset(request: Request) -> HTMLResponse:
|
||||
<div><span style="color: #999;">Not Enabled</span></div>
|
||||
<div>
|
||||
<button
|
||||
hx-post="/user/webhooks/enable/{preset_id}"
|
||||
hx-post="/app/webhooks/enable/{preset_id}"
|
||||
hx-target="#preset-{preset_id}"
|
||||
hx-swap="outerHTML"
|
||||
class="button button-primary">
|
||||
|
||||
Reference in New Issue
Block a user