github-actions[bot]
Deploy from GitHub 39b3777315c11d9c8bcd39ad7bf034f2a88a7379 (filtered: code + Dockerfile + README + NOTICES only)
2e175db
Raw
History Blame Contribute Delete
2.36 kB
"""
Scan-record persistence.
Important
---------
This module persists ONLY metadata (scan_id, verdict, model_version, latency,
timestamp). The image itself is NEVER stored here. Image storage is a separate,
opt-in path handled by `storage.blob`.
Stage 1 ships an in-memory fallback so the service runs out of the box with
no database. Set DATABASE_URL to switch to Postgres (Neon, Supabase, etc.).
"""
from __future__ import annotations
import logging
import time
from dataclasses import dataclass, field
from ..config import settings
log = logging.getLogger(__name__)
@dataclass
class ScanRecord:
scan_id: str
verdict: str
confidence: float
model_version: str
latency_ms: float
c2pa_present: bool
timestamp_unix: float = field(default_factory=time.time)
# In-memory store β€” bounded to last N records to avoid leaking memory.
_MEMORY_CAP = 1000
_memory_store: list[ScanRecord] = []
def record_scan(record: ScanRecord) -> None:
"""Persist a scan record. No-op-safe; never raises into the request path."""
try:
if settings.database_url:
_record_postgres(record)
else:
_record_memory(record)
except Exception as exc:
# Persistence failures must NEVER break the user-facing scan response.
log.warning("Failed to persist scan record: %s", exc)
def _record_memory(record: ScanRecord) -> None:
_memory_store.append(record)
if len(_memory_store) > _MEMORY_CAP:
del _memory_store[: len(_memory_store) - _MEMORY_CAP]
def _record_postgres(record: ScanRecord) -> None: # pragma: no cover - infra
"""Lazy import β€” SQLAlchemy is heavy and not needed in dev/tests."""
# Implementation intentionally deferred until Stage 2 β€” schema below.
#
# CREATE TABLE scans (
# scan_id TEXT PRIMARY KEY,
# verdict TEXT NOT NULL,
# confidence REAL NOT NULL,
# model_version TEXT NOT NULL,
# latency_ms REAL NOT NULL,
# c2pa_present BOOLEAN NOT NULL,
# created_at TIMESTAMPTZ NOT NULL DEFAULT now()
# );
raise NotImplementedError("Postgres persistence wired up in Stage 2.")
def memory_store_snapshot() -> list[ScanRecord]:
"""Test/debug helper β€” returns a copy of the in-memory store."""
return list(_memory_store)