adacam-api/adacam_api/forwarder.py
kayos 37aefb84c8 Initial commit: adacam-api v1.0.0
Clean Python Flask replacement for odc-api (434k lines Node.js → ~350 lines Python)
- GET /api/1/landmarks/last/{N} - last N detections from SQLite
- POST /api/1/landmarks - ingest detections + forward to AdaMaps
- GET /api/1/gnssConcise/latestValid - GPS fix from Redis
- GET /api/1/status - device status
- GET /api/1/deviceinfo - device identity
- GET /api/1/recording/frames/latest - latest frame path

No /api/1/cmd - that's the CVE, it's gone.

Includes:
- SQLite for local storage + offline queue
- Background thread for AdaMaps retry
- systemd service unit
- install.sh for device deployment
2026-03-14 08:13:04 -07:00

83 lines
2.2 KiB
Python

"""AdaMaps forwarding with offline queue."""
import threading
import time
import requests
from . import config, db
_thread = None
_running = False
def forward_detection(data):
"""Forward a detection to AdaMaps, queue if offline."""
api_url = config.get("adamaps_api", "https://api.adamaps.org")
api_key = config.get("adamaps_key", "")
device_id = config.get("device_id", "unknown")
payload = {
"device_id": device_id,
"detection": data,
}
try:
resp = requests.post(
f"{api_url}/api/ingest",
json=payload,
headers={"X-AdaMaps-Key": api_key},
timeout=10,
)
if resp.status_code == 200:
return True
except requests.RequestException:
pass
# Queue for retry
db.queue_forward(payload)
return False
def _retry_loop():
"""Background thread to retry queued forwards."""
global _running
while _running:
try:
items = db.pop_queue(limit=20)
if not items:
time.sleep(30)
continue
api_url = config.get("adamaps_api", "https://api.adamaps.org")
api_key = config.get("adamaps_key", "")
for _, payload in items:
try:
resp = requests.post(
f"{api_url}/api/ingest",
json=payload,
headers={"X-AdaMaps-Key": api_key},
timeout=10,
)
if resp.status_code != 200:
db.queue_forward(payload)
except requests.RequestException:
db.queue_forward(payload)
time.sleep(5)
time.sleep(5)
except Exception:
time.sleep(30)
def start_retry_thread():
"""Start the background retry thread."""
global _thread, _running
if _thread is None or not _thread.is_alive():
_running = True
_thread = threading.Thread(target=_retry_loop, daemon=True)
_thread.start()
def stop_retry_thread():
"""Stop the background retry thread."""
global _running
_running = False