62 lines
2.1 KiB
Python
62 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import Request
|
|
from fastapi.responses import RedirectResponse
|
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
|
from chat.db.connection import open_db
|
|
from chat.state.entities import get_you, list_bots
|
|
|
|
|
|
class FirstRunRedirectMiddleware(BaseHTTPMiddleware):
|
|
"""Redirect users through the first-run flow (per requirements §16.2).
|
|
|
|
Behavior on GET requests to landing routes (``/`` and ``/chats``):
|
|
|
|
- No ``you_entity`` → ``/settings``
|
|
- ``you_entity`` exists but no bots → ``/bots/new``
|
|
- Otherwise pass through to the underlying handler.
|
|
|
|
The middleware is a no-op for:
|
|
|
|
- Non-GET requests (POST/PUT writes proceed and surface their own errors).
|
|
- Static assets, health checks, and any path under ``/settings``,
|
|
``/bots``, ``/api``, ``/health``, ``/favicon`` — so the user can
|
|
actually complete setup once redirected.
|
|
- Sub-paths of ``/chats`` (e.g. ``/chats/<id>``, ``/chats/<id>/drawer``);
|
|
only the bare landing pages get the redirect treatment. Sub-resources
|
|
either 404 cleanly or are HTMX partials that should not page-redirect.
|
|
"""
|
|
|
|
SKIP_PREFIXES = (
|
|
"/static",
|
|
"/settings",
|
|
"/bots",
|
|
"/health",
|
|
"/favicon",
|
|
"/api",
|
|
)
|
|
|
|
async def dispatch(self, request: Request, call_next):
|
|
if request.method != "GET":
|
|
return await call_next(request)
|
|
|
|
path = request.url.path
|
|
if any(path.startswith(p) for p in self.SKIP_PREFIXES):
|
|
return await call_next(request)
|
|
|
|
# Only fire on the landing routes themselves.
|
|
if path != "/" and path != "/chats":
|
|
return await call_next(request)
|
|
|
|
settings = request.app.state.settings
|
|
with open_db(settings.db_path) as conn:
|
|
you = get_you(conn)
|
|
bots = list_bots(conn)
|
|
|
|
if you is None:
|
|
return RedirectResponse(url="/settings", status_code=303)
|
|
if not bots:
|
|
return RedirectResponse(url="/bots/new", status_code=303)
|
|
return await call_next(request)
|