Add multi-user support: Authelia OIDC, SQLite, Garage S3

- Auth via Authelia OIDC (public client + PKCE) with signed session cookies
- SQLite DB for users, rides, and insights (SQLAlchemy)
- Garage S3 for GPX file storage (boto3)
- All API endpoints scoped to authenticated user
- Dockerfile + docker-compose.yml with Garage service
- Frontend auth check: redirects to /auth/login on 401, shows user badge

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 22:12:23 -04:00
parent 65a1bb010d
commit c6d9a3b8b6
12 changed files with 853 additions and 134 deletions
+36
View File
@@ -0,0 +1,36 @@
import os
import boto3
from botocore.config import Config
S3_ENDPOINT = os.getenv("S3_ENDPOINT")
S3_BUCKET = os.getenv("S3_BUCKET", "bikeslop-gpx")
S3_ACCESS_KEY = os.getenv("S3_ACCESS_KEY")
S3_SECRET_KEY = os.getenv("S3_SECRET_KEY")
def get_s3_client():
if not S3_ENDPOINT or not S3_ACCESS_KEY or not S3_SECRET_KEY:
raise RuntimeError(
"S3 not configured — set S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY"
)
return boto3.client(
"s3",
endpoint_url=S3_ENDPOINT,
aws_access_key_id=S3_ACCESS_KEY,
aws_secret_access_key=S3_SECRET_KEY,
config=Config(signature_version="s3v4"),
)
def upload_gpx(user_id: str, filename: str, data: bytes) -> str:
s3_key = f"users/{user_id}/{filename}"
client = get_s3_client()
client.put_object(Bucket=S3_BUCKET, Key=s3_key, Body=data, ContentType="application/gpx+xml")
return s3_key
def download_gpx(s3_key: str) -> bytes:
client = get_s3_client()
response = client.get_object(Bucket=S3_BUCKET, Key=s3_key)
return response["Body"].read()