diff --git a/moxie/web/routes.py b/moxie/web/routes.py index fec98d0..223c0c1 100644 --- a/moxie/web/routes.py +++ b/moxie/web/routes.py @@ -9,7 +9,7 @@ from pathlib import Path from typing import Optional, List from fastapi import APIRouter, Request, Response, Cookie, Form, UploadFile, File, HTTPException from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse -from fastapi.templating import Jinja2Templates +from jinja2 import Environment, FileSystemLoader from pydantic import BaseModel from loguru import logger @@ -20,15 +20,18 @@ from core.orchestrator import Orchestrator router = APIRouter() -# Templates - use string path for better compatibility -# Disable cache to avoid hashing issues with Pydantic objects -from jinja2 import Environment, FileSystemLoader -template_env = Environment( - loader=FileSystemLoader(str(Path(__file__).parent / "templates")), +# Templates - use Jinja2 directly without Starlette wrapper +TEMPLATES_DIR = Path(__file__).parent / "templates" +jinja_env = Environment( + loader=FileSystemLoader(str(TEMPLATES_DIR)), autoescape=True, - cache_size=0, # Disable cache ) -templates = Jinja2Templates(env=template_env) + + +def render_template(name: str, context: dict) -> str: + """Render a template with the given context.""" + template = jinja_env.get_template(name) + return template.render(**context) def get_template_settings(): @@ -90,28 +93,30 @@ async def landing_page(request: Request): headers={"Location": "/chat"} ) - return templates.TemplateResponse( - "landing.html", - {"request": request, "settings": get_template_settings()} - ) + html = render_template("landing.html", { + "settings": get_template_settings() + }) + return HTMLResponse(content=html) @router.get("/login", response_class=HTMLResponse) async def login_page(request: Request, error: Optional[str] = None): """Login page.""" - return templates.TemplateResponse( - "login.html", - {"request": request, "settings": get_template_settings(), "error": error} - ) + html = render_template("login.html", { + "settings": get_template_settings(), + "error": error + }) + return HTMLResponse(content=html) @router.get("/signup", response_class=HTMLResponse) async def signup_page(request: Request, error: Optional[str] = None): """Signup page.""" - return templates.TemplateResponse( - "signup.html", - {"request": request, "settings": get_template_settings(), "error": error} - ) + html = render_template("signup.html", { + "settings": get_template_settings(), + "error": error + }) + return HTMLResponse(content=html) @router.post("/login") @@ -125,11 +130,11 @@ async def login_submit( user = auth.authenticate(username, password) if not user: - return templates.TemplateResponse( - "login.html", - {"request": request, "settings": get_template_settings(), "error": "Invalid username or password"}, - status_code=401 - ) + html = render_template("login.html", { + "settings": get_template_settings(), + "error": "Invalid username or password" + }) + return HTMLResponse(content=html, status_code=401) # Create session token = auth.create_session(user.id) @@ -157,28 +162,28 @@ async def signup_submit( ): """Process signup form.""" if password != confirm_password: - return templates.TemplateResponse( - "signup.html", - {"request": request, "settings": get_template_settings(), "error": "Passwords do not match"}, - status_code=400 - ) + html = render_template("signup.html", { + "settings": get_template_settings(), + "error": "Passwords do not match" + }) + return HTMLResponse(content=html, status_code=400) if len(password) < 6: - return templates.TemplateResponse( - "signup.html", - {"request": request, "settings": get_template_settings(), "error": "Password must be at least 6 characters"}, - status_code=400 - ) + html = render_template("signup.html", { + "settings": get_template_settings(), + "error": "Password must be at least 6 characters" + }) + return HTMLResponse(content=html, status_code=400) auth = get_auth_manager() user = auth.create_user(username, email, password) if not user: - return templates.TemplateResponse( - "signup.html", - {"request": request, "settings": get_template_settings(), "error": "Username or email already exists"}, - status_code=400 - ) + html = render_template("signup.html", { + "settings": get_template_settings(), + "error": "Username or email already exists" + }) + return HTMLResponse(content=html, status_code=400) # Create session token = auth.create_session(user.id) @@ -225,15 +230,12 @@ async def chat_page(request: Request, session_token: Optional[str] = Cookie(None rate_info = auth.check_rate_limit(user.id) - return templates.TemplateResponse( - "chat.html", - { - "request": request, - "settings": get_template_settings(), - "user": user, - "rate_info": rate_info - } - ) + html = render_template("chat.html", { + "settings": get_template_settings(), + "user": user, + "rate_info": rate_info + }) + return HTMLResponse(content=html) @router.get("/profile", response_class=HTMLResponse) @@ -254,16 +256,13 @@ async def profile_page(request: Request, session_token: Optional[str] = Cookie(N documents = auth.get_user_documents(user.id) rate_info = auth.check_rate_limit(user.id) - return templates.TemplateResponse( - "profile.html", - { - "request": request, - "settings": get_template_settings(), - "user": user, - "documents": documents, - "rate_info": rate_info - } - ) + html = render_template("profile.html", { + "settings": get_template_settings(), + "user": user, + "documents": documents, + "rate_info": rate_info + }) + return HTMLResponse(content=html) # ============================================================================