Add dev/prod environments with TrueNAS Docker production deploy.

Split PostgreSQL into gallery_dev and gallery_prod, add Docker/Gitea deploy tooling,
SMB image sync, pgAdmin split script, dev:web on Keenetic :5173, and operator docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-07-04 15:15:19 +03:00
co-authored by Cursor
parent 02d238b043
commit 2edf577faf
34 changed files with 1742 additions and 80 deletions
+30 -43
View File
@@ -20,10 +20,10 @@ cp .env.example .env
| `DB_PORT` | Port (default `5432`) |
| `DB_USER` | Database user |
| `DB_PASSWORD` | Database password |
| `DB_NAME` | Database name (`Gallery`) |
| `PORT` | API listen port (default `3001`; production uses `3520`) |
| `DB_NAME` | Database name (`gallery_dev` for dev; prod uses `gallery_prod`) |
| `PORT` | API listen port (`3451` for `dev:web`; prod container uses `5173`) |
| `HOST` | Bind address (default `0.0.0.0` — required for LAN access) |
| `PUBLIC_URL` | Optional public URL shown at startup (e.g. `http://gallery.mysuperlab.netcraze.pro`) |
| `PUBLIC_URL` | Public URL (dev: `https://devgallery.mysuperlab.netcraze.pro`; prod: `https://gallery.mysuperlab.netcraze.pro`) |
| `TRUST_PROXY` | Set to `true` when behind nginx/reverse proxy (honours `X-Forwarded-*`) |
| `IMAGE_DIR` | Root for cached images (default `./data/images`) |
@@ -89,14 +89,22 @@ Image fetch can take hours if you run it for the entire catalog. The first line
| Command | Purpose |
|---------|---------|
| `npm run dev:web` | **Public dev stack** — Vite `:5173`, API `:3451` (Keenetic → devgallery…) |
| `npm run docker:publish` | Build + push prod image to Gitea |
| `npm run images:sync-to-prod` | Copy `data/images/` → TrueNAS via SMB `Gallery` share |
| `npm run images:sync-from-prod` | Copy prod images → dev repo |
| `npm run db:sync-from-prod` | Clone `gallery_prod``gallery_dev` |
| `npm run db:backup` / `db:restore:prod` | Dev backup / promote to prod |
| `npm run build` | Build production SPA into `client/dist` |
| `npm run start` | API + static SPA on `HOST`:`PORT` |
| `npm run start:prod` | Build client, then start server |
| `npm run server` | Alias for `start` |
| `npm run dev:server` | API with nodemon reload |
| `npm run dev:server` | API with nodemon reload (local `:3520` / `:5173` workflow) |
| `npm run dev:client` | Vite dev server on :5173 |
| `npm run dev` | Alias for `start` |
See [environments.md](environments.md) for dev/prod URLs, database split, Docker deploy, and sync commands.
**Production frontend:** build the client, then start the server:
```bash
@@ -105,53 +113,30 @@ npm run build
npm run start
```
Open http://localhost:3520 (or your configured `HOST`/`PORT`).
Open http://localhost:5173 (Vite) or http://localhost:3451 (API only).
## Production deployment
This install is intended to run at:
Production runs as **`gallery-web`** on TrueNAS at **https://gallery.mysuperlab.netcraze.pro** (Keenetic → `:5173`). Full guide: [environments.md](environments.md) and [infra/docker/DEPLOY-truenas.md](../infra/docker/DEPLOY-truenas.md).
| Access | URL |
|--------|-----|
| Public (reverse proxy) | http://gallery.mysuperlab.netcraze.pro |
| LAN direct | http://192.168.10.70:3520 |
| Production (Keenetic) | https://gallery.mysuperlab.netcraze.pro |
| Development (Keenetic) | https://devgallery.mysuperlab.netcraze.pro |
| LAN direct (prod container) | http://192.168.10.122:5173 |
| LAN direct (dev PC) | http://192.168.10.70:5173 |
### 1. Configure `.env`
Quick deploy checklist:
Copy `.env.example``.env` and set at least:
1. One-time DB split in **pgAdmin** on dev PC: [`db/split-dev-prod-pgadmin.sql`](../db/split-dev-prod-pgadmin.sql)
2. Dev `.env``DB_NAME=gallery_dev`, `PORT=3451`, `PUBLIC_URL=https://devgallery.mysuperlab.netcraze.pro`
3. `net use \\192.168.10.122\Gallery``npm run images:sync-to-prod`
4. `npm run docker:publish` → TrueNAS Custom App from `infra/docker/compose.truenas.yaml`
5. Keenetic: both domains → `:5173`, protocol to device **`http`**, correct IP per environment
```env
PORT=3520
HOST=0.0.0.0
PUBLIC_URL=http://gallery.mysuperlab.netcraze.pro
TRUST_PROXY=true
```
### Legacy deployment (optional)
`HOST=0.0.0.0` lets the app accept connections on the machines LAN IP (`192.168.10.70`). The client uses relative `/api` and `/images` paths, so no frontend URL changes are needed.
### 2. Build and start
```bash
npm install
cd client && npm install && cd ..
npm run start:prod
```
Or use the systemd unit in [`deploy/gallery.service`](../deploy/gallery.service) (adjust `User`, `WorkingDirectory`, and `EnvironmentFile`).
### 3. Reverse proxy (public domain)
Point `gallery.mysuperlab.netcraze.pro` at the host running the app. Example nginx config: [`deploy/nginx-gallery.conf`](../deploy/nginx-gallery.conf).
**Development (default in repo):** nginx forwards to **Vite on `127.0.0.1:5173`**. Run both `npm run dev:server` (API on `3520`) and `npm run dev:client` (`5173`). Vite proxies `/api` and `/images` to the API. If either process stops, the public hostname may return **503** (reverse proxy cannot reach upstream).
**Production (built SPA):** change nginx `proxy_pass` to `http://127.0.0.1:3520` after `npm run build` and `npm run start` — Node serves `client/dist` and the API on one port. Prefer the systemd unit in [`deploy/gallery.service`](../deploy/gallery.service) so the process restarts automatically.
Keep `TRUST_PROXY=true` in `.env` so Express sees the correct client IP and scheme.
### 4. Firewall
Allow inbound **TCP 3520** on the gallery host if clients reach it directly on the LAN (`192.168.10.70:3520`). The public hostname only needs **80/443** on the reverse-proxy host.
Node on the dev PC at `:3520` with nginx → Vite `:5173` is superseded by TrueNAS Docker prod and `npm run dev:web` for public dev. See [`deploy/nginx-gallery.conf`](../deploy/nginx-gallery.conf) and [`deploy/gallery.service`](../deploy/gallery.service) only if you need a local nginx/systemd setup.
## Maintenance scripts
@@ -226,14 +211,16 @@ Gitea: [Danilka/Art-gallery](https://gitea.mysuperlab.netcraze.pro/Danilka/Art-g
git clone https://gitea.mysuperlab.netcraze.pro/Danilka/Art-gallery.git
```
After clone: copy `.env.example``.env`, install dependencies, run `npm run setup` against your Postgres instance, then the post-seed steps above.
After clone: copy `.env.example``.env`, install dependencies, run [one-time DB split](Documentation/environments.md#step-b--split-the-database-pgadmin), then post-seed steps. Deploy: [environments.md](Documentation/environments.md).
## Troubleshooting
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| Empty timeline | DB not seeded | `npm run seed` |
| 503 on public URL | Vite or API not running behind nginx | Start `npm run dev:server` + `npm run dev:client`, or switch nginx to production `:3520` |
| 502 / 504 on public URL | Keenetic rule wrong (IP, port, or `https` to device) | Dev → `192.168.10.70:5173`; prod → `192.168.10.122:5173`; protocol **`http`** — see [environments.md](environments.md) |
| 503 on public URL | Dev servers not running | `npm run dev:web` (or `dev:server` + `dev:client` for local HMR) |
| **`manifest unknown`** on TrueNAS deploy | Image not in Gitea | `npm run docker:publish` on dev PC first |
| 500 on all `/api/*` | Wrong `.env` or Postgres down | Check connection, logs |
| “Biographical information not yet available” | Bios not fetched | `npm run fetch-artist-bios` |
| Placeholder portraits on timeline | `portrait_path` not set | `npm run fetch-artist-images` |