# Deploy dev → prod (release runbook) Step-by-step guide for promoting the **development** version of Gallery to **production**. Covers code, database schema, database data, and image files. > **Default is dev.** This runbook is for a **scheduled release** (~weekly, or when you explicitly decide to ship). Day-to-day work stays on dev — see [environments.md](environments.md#development-first-workflow-default). If the one-time prod install is not done yet, follow [environments.md → One-time setup](environments.md#one-time-setup-full-walkthrough) and [infra/docker/DEPLOY-truenas.md](../infra/docker/DEPLOY-truenas.md) first. | | Dev (source) | Prod (target) | |---|---|---| | Database | `gallery_dev` | `gallery_prod` | | Images | repo `data/images/` | `/mnt/BasePool/Applications/Gallery/data/images` (SMB share **`Gallery`**) | | App | `npm run dev:web` on dev PC `:5173` | `gallery-web` container on TrueNAS `:5173` | | URL | https://devgallery.mysuperlab.netcraze.pro | https://gallery.mysuperlab.netcraze.pro | | Env file | root [`.env`](../.env) (`gallery_dev`) | [`infra/docker/.env.prod`](../infra/docker/.env.prod) (`gallery_prod`) | All commands run on the **dev PC** from the repo root (`C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery`) unless a step says **TrueNAS**. --- ## What changed → which steps to run Run only the steps that match what you changed. Steps are independent except that **code** needs a rebuilt image and **schema changes** must be applied before a **data** restore. | You changed… | Required steps | |--------------|----------------| | Application code (client/server) | 0 → 1 → 6 → 7 → 8 | | DB schema (new `db/migrate-*.sql`, `schema.sql`) | 0 → 1 → 2 → **3** → (4 if data too) → 7 → 8 | | Catalog data (movements, artists, paintings, influences, bios) | 0 → 2 → 4 → 8 | | Image files (new/replaced paintings, portraits, thumbs) | 0 → 5 → 8 | | Everything (typical weekly release) | 0 → 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 | --- ## Prerequisites (per release) - Prod already installed once on TrueNAS (`gallery-web` app exists). - **Docker Desktop** running on the dev PC; logged in: `docker login gitea.mysuperlab.netcraze.pro`. - Prod env file present: [`infra/docker/.env.prod`](../infra/docker/.env.prod) with `DB_NAME=gallery_prod` (copy from [`.env.prod.example`](../infra/docker/.env.prod.example) if missing). - SMB share mapped for image sync (per Windows session): ```powershell net use \\192.168.10.122\Gallery /user:YOUR_TRUENAS_USER ``` --- ## Step 0 — Validate on dev **Where:** Dev PC + browser 1. Make sure the change works on https://devgallery.mysuperlab.netcraze.pro (or `http://localhost:5173`). 2. Build the client to catch type/compile errors before shipping: ```powershell npm run prod:build ``` 3. Sanity-check the dev API: ```powershell curl.exe -sk https://devgallery.mysuperlab.netcraze.pro/api/bounds ``` Do not proceed until dev is correct — prod is a copy of dev. --- ## Step 1 — Commit and push code **Where:** Dev PC Commit the release so prod is reproducible and the Docker build ships the intended code. ```powershell git status git add . git commit -m "Release: " git push origin main ``` --- ## Step 2 — Back up the dev database **Where:** Dev PC Creates a data-only dump (INSERT statements) under `db/DataBackup/`. ```powershell npm run dev:db:backup ``` Output (note the exact path — you pass it to Step 4): ``` Backup written: db/DataBackup/gallery_dev_data_YYYYMMDD_HHMMSS.txt Archive written: db/DataBackup/gallery_dev_data_YYYYMMDD_HHMMSS.zip ``` > Also back up prod before overwriting it (optional but recommended): `npm run prod:db:backup` → `db/DataBackup/gallery_prod_data_*.txt`. Keep it as a rollback point (see [Rollback](#rollback)). --- ## Step 3 — Apply schema changes to prod (only if schema changed) **Where:** Dev PC The data restore in Step 4 only inserts rows — it does **not** create tables or add columns. If this release added a migration (`db/migrate-*.sql`) or changed `db/schema.sql`, the prod schema must be updated **first** so the new columns/tables exist. `server/migrate.js` is idempotent (schema and migrations use `IF NOT EXISTS` / additive `ALTER`s). Point it at prod for one run by overriding `DB_NAME` (all other credentials come from `.env`; the `gallery` user has rights to both databases): ```powershell $env:DB_NAME = "gallery_prod" npm run dev:migrate Remove-Item Env:\DB_NAME # restore dev default so later commands hit gallery_dev ``` Expect `schema.sql` + each `migrate-*.sql` to log `OK`. Re-running is safe. > If you prefer not to override env, temporarily set `DB_NAME=gallery_prod` in a scratch shell only — **never** edit the committed root `.env` to point at prod. --- ## Step 4 — Restore dev data into prod (only if data/DB changed) **Where:** Dev PC Loads the Step 2 backup into `gallery_prod`. This **TRUNCATES all public tables** in prod first, then inserts dev's rows — prod becomes an exact copy of dev's data. ```powershell npm run devtoprod:db:restore -- --file db/DataBackup/gallery_dev_data_YYYYMMDD_HHMMSS.txt ``` Type `yes` at the confirmation prompt (or set `CONFIRM_PROD=1` to skip it in automation). **Caveats — prod tables are replaced by dev's contents:** - `users` and `curator_audit_log` are overwritten. The **dev curator account and password become the prod login**, and prod audit history is replaced. Make sure the dev curator credentials are the ones you want in prod. - The `session` table is truncated, so any active prod curator sessions are logged out. - The target is guarded: the restore refuses to run unless the database name ends with `_prod` and only reads `infra/docker/.env.prod`. --- ## Step 5 — Sync images to prod (only if image files changed) **Where:** Dev PC (SMB share mapped — see Prerequisites) Copies `data/images/` (paintings, portraits, and `thumbs/`) from the repo to the TrueNAS volume. ```powershell npm run devtoprod:images ``` Type `yes` when prompted. Robocopy exit codes **0–7** = success. Destination: `\\192.168.10.122\Gallery\data\images`. > Thumbnails are files, generated on dev (`npm run dev:regenerate-thumbnails` / `dev:regenerate-portrait-thumbs`) and shipped here — prod does **not** regenerate them. Regenerate on dev **before** this step if needed. --- ## Step 6 — Build and push the Docker image (only if code changed) **Where:** Dev PC — **PowerShell as Administrator**, Docker Desktop running Builds the Express API + freshly built client SPA and pushes to Gitea. ```powershell docker login gitea.mysuperlab.netcraze.pro npm run prod:docker:publish ``` - Already built and only need to push: `npm run prod:docker:push-only` - Image tag: `gitea.mysuperlab.netcraze.pro/danilka/gallery-web:latest` - No registry / offline: `.\infra\docker\save-for-truenas.ps1` → copy `gallery-web.tar` via the `Gallery` SMB share → on TrueNAS `sudo bash infra/docker/truenas-load-image.sh /path/to/gallery-web.tar`. --- ## Step 7 — Restart gallery-web on TrueNAS (after code or schema changes) **Where:** TrueNAS — Web UI 1. **Apps** → **gallery-web** → **Restart** (pulls `:latest` when `pull_policy: always`, otherwise redeploy to force a pull). 2. Wait for status **Running**. Data-only or image-only releases (no new image) do not require a restart, but a restart is harmless. --- ## Step 8 — Verify production **Where:** Dev PC / browser Bypass the router first (should return JSON instantly), then check the public URL: ```powershell curl.exe -s http://192.168.10.122:5173/api/bounds curl.exe -sk https://gallery.mysuperlab.netcraze.pro/api/bounds ``` Then open **https://gallery.mysuperlab.netcraze.pro/** and confirm: - Timeline + movement flow load; portraits appear. - Click a portrait / movement → 3D hall renders with painting images (`/images/paintings/...`). - **Curator login** (top-right) works with the intended credentials. Optional on TrueNAS shell: `bash infra/docker/truenas-verify.sh`. --- ## Rollback | Problem | Action | |---------|--------| | Bad **data** release | Re-run Step 4 with the pre-release prod backup: `npm run devtoprod:db:restore -- --file db/DataBackup/gallery_prod_data_.txt` | | Bad **code** release | Rebuild from the last good commit: `git checkout `, `npm run prod:docker:publish`, restart `gallery-web` | | Bad **images** | Re-sync from a known-good dev copy, or pull prod back to dev with `npm run prodto:dev:images` to compare | Keep at least the most recent `gallery_prod_data_*.txt` from Step 2 so a data rollback is always possible. --- ## Command quick reference | Step | Command | Runs on | |------|---------|---------| | 0 Validate | `npm run prod:build` | Dev PC | | 1 Ship code | `git add . ; git commit -m "…" ; git push origin main` | Dev PC | | 2 Backup dev | `npm run dev:db:backup` | Dev PC | | 2 Backup prod (rollback point) | `npm run prod:db:backup` | Dev PC | | 3 Migrate prod schema | `$env:DB_NAME="gallery_prod"; npm run dev:migrate; Remove-Item Env:\DB_NAME` | Dev PC | | 4 Restore data → prod | `npm run devtoprod:db:restore -- --file .txt` | Dev PC | | 5 Sync images → prod | `npm run devtoprod:images` | Dev PC (SMB) | | 6 Build + push image | `npm run prod:docker:publish` | Dev PC (Admin) | | 7 Restart app | Apps → gallery-web → Restart | TrueNAS UI | | 8 Verify | `curl.exe -sk https://gallery.mysuperlab.netcraze.pro/api/bounds` | Dev PC | --- ## Safety guards (built in) - Prod DB scripts read **only** `infra/docker/.env.prod`; dev scripts refuse `_prod` database names. - Prod restore/backup require typing `yes` (or `CONFIRM_PROD=1`). - `npm run dev:migrate` targets whatever `DB_NAME` is set — always `Remove-Item Env:\DB_NAME` after Step 3 so later commands stay on dev. ## Related docs | Document | Contents | |----------|----------| | [environments.md](environments.md) | Full dev/prod walkthrough, one-time setup, Keenetic | | [infra/docker/DEPLOY-truenas.md](../infra/docker/DEPLOY-truenas.md) | TrueNAS container install and troubleshooting | | [FAC.md](FAC.md#promote-dev--prod-scheduled-release) | Command cheat sheet | | [data-and-images.md](data-and-images.md) | Catalog and image pipeline |