Fixed database code everywhere, dashboard is still happy everywhere now.

This commit is contained in:
Michael Woods
2025-12-25 17:16:24 -05:00
parent 5018012dc7
commit bc8a649ff4
7 changed files with 50 additions and 48 deletions

View File

@@ -37,8 +37,7 @@ def init_db() -> ZODB.DB:
return _db return _db
host, port = _get_zeo_address(settings.zeo_file) host, port = _get_zeo_address(settings.zeo_file)
storage = ZEO.ClientStorage((host, port)) _db = ZEO.DB((host, port))
_db = ZODB.DB(storage)
return _db return _db
def get_db() -> ZODB.DB: def get_db() -> ZODB.DB:
@@ -54,7 +53,9 @@ def get_connection() -> Generator[Connection, None, None]:
try: try:
yield conn yield conn
finally: finally:
conn.close() #print("not closing connection")
#conn.close()
pass
# Optional: per-request transaction (if you want automatic commit/abort) # Optional: per-request transaction (if you want automatic commit/abort)
def get_transaction_manager(): def get_transaction_manager():

View File

@@ -3,18 +3,17 @@ from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.security import HTTPBasic, HTTPBasicCredentials
from .auth import HttpUser from .auth import HttpUser
from .database import get_transaction from .database import ConnectionDependency
security = HTTPBasic() security = HTTPBasic()
async def get_current_http_user(credentials: HTTPBasicCredentials = Depends(security)): async def get_current_http_user(conn: ConnectionDependency, credentials: HTTPBasicCredentials = Depends(security)):
""" """
Authenticate via Basic Auth using HttpUser from ZODB. Authenticate via Basic Auth using HttpUser from ZODB.
Injected by the standalone runner (get_db_connection available). Injected by the standalone runner (get_db_connection available).
""" """
with get_transaction() as conn:
root = conn.root() root = conn.root()
http_users = root.get("httpUsers") http_users = root.get("httpUsers")

View File

