watch-party/README.md
Nik Afiq 9674fc9cbe Squashed commit of the following:
commit da3f3bff39f09e753c1c52de0c47a12383cd20dc
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:49:31 2025 +0900

    docs: add README files for frontend and backend with setup instructions and commands

commit df71faa0751d1cef3ad6d50d0293fb8f8239d000
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:44:13 2025 +0900

    chore: update Vite version and add Vitest configuration

    - Updated Vite dependency from rolldown-vite@7.1.12 to ^5.4.11 in package.json.
    - Added setup file for Vitest to handle Vite SSR helpers, preventing ReferenceError during unit tests.
    - Created a new tsconfig.vitest.json file extending the main tsconfig for Vitest compatibility.
    - Added vitest.config.ts to configure Vitest with Node environment and setup files.

commit f3a1710dd0fe12998965992f6b5f8803422087cf
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:37:32 2025 +0900

    feat: add testing framework and implement unit tests for API and config

    - Added Vitest as a testing framework with scripts for running tests.
    - Created unit tests for the `fetchSchedule` and `fetchServerNow` functions in `watchparty.test.ts`.
    - Implemented unit tests for configuration functions in `config.test.ts`.
    - Added utility functions for parsing time in `time.ts` with corresponding tests in `time.test.ts`.
    - Updated API error handling to use `unknown` type for better type safety.
    - Refactored `TimeSyncNotice` and `Timer` components to improve performance and error handling.
    - Enhanced toast notifications by moving related functions to `toastBus.ts`.
    - Improved type definitions across various files for better type safety and clarity.

commit 0d436849fc4a0b87d0c73f4fe14fe1e272d47ad9
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:30:01 2025 +0900

    Refactor components to utilize centralized configuration: update TimeSyncNotice and Timer to use config for intervals; enhance error handling and retry logic in ShowsPage.

commit 8dbd4d207a471d05fab8c6f9cd95e3f4f7ec9099
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:22:28 2025 +0900

    Refactor API endpoint handling: replace join function with buildApiUrl for cleaner URL construction; update BrowserRouter basename to use config

commit 3e327aa73877034019dffe262580536f4be7c62e
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:18:31 2025 +0900

    Refactor configuration management: introduce config.ts for centralized app configuration; update API endpoint handling and logger to use new config

commit 131984d1baf368f94a14a62986eaf028ebbd7c86
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:11:33 2025 +0900

    Refactor API types: move ScheduleResponse and ShowItem types to a new types.ts file; update imports in watchparty and Timer components

commit 8faa4661a9ccc0691490a5766f0eb1d97f24b6e5
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 02:05:42 2025 +0900

    Refactor API handling: introduce centralized error handling and logging; replace direct fetch calls with apiFetch in Timer, ShowsPage, and hooks

commit ffde7e89fcab6f48c6023afab73e4b2e1122efa5
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 01:52:36 2025 +0900

    Add dist directory to .gitignore

commit 128a5be6eaa16bf4db5f7dd832b1d461fa2b835d
Author: Nik Afiq <nik.afiq98@ymail.com>
Date:   Sat Dec 6 01:52:28 2025 +0900

    Add toast notifications and debug overlay components; refactor Timer and ShowsPage for error handling
2025-12-06 03:03:30 +09:00

186 lines
5.2 KiB
Markdown
Raw Permalink 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 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 `.env` file at repo root (see template below).
## Deploy (compose)
```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 via web proxy: `curl http://<mac-mini-LAN-IP>:3000/api/healthz`
- API direct (inside network): `curl http://api:8082/healthz`
Logs:
```bash
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/*` 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`.
---
## 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)
See per-service READMEs:
- Frontend: `frontend/README.md`
- Backend: `backend/README.md`
---
## 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