watch-party/README.md
2025-11-04 20:53:28 +09:00

197 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Watch Party (Frontend + Backend)
Watch Party is a small full-stack app:
- **Frontend:** Vite + React + TypeScript, served by **Nginx**
- **Backend:** Go (Gin) API + PostgreSQL
- **Orchestration:** One **docker-compose** that keeps FE/BE split on the same internal network
- **Routing:** Your Debian Nginx is the public entrypoint and reverse-proxies to the Mac mini (where this stack runs)
```
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/*` to `api:8082` (Docker DNS)
- **api**: Go server (Gin), listens on `:8082`, health at `/healthz`
- **migrate**: one-off job that runs the `migrate` binary, applies SQL from `backend/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)
```dotenv
##################
## 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`, and `PGSSLMODE`.
---
## Run (Production-like)
```bash
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 through web proxy: `curl http://<mac-mini-LAN-IP>:3000/api/healthz`
- API direct (inside network only): `curl http://api:8082/healthz` (from another container)
Logs:
```bash
docker compose logs -f migrate # should start, apply SQL, and exit 0
docker compose logs -f api
docker compose logs -f web
docker compose logs -f db
```
---
## Debian Nginx (public reverse proxy)
Terminate TLS on Debian and forward to the Mac mini:
```nginx
# 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)
You can dev each side outside Docker:
- **Frontend**
```bash
cd frontend
npm ci
npm run dev # http://localhost:5173
```
Configure your FE to call the API (e.g., via a local `.env` or vite config).
- **Backend**
```bash
cd backend
go run ./cmd/server
```
Make sure Postgres is reachable (either from the compose `db` or your local).
For a dev-only compose with Vite HMR, create an override (not included here).
---
## Notes & Conventions
- **Only `web` is exposed** to the LAN. `api` and `db` are `expose`d internally.
- The frontend **does not** use `host.docker.internal`; it proxies to `api:8082` by Docker DNS.
- Backend healthcheck probes `http://localhost:8082/healthz` and the server binds `ADDR=: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
```bash
# 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 uses `CMD` (not `ENTRYPOINT`) and compose sets:
```yaml
migrate:
command: ["/app/migrate"]
api:
command: ["/app/server"]
```
- **404 for deep links** under `/watch-party/`
Confirm SPA fallback is active in `frontend/ops/nginx.conf.template` and Vite `base` matches `PUBLIC_BASE_PATH`.
- **DB not reachable**
Check `.env` values match the mixed DSN env names. `PGHOST=db`, `POSTGRES_PORT=5432`, etc.
- **CORS issues**
There shouldnt be any—browser hits `web`, which proxies to `api`, so its same-origin.
---
## License
MIT