Document development-first workflow with weekly prod releases.

Default all day-to-day work to gallery_dev and devgallery; add Cursor rule and update docs, env examples, and deploy guide for scheduled prod promotion.
This commit is contained in:
Danila Khodjaef
2026-07-06 09:58:01 +03:00
parent 9da065acbe
commit bdddadc4d6
9 changed files with 245 additions and 166 deletions
+43
View File
@@ -0,0 +1,43 @@
---
description: Default to dev for all changes; prod only when explicitly requested
alwaysApply: true
---
# Development-first workflow
**Default target is always development** unless the user explicitly names **prod** / **production**.
## What "dev" means
| Layer | Dev default |
|-------|-------------|
| Code | Edit in this repo; run `npm run dev:web` on the dev PC |
| Database | `gallery_dev` via root `.env` (`DB_NAME=gallery_dev`) |
| Data / images | `data/images/` in the repo |
| Public URL | https://devgallery.mysuperlab.netcraze.pro |
| Migrations / seeds / scripts | `npm run migrate`, `npm run seed`, etc. against **dev** |
## Production is scheduled, not daily
Prod (`gallery_prod`, TrueNAS Docker, https://gallery.mysuperlab.netcraze.pro) is updated **roughly weekly** (or when the user explicitly asks for prod):
1. Validate on dev
2. `npm run db:backup` → `npm run db:restore:prod` (if DB/data changed)
3. `npm run images:sync-to-prod` (if images changed)
4. `npm run docker:publish` (if code changed)
5. Restart **gallery-web** on TrueNAS
Do **not** edit `infra/docker/.env.prod`, run `db:restore:prod`, `images:sync-to-prod`, or `docker:publish` unless the user clearly targets prod.
## When the user says "fix", "change", or "modify"
- Assume **dev** code, **dev** DB, and **dev** data paths
- Test against devgallery or localhost
- Mention prod steps only as optional follow-up for the next release
## When the user says "prod" or "production"
- Use `gallery_prod`, `infra/docker/.env.prod`, TrueNAS compose, and prod deploy docs
- Require explicit confirmation before destructive prod operations (`db:restore:prod`, etc.)
Full operator guide: `Documentation/environments.md`
+2 -1
View File
@@ -1,4 +1,5 @@
# Development — copy to .env (not committed) # Development only — copy to .env (not committed). All day-to-day work uses gallery_dev.
# Promote to prod on a scheduled release (~weekly); see Documentation/environments.md
DB_HOST=192.168.10.122 DB_HOST=192.168.10.122
DB_PORT=5432 DB_PORT=5432
DB_USER=gallery DB_USER=gallery
+10 -5
View File
@@ -2,6 +2,8 @@
Quick cheat sheet for daily operations. All `npm` commands run from the **repository root** unless noted. Quick cheat sheet for daily operations. All `npm` commands run from the **repository root** unless noted.
**Default target: dev.** Day-to-day commands use `gallery_dev`, repo `data/images/`, and https://devgallery.mysuperlab.netcraze.pro. Prod (`gallery_prod`, Docker, `docker:publish`) is for **scheduled releases** (~weekly) — see [Promote dev → prod](#promote-dev--prod-scheduled-release) below.
**Environments:** **Environments:**
| | Dev | Prod | | | Dev | Prod |
@@ -283,14 +285,17 @@ Expect JSON with `min_year` / `max_year`. HTML shell only from `curl` on `/` is
--- ---
## Promote dev → prod (checklist) ## Promote dev → prod (scheduled release)
1. `npm run db:backup` ~Weekly (or when explicitly releasing to prod). Not part of daily dev.
2. Test on https://devgallery.mysuperlab.netcraze.pro
3. `npm run db:restore:prod -- --file db/DataBackup/gallery_dev_data_....txt` 1. Test on https://devgallery.mysuperlab.netcraze.pro
4. `npm run images:sync-to-prod` 2. `npm run db:backup`
3. `npm run db:restore:prod -- --file db/DataBackup/gallery_dev_data_....txt` (if DB/catalog changed)
4. `npm run images:sync-to-prod` (if images changed)
5. `npm run docker:publish` (if code changed) 5. `npm run docker:publish` (if code changed)
6. Restart **gallery-web** on TrueNAS 6. Restart **gallery-web** on TrueNAS
7. Verify https://gallery.mysuperlab.netcraze.pro
--- ---
+6 -4
View File
@@ -80,11 +80,9 @@ Gallery/
## Runtime modes ## Runtime modes
### Production (TrueNAS Docker) **Default:** develop and test on **dev** (`gallery_dev`, devgallery URL). **Production** is updated on a scheduled release (~weekly), not on every edit. See [environments.md — Development-first workflow](environments.md#development-first-workflow-default).
Production runs in **`gallery-web`** on TrueNAS port **5173**, database **`gallery_prod`**, public URL **https://gallery.mysuperlab.netcraze.pro**. Images: `/mnt/BasePool/Applications/Gallery/data/images` (SMB share **`Gallery`**). See [environments.md](environments.md). ### Public development (`dev:web`) — primary
### Public development (`dev:web`)
```bash ```bash
npm run dev:web # Vite :5173 + API :3451 — https://devgallery.mysuperlab.netcraze.pro npm run dev:web # Vite :5173 + API :3451 — https://devgallery.mysuperlab.netcraze.pro
@@ -92,6 +90,10 @@ npm run dev:web # Vite :5173 + API :3451 — https://devgallery.mysuperlab.net
Uses database **`gallery_dev`** on the same PostgreSQL host. Uses database **`gallery_dev`** on the same PostgreSQL host.
### Production (TrueNAS Docker) — scheduled releases
Production runs in **`gallery-web`** on TrueNAS port **5173**, database **`gallery_prod`**, public URL **https://gallery.mysuperlab.netcraze.pro**. Images: `/mnt/BasePool/Applications/Gallery/data/images` (SMB share **`Gallery`**). Deploy via `npm run docker:publish` and the promote checklist in [environments.md](environments.md).
### Production-style single process (local) ### Production-style single process (local)
```bash ```bash
+27 -6
View File
@@ -9,6 +9,23 @@ Gallery uses **one PostgreSQL server** on TrueNAS (`192.168.10.122`) with **two
--- ---
## Development-first workflow (default)
**All day-to-day work happens on dev.** Code edits, database changes, catalog updates, image fixes, migrations, and testing use **`gallery_dev`**, the repos **`data/images/`**, and **https://devgallery.mysuperlab.netcraze.pro** (or `http://localhost:5173` on the dev PC).
Production is **not** updated on every change. Promote to prod on a **regular cadence** (typically about once a week) or when you explicitly decide to release:
| Phase | Where | What |
|-------|-------|------|
| **Daily** | Dev PC + `gallery_dev` | Feature work, fixes, seed/migrate, curator edits, image pipeline |
| **Release** | Dev PC → TrueNAS | Backup dev → restore prod DB (if data changed) → sync images → `docker:publish` → restart **gallery-web** |
Unless a task explicitly says **prod** or **production**, assume the target is **dev** — including when asking an assistant to change, fix, or modify code, data, or the database.
**Never** point the dev PC `.env` at `gallery_prod`. Prod-only files (`infra/docker/.env.prod`, `db:restore:prod`, `images:sync-to-prod`) are for scheduled releases, not routine development.
---
## Where to run what (quick reference) ## Where to run what (quick reference)
| Step | Machine | Interface | Privilege | What | | Step | Machine | Interface | Privilege | What |
@@ -286,15 +303,19 @@ See also [Drunkmeyou gitea-https-keenetic-npm-setup.md](../../Drunkmeyou/Documen
--- ---
## Promote dev → prod ## Promote dev → prod (scheduled release)
Run this when you are ready to ship dev to production — **not** after every small change. Typical cadence: **about once a week**.
**Where:** Dev PC unless noted **Where:** Dev PC unless noted
1. `npm run db:backup` 1. Finish and test on https://devgallery.mysuperlab.netcraze.pro
2. Test on https://devgallery.mysuperlab.netcraze.pro 2. `npm run db:backup`
3. `npm run db:restore:prod -- --file db/DataBackup/gallery_dev_data_....txt` (type `yes`) 3. `npm run db:restore:prod -- --file db/DataBackup/gallery_dev_data_....txt` (type `yes`) — skip if only code changed and prod DB should stay as-is
4. `npm run images:sync-to-prod` 4. `npm run images:sync-to-prod` — skip if no new/changed images
5. **TrueNAS Web UI** → restart **gallery-web** (or `npm run docker:publish` if code changed) 5. `npm run docker:publish` — required when application code changed
6. **TrueNAS Web UI** → restart **gallery-web**
7. Verify https://gallery.mysuperlab.netcraze.pro
--- ---
+2
View File
@@ -8,6 +8,8 @@
## Environment ## Environment
**Development-first:** The root `.env` is for **dev only** (`DB_NAME=gallery_dev`). All routine work — code, migrations, seeds, image scripts, curator edits — targets dev. Production uses `infra/docker/.env.prod` and TrueNAS; promote on a scheduled release (~weekly), not after every change. See [environments.md — Development-first workflow](environments.md#development-first-workflow-default).
Copy the example file and fill in credentials: Copy the example file and fill in credentials:
```bash ```bash
+2
View File
@@ -75,6 +75,8 @@ Operator guide: [Documentation/environments.md](Documentation/environments.md).
## Development ## Development
**Workflow:** All changes are made and tested on **dev** (`gallery_dev`, https://devgallery.mysuperlab.netcraze.pro). Production is updated on a **scheduled release** (~weekly): promote DB/images, build/push the Docker image, restart TrueNAS. See [Documentation/environments.md](Documentation/environments.md#development-first-workflow-default).
**Public dev (Keenetic):** **Public dev (Keenetic):**
```bash ```bash
+3 -2
View File
@@ -1,5 +1,6 @@
# Copy to infra/docker/.env.prod on TrueNAS or build PC (do not commit). # Production only — copy to infra/docker/.env.prod (do not commit).
# Used by compose.prod.yaml and npm run db:migrate:prod / db:restore:prod. # Used for scheduled releases: db:restore:prod, docker:publish, TrueNAS gallery-web.
# Day-to-day development uses root .env → gallery_dev.
DB_HOST=192.168.10.122 DB_HOST=192.168.10.122
DB_PORT=5432 DB_PORT=5432
+150 -148
View File
@@ -1,149 +1,151 @@
# Deploy Gallery on TrueNAS Scale # Deploy Gallery on TrueNAS Scale
Production deployment for the **Express API + built Vite SPA** container. PostgreSQL stays on the host at `192.168.10.122:5432`. Public URL: **https://gallery.mysuperlab.netcraze.pro** (Keenetic → TrueNAS `:5173`, protocol to device **http**).
Production deployment for the **Express API + built Vite SPA** container. PostgreSQL stays on the host at `192.168.10.122:5432`. Public URL: **https://gallery.mysuperlab.netcraze.pro** (Keenetic → TrueNAS `:5173`, protocol to device **http**). **Not for daily work.** Develop on dev (`gallery_dev`, https://devgallery.mysuperlab.netcraze.pro). Use this guide when **releasing** to prod (~weekly): build/push image, sync data/images if needed, restart the TrueNAS app.
See also [Documentation/environments.md](../../Documentation/environments.md#development-first-workflow-default).
See also [Documentation/environments.md](../../Documentation/environments.md). ## Architecture
```text
Browser (HTTPS)
## Architecture → Keenetic (KeenDNS, SSL termination)
→ gallery-web container on TrueNAS (:5173, HTTP)
→ PostgreSQL (192.168.10.122:5432) → gallery_prod
→ /mnt/BasePool/Applications/Gallery/data/images (volume)
```text → SMB share Gallery → \\192.168.10.122\Gallery (image sync from dev PC)
```
Browser (HTTPS)
## Prerequisites
→ Keenetic (KeenDNS, SSL termination)
| Item | Notes |
→ gallery-web container on TrueNAS (:5173, HTTP) |------|-------|
| TrueNAS Scale 25.04+ | Apps → Custom App support |
PostgreSQL (192.168.10.122:5432) → gallery_prod | PostgreSQL | `gallery_prod` (dev: `gallery_dev` on same host) — split via [pgAdmin script](../../db/split-dev-prod-pgadmin.sql) |
| Gitea registry | `gitea.mysuperlab.netcraze.pro` — image **`danilka/gallery-web:latest`** pushed before deploy |
/mnt/BasePool/Applications/Gallery/data/images (volume) | SMB share | **`Gallery`** at `/mnt/BasePool/Applications/Gallery` for `images:sync-to-prod` |
| Keenetic | `gallery.mysuperlab.netcraze.pro``192.168.10.122:5173`, protocol **`http`**, Preserve Host ON |
→ SMB share Gallery → \\192.168.10.122\Gallery (image sync from dev PC)
If deploy fails with **`manifest unknown`**, the image is not in Gitea yet — complete [§1 Build and push](#1-build-and-push-image-dev-machine) first.
```
## 1. Build and push image (dev machine)
**Where:** Dev PC — **PowerShell as Administrator** (LAN push hosts entry), **Docker Desktop running**
## Prerequisites
```powershell
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery
docker login gitea.mysuperlab.netcraze.pro
| Item | Notes | npm run docker:publish
```
|------|-------|
Push only (already built): `npm run docker:publish:push-only`
| TrueNAS Scale 25.04+ | Apps → Custom App support |
Image: `gitea.mysuperlab.netcraze.pro/danilka/gallery-web:latest`
| PostgreSQL | `gallery_prod` (dev: `gallery_dev` on same host) — split via [pgAdmin script](../../db/split-dev-prod-pgadmin.sql) |
### Gitea registry tokens
| Gitea registry | `gitea.mysuperlab.netcraze.pro` — image **`danilka/gallery-web:latest`** pushed before deploy |
| Machine | Token scope |
| SMB share | **`Gallery`** at `/mnt/BasePool/Applications/Gallery` for `images:sync-to-prod` | |---------|-------------|
| Build PC | `write:package` |
| Keenetic | `gallery.mysuperlab.netcraze.pro``192.168.10.122:5173`, protocol **`http`**, Preserve Host ON | | TrueNAS pull | `read:package` |
Register on TrueNAS: **Apps → Configuration → Sign in to a Docker registry** — URL `https://gitea.mysuperlab.netcraze.pro`, username `danilka` (lowercase for registry).
If deploy fails with **`manifest unknown`**, the image is not in Gitea yet — complete [§1 Build and push](#1-build-and-push-image-dev-machine) first. ### Registry token URL fix
If `docker push` fails with internal HTTP token URL, set Gitea `ROOT_URL` to `https://gitea.mysuperlab.netcraze.pro/` and restart Gitea. Full walkthrough: [Drunkmeyou gitea-https-keenetic-npm-setup.md](../../../Drunkmeyou/Documentation/gitea-https-keenetic-npm-setup.md).
## 1. Build and push image (dev machine) ### Offline fallback (no registry)
On **dev PC**: `.\infra\docker\save-for-truenas.ps1` → copy `gallery-web.tar` via SMB `Gallery` share.
**Where:** Dev PC — **PowerShell as Administrator** (LAN push hosts entry), **Docker Desktop running** On **TrueNAS shell**: `sudo bash truenas-load-image.sh /path/to/gallery-web.tar`
In Custom App YAML: `pull_policy: if_not_present`, then redeploy.
```powershell ## 2. Prepare TrueNAS storage
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery **Where:** TrueNAS — **Shell**, as **root**
docker login gitea.mysuperlab.netcraze.pro ```bash
mkdir -p /mnt/BasePool/Applications/Gallery/data/images/portraits
npm run docker:publish 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
```
Enable SMB share **`Gallery`** → `/mnt/BasePool/Applications/Gallery` (for image sync from dev PC).
Push only (already built): `npm run docker:publish:push-only`
## 3. Sync images (first deploy)
**Where:** Dev PC — PowerShell (normal), repo root
Image: `gitea.mysuperlab.netcraze.pro/danilka/gallery-web:latest`
```powershell
net use \\192.168.10.122\Gallery /user:YOUR_TRUENAS_USER
cd C:\Users\SNAP\Nextcloud\Personal\Repo\Gallery
### Gitea registry tokens npm run images:sync-to-prod
```
UNC destination: `\\192.168.10.122\Gallery\data\images`
| Machine | Token scope |
## 4. Install Custom App
|---------|-------------|
**Where:** TrueNAS — **Web UI**
| Build PC | `write:package` |
1. **Apps → Discover Apps → Custom App**
| TrueNAS pull | `read:package` | 2. Paste [`compose.truenas.yaml`](compose.truenas.yaml)
3. Set `DB_PASSWORD` (replace `YOUR_POSTGRES_PASSWORD`)
4. Deploy → wait for **gallery-web** **Running**
Register on TrueNAS: **Apps → Configuration → Sign in to a Docker registry** — URL `https://gitea.mysuperlab.netcraze.pro`, username `danilka` (lowercase for registry). ## 5. Keenetic (production)
| Field | Value |
|-------|-------|
### Registry token URL fix | Domain | `gallery.mysuperlab.netcraze.pro` |
| Upstream | `192.168.10.122:5173` |
| Protocol to device | **`http`** |
| Preserve Host | ON |
If `docker push` fails with internal HTTP token URL, set Gitea `ROOT_URL` to `https://gitea.mysuperlab.netcraze.pro/` and restart Gitea. Full walkthrough: [Drunkmeyou gitea-https-keenetic-npm-setup.md](../../../Drunkmeyou/Documentation/gitea-https-keenetic-npm-setup.md).
Wrong IP or `https` to device → **502 / 504** from Keenetic (`Server: Web server`).
## 6. Verify
### Offline fallback (no registry)
**Bypass Keenetic** (should be instant JSON):
```powershell
On **dev PC**: `.\infra\docker\save-for-truenas.ps1` → copy `gallery-web.tar` via SMB `Gallery` share. curl.exe -s http://192.168.10.122:5173/api/bounds
```
**Public URL:**
On **TrueNAS shell**: `sudo bash truenas-load-image.sh /path/to/gallery-web.tar`
```powershell
curl.exe -sk https://gallery.mysuperlab.netcraze.pro/api/bounds
```
In Custom App YAML: `pull_policy: if_not_present`, then redeploy.
Open **https://gallery.mysuperlab.netcraze.pro/** — timeline and `/images/paintings/...` should load.
On TrueNAS shell: `bash infra/docker/truenas-verify.sh`
## 2. Prepare TrueNAS storage
## 7. Update production
After code changes on dev PC:
**Where:** TrueNAS — **Shell**, as **root**
```powershell
npm run docker:publish
```
```bash
Restart **gallery-web** on TrueNAS (or rely on `pull_policy: always`).
mkdir -p /mnt/BasePool/Applications/Gallery/data/images/portraits
## Troubleshooting
mkdir -p /mnt/BasePool/Applications/Gallery/data/images/paintings/thumbs
| Issue | Fix |
chown -R 1001:1001 /mnt/BasePool/Applications/Gallery |-------|-----|
| **`manifest unknown`** | Run `npm run docker:publish` first; or offline `truenas-load-image.sh` |
chmod -R u+rwX,g+rwX /mnt/BasePool/Applications/Gallery | **502 / 504 public URL** | Keenetic → `192.168.10.122:5173`, protocol **`http`**; verify LAN curl above |
| Container cannot reach Postgres | `DB_HOST=192.168.10.122`; `extra_hosts` in compose |
``` | Empty timeline | `DB_NAME=gallery_prod`; run pgAdmin split if still on legacy `Gallery` |
| Missing images | `npm run images:sync-to-prod`; check volume mount and `chown 1001:1001` |
| Pull 401 | Gitea registry credentials on TrueNAS; `read:package` token |
| Push fails on dev PC | Gitea `ROOT_URL` HTTPS fix; use Admin PowerShell for LAN push |