@@ -5,6 +5,7 @@ from fastapi.responses import HTMLResponse
from packetserver.http.dependencies import get_current_http_user from packetserver.http.dependencies import get_current_http_user
from packetserver.http.auth import HttpUser from packetserver.http.auth import HttpUser
from packetserver.http.server import templates from packetserver.http.server import templates
from packetserver.http.database import ConnectionDependency
router = APIRouter(tags=["dashboard"]) router = APIRouter(tags=["dashboard"])
@@ -15,11 +16,13 @@ from .bulletins import list_bulletins
@router.get("/dashboard", response_class=HTMLResponse) @router.get("/dashboard", response_class=HTMLResponse)
async def dashboard( async def dashboard(
conn: ConnectionDependency,
request: Request, request: Request,
current_user: HttpUser = Depends(get_current_http_user) current_user: HttpUser = Depends(get_current_http_user)
): ):
# Internal call pass explicit defaults to avoid Query object injection # Internal call pass explicit defaults to avoid Query object injection
messages_resp = await api_get_messages( messages_resp = await api_get_messages(
conn,
current_user=current_user, current_user=current_user,
type="all", type="all",
limit=100, limit=100,
@@ -27,7 +30,7 @@ async def dashboard(
) )
messages = messages_resp["messages"] messages = messages_resp["messages"]
bulletins_resp = await list_bulletins(limit=10, since=None) bulletins_resp = await list_bulletins(conn, limit=10, since=None)
recent_bulletins = bulletins_resp["bulletins"] recent_bulletins = bulletins_resp["bulletins"]
return templates.TemplateResponse( return templates.TemplateResponse(
@@ -42,11 +45,12 @@ async def dashboard(
@router.get("/dashboard/profile", response_class=HTMLResponse) @router.get("/dashboard/profile", response_class=HTMLResponse)
async def profile_page( async def profile_page(
conn: ConnectionDependency,
request: Request, request: Request,
current_user: HttpUser = Depends(get_current_http_user) current_user: HttpUser = Depends(get_current_http_user)
): ):
from packetserver.http.routers.profile import profile as api_profile from packetserver.http.routers.profile import profile as api_profile
profile_data = await api_profile(current_user=current_user) profile_data = await api_profile(conn, current_user=current_user)
return templates.TemplateResponse( return templates.TemplateResponse(
"profile.html", "profile.html",

View File

@@ -161,6 +161,7 @@ async def mark_message_retrieved(
@html_router.get("/messages", response_class=HTMLResponse) @html_router.get("/messages", response_class=HTMLResponse)
async def message_list_page( async def message_list_page(
conn: ConnectionDependency,
request: Request, request: Request,
type: str = Query("received", alias="msg_type"), # matches your filter links type: str = Query("received", alias="msg_type"), # matches your filter links
limit: Optional[int] = Query(50, le=100), limit: Optional[int] = Query(50, le=100),
@@ -168,7 +169,7 @@ async def message_list_page(
): ):
from packetserver.http.server import templates from packetserver.http.server import templates
# Directly call the existing API endpoint function # Directly call the existing API endpoint function
api_resp = await get_messages(current_user=current_user, type=type, limit=limit, since=None) api_resp = await get_messages(conn, current_user=current_user, type=type, limit=limit, since=None)
messages = api_resp["messages"] messages = api_resp["messages"]
return templates.TemplateResponse( return templates.TemplateResponse(

View File

@@ -6,6 +6,7 @@ import mimetypes
from packetserver.http.dependencies import get_current_http_user from packetserver.http.dependencies import get_current_http_user
from packetserver.http.auth import HttpUser from packetserver.http.auth import HttpUser
from packetserver.http.database import DbDependency, ConnectionDependency
from packetserver.server.objects import Object from packetserver.server.objects import Object
from pydantic import BaseModel from pydantic import BaseModel
@@ -22,15 +23,11 @@ class ObjectSummary(BaseModel):
modified_at: datetime modified_at: datetime
@router.get("/objects", response_model=List[ObjectSummary]) @router.get("/objects", response_model=List[ObjectSummary])
async def list_my_objects(current_user: HttpUser = Depends(get_current_http_user)): async def list_my_objects(db: DbDependency, current_user: HttpUser = Depends(get_current_http_user)):
from packetserver.runners.http_server import get_db_connection
conn = get_db_connection()
root = conn.root()
username = current_user.username # uppercase callsign username = current_user.username # uppercase callsign
core_objects = Object.get_objects_by_username(username, root) core_objects = Object.get_objects_by_username(username, db)
# Sort newest first by created_at # Sort newest first by created_at
core_objects.sort(key=lambda o: o.created_at, reverse=True) core_objects.sort(key=lambda o: o.created_at, reverse=True)

View File

@@ -4,6 +4,7 @@ from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from pathlib import Path from pathlib import Path
from .database import init_db
from .routers import public, profile, messages, send from .routers import public, profile, messages, send
BASE_DIR = Path(__file__).parent.resolve() BASE_DIR = Path(__file__).parent.resolve()
@@ -40,6 +41,9 @@ from .routers.message_detail import router as message_detail_router
from .routers.messages import html_router from .routers.messages import html_router
from .routers.objects import router as objects_router from .routers.objects import router as objects_router
# initialize database
init_db()
# Include routers # Include routers
app.include_router(public.router) app.include_router(public.router)
app.include_router(profile.router) app.include_router(profile.router)

View File

@@ -13,14 +13,10 @@ import argparse
import sys import sys
import uvicorn import uvicorn
import ZODB.FileStorage
import ZODB.DB
import logging
from packetserver.http.server import app from packetserver.http.server import app
def main(): def main():
parser = argparse.ArgumentParser(description="Run the PacketServer HTTP API server") parser = argparse.ArgumentParser(description="Run the PacketServer HTTP API server")
parser.add_argument("--db", required=True, help="DB path (local /path/to/Data.fs) or ZEO (host:port)")
parser.add_argument("--host", default="0.0.0.0", help="Bind host (default: 0.0.0.0)") parser.add_argument("--host", default="0.0.0.0", help="Bind host (default: 0.0.0.0)")
parser.add_argument("--port", type=int, default=8080, help="Port to listen on (default: 8080)") parser.add_argument("--port", type=int, default=8080, help="Port to listen on (default: 8080)")
parser.add_argument("--reload", action="store_true", help="Enable auto-reload during development") parser.add_argument("--reload", action="store_true", help="Enable auto-reload during development")