diff --git a/packetserver/http/routers/messages.py b/packetserver/http/routers/messages.py index c2e1cc1..a0d765f 100644 --- a/packetserver/http/routers/messages.py +++ b/packetserver/http/routers/messages.py @@ -1,5 +1,5 @@ # packetserver/http/routers/messages.py -from fastapi import APIRouter, Depends, Query, HTTPException, Path, Request +from fastapi import APIRouter, Depends, Query, HTTPException, Path, Request, Query from fastapi.responses import HTMLResponse from typing import Optional from datetime import datetime @@ -11,6 +11,7 @@ from pydantic import BaseModel, Field, validator from packetserver.http.dependencies import get_current_http_user from packetserver.http.auth import HttpUser from packetserver.http.database import DbDependency +from packetserver.http.server import templates html_router = APIRouter(tags=["messages-html"]) @@ -27,58 +28,78 @@ class MarkRetrievedRequest(BaseModel): return v -@router.get("/messages") -async def get_messages( +@router.get("/messages", response_class=HTMLResponse) +async def message_list( + request: Request, db: DbDependency, current_user: HttpUser = Depends(get_current_http_user), - type: str = Query("received", description="received, sent, or all"), - limit: Optional[int] = Query(20, le=100, description="Max messages to return (default 20, max 100)"), - since: Optional[str] = Query(None, description="ISO UTC timestamp filter (e.g. 2025-12-01T00:00:00Z)"), - + msg_type: str = Query("received", alias="type"), # received, sent, all + search: Optional[str] = Query(None), + page: int = Query(1, ge=1), + per_page: int = Query(50, ge=1, le=200), ): - if limit is None or limit < 1: - limit = 20 + username = current_user.username.upper().strip() + + valid_types = {"received", "sent", "all"} + if msg_type not in valid_types: + msg_type = "received" - username = current_user.username with db.transaction() as conn: root = conn.root() + user_messages = root["messages"].get(username, []) - if 'messages' not in root: - root['messages'] = PersistentMapping() - if username not in root['messages']: - root['messages'][username] = persistent.list.PersistentList() + # Convert to list of dicts (as current code does) + messages = [] + for msg_id, msg in user_messages.items(): + messages.append({ + "id": msg_id, + "from": msg["from"], + "to": msg["to"], + "text": msg["text"], + "timestamp": msg["timestamp"], + }) - mailbox = root['messages'][username] + # Filter by type + if msg_type == "received": + filtered = [m for m in messages if username in m["to"]] + elif msg_type == "sent": + filtered = [m for m in messages if m["from"] == username] + else: # all + filtered = messages - since_dt = None - if since: - try: - since_dt = datetime.fromisoformat(since.replace("Z", "+00:00")) - except ValueError: - raise HTTPException(status_code=400, detail="Invalid 'since' format") + # Apply search + if search: + search_lower = search.strip().lower() + filtered = [ + m for m in filtered + if (search_lower in m["from"].lower() or + any(search_lower in t.lower() for t in m["to"]) or + search_lower in m["text"].lower()) + ] - messages = [] - for msg in mailbox: - if type == "received" and msg.msg_from == username: - continue - if type == "sent" and msg.msg_from != username: - continue - if since_dt and msg.sent_at < since_dt: - continue + # Sort newest first + filtered.sort(key=lambda m: m["timestamp"], reverse=True) - messages.append({ - "id": str(msg.msg_id), - "from": msg.msg_from, - "to": list(msg.msg_to) if isinstance(msg.msg_to, tuple) else [msg.msg_to], - "sent_at": msg.sent_at.isoformat() + "Z", - "text": msg.text, - "has_attachments": len(msg.attachments) > 0, - "retrieved": msg.retrieved, - }) + # Pagination + total = len(filtered) + start = (page - 1) * per_page + paginated = filtered[start:start + per_page] + total_pages = (total + per_page - 1) // per_page - messages.sort(key=lambda m: m["sent_at"], reverse=True) - - return {"messages": messages[:limit], "total_returned": len(messages[:limit])} + return templates.TemplateResponse( + "message_list.html", + { + "request": request, + "messages": paginated, + "current_user": current_user, + "total": total, + "page": page, + "per_page": per_page, + "total_pages": total_pages, + "current_type": msg_type, + "current_search": search, + } + ) @router.get("/messages/{msg_id}") async def get_message( diff --git a/packetserver/http/templates/message_list.html b/packetserver/http/templates/message_list.html index 376916b..5e77fd7 100644 --- a/packetserver/http/templates/message_list.html +++ b/packetserver/http/templates/message_list.html @@ -10,6 +10,45 @@ +
+ Showing {{ (page-1)*per_page + 1 }}–{{ min(page*per_page, total) }} of {{ total }} messages +
+ +{% if total_pages > 1 %} + +{% endif %} + {% else %}No messages found.
{% endif %}