Photo Q&A platform — someone asks a question with a photo, others answer with their own.
peeka.pics is a community where members open questions and answer with photos of their own. Live at https://peeka.pics.
- Backend — Django 4.2 + Django Ninja API, Python 3.11+, managed with uv
- Frontend — Next.js 16, React 19, Tailwind CSS v4
- E2E — Playwright with Gherkin/BDD
- Storage — MinIO (S3-compatible) for media
- No Celery, no Redis — background work runs synchronously
backend/ Django project + Django Ninja API
frontend/ Next.js app
e2e/ Playwright BDD tests
justfile task runner
Requires Python 3.11+, uv, Node.js, just, and Docker (for the E2E suite).
# backend dependencies
cd backend && uv sync && cd ..
# frontend dependencies
cd frontend && npm install && cd ..
# database
just migrate
just seed # optional: sample data
# run backend + frontend together
just devThe frontend dev server runs on port 3005; the backend on Django's default port 8000.
Run just to list every recipe.
| Command | Description |
|---|---|
just dev |
Start backend + frontend |
just backend / just frontend |
Start one side only |
just migrate |
Run database migrations |
just seed |
Seed the database with sample data |
just shell |
Open the Django shell |
just test |
Backend unit tests |
just test-cov |
Backend tests with coverage |
just e2e |
End-to-end tests (via Docker) |
just lint / just lint-fix |
Lint the backend (ruff) |
just lint-frontend |
Lint the frontend |
- Unit —
just testruns pytest, scoped toresmin/api/. - End-to-end —
just e2eruns the Playwright BDD suite via Docker.
When you stand up MinIO with a new bucket, its default public policy
includes s3:ListBucket, which lets any anonymous visitor enumerate
every object via GET /<bucket>/. That exposes the storage paths of
every avatar and photo response on the site (including ones meant to
live behind a private group). The app only needs s3:GetObject so
individual files load via <img src>; listing is never required.
Lock the bucket down to download-only access after creating it:
mc alias set local https://<minio-host> <access-key> <secret-key> --api S3v4
mc anonymous set download local/<bucket-name>Verify:
curl -I https://<minio-host>/<bucket-name>/ # expect 403
curl -I https://<minio-host>/<bucket-name>/<known-key> # expect 200MIT — see LICENSE.