import logging from pathlib import Path from fastapi import APIRouter, Depends, Request, status from fastapi.responses import HTMLResponse, RedirectResponse, Response from fastapi.templating import Jinja2Templates from app.config import Settings, get_settings from app.dependencies import get_app_settings, get_auth_db, get_current_auth_session from app.services.auth import AuthenticatedSession from app.services.config_page import ConfigSaveError, build_config_sections, save_config_updates from sqlalchemy.orm import Session templates = Jinja2Templates(directory=str(Path(__file__).resolve().parents[2] / "templates")) router = APIRouter(tags=["pages"]) logger = logging.getLogger(__name__) @router.get("/", response_class=HTMLResponse) def home( request: Request, current_auth: AuthenticatedSession | None = Depends(get_current_auth_session), ) -> RedirectResponse: if current_auth is None: return RedirectResponse(url="/login", status_code=status.HTTP_303_SEE_OTHER) return RedirectResponse(url="/config", status_code=status.HTTP_303_SEE_OTHER) @router.get("/admin", response_class=HTMLResponse) def admin_redirect( request: Request, current_auth: AuthenticatedSession | None = Depends(get_current_auth_session), ) -> RedirectResponse: if current_auth is None: return RedirectResponse(url="/login", status_code=status.HTTP_303_SEE_OTHER) return RedirectResponse(url="/config", status_code=status.HTTP_303_SEE_OTHER) @router.get("/config", response_class=HTMLResponse) def config_page( request: Request, auth_db_session: Session = Depends(get_auth_db), settings: Settings = Depends(get_app_settings), current_auth: AuthenticatedSession | None = Depends(get_current_auth_session), ) -> Response: if current_auth is None: return RedirectResponse(url="/login", status_code=status.HTTP_303_SEE_OTHER) context = { "app_name": settings.app_name, "app_env": settings.app_env, "current_username": current_auth.user.username, "csrf_token": current_auth.session.csrf_token, "force_password_change": current_auth.user.force_password_change, "password_change_error": None, "config_error": None, "config_saved": request.query_params.get("saved") == "1", "config_sections": build_config_sections(auth_db_session, settings), } return templates.TemplateResponse(request, "config.html", context) @router.post("/config", response_class=HTMLResponse) async def config_submit( request: Request, auth_db_session: Session = Depends(get_auth_db), settings: Settings = Depends(get_app_settings), current_auth: AuthenticatedSession | None = Depends(get_current_auth_session), ) -> Response: if current_auth is None: return RedirectResponse(url="/login", status_code=status.HTTP_303_SEE_OTHER) form = await request.form() csrf_token = form.get("csrf_token") if csrf_token != current_auth.session.csrf_token: logger.warning("Rejected config update due to CSRF validation failure") context = { "app_name": settings.app_name, "app_env": settings.app_env, "current_username": current_auth.user.username, "csrf_token": current_auth.session.csrf_token, "force_password_change": current_auth.user.force_password_change, "password_change_error": None, "config_error": "invalid config update request", "config_saved": False, "config_sections": build_config_sections(auth_db_session, settings), } return templates.TemplateResponse( request, "config.html", context, status_code=status.HTTP_400_BAD_REQUEST, ) try: save_config_updates(auth_db_session, dict(form), settings) except ConfigSaveError: logger.warning("Rejected config update due to invalid submitted values") refreshed_settings = build_runtime_settings(auth_db_session, get_settings()) context = { "app_name": refreshed_settings.app_name, "app_env": refreshed_settings.app_env, "current_username": current_auth.user.username, "csrf_token": current_auth.session.csrf_token, "force_password_change": current_auth.user.force_password_change, "password_change_error": None, "config_error": "invalid config submission", "config_saved": False, "config_sections": build_config_sections(auth_db_session, refreshed_settings), } return templates.TemplateResponse( request, "config.html", context, status_code=status.HTTP_400_BAD_REQUEST, ) return RedirectResponse(url="/config?saved=1", status_code=status.HTTP_303_SEE_OTHER)