From 5018012dc7adec913fbc259fdc34c5ac5e82da3a Mon Sep 17 00:00:00 2001 From: Michael Woods Date: Thu, 25 Dec 2025 16:06:29 -0500 Subject: [PATCH] Updated database methodology for everything else. --- packetserver/http/auth.py | 48 +++++++++++++-------------- packetserver/http/routers/messages.py | 14 ++++---- packetserver/http/routers/profile.py | 6 ++-- packetserver/http/routers/send.py | 4 +-- 4 files changed, 34 insertions(+), 38 deletions(-) diff --git a/packetserver/http/auth.py b/packetserver/http/auth.py index 1f274af..6886508 100644 --- a/packetserver/http/auth.py +++ b/packetserver/http/auth.py @@ -1,5 +1,6 @@ # packetserver/http/auth.py import ax25 +import transaction from persistent import Persistent from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError @@ -7,7 +8,7 @@ import time from persistent.mapping import PersistentMapping from persistent.list import PersistentList from packetserver.common.util import is_valid_ax25_callsign -from .database import get_db, get_transaction +from .database import ConnectionDependency ph = PasswordHasher() @@ -51,17 +52,16 @@ class HttpUser(Persistent): # rf enabled checks.. # - def is_rf_enabled(self) -> bool: + def is_rf_enabled(self, conn: ConnectionDependency) -> bool: """ Check if RF gateway is enabled (i.e., callsign NOT in global blacklist). Requires an open ZODB connection. """ - with get_transaction() as storage: - root = storage.root() - blacklist = root.get('config', {}).get('blacklist', []) - return self.username not in blacklist + root = conn.root() + blacklist = root.get('config', {}).get('blacklist', []) + return self.username not in blacklist - def set_rf_enabled(self, connection, allow: bool): + def set_rf_enabled(self, conn: ConnectionDependency, allow: bool): """ Enable/disable RF gateway by adding/removing from global blacklist. Requires an open ZODB connection (inside a transaction). @@ -69,26 +69,26 @@ class HttpUser(Persistent): """ from packetserver.common.util import is_valid_ax25_callsign # our validator - with get_transaction() as storage: - root = storage.root() - config = root.setdefault('config', PersistentMapping()) - blacklist = config.setdefault('blacklist', PersistentList()) + root = conn.root() + config = root.setdefault('config', PersistentMapping()) + blacklist = config.setdefault('blacklist', PersistentList()) - upper_name = self.username + upper_name = self.username - if allow: - if not is_valid_ax25_callsign(upper_name): - raise ValueError(f"{upper_name} is not a valid AX.25 callsign – cannot enable RF access") - if upper_name in blacklist: - blacklist.remove(upper_name) - blacklist._p_changed = True - else: - if upper_name not in blacklist: - blacklist.append(upper_name) - blacklist._p_changed = True + if allow: + if not is_valid_ax25_callsign(upper_name): + raise ValueError(f"{upper_name} is not a valid AX.25 callsign – cannot enable RF access") + if upper_name in blacklist: + blacklist.remove(upper_name) + blacklist._p_changed = True + else: + if upper_name not in blacklist: + blacklist.append(upper_name) + blacklist._p_changed = True - config._p_changed = True - root._p_changed = True + config._p_changed = True + root._p_changed = True + transaction.commit() # ------------------------------------------------------------------ # Password handling (unchanged) diff --git a/packetserver/http/routers/messages.py b/packetserver/http/routers/messages.py index 492d1df..e1cdd2e 100644 --- a/packetserver/http/routers/messages.py +++ b/packetserver/http/routers/messages.py @@ -10,6 +10,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 ConnectionDependency html_router = APIRouter(tags=["messages-html"]) @@ -28,18 +29,17 @@ class MarkRetrievedRequest(BaseModel): @router.get("/messages") async def get_messages( + conn: ConnectionDependency, 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)") + since: Optional[str] = Query(None, description="ISO UTC timestamp filter (e.g. 2025-12-01T00:00:00Z)"), + ): if limit is None or limit < 1: limit = 20 username = current_user.username - - from packetserver.runners.http_server import get_db_connection - conn = get_db_connection() root = conn.root() if 'messages' not in root: @@ -81,12 +81,11 @@ async def get_messages( @router.get("/messages/{msg_id}") async def get_message( + conn: ConnectionDependency, msg_id: str = Path(..., description="UUID of the message (as string)"), mark_retrieved: bool = Query(False, description="If true, mark message as retrieved/read"), 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 @@ -127,12 +126,11 @@ async def get_message( @router.patch("/messages/{msg_id}") async def mark_message_retrieved( + conn: ConnectionDependency, msg_id: str = Path(..., description="Message UUID as string"), payload: MarkRetrievedRequest = None, 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 diff --git a/packetserver/http/routers/profile.py b/packetserver/http/routers/profile.py index bcab2d2..76b18c0 100644 --- a/packetserver/http/routers/profile.py +++ b/packetserver/http/routers/profile.py @@ -3,16 +3,14 @@ from fastapi import APIRouter, Depends from packetserver.http.dependencies import get_current_http_user from packetserver.http.auth import HttpUser +from packetserver.http.database import ConnectionDependency router = APIRouter(prefix="/api/v1", tags=["auth"]) @router.get("/profile") -async def profile(current_user: HttpUser = Depends(get_current_http_user)): +async def profile(conn: ConnectionDependency,current_user: HttpUser = Depends(get_current_http_user)): username = current_user.username - - from packetserver.runners.http_server import get_db_connection - conn = get_db_connection() root = conn.root() # Get main BBS User and safe dict diff --git a/packetserver/http/routers/send.py b/packetserver/http/routers/send.py index 2b2cd7e..a0d5551 100644 --- a/packetserver/http/routers/send.py +++ b/packetserver/http/routers/send.py @@ -11,6 +11,7 @@ from packetserver.http.dependencies import get_current_http_user from packetserver.http.auth import HttpUser from packetserver.server.messages import Message from packetserver.common.util import is_valid_ax25_callsign +from packetserver.http.database import ConnectionDependency router = APIRouter(prefix="/api/v1", tags=["messages"]) @@ -39,11 +40,10 @@ class SendMessageRequest(BaseModel): @router.post("/messages") async def send_message( + conn: ConnectionDependency, payload: SendMessageRequest, current_user: HttpUser = Depends(get_current_http_user) ): - from packetserver.runners.http_server import get_db_connection - conn = get_db_connection() root = conn.root() if not current_user.is_rf_enabled(conn):