More bulletins changes.
This commit is contained in:
@@ -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:
|
||||
if since:
|
||||
bulletins_list = [b for b in bulletins_list if b.created_at > since]
|
||||
|
||||
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 {"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")
|
||||
|
||||
current_user = None
|
||||
try:
|
||||
current_user = await get_current_http_user()(request)
|
||||
except:
|
||||
pass
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"bulletin_detail.html",
|
||||
{"request": request, "bulletin": bulletin, "current_user": current_user.username if current_user else None}
|
||||
)
|
||||
|
||||
@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}
|
||||
)
|
||||
Reference in New Issue
Block a user