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
83 lines
2.2 KiB
Python
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
|