Files
gold_land_user/docs/DEPLOY-TRIAL.md
T
safavimohadeseh78andCursor e3f0457dc9
Frontend CI / unit (test, type-check, build) (push) Successful in 1m11s
Frontend CI / browser E2E (frontend + pinned backend) (push) Failing after 2m48s
feat: complete server-backed six-lesson app and trial tooling
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 14:40:37 +03:30

5.5 KiB

Trial deploy — 6 active lessons (no public launch checklist)

This document prepares a trial stack using the existing Docker Compose layout. It does not deploy to a real server from this repo.

Architecture (default)

Browser → [TLS terminator — your choice] → frontend nginx :80
              ├─ /        → Vue SPA (try_files → index.html)
              ├─ /api/*   → proxy → backend Nest :3000
              └─ /health  → proxy → backend /health
Backend → SQLite file on named volume (single instance)
Component Image / build User
Frontend frontend/Dockerfile → nginx:alpine nginx (non-root worker)
Backend backend-nest/Dockerfile → node:22.18.0-slim node
Data Docker volume child-gold-land-sqlite → /app/data/app.db writable by node

Not supported without proof: SQLite on NFS/network shares, horizontal multi-instance backend with this rate limiter.

Placeholders (set on server — never commit)

Copy frontend/.env.docker.example → frontend/.env.docker:

Variable Example placeholder
JWT_SECRET <generate-48+-char-random>
CORS_ORIGIN https://<your-trial-host> or http://<host>:8080
VITE_API_URL /api (keep for same-origin nginx proxy)
FRONTEND_PORT 8080

Proxy trust boundary (compose default)

Variable Compose default Meaning
TRUST_PROXY true Use X-Real-IP / X-Forwarded-For only when TCP peer is in TRUST_PROXY_CIDRS
TRUST_PROXY_CIDRS Explicit list in compose (trial) Production: set the CIDR of the nginx→backend hop (e.g. docker network inspect child-gold-land); backend refuses TRUST_PROXY=true without TRUST_PROXY_CIDRS when NODE_ENV=production
  • Fastify trustProxy stays off — client IP for rate limits comes from getClientIp() (trusted peer + single-hop headers).
  • nginx sets X-Forwarded-For $remote_addr (replaces forged client chains).
  • Backend is not published on the host — only frontend port is exposed; direct access to Nest bypasses nginx and should not be exposed in trial deploy.

Rate limiting: in-memory, per client IP, single backend instance. Many users behind one NAT share one bucket. For stricter limits use an edge proxy or account-based throttling (out of scope).

Do not expose backend port 3000 on the host in production trial.

Build and start (local trial)

From sibling clones (frontend/, backend-nest/):

cd frontend
copy .env.docker.example .env.docker
# Edit .env.docker — set JWT_SECRET and CORS_ORIGIN

docker compose config    # validate compose file
docker compose build
docker compose up -d

Checks:

curl http://localhost:8080/health
curl http://localhost:8080/health/ready
curl http://localhost:8080/api/...   # via app UI login

Vue routes: refresh on /map, /market, etc. must return index.html (configured in nginx.conf).

HTTPS

  • Terminate TLS at your reverse proxy (Caddy, Traefik, nginx on host) or cloud LB.
  • Redirect HTTP→HTTPS at the terminator.
  • Set CORS_ORIGIN to the exact browser origin (https://host, no trailing slash).
  • Do not generate real certificates in this repo.

Persistent data

  • Path in container: /app/data/app.db (+ -wal/-shm when WAL mode active)
  • Volume: child-gold-land-sqlite (compose)
  • Host permissions: ensure volume UID/GID allows node (uid 1000 in image) to read/write; if bind-mounting a host dir: chown 1000:1000 <dir>

Startup never replaces an existing DB with seed data. Migrations run forward-only on boot; failure aborts startup.

Backup and restore

On backend repo (host or one-off container with volume mounted):

cd backend-nest
$env:DB_PATH = "/path/to/app.db"   # or container path
$env:BACKUP_DIR = "/path/to/backups"
$env:BACKUP_RETENTION_DAYS = "14"
npm run backup:sqlite

npm run restore:sqlite -- backups/app-....db /tmp/restored.db
# Stop backend first. Restore to a sibling path (not live DB_PATH), then swap files — see backend-nest/docs/restore-production-db.md.
# RESTORE_ACK_STOPPED=YES = operator ack only (not automatic stop detection).

Uses SQLite backup API (WAL-safe), then integrity_check.

Health vs readiness

Endpoint Meaning
GET /health Liveness — process up
GET /health/ready Readiness — DB migrated

Compose depends_on: service_healthy uses ready.

Rollback

Action Effect
docker compose pull + run previous image tags Rolls back code only
DB Not rolled back with image — restore from backup if needed
Forward migrations Newer code may require newer schema; rolling back code without DB restore can break — test rollback pairs in staging

Safe shutdown

docker compose down
# Data remains in volume child-gold-land-sqlite

CI backend pin (E2E)

No silent main fallback: set Gitea variable E2E_BACKEND_REF to backend commit SHA. See e2e/backend.contract.json and frontend README.

Secrets (names only): E2E_BACKEND_CHECKOUT_TOKEN.

Still required for real deploy

  • Registered Gitea act_runner (CI not verified here)
  • Trial host OS, domain, TLS certificates
  • Strong JWT_SECRET on server
  • Off-site backup storage
  • Monitoring / log aggregation (no stack traces to clients — API returns generic خطای سرور)