Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 4s
Watch Party (Frontend + Backend)
Watch Party is a small full-stack app for synchronized viewing with a live timer and episode control.
Features
- Live timer with server-synced clock and schedule-driven start/end.
- Episode selector (set current episode + optional custom start time).
- Time-skew banner (warns when client clock is off).
- Debug-only logging overlay and error toasts for API failures (enable with
FRONTEND_MODE=debug/VITE_APP_MODE=debug).
Prerequisites
- Docker + Docker Compose plugin.
- Node.js 20+ (for local frontend dev/test).
- Go 1.22+ (for local backend dev/test).
- A
.envfile at repo root (see template below).
Deploy (compose)
docker compose build
docker compose up -d
Open: http://<mac-mini-LAN-IP>:3000/watch-party/
Health checks:
- Web (nginx):
curl http://<mac-mini-LAN-IP>:3000/ - API via web proxy:
curl http://<mac-mini-LAN-IP>:3000/api/healthz - API direct (inside network):
curl http://api:8082/healthz
Logs:
docker compose logs -f migrate
docker compose logs -f api
docker compose logs -f web
docker compose logs -f db
Architecture:
router → Debian nginx (TLS) → Mac mini :3000 → [web nginx ↔ api ↔ db]
Repository Layout
.
├── docker-compose.yml # single compose for web + api + db + migrate
├── .env # stack configuration (see template below)
├── backend/ # Go API + migrations
│ ├── cmd/
│ │ ├── migrate/ # one-off migration binary
│ │ └── server/ # API server (Gin)
│ ├── db/migration/ # SQL migrations (0001_init.sql, etc.)
│ ├── Dockerfile # builds server + migrate
│ └── go.mod / go.sum
└── frontend/ # Vite + React + TS
├── Dockerfile # Vite build → Nginx runtime (envsubst for proxy)
├── ops/ # container runtime bits
│ ├── entrypoint.sh
│ ├── nginx.conf # (optional/manual)
│ └── nginx.conf.template # uses BACKEND_ORIGIN
├── src/, public/, vite.config.ts, package*.json, tsconfig*.json
Services (Compose)
- web: Nginx serving the built SPA, and proxying
/api/*toapi:8082(Docker DNS) - api: Go server (Gin), listens on
:8082, health at/healthz - migrate: one-off job that runs the
migratebinary, applies SQL frombackend/db/migration - db: PostgreSQL 16 (internal only)
Only web is published to your LAN (port 3000 by default). api and db are internal to the compose network.
.env (template)
##################
## Frontend env ##
##################
WEB_PORT=3000
PUBLIC_BASE_PATH=/watch-party/
BACKEND_ORIGIN=http://api:8082
#################
## Backend env ##
#################
# Postgres (container)
POSTGRES_DB=watchparty
POSTGRES_USER=admin
POSTGRES_PASSWORD=change-me
TZ=Asia/Tokyo
COMPOSE_PLATFORM=linux/arm64/v8
# Backend server
ADDR=:8082
GIN_MODE=release
PGHOST=db
POSTGRES_PORT=5432
PGSSLMODE=disable
Why mixed names? The current Go code builds its DSN from
PGHOST,POSTGRES_PORT,POSTGRES_USER,POSTGRES_PASSWORD,POSTGRES_DB, andPGSSLMODE.
Debian Nginx (public reverse proxy)
Terminate TLS on Debian and forward to the Mac mini:
# Serve the SPA under /watch-party/
location /watch-party/ {
proxy_pass http://<mac-mini-LAN-IP>:3000/watch-party/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
# Proxy API via the web container (keeps same-origin from browser POV)
location /api/ {
proxy_pass http://<mac-mini-LAN-IP>:3000/api/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
Security headers & HSTS on Debian are recommended.
Local Development (hot reload)
See per-service READMEs:
- Frontend:
frontend/README.md - Backend:
backend/README.md
Notes & Conventions
- Only
webis exposed to the LAN.apianddbareexposed internally. - The frontend does not use
host.docker.internal; it proxies toapi:8082by Docker DNS. - Backend healthcheck probes
http://localhost:8082/healthzand the server bindsADDR=:8082. - Migrations live in
backend/db/migration/and are applied by the migrate container at startup. - Vite base path is controlled by
PUBLIC_BASE_PATH(e.g.,/watch-party/).
Quick Test Endpoints
# Latest health:
curl http://<mac-mini-LAN-IP>:3000/api/healthz
# Current show (if table seeded and current_ep=true):
curl http://<mac-mini-LAN-IP>:3000/api/current
Troubleshooting
- Migrate “hangs” / API starts instead
Ensure the backend image usesCMD(notENTRYPOINT) and compose sets:migrate: command: ["/app/migrate"] api: command: ["/app/server"] - 404 for deep links under
/watch-party/
Confirm SPA fallback is active infrontend/ops/nginx.conf.templateand VitebasematchesPUBLIC_BASE_PATH. - DB not reachable
Check.envvalues match the mixed DSN env names.PGHOST=db,POSTGRES_PORT=5432, etc. - CORS issues
There shouldn’t be any—browser hitsweb, which proxies toapi, so it’s same-origin.
License
MIT
Description
Languages
TypeScript
45.6%
Go
42.6%
CSS
10.1%
Dockerfile
1.1%
JavaScript
0.3%
Other
0.2%