Introduces the Go photo-api service, nginx/systemd deploy wiring, and Next.js gallery/lightbox pages so event photos can be managed and browsed. Co-authored-by: Cursor <cursoragent@cursor.com>
4.2 KiB
photo-api
Standalone Go service for event photo galleries: admins upload photos from past events, group them into galleries, and share them with the community. Lives in this monorepo but is its own module, binary, and deployable unit. Design/decisions: PLAN.md.
What it does
- Galleries with four visibility modes:
public(listed on /photos),private(admins only),link(share-token URL),ticket(logged-in users with a confirmed ticket for the linked event — any confirmed ticket when the event is free). - Originals are stored byte-identical for download; a worker generates JPEG variants (thumb 512px q78, preview 2048px q85, EXIF stripped/orientation applied) queued in the DB with retries.
- Storage: local disk (
STORAGE_PATH) or S3/Garage/MinIO (setS3_ENDPOINTS3_BUCKET), same selection convention as the backend. S3 downloads use short-lived presigned URLs; nothing in the bucket is public.
- Auth: validates the backend's HS256 JWTs with the shared
JWT_SECRET(issuerspanglish, audiencespanglish-app) including the DB-backed tokenVersion/account-status revocation check. Admin surface isadmin/organizeronly. - Database: the same Postgres or SQLite database as the backend (
DB_TYPE,DATABASE_URL). This service owns only thephotos_*tables via its own embedded migrations (photo-api migrate); drizzle-kit never sees them. Reads of backend tables are confined tointernal/store/access.goand sanity-checked at startup.
Develop
cp .env.example .env # set JWT_SECRET + DATABASE_URL to match backend/.env
go run ./cmd/photo-api migrate
go run ./cmd/photo-api # serves on :3003 (dev)
go test ./... # SQLite; add PHOTO_TEST_PG=<url> to also run on Postgres
From the repo root: npm run dev:photos, npm run build:photos,
npm run test:photos. The Next dev server rewrites /api/photos/* to
PHOTO_API_URL (default http://localhost:3003), so the frontend needs no
extra config in dev.
HEIC uploads require a converter CLI on the host: apt install libvips-tools
(or libheif-examples). Without one, HEIC uploads are rejected with a clear
message and a startup warning is logged.
API
Everything under /api/photos. Errors are {"error": string}.
Admin (Bearer token, role admin/organizer):
| Method | Path | Purpose |
|---|---|---|
| POST | /api/photos/galleries |
create gallery |
| GET | /api/photos/galleries |
list all (?eventId=) |
| GET | /api/photos/galleries/:id |
gallery + photos (all statuses) |
| PATCH | /api/photos/galleries/:id |
update title/visibility/event/cover |
| DELETE | /api/photos/galleries/:id |
delete gallery + objects |
| POST | /api/photos/galleries/:id/photos |
multipart upload (files) |
| PATCH | /api/photos/galleries/:id/order |
reorder ({photoIds}) |
| POST | /api/photos/galleries/:id/share-token |
rotate share token |
| DELETE | /api/photos/photos/:photoId |
delete photo |
| POST | /api/photos/photos/:photoId/retry |
requeue failed photo |
Viewer (anonymous or member token; ?token= carries the share token):
| Method | Path | Purpose |
|---|---|---|
| GET | /api/photos/public/galleries |
public index |
| GET | /api/photos/public/galleries/:slug |
gallery view (access-checked) |
| GET | /api/photos/public/events/:eventSlug/gallery |
newest gallery of an event (access-checked) |
| GET | /api/photos/files/:photoId/:variant |
bytes; thumb|preview|original |
| GET | /api/photos/health |
liveness |
Deploy (systemd, primary)
cd photo-api && go build -o bin/photo-api ./cmd/photo-api
sudo apt install libvips-tools
sudo cp ../deploy/spanglish-photos.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now spanglish-photos
sudo nginx -t && sudo systemctl reload nginx # after installing the updated confs
nginx routes location ^~ /api/photos/ → 127.0.0.1:3020 (see
deploy/front-end_nginx.conf, deploy/back-end_nginx.conf,
deploy/spanglish_upstreams.conf). The frontend's production .env should
set PHOTO_API_URL=http://127.0.0.1:3020 for server-side rendering of the
public gallery pages. A Dockerfile is included as a secondary path for the
compose-scale setup.