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

5.2 KiB
Raw Permalink Blame History

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)

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/* 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)

##################
## 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:

# 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 exposed 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

# 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:
    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