Private, cost-efficient backend for storing, organizing, and sharing personal photos and videos. Built as a progressive learning project that evolves from a minimal FastAPI service to a production-style system with persistence, background processing, observability, and deployment.
- Ship a clean, typed Python backend you can deploy cheaply.
- Learn modern backend practices by building real features.
- Support your personal portfolio site with a real app behind it.
Language: Python 3.12 Framework: FastAPI DB: PostgreSQL (SQLAlchemy 2.0 async, Alembic) Cache & Jobs: Redis, Celery Events (later): Kafka Object Storage: MinIO locally, S3/R2 in prod Testing & Quality: pytest, mypy, Ruff, Black, isort, pre-commit Packaging & Dev: Poetry, Docker Compose Observability: OpenTelemetry, Prometheus, Grafana CI: GitHub Actions
Outcomes
- Minimal FastAPI service with typed endpoints and Pydantic models.
- pytest suite with strict typing and pre-commit hooks (Black, Ruff, isort, mypy, pytest).
- CI on GitHub Actions mirroring local checks.
Focus
- Async SQLAlchemy models and Alembic migrations.
- Core entities and endpoints:
Album: id, title, descriptionMediaAsset: id, album_id, media_type (photo|video), original_filename, mime_type, size_bytes, storage_key, width, height, duration_sec- Endpoints: create/list/get/update albums; create/list assets (metadata only).
- Integration tests against Postgres via Docker.
Focus
- S3-style storage (MinIO locally; S3/R2 in prod).
- Presigned upload URLs for direct browser → storage uploads.
- Celery worker pipeline:
- Ingest uploaded files, probe metadata (e.g., image size, video duration).
- Generate thumbnails (photos) and transcodes (videos).
- Idempotent tasks, retries, and progress flags.
Focus
- Docker multi-stage images for API, worker, and migrations (see
infrastructure/Dockerfile). - Local orchestration with Docker Compose:
make db-up,make worker-up,make runfor hot-reload API;make migrateto apply Alembic revisions. - VPS deploys land in
/srv/throwback-{APP_ENV}per environment to keep staging and production isolated. - Delivery targets: compose stack for cheap VPS, or push images to Fly.io/Railway.
- Managed services: Neon/Supabase for Postgres, S3/R2 for object storage.
- Edge: Cloudflare DNS/TLS, Pages for the portfolio frontend.
Focus
- Solidify auth/signup/login and JWT handling.
- Enforce rate limits and per-user quotas with consistent error responses.
- Clean API surface: consistent schemas, validation, and background task handoffs.
Focus
- OpenTelemetry traces across API → DB → Celery.
- Prometheus metrics and Grafana dashboards.
- Request/task log correlation and golden signals (RPS, latency, errors, queue length).
Focus
- TBA
Goal: Allow authenticated guests to upload into designated albums (e.g., “Guests’ Photos”) with proper access control.
Approach
- Managed auth (Clerk/Auth0/Cognito) to avoid password handling.
- Roles: owner (you), contributor (guests who can upload to specific albums), viewer (read-only).
- Entities:
User,AlbumMember(album-level role binding),ShareLink(time-bounded public view). - Flow: guest logs in → requests presigned upload for an assigned album → uploads → ingest task attaches asset to that album.
This can be introduced incrementally after Phase 2 or alongside Phase 3 without large refactors.
Current auth groundwork (in progress)
- All API routes now live under
/api/...; album/asset routes requireAuthorization: Bearer <token>. - Endpoints:
POST /api/auth/signup(creates user + returns access token) andPOST /api/auth/login(returns new token). - Tokens are JWTs (HS256) with stored JTIs/expirations for revocation checks in the DB.
- Public exceptions:
/api/health,/docs,/openapi.json. - Next: move to managed auth, add refresh tokens, roles/ACLs, rate limiting, and guest upload permissions.
Postman collection
- Collection:
postman/throwbackmemo.postman_collection.json - Environments:
postman/environments/*.postman_environment.json(local/staging/prod templates). Setapi_base_url,auth_email,auth_password, and optionallyaccess_token(Auth → Login/Signup will store it automatically). - Requests cover health, auth, albums, and assets (upload uses
/api/assets/upload; select a file manually and setalbum_id). - Legacy curl examples live in
tests/API_LOCAL_TEST_GUIDANCE.md.
- Pre-commit: automatic formatting, linting, typing, and quick tests on each commit.
- VS Code: Ruff + Black on save; optional mypy extension for inline type feedback.
- Makefile (optional):
make run,make test,make lint,make type,make migrate. - Docs:
docs/code-quality-refactor.md(cleanup guardrails),docs/observability-roadmap.md(Phase 6 plan),docs/multi-tenancy-media-roadmap.md(tenant/media plans),docs/deployment-roadmap.md(VPS vs k8s/Helm path),docs/ci-optimization.md(skip CI/deploy on docs-only changes).
- Local development uses a
.envin the repo root (ignored by git); runningdocker compose -f infrastructure/docker-compose.yaml ...from the repo root will pick it up automatically, or pass--env-file .envexplicitly if you prefer. - Each deploy target lives in its own folder (e.g.,
/srv/throwback-production,/srv/throwback-staging), each carrying a plain.envso filenames stay simple now that environments are separated. - CI deploy writes that per-folder
.envbefore runningdocker compose --env-file /srv/throwback-<env>/.env ...on the VPS; reuse the same pattern for manual runs. REDIS_PASSWORDis required for staging/prod; the dev.envships with a default for local use only. The dev compose binds Redis to127.0.0.1and requires that password; avoid publishing Redis to the internet.- Auth config: set
AUTH_SECRET_KEY(required for staging/prod) and optionallyACCESS_TOKEN_TTL_MINUTES(default 60). - Rate limiting and quotas: tune
REQUEST_RATE_LIMIT_PER_MINUTE(default 60) andDAILY_UPLOAD_LIMIT_BYTES(default 1 GB) as needed per environment.
- Request throttling via
fastapi-limiter: per-user (or IP if unauthenticated) caps usingREQUEST_RATE_LIMIT_PER_MINUTE. - Per-user daily upload quota using Redis counters keyed by date and user (
DAILY_UPLOAD_LIMIT_BYTES). - Short-lived caching for album/asset list responses.
- Celery broker and result backend.
Build small, observable components with clear contracts. Keep feedback loops tight: test, type-check, and lint continuously.
- Phase 1 ✅ complete (FastAPI + tests/tooling)
- Phase 2 ✅ complete (Postgres + Alembic migrations + schema)
- Phase 3 ✅ complete (storage + ingestion tasks)
- Phase 4 ✅ complete (delivery hardening and prod paths)
- Phase 5 ✅ complete (basic user, login auth, api cleanup)
- Phase 6 🚧 in progress (observability/logging dashboards/security)
- Phase 7 ⏳ not started (frontend)