diff --git a/packetserver/http/routers/bulletins.py b/packetserver/http/routers/bulletins.py index 4b29235..0535433 100644 --- a/packetserver/http/routers/bulletins.py +++ b/packetserver/http/routers/bulletins.py @@ -1,61 +1,81 @@ from fastapi import APIRouter, Path, Query, Depends, HTTPException, Request, status from fastapi.responses import HTMLResponse -from typing import Optional +from typing import Optional, List from pydantic import BaseModel, Field, constr from datetime import datetime import transaction from persistent.list import PersistentList -from ..dependencies import get_current_http_user # relative +from ..dependencies import get_current_http_user from ..auth import HttpUser -from ..server import templates # relative import - this is now safe +from ..server import templates +from packetserver.runners.http_server import get_db_connection from packetserver.server.bulletin import Bulletin -# Existing API router (keep) +# API router (/api/v1) router = APIRouter(prefix="/api/v1", tags=["bulletins"]) -# ... your existing list_bulletins and get_bulletin routes ... - -# New non-prefixed router for HTML pages +# HTML router (pretty URLs: /bulletins, /bulletins/{bid}) html_router = APIRouter(tags=["bulletins-html"]) -@html_router.get("/bulletins", response_class="HTMLResponse") -async def bulletin_list_page(request: Request, limit: Optional[int] = Query(50, le=100)): - # Local import to avoid circular issues - from .bulletins import list_bulletins as api_list_bulletins - api_resp = await api_list_bulletins(limit=limit, since=None) - bulletins = api_resp["bulletins"] +# --- API Endpoints --- - current_user = None - try: - current_user = await get_current_http_user()(request) # optional call - except: - pass +async def list_bulletins(limit: int = 50, since: Optional[datetime] = None) -> dict: + conn = get_db_connection() + root = conn.root() + bulletins_list: List[Bulletin] = root.get("bulletins", []) - return templates.TemplateResponse( - "bulletin_list.html", - {"request": request, "bulletins": bulletins, "current_user": current_user.username if current_user else None} - ) + # Newest first + bulletins_list = sorted(bulletins_list, key=lambda b: b.created_at, reverse=True) -@html_router.get("/bulletins/{bid}", response_class="HTMLResponse") -async def bulletin_detail_page(request: Request, bid: int): - from .bulletins import get_bulletin as api_get_bulletin - try: - bulletin = await api_get_bulletin(bid=bid, current_user=None) - except HTTPException: - raise HTTPException(status_code=404, detail="Bulletin not found") + if since: + bulletins_list = [b for b in bulletins_list if b.created_at > since] - current_user = None - try: - current_user = await get_current_http_user()(request) - except: - pass + bulletins = [ + { + "id": b.id, + "author": b.author, + "subject": b.subject, + "body": b.body, + "created_at": b.created_at.isoformat() + "Z", + "updated_at": b.updated_at.isoformat() + "Z", + } + for b in bulletins_list[:limit] + ] - return templates.TemplateResponse( - "bulletin_detail.html", - {"request": request, "bulletin": bulletin, "current_user": current_user.username if current_user else None} - ) + return {"bulletins": bulletins} +@router.get("/bulletins") +async def api_list_bulletins( + limit: Optional[int] = Query(50, le=100), + since: Optional[datetime] = None, + current_user: HttpUser = Depends(get_current_http_user) +): + return await list_bulletins(limit=limit, since=since) + +async def get_one_bulletin(bid: int) -> dict: + conn = get_db_connection() + root = conn.root() + bulletins_list: List[Bulletin] = root.get("bulletins", []) + + for b in bulletins_list: + if b.id == bid: + return { + "id": b.id, + "author": b.author, + "subject": b.subject, + "body": b.body, + "created_at": b.created_at.isoformat() + "Z", + "updated_at": b.updated_at.isoformat() + "Z", + } + raise HTTPException(status_code=404, detail="Bulletin not found") + +@router.get("/bulletins/{bid}") +async def api_get_bulletin( + bid: int, + current_user: HttpUser = Depends(get_current_http_user) +): + return await get_one_bulletin(bid) class CreateBulletinRequest(BaseModel): subject: constr(min_length=1, max_length=100) = Field(..., description="Bulletin subject/title") @@ -66,29 +86,24 @@ async def create_bulletin( payload: CreateBulletinRequest, current_user: HttpUser = Depends(get_current_http_user) ): - from packetserver.runners.http_server import get_db_connection - from packetserver.server.bulletin import Bulletin - conn = get_db_connection() root = conn.root() - # Ensure the bulletins list and counter exist if 'bulletins' not in root: root['bulletins'] = PersistentList() new_bulletin = Bulletin( - author=current_user.username, # uppercase callsign + author=current_user.username, subject=payload.subject.strip(), - text=payload.body.strip() # note: 'text' param, assigned to body + text=payload.body.strip() ) - # This assigns the ID, sets timestamps (if not already), and appends new_id = new_bulletin.write_new(root) transaction.commit() return { - "id": new_id, # or new_bulletin.id + "id": new_id, "author": new_bulletin.author, "subject": new_bulletin.subject, "body": new_bulletin.body, @@ -96,32 +111,31 @@ async def create_bulletin( "updated_at": new_bulletin.updated_at.isoformat() + "Z", } +# --- HTML Pages (require login) --- + @html_router.get("/bulletins", response_class=HTMLResponse) async def bulletin_list_page( request: Request, limit: Optional[int] = Query(50, le=100), - current_user: Optional[HttpUser] = Depends(get_current_http_user, use_cache=False) # optional auth + current_user: HttpUser = Depends(get_current_http_user) ): - # Internal call to existing API function api_resp = await list_bulletins(limit=limit, since=None) bulletins = api_resp["bulletins"] return templates.TemplateResponse( "bulletin_list.html", - {"request": request, "bulletins": bulletins, "current_user": current_user.username if current_user else None} + {"request": request, "bulletins": bulletins, "current_user": current_user.username} ) @html_router.get("/bulletins/{bid}", response_class=HTMLResponse) async def bulletin_detail_page( request: Request, bid: int = Path(...), - current_user: Optional[HttpUser] = Depends(get_current_http_user, use_cache=False) + current_user: HttpUser = Depends(get_current_http_user) ): - # Internal call to existing detail function - # Pass a dummy None user since the API requires it but doesn't enforce privileges - bulletin = await get_bulletin(bid=bid, current_user=None) + bulletin = await get_one_bulletin(bid=bid) return templates.TemplateResponse( "bulletin_detail.html", - {"request": request, "bulletin": bulletin, "current_user": current_user.username if current_user else None} + {"request": request, "bulletin": bulletin, "current_user": current_user.username} ) \ No newline at end of file