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
+316
View File
@@ -0,0 +1,316 @@
# Development and production environments
Gallery uses **one PostgreSQL server** on TrueNAS (`192.168.10.122`) with **two databases**. The **dev PC** (`192.168.10.70`) runs `npm run dev:web` on port **5173**, published at **`https://devgallery.mysuperlab.netcraze.pro`** via Keenetic.
| Environment | Database | Public URL | App host |
|-------------|----------|------------|----------|
| **Development** | `gallery_dev` | `https://devgallery.mysuperlab.netcraze.pro` | Dev PC `192.168.10.70:5173` |
| **Production** | `gallery_prod` | `https://gallery.mysuperlab.netcraze.pro` | TrueNAS container `192.168.10.122:5173` |
---
## Where to run what (quick reference)
| Step | Machine | Interface | Privilege | What |
|------|---------|-----------|-----------|------|
| Stop dev servers | Dev PC `192.168.10.70` | PowerShell (normal) | your user | Close `dev:web` / `dev:server` / `dev:client` terminals |
| **DB split (one-time)** | Dev PC | **pgAdmin** → Query Tool on `postgres` | **postgres superuser** | [`db/split-dev-prod-pgadmin.sql`](../db/split-dev-prod-pgadmin.sql) step by step |
| Dev migrate / dev:web | Dev PC | PowerShell (normal) | your user | `npm run migrate`, `npm run dev:web` |
| Image dir on TrueNAS | TrueNAS `192.168.10.122` | **Shell** (SSH or UI → System Settings → Shell) | root / sudo | `bash infra/docker/truenas-setup.sh` |
| Copy images to prod | Dev PC | PowerShell (normal) | SMB `\\192.168.10.122\Gallery` | `net use` then `npm run images:sync-to-prod` |
| Build + push Docker image | Dev PC | **PowerShell as Administrator** | admin (for LAN hosts entry) | `npm run docker:publish` |
| Install prod app | TrueNAS | **Web UI** → Apps → Custom App | admin | Paste `infra/docker/compose.truenas.yaml` |
| Verify prod | Dev PC or TrueNAS | PowerShell / browser | any | `curl.exe -sk https://gallery.mysuperlab.netcraze.pro/api/bounds` |
You do **not** need `psql` on TrueNAS. Database work is done from **pgAdmin on the dev PC** connected to `192.168.10.122:5432`.
---
## One-time setup (full walkthrough)
### Step A — Stop Gallery on the dev PC
**Where:** Dev PC — PowerShell or open terminal tabs (no admin needed)
Close anything using ports **5173**, **3451**, or **3520**:
- Stop `npm run dev:web`, `npm run dev:server`, `npm run dev:client`
- If prod container already runs on TrueNAS: **TrueNAS Web UI** → Apps → **gallery-web****Stop**
### Step B — Split the database (pgAdmin)
**Where:** Dev PC — **pgAdmin** (not TrueNAS shell)
1. Open **pgAdmin**.
2. Add/connect to server:
- **Host:** `192.168.10.122`
- **Port:** `5432`
- **Maintenance database:** `postgres`
- **Username:** `postgres` (superuser — **not** `gallery`)
- **Password:** your postgres password
3. Tree: **Servers** → your server → **Databases** → click **`postgres`**
4. **Tools****Query Tool** (or right-click `postgres` → Query Tool)
5. **File****Open**`Gallery\db\split-dev-prod-pgadmin.sql`
6. Run **each STEP separately** (highlight from `-- STEP N` through that section, press **F5** / Execute):
| Step | Action |
|------|--------|
| **STEP 0** | Pre-flight — should show database `Gallery` |
| **STEP 1** | Terminate connections |
| **STEP 2** | `ALTER DATABASE "Gallery" RENAME TO gallery_prod` (use lowercase variant only if STEP 0 showed `gallery`) |
| **STEP 3** | `CREATE DATABASE gallery_dev WITH TEMPLATE gallery_prod` (wait ~12 min) |
| **STEP 4** | `GRANT` to user `gallery` |
| **STEP 5** | Verify — should show `gallery_prod` and `gallery_dev` |
| **STEP 6** | **New Query Tool on `gallery_dev`** (not postgres!) — `SELECT current_database()` then `SELECT count(*) FROM paintings` |
If STEP 2 fails with “database already exists”, STEP 0 likely already shows `gallery_prod` — skip STEP 2 and continue from STEP 3 (skip STEP 3 too if `gallery_dev` exists).
**Alternative (dev PC with psql installed):**
```powershell
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery
psql -h 192.168.10.122 -U postgres -d postgres -f db/split-dev-prod.sql
```
**Alternative (Node, postgres password in env):**
```powershell
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery
$env:PGHOST="192.168.10.122"; $env:PGUSER="postgres"; $env:PGPASSWORD="YOUR_POSTGRES_PASSWORD"
npm run db:split-databases
```
### Step C — Configure dev PC and test
**Where:** Dev PC — PowerShell (normal), repo root
1. Edit `.env` (copy from `.env.example` if needed):
```env
DB_NAME=gallery_dev
PORT=3451
PUBLIC_URL=https://devgallery.mysuperlab.netcraze.pro
```
2. Run:
```powershell
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery
npm run migrate
npm run dev:web
```
3. Open **https://devgallery.mysuperlab.netcraze.pro** (or http://localhost:5173 on the dev PC)
### Step D — Prepare prod image folder on TrueNAS
**Where:** TrueNAS — **Shell** (SSH to `192.168.10.122`, or TrueNAS UI → **System Settings** → **Shell**)
```bash
# Copy script to TrueNAS first, or paste commands manually:
mkdir -p /mnt/BasePool/Applications/Gallery/data/images/portraits
mkdir -p /mnt/BasePool/Applications/Gallery/data/images/paintings/thumbs
chown -R 1001:1001 /mnt/BasePool/Applications/Gallery
chmod -R u+rwX,g+rwX /mnt/BasePool/Applications/Gallery
```
Or from a checkout on TrueNAS: `bash infra/docker/truenas-setup.sh`
### Step E — Copy images dev → prod volume
**Where:** Dev PC — PowerShell (normal), repo root
Requires SMB share **`Gallery`** → `/mnt/BasePool/Applications/Gallery` on TrueNAS.
**Connect to the share first** (once per Windows session), then sync:
```powershell
# Map share (use your TrueNAS SMB user/password)
net use \\192.168.10.122\Gallery /user:YOUR_TRUENAS_USER
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery
npm run images:sync-to-prod
```
UNC destination: `\\192.168.10.122\Gallery\data\images`
Type `yes` when prompted. First run copies ~1000+ files (several minutes).
If `net use` fails, open `\\192.168.10.122\Gallery` in File Explorer and sign in, then retry.
### Step F — Build and push Docker image
**Where:** Dev PC — **PowerShell as Administrator** (for fast LAN push to Gitea)
Prerequisites: **Docker Desktop running**, logged in to Gitea.
```powershell
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery
docker login gitea.mysuperlab.netcraze.pro
npm run docker:publish
```
Use normal PowerShell with `-SkipHosts` if you already added `192.168.10.122 gitea.mysuperlab.netcraze.pro` to `C:\Windows\System32\drivers\etc\hosts`.
### Step G — Install production on TrueNAS
**Where:** TrueNAS — **Web UI** (browser)
1. **Apps** → **Discover Apps** → **Custom App** → **Install via Docker Compose**
2. Paste contents of `infra/docker/compose.truenas.yaml` from the repo
3. Replace `YOUR_POSTGRES_PASSWORD` with the `gallery` user password
4. **Apps** → **Settings** → register Gitea registry (`gitea.mysuperlab.netcraze.pro`, token with `read:package`)
5. Deploy → wait for **gallery-web** to show **Running**
### Step H — Verify production
**Where:** Dev PC — PowerShell (normal) or any browser
```powershell
curl.exe -sk https://gallery.mysuperlab.netcraze.pro/api/bounds
curl.exe -s http://192.168.10.122:5173/api/bounds
```
Open **https://gallery.mysuperlab.netcraze.pro/** — timeline and sample painting images should load.
---
## Environment files
| File | Git | Purpose |
|------|-----|---------|
| [`.env`](../.env) | ignored | **Dev** — `DB_NAME=gallery_dev`, `PORT=3451` |
| [`.env.example`](../.env.example) | tracked | Dev template |
| [`infra/docker/.env.prod`](../infra/docker/.env.prod) | ignored | **Prod** — migrate/restore prod scripts |
| [`infra/docker/.env.prod.example`](../infra/docker/.env.prod.example) | tracked | Prod template |
| [`infra/docker/compose.truenas.yaml`](../infra/docker/compose.truenas.yaml) | tracked | TrueNAS Custom App |
Never point dev `.env` at `gallery_prod`. Prod scripts refuse dev env files.
---
## Dev public access (Keenetic)
**Where:** Keenetic router Web UI (not dev PC)
| Field | Value |
|-------|-------|
| Domain | `devgallery.mysuperlab.netcraze.pro` |
| Upstream IP | `192.168.10.70` |
| Upstream port | **`5173`** |
| **Protocol to device** | **`http`** (not https) |
| Preserve Host | ON |
Vite on the dev PC speaks **plain HTTP** only. Keenetic terminates HTTPS from the browser, then must forward **HTTP** to `192.168.10.70:5173`.
If **Protocol to device** is `https`, Keenetic tries TLS against Vite → **502 Bad Gateway** (`Server: Web server`).
**Verify from dev PC** (servers must be running: `npm run dev:web`):
```powershell
# PowerShell: curl is an alias — use curl.exe for -k, or Invoke-WebRequest
curl.exe -s -o NUL -w "HTTP %{http_code}`n" http://192.168.10.70:5173/
curl.exe -sk -o NUL -w "HTTP %{http_code}`n" https://devgallery.mysuperlab.netcraze.pro/
# Or native PowerShell (skip cert check):
Invoke-WebRequest -Uri https://devgallery.mysuperlab.netcraze.pro/ -SkipCertificateCheck | Select-Object StatusCode
```
Expect **HTTP 200** (not 502).
**`.env`:** use the same scheme as the browser URL. If Keenetic serves HTTPS publicly:
```env
PUBLIC_URL=https://devgallery.mysuperlab.netcraze.pro
TRUST_PROXY=true
```
Restart `npm run dev:web` after changing `PUBLIC_URL`.
---
## Production public access (Keenetic)
**Where:** Keenetic router Web UI
| Field | Value |
|-------|-------|
| Domain | `gallery.mysuperlab.netcraze.pro` |
| Upstream IP | **`192.168.10.122`** (TrueNAS — not the dev PC) |
| Upstream port | **`5173`** |
| **Protocol to device** | **`http`** (container speaks HTTP, not HTTPS) |
| Preserve Host | ON |
The prod container listens on plain HTTP on port **5173**. Keenetic terminates HTTPS from the browser and must forward **HTTP** to TrueNAS.
**Verify the app first (bypass Keenetic):**
```powershell
curl.exe -s http://192.168.10.122:5173/api/bounds
```
Expect JSON immediately (~200). If this works but the public URL returns **502/504**, the Keenetic rule is wrong (IP, port, or `https` to device).
**Then verify public URL:**
```powershell
curl.exe -sk https://gallery.mysuperlab.netcraze.pro/api/bounds
```
See also [Drunkmeyou gitea-https-keenetic-npm-setup.md](../../Drunkmeyou/Documentation/gitea-https-keenetic-npm-setup.md) — same Keenetic HTTPS → HTTP upstream pattern.
---
## Daily development
**Where:** Dev PC — PowerShell (normal), repo root
| Task | Command |
|------|---------|
| Public dev URL (Keenetic) | `npm run dev:web` |
| Fast local HMR (no Keenetic) | `npm run dev:server` + `npm run dev:client` |
| Refresh dev DB from prod | `npm run db:sync-from-prod` |
| Pull prod images to dev | `npm run images:sync-from-prod` |
---
## Promote dev → prod
**Where:** Dev PC unless noted
1. `npm run db:backup`
2. Test on https://devgallery.mysuperlab.netcraze.pro
3. `npm run db:restore:prod -- --file db/DataBackup/gallery_dev_data_....txt` (type `yes`)
4. `npm run images:sync-to-prod`
5. **TrueNAS Web UI** → restart **gallery-web** (or `npm run docker:publish` if code changed)
---
## Database scripts
| Command | Where | Purpose |
|---------|-------|---------|
| pgAdmin + `split-dev-prod-pgadmin.sql` | Dev PC pgAdmin | One-time split (recommended) |
| `npm run db:split-databases` | Dev PC PowerShell | Same split (needs `PGUSER=postgres`) |
| `npm run db:sync-from-prod` | Dev PC PowerShell | Clone prod → dev |
| `npm run db:backup` | Dev PC PowerShell | Dev backup |
| `npm run db:restore:prod` | Dev PC PowerShell | Restore into prod |
## Image sync
| Command | Where | Direction |
|---------|-------|-----------|
| `npm run images:sync-to-prod` | Dev PC PowerShell | Dev → TrueNAS volume |
| `npm run images:sync-from-prod` | Dev PC PowerShell | TrueNAS → dev repo |
## Safety guards
- Prod restore reads only `infra/docker/.env.prod`
- Dev backup refuses `_prod` database names without `--prod`
- Destructive prod ops require typing `yes` or `CONFIRM_PROD=1`
## Legacy deployment
Node on dev PC `:3520` + nginx → Vite `:5173` is optional; see [`deploy/nginx-gallery.conf`](../deploy/nginx-gallery.conf). Production should use TrueNAS Docker on `:5173`.
Deploy details: [`infra/docker/DEPLOY-truenas.md`](../infra/docker/DEPLOY-truenas.md).