Files
Art-gallery/Documentation/FAC.md
Danila KhodjaefandCursor 0466b77328 Add curator roles/permissions with Users admin, and fix lineage branch joins.
Staff accounts use admin/curator roles and fine-grained flags; transitions connect source-to-target with color gradients and stream cutout masks so overlaps stay seamless.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 18:04:17 +03:00

381 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gallery — command reference (FAC)
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, `prod:docker:publish`) is for **scheduled releases** (~weekly) — see [Promote dev → prod](#promote-dev--prod-scheduled-release) below.
**Script prefixes:** `dev:``gallery_dev` / dev PC · `prod:` → production / TrueNAS · `devtoprod:` → promote dev → prod · `prodto:dev:` → refresh dev from prod · `infra:` → one-time setup
**Environments:**
| | Dev | Prod |
|---|-----|------|
| URL | https://devgallery.mysuperlab.netcraze.pro | https://gallery.mysuperlab.netcraze.pro |
| Host | Dev PC `192.168.10.70:5173` | TrueNAS `192.168.10.122:5173` |
| Database | `gallery_dev` | `gallery_prod` |
Details: [environments.md](environments.md) · Deploy: [../infra/docker/DEPLOY-truenas.md](../infra/docker/DEPLOY-truenas.md)
---
## Start and stop servers
### Start — public dev (Keenetic URL)
```powershell
cd T:\Repo\Gallery
npm run dev:web
```
Vite on **:5173**, API on **:3451**. Open https://devgallery.mysuperlab.netcraze.pro or http://localhost:5173.
### Start — local HMR (no Keenetic)
Two terminals:
```powershell
npm run dev:server # API — PORT from .env (default 3451)
npm run dev:client # Vite on :5173
```
### Start — production-style (single Node process, built SPA)
```powershell
npm run prod:start # build client + serve on PORT from .env
# or:
npm run prod:build
npm run dev:start
```
### Stop dev servers
| Method | When |
|--------|------|
| **Ctrl+C** in the terminal running `dev:web` / `dev:server` / `dev:client` | Normal stop |
| Close the terminal tab | Same effect |
Free ports **5173**, **3451**, **3520** before DB maintenance or if “port in use” errors appear.
**Find what holds a port (PowerShell):**
```powershell
netstat -ano | findstr ":5173 :3451"
# Stop by PID:
Stop-Process -Id <PID> -Force
```
### Stop / restart production (TrueNAS)
| Action | Where |
|--------|-------|
| **Stop** | TrueNAS Web UI → Apps → **gallery-web** → Stop |
| **Restart** | Same → Restart (after `docker:publish` or config change) |
| **Update image** | Dev PC: `npm run prod:docker:publish` → restart app on TrueNAS |
---
## First-time install
```powershell
cd T:\Repo\Gallery
copy .env.example .env # edit DB credentials, PUBLIC_URL
npm install
cd client; npm install; cd ..
npm run dev:migrate # schema + incremental SQL (+ auth tables, bootstrap curator)
npm run dev:reset-curator # upsert curator password from .env CURATOR_* (clears sessions)
npm run dev:setup # migrate + seed (fresh empty DB only)
```
**Curator auth (after migrate):** set in `.env` before first `npm run dev:migrate` if the DB has no curator yet:
```env
SESSION_SECRET=your-long-random-secret
CURATOR_USERNAME=curator
CURATOR_PASSWORD=your-secure-password
```
Then open the gallery → **Curator login** (top-right) → use tools allowed by your role/permissions (debug, Checkup, Translations, Influences, Tour editor, **Users**). Mutations are logged in `curator_audit_log` per user (view in pgAdmin).
If login fails after changing `CURATOR_PASSWORD` in `.env`, run `npm run dev:reset-curator` (bootstrap only runs when `users` is empty; reset upserts the env account as **admin**).
**Roles:**
| Role | Access |
|------|--------|
| Guest (`user`) | Timeline, movement flow, 3D halls, painting detail, bios |
| Curator | Public browse + assigned permission flags (`images`, `checkup`, `curator_notes`, `translations`, `influences`, `tours`, `users`) |
| Admin | All curator tools + **Users** page to create accounts with individual passwords and permissions |
**Users page:** after admin login, header → **Users** — create/edit staff, reset passwords, disable accounts.
**Audit log (pgAdmin on `gallery_dev` or `gallery_prod`):**
```sql
SELECT l.created_at, u.username, l.action, l.resource_type, l.resource_id
FROM curator_audit_log l
JOIN users u ON u.id = l.user_id
ORDER BY l.created_at DESC
LIMIT 30;
```
**Prod auth env** (TrueNAS app or `infra/docker/.env.prod`):
```env
SESSION_SECRET=long-random-secret
SESSION_COOKIE_SECURE=true
CURATOR_USERNAME=curator
CURATOR_PASSWORD=your-secure-password
```
Run `npm run dev:migrate` against prod DB after first deploy with auth vars set (creates tables + bootstrap curator if `users` is empty).
---
## Database
| Command | Description |
|---------|-------------|
| `npm run dev:migrate` | Apply `db/schema.sql` + incremental migrations (safe to re-run) |
| `npm run dev:setup` | `migrate` + `seed` — fresh catalog from Wikipedia data |
| `npm run infra:db:split-dev-prod` | One-time: legacy `Gallery``gallery_prod` + `gallery_dev` (needs `PGUSER=postgres`) |
| `npm run prodto:dev:db` | Clone `gallery_prod``gallery_dev` (TEMPLATE); also runs image sync from prod |
| `npm run dev:db:backup` | Dev data-only backup → `db/DataBackup/*.txt` + `.zip` |
| `npm run prod:db:backup` | Prod backup (reads `infra/docker/.env.prod`) |
| `npm run dev:db:restore -- --file <path>` | Restore backup into **dev** (truncates tables first; prompts `yes`) |
| `npm run devtoprod:db:restore -- --file <path>` | Restore into **prod** (requires confirmation) |
| `npm run harmonize` | Bidirectional catalog DB + image merge by `updated_at` / file mtime — [harmonize-dev-prod.md](harmonize-dev-prod.md) |
| `npm run harmonize:schema` | Apply dev migrations to prod schema only (dev → prod) |
| `npm run harmonize:db` / `harmonize:images` | DB or image merge only (`harmonize:images` also merges artists/paintings checkup flags + image paths, then regenerates thumbs on both sides) |
| `npm run dev:backfill-updated-at` | Backfill catalog `updated_at` from image mtimes (dev) |
**One-time split (recommended):** pgAdmin on dev PC → open [`db/split-dev-prod-pgadmin.sql`](../db/split-dev-prod-pgadmin.sql) → run each STEP on database `postgres`, then verify on `gallery_dev`.
**Dev `.env` essentials:**
```env
DB_NAME=gallery_dev
PORT=3451
PUBLIC_URL=https://devgallery.mysuperlab.netcraze.pro
SESSION_SECRET=your-long-random-secret
# Omit SESSION_COOKIE_SECURE for auto (HTTPS via Keenetic → Secure cookie)
CURATOR_USERNAME=curator
CURATOR_PASSWORD=your-secure-password
```
---
## Import and enrich catalog data
Run in this order on a **new** or **re-seeded** database:
| # | Command | What it does |
|---|---------|----------------|
| 1 | `npm run dev:seed` | Eras, movements, artists, one flagship painting per artist |
| 2 | `npm run dev:sync-image-paths` | Import paintings from `data/images/paintings/`; link paths in DB |
| 3 | `npm run dev:fetch-artist-images` | Portraits → `data/images/portraits/`, set `portrait_path` |
| 4 | `npm run dev:fetch-artist-bios` | Wikipedia bios → `bio_short` / `bio_full` |
| 5 | `npm run dev:expand-catalog` | Add famous works per artist (below `MIN_PAINTINGS`) |
| 6 | `npm run dev:update-influences` | Influence graph (detail panels, 3D hall lamps) |
| 7 | `npm run dev:fetch-images -- --limit=50` | Download missing painting files (batch) |
**One-shot bootstrap:** `npm run dev:setup` = steps 1 + migrate only; still run 27 for a full catalog.
### Useful flags
```powershell
npm run dev:sync-image-paths -- --dry-run
npm run dev:fetch-artist-images -- --force
npm run dev:fetch-artist-bios -- --force
npm run dev:expand-catalog -- --fetch-images
npm run dev:fetch-images -- --artist="Claude Monet"
npm run dev:fetch-images -- --limit=50 --max-wait=120
npm run dev:update-influences -- --discover
npm run dev:discover-influences # discovery only, no curated insert
```
### Optional migrations / imports
| Command | Description |
|---------|-------------|
| `npm run dev:migrate:thumbnails` | Add thumbnail columns |
| `npm run dev:migrate:influence-sources` | `painting_influence_sources` table + backfill |
| `npm run dev:migrate:checkup-flags` | Review flags on `paintings` |
| `npm run dev:migrate:artist-checkup-flags` | Review flags on `artists` |
| `npm run dev:migrate:painting-annotations` | Art-history notes table |
| `npm run dev:migrate:artist-palette` | `palette_metadata` JSONB on artists |
| `npm run dev:import-painter-palette` | Enrich from `Inputs/PainterPalette.csv` |
| `npm run dev:update-painting-annotations` | Load curated notes |
| `npm run dev:update-painting-annotations -- --wikipedia` | Add Wikipedia intro sentences |
### Audit / export
| Command | Description |
|---------|-------------|
| `npm run dev:audit-painting-images` | Thumb vs full aspect-ratio mismatches |
| `npm run dev:find-duplicates` | Duplicate / near-duplicate painting rows |
| `npm run dev:audit-influence-duplicates` | Duplicate influence edges |
| `npm run dev:analyze-painter-palette` | CSV ↔ artist name match report |
| `npm run dev:export-paintings` | Write `Output/paintings.csv` (gitignored folder) |
**Influence workbooks** (curator import): see [influence-import.md](influence-import.md) — e.g. `Inputs/gariff_influential_painters_influences.xlsx`, `Inputs/story_of_art_influences.xlsx`.
---
## Images (local files)
| Command | Description |
|---------|-------------|
| `npm run dev:fetch-images` | Search/download missing painting files (alias: `search-missing-paintings`) |
| `npm run dev:fetch-artist-images` | Download or link artist portraits |
| `npm run dev:sync-image-paths` | Align DB paths with files on disk; import new rows |
| `npm run dev:regenerate-thumbnails` | Rebuild painting thumbs from full images |
| `npm run dev:regenerate-portrait-thumbs` | Rebuild timeline portrait thumbs (~256px) |
| `npm run devtoprod:thumbnails` | Both of the above — run on dev before promote backup/sync |
| `npm run devtoprod:release` | Config-driven full promote (see [deploy-dev-to-prod.md](deploy-dev-to-prod.md#one-command-release-automated)) |
**Local paths:** `data/images/portraits/`, `data/images/paintings/`, `data/images/paintings/thumbs/`
---
## Dev ↔ prod image sync (SMB)
SMB share **`Gallery`** → `/mnt/BasePool/Applications/Gallery` on TrueNAS.
```powershell
net use \\192.168.10.122\Gallery /user:YOUR_TRUENAS_USER
npm run devtoprod:release # full promote from infra/deploy/devtoprod.config.json
npm run devtoprod:thumbnails # rebuild thumb files + DB paths on dev (before backup)
npm run devtoprod:images # dev repo → TrueNAS (promote / first deploy)
npm run prodto:dev:images # TrueNAS → dev repo (all images)
npm run harmonize # bidirectional merge (newer wins) — see harmonize-dev-prod.md
npm run harmonize:dry-run # preview DB + image changes only
```
**One artist only (prod → dev):** after `net use`, robocopy the artist file prefix (example: Duccio):
```powershell
$src = "\\192.168.10.122\Gallery\data\images\paintings"
$dst = "T:\Repo\Gallery\data\images\paintings"
robocopy $src $dst "Duccio*" /XO /R:2 /W:3
robocopy "$src\thumbs" "$dst\thumbs" "Duccio*" /XO /R:2 /W:3
```
Type `yes` when prompted (or set `autoConfirm: true` in release config). Robocopy exit codes **07** = success. Deploy scripts print a final **`===== SUCCESS =====`** or **`===== FAILED =====`** banner.
---
## Docker and production deploy
| Command | Where | Description |
|---------|-------|-------------|
| `npm run prod:docker:publish` | Dev PC, **Admin** PowerShell, Docker Desktop running | Build + push `gallery-web:latest` to Gitea |
| `npm run prod:docker:push-only` | Same | Push only (skip rebuild) |
| `.\infra\docker\save-for-truenas.ps1` | Dev PC | Save image to `gallery-web.tar` (offline deploy) |
**Before first deploy:** `npm run prod:docker:publish` → TrueNAS Custom App from `infra/docker/compose.truenas.yaml`.
**After code changes:** `npm run prod:docker:publish` → restart **gallery-web** on TrueNAS.
```powershell
docker login gitea.mysuperlab.netcraze.pro
```
---
## Build frontend
```powershell
npm run prod:build # client → client/dist/
cd client && npm run build && cd ..
```
Prod container serves `client/dist/` from the Docker image (rebuild image after UI changes).
---
## Verify (health checks)
```powershell
# Dev (servers running)
curl.exe -sk https://devgallery.mysuperlab.netcraze.pro/api/bounds
curl.exe -s http://192.168.10.70:5173/api/bounds
# Prod (bypass Keenetic)
curl.exe -s http://192.168.10.122:5173/api/bounds
# Prod (public)
curl.exe -sk https://gallery.mysuperlab.netcraze.pro/api/bounds
```
Expect JSON with `min_year` / `max_year`. HTML shell only from `curl` on `/` is normal (Vite dev).
**PowerShell note:** use `curl.exe`, not `curl` — PowerShell aliases `curl` to `Invoke-WebRequest` (no `-k` flag).
---
## Promote dev → prod (scheduled release)
~Weekly (or when explicitly releasing to prod). Not part of daily dev. Full runbook with per-change decision matrix and rollback: [deploy-dev-to-prod.md](deploy-dev-to-prod.md).
**One command (recommended):** copy `infra/deploy/devtoprod.config.example.json``infra/deploy/devtoprod.config.json`, edit SMB/git settings, then:
```powershell
npm run devtoprod:release
# or: deploy-dev-to-prod.cmd
```
Dry-run: `npm run devtoprod:release -- -DryRun`. The script pauses for a manual **gallery-web** restart on TrueNAS before verify.
**Manual steps** (partial releases):
1. Test on https://devgallery.mysuperlab.netcraze.pro
2. `npm run devtoprod:thumbnails` (if paintings/portraits changed — rebuild thumb files + DB paths on dev)
3. `npm run dev:db:backup`
4. `npm run devtoprod:db:restore -- --file db/DataBackup/gallery_dev_data_....txt` (if DB/catalog changed)
5. `npm run devtoprod:images` (if images changed)
6. `npm run prod:docker:publish` (if code changed)
7. Restart **gallery-web** on TrueNAS
8. Verify https://gallery.mysuperlab.netcraze.pro
---
## Keenetic (router)
Both rules: **protocol to device = `http`**, **Preserve Host = ON**.
| Domain | Upstream |
|--------|----------|
| `devgallery.mysuperlab.netcraze.pro` | `192.168.10.70:5173` |
| `gallery.mysuperlab.netcraze.pro` | `192.168.10.122:5173` |
Wrong IP or `https` to device → **502 / 504** (`Server: Web server`).
---
## Git (Gitea)
```powershell
git status
git add .
git commit -m "Your message"
git push origin main
```
Remote: https://gitea.mysuperlab.netcraze.pro/Danilka/Art-gallery
---
## Related docs
| Document | Contents |
|----------|----------|
| [deploy-dev-to-prod.md](deploy-dev-to-prod.md) | Step-by-step release runbook (code, DB, data, images) |
| [environments.md](environments.md) | Full dev/prod walkthrough |
| [setup.md](setup.md) | Install, env vars, troubleshooting |
| [data-and-images.md](data-and-images.md) | Catalog and image pipeline |
| [API.md](API.md) | REST endpoints |
| [tours.md](tours.md) | Guided tours |
| [DB_structure.md](DB_structure.md) | PostgreSQL schema |