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:
@@ -0,0 +1,40 @@
|
||||
"""API-key authentication for device uploads.
|
||||
|
||||
Devices authenticate with two headers:
|
||||
X-Device-Name: <device name row in the devices table, e.g. "yaris-2013">
|
||||
X-Api-Key: <raw API key, only ever compared as a sha256 hash>
|
||||
|
||||
The raw key is never stored - only its sha256 hex digest is kept in the
|
||||
devices.api_key_hash column, generated once via scripts/create_device.py.
|
||||
"""
|
||||
import hashlib
|
||||
from functools import wraps
|
||||
|
||||
from flask import request, jsonify, g
|
||||
|
||||
from modules import db
|
||||
|
||||
|
||||
def _hash_key(raw_key):
|
||||
return hashlib.sha256(raw_key.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def require_device_auth(view):
|
||||
@wraps(view)
|
||||
def wrapped(*args, **kwargs):
|
||||
device_name = request.headers.get("X-Device-Name", "")
|
||||
api_key = request.headers.get("X-Api-Key", "")
|
||||
|
||||
if not device_name or not api_key:
|
||||
return jsonify(error="missing X-Device-Name or X-Api-Key header"), 401
|
||||
|
||||
device = db.get_device_by_name(device_name)
|
||||
if device is None or device["api_key_hash"] != _hash_key(api_key):
|
||||
return jsonify(error="invalid device name or api key"), 401
|
||||
|
||||
g.device_id = device["id"]
|
||||
g.device_name = device["name"]
|
||||
db.touch_device_last_seen(device["id"])
|
||||
return view(*args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
Reference in New Issue
Block a user