Phase 9: scaffold remote server (Flask + Postgres, docker-compose for unraid)

- server/sql/init.sql: devices/tracking_points/dashcam_files schema
- server/modules/db.py: psycopg3 pool + query helpers
- server/modules/auth.py: X-Device-Name/X-Api-Key header auth
- server/app.py: /health, /api/v1/tracking/batch, /api/v1/dashcam/upload
- server/Dockerfile + docker-compose.yml: api (gunicorn) + postgres services
- server/scripts/create_device.py: one-time device/API-key registration
- server/README.md: unraid deployment + Cloudflare Tunnel exposure docs
- PLAN.md: Phase 9 marked in progress with full build notes

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-08 19:50:52 -04:00
co-authored by Copilot
parent 261ef3f4db
commit 9bdf5bad2c
12 changed files with 582 additions and 1 deletions
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Register a new device (e.g. the Pi) and print its API key.
Run this INSIDE the api container so it shares the same DB connection env
vars already configured by docker-compose, e.g.:
docker compose exec api python3 scripts/create_device.py yaris-2013
The raw API key is only ever shown once here - copy it into the Pi's
upload-client config immediately. Only its sha256 hash is stored server-side.
"""
import hashlib
import secrets
import sys
sys.path.insert(0, "/app")
from modules import db # noqa: E402
def main():
if len(sys.argv) != 2:
print(f"usage: {sys.argv[0]} <device-name>", file=sys.stderr)
sys.exit(1)
device_name = sys.argv[1]
raw_key = secrets.token_urlsafe(32)
key_hash = hashlib.sha256(raw_key.encode("utf-8")).hexdigest()
with db.get_cursor(commit=True) as cur:
cur.execute(
"""
INSERT INTO devices (name, api_key_hash) VALUES (%s, %s)
ON CONFLICT (name) DO UPDATE SET api_key_hash = EXCLUDED.api_key_hash
RETURNING id
""",
(device_name, key_hash),
)
device_id = cur.fetchone()[0]
print(f"Device '{device_name}' registered (id={device_id}).")
print()
print("Save these on the Pi (e.g. in server/.env-style local config, gitignored):")
print(f" YARIS_UPLOAD_DEVICE_NAME={device_name}")
print(f" YARIS_UPLOAD_API_KEY={raw_key}")
print()
print("This key will not be shown again - only its hash is stored server-side.")
if __name__ == "__main__":
main()