1+ from datetime import datetime , timezone
2+
3+ from fastapi import APIRouter , Depends , HTTPException , Request
4+ from starlette .status import HTTP_201_CREATED , HTTP_400_BAD_REQUEST
5+ from sqlalchemy import insert
6+
7+ from odp .db import Session
8+ from odp .db .models import DownloadAudit
9+ from odp .api .lib .auth import Authorize , Authorized # only if you want auth; otherwise omit
10+
11+ router = APIRouter ()
12+
13+ @router .post ('/audit' , status_code = HTTP_201_CREATED )
14+ async def create_download_audit (request : Request ):
15+ """
16+ Accept JSON payload to record a download audit and return success.
17+ """
18+ payload = await request .json ()
19+ if not isinstance (payload , dict ):
20+ raise HTTPException (HTTP_400_BAD_REQUEST , 'Invalid JSON payload' )
21+
22+ # derive client_id/user_id from request context (if you have that info).
23+ # Use placeholders if not available
24+ client_id = getattr (request .state , 'client_id' , None ) or payload .get ('client_id' ) or 'unknown'
25+ user_id = getattr (request .state , 'user_id' , None ) or payload .get ('user_id' )
26+
27+ download_url = payload .get ('download_url' )
28+ file_size = payload .get ('file_size' )
29+ success = bool (payload .get ('success' , True ))
30+ meta = payload .get ('meta' , {}) or {}
31+
32+ # copy optional form fields into meta for storage
33+ for k in ('name' , 'email' , 'organisation' ):
34+ if payload .get (k ) is not None :
35+ meta [k ] = payload .get (k )
36+
37+ # capture IP and UA
38+ ip_address = request .client .host if request .client else None
39+ user_agent = request .headers .get ('user-agent' )
40+
41+ # persist using Session
42+ #
43+ # --- THIS IS THE FIX ---
44+ # We use `Session.begin()` to manage the transaction,
45+ # and call methods on the imported `Session` object itself.
46+ #
47+ with Session .begin ():
48+ audit = DownloadAudit (
49+ client_id = client_id ,
50+ user_id = user_id ,
51+ download_url = download_url ,
52+ ip_address = ip_address ,
53+ user_agent = user_agent ,
54+ file_size = file_size ,
55+ success = success ,
56+ timestamp = datetime .now (timezone .utc ),
57+ meta = meta ,
58+ )
59+ Session .add (audit )
60+ Session .flush ()
61+ audit_id = audit .id
62+ # --- END OF FIX ---
63+
64+ return {"status" : "ok" , "audit_id" : audit_id }
0 commit comments