diff --git a/Documentation/DB_structure.md b/Documentation/DB_structure.md index e5e237b..e1cc74c 100644 --- a/Documentation/DB_structure.md +++ b/Documentation/DB_structure.md @@ -161,8 +161,9 @@ Curated guided tours. See [tours.md](tours.md). | `painting_id` | FK → `paintings` | ON DELETE CASCADE; UNIQUE with `tour_id` | | `sort_order` | INTEGER | Visitor / editor order | | `body` | TEXT | English tour notes for the stop (v1) | +| `updated_at` | TIMESTAMPTZ | Trigger on UPDATE; required for `npm run harmonize:db` | -Applied by `npm run dev:migrate` (`db/migrate-tours.sql`). +Applied by `npm run dev:migrate` (`db/migrate-tours.sql`; `updated_at` also covered by `migrate-sync-timestamps.sql` when the table already exists). ### `painting_influences` diff --git a/Documentation/FAC.md b/Documentation/FAC.md index 654d4ee..f079480 100644 --- a/Documentation/FAC.md +++ b/Documentation/FAC.md @@ -243,11 +243,20 @@ 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 +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 **0–7** = success. Deploy scripts print a final **`===== SUCCESS =====`** or **`===== FAILED =====`** banner. --- diff --git a/Documentation/basics.md b/Documentation/basics.md index bc9eee6..eb99c22 100644 --- a/Documentation/basics.md +++ b/Documentation/basics.md @@ -210,7 +210,7 @@ Pan, zoom, and era/event click-to-zoom only update **local** `viewStart` / `view | Overlay **“Loading art history…”** | Until the first catalog fetch (`bounds` + `timeline` + `artists`) completes | | Bottom banner **“Loading portraits…”** | While artist portrait thumbnails are still downloading on the movement flow (timeline stays interactive) | | Overlay **“Opening artist/movement gallery…”** | Between clicking a portrait/movement and the 3D hall data being ready | -| Overlay **“Loading gallery…”** | While the 3D canvas initializes, HDR Environment settles, or door/hall shaders warm up after the hall opens | +| Overlay **“Loading gallery…”** | While the 3D canvas initializes, HDR Environment settles, or door/hall shaders warm up after the hall opens (painting images continue loading after the overlay dismisses) | | Overlay **“Loading paintings…”** | While wall painting textures are still downloading / uploading to the GPU | | Overlay **“Restoring gallery…”** | Briefly after WebGL context loss while the canvas remounts | @@ -374,7 +374,7 @@ Full guide: [tours.md](tours.md). **3D images** use locally cached files only (`galleryImageUrl` in `client/src/api/client.ts`). Remote fetches are too slow for realtime WebGL textures; the client calls `POST /api/artists/:id/preload-images` automatically when entering an **artist** hall (public route — links disk files only). Movement galleries load painting lists from the API without a separate preload step. While a texture is loading, the frame shows the canvas cover instead of a white placeholder. -**Boot overlay** (`VirtualGallery.tsx`): the center shows **“Loading gallery…”** / **“Loading paintings…”** until all of the following settle — WebGL canvas ready, painting textures downloaded and GPU-uploaded (`gl.initTexture` via `GalleryTextureLoadContext`), HDR `Environment` ready (or timed out / failed), and a one-shot `gl.compileAsync` warm-up so entrance doors / passages (often frustum-culled at spawn) do not hitch on the first turn. Env and shader warm-up have short timeouts so a slow CDN cannot leave the overlay up forever. The 3D hall stays mounted while painting detail or bio overlays are open; returning remounts the canvas when the hall becomes active again. +**Boot overlay** (`VirtualGallery.tsx`): the center shows **“Loading gallery…”** until the WebGL canvas is ready, HDR `Environment` has settled (or timed out / failed), and a one-shot `gl.compileAsync` warm-up finishes so entrance doors / passages (often frustum-culled at spawn) do not hitch on the first turn. Painting textures keep loading in the background (prefer thumbnails; per-image timeouts; GPU upload after decode) so large halls (50+ works) are not stuck on the overlay. Env and shader warm-up also have short timeouts. The 3D hall stays mounted while painting detail or bio overlays are open; returning remounts the canvas when the hall becomes active again. **WebGL context-loss recovery:** on some GPUs/drivers (notably certain Chrome setups) the browser can drop the WebGL context right after entering a hall, which would otherwise leave a permanent dark window. `VirtualGallery.tsx` listens for `webglcontextlost` / `webglcontextrestored`, calls `preventDefault()` so the browser can restore the context, and remounts the `` with a fresh context (a **“Restoring gallery…”** overlay shows briefly). The network-loaded HDR `Environment` map is wrapped in an error boundary so, if it fails to load, the hall still renders without reflections instead of unmounting the whole scene. diff --git a/Documentation/data-and-images.md b/Documentation/data-and-images.md index 8e5b1ae..e823ac1 100644 --- a/Documentation/data-and-images.md +++ b/Documentation/data-and-images.md @@ -31,7 +31,7 @@ File names are sanitised `{Artist}_{Title}.{ext}`. The image service can redisco | **Development** | `./data/images/` in repo | Working copy on dev PC | | **Production** | `/mnt/BasePool/Applications/Gallery/data/images` on TrueNAS | SMB `\\192.168.10.122\Gallery\data\images` | -**One-direction promote:** `npm run devtoprod:images` (dev → prod, skip older). **Refresh dev from prod:** `npm run prodto:dev:images`. **Bidirectional merge** (newer file wins): `npm run harmonize:images` or full `npm run harmonize` — see [harmonize-dev-prod.md](harmonize-dev-prod.md). General sync reference: [environments.md](environments.md). +**One-direction promote:** `npm run devtoprod:images` (dev → prod, skip older). **Refresh all images on dev from prod:** `npm run prodto:dev:images`. **One artist only:** map the SMB share, then `robocopy` that artist’s `ArtistName*` files under `paintings/` and `paintings/thumbs/` (see [FAC.md](FAC.md#dev--prod-image-sync-smb)). **Bidirectional merge** (newer file wins): `npm run harmonize:images` or full `npm run harmonize` — see [harmonize-dev-prod.md](harmonize-dev-prod.md). General sync reference: [environments.md](environments.md). ## Scripts overview diff --git a/Documentation/harmonize-dev-prod.md b/Documentation/harmonize-dev-prod.md index b11e9eb..43c5ff2 100644 --- a/Documentation/harmonize-dev-prod.md +++ b/Documentation/harmonize-dev-prod.md @@ -45,7 +45,7 @@ For refreshing dev from prod entirely, use `npm run prodto:dev:db` (destructive net use \\192.168.10.122\Gallery /user:YOUR_TRUENAS_USER ``` -5. Copy harmonize config: +5. Copy **dedicated** harmonize config (do not rely on `devtoprod.config.json` alone — that file is a release profile and its step flags are ignored for harmonize): ```powershell Copy-Item infra/deploy/harmonize.config.example.json infra/deploy/harmonize.config.json @@ -53,6 +53,8 @@ For refreshing dev from prod entirely, use `npm run prodto:dev:db` (destructive Edit `harmonize.config.json` (gitignored) — optional `smb.user` / `smb.password`, `prefer` for tie-breaks (`dev` | `prod`), `schemaChanged: true` when new migrations shipped. +6. Prod DB targeting uses [`infra/docker/.env.prod`](../infra/docker/.env.prod). `scripts/db-env.js` loads that file with **file values winning** over the root `.env`, so `harmonize:db` can open `gallery_dev` and `gallery_prod` in the same process without the safety check rejecting `gallery_dev` as a fake prod target. + --- ## One-command harmonize @@ -107,7 +109,9 @@ Reports are written to `db/SyncReports/harmonize_db_*.json` and `harmonize_image Processed in FK order: -`historical_eras` → `art_movements` → `artists` → `artist_periods` → `paintings` → `painting_influences` → `painting_influence_sources` → `painting_annotations` → **`entity_translations`** +`historical_eras` → `art_movements` → `artists` → `artist_periods` → `paintings` → `painting_influences` → `painting_influence_sources` → `painting_annotations` → **`entity_translations`** → **`tours`** → **`tour_stops`** + +Every synced table needs an `updated_at` column (including `tour_stops` — added via `migrate-tours.sql` / `migrate-sync-timestamps.sql`). --- @@ -134,6 +138,7 @@ Harmonize reports conflicts in the JSON report and skips those rows: | New migration in repo | `harmonize:schema` or deploy step 4 | | Only images changed on one side | `npm run harmonize:images` | | Only DB metadata changed | `npm run harmonize:db` | +| Pull **one artist’s** images from prod → dev | Map SMB, then robocopy `Duccio*` (or the artist prefix) under `paintings/` and `paintings/thumbs/` — see [FAC.md — Dev ↔ prod image sync](FAC.md#dev--prod-image-sync-smb) | --- @@ -163,6 +168,16 @@ npm run harmonize --- +## Troubleshooting + +| Symptom | Likely cause | Fix | +|---------|--------------|-----| +| `Refusing prod operation on database "gallery_dev"` | Old `db-env` left `DB_NAME` from root `.env` when loading prod | Update to current `scripts/db-env.js` (`.env.prod` wins); confirm `infra/docker/.env.prod` has `DB_NAME=gallery_prod` | +| `Table tour_stops missing updated_at` | Prod/dev schema behind | `npm run dev:migrate` and migrate prod (`$env:DB_NAME="gallery_prod"; npm run dev:migrate; Remove-Item Env:\DB_NAME`) | +| Orchestrator lists release steps (`restoreProd`, …) | Missing `harmonize.config.json`; fallback to release config | Copy `harmonize.config.example.json` → `harmonize.config.json` (current `harmonize.ps1` ignores release-only step keys) | + +--- + ## Safety - Pre-flight backups of dev and prod DB (configurable; on by default) diff --git a/client/src/components/VirtualGallery.tsx b/client/src/components/VirtualGallery.tsx index 753723c..4057d9f 100644 --- a/client/src/components/VirtualGallery.tsx +++ b/client/src/components/VirtualGallery.tsx @@ -98,6 +98,9 @@ const REVIEWED_MAT_BORDER = FRAME_MAT_BORDER * 2; const REVIEWED_RAIL = FRAME_RAIL * 2; const EYE_HEIGHT = 1.65; const FRAME_GAP = 0.32; +const SHADER_WARM_TIMEOUT_MS = 4000; +/** Per-image fetch/decode deadline so a stuck request cannot hold the counter forever. */ +const TEXTURE_LOAD_TIMEOUT_MS = 10000; const MIN_FRAME_W = 0.45; const MAX_FRAME_W = 1.05; const MAX_FRAME_H = 1.35; @@ -602,9 +605,16 @@ function usePaintingTexture(url: string | null) { }; textureLoad?.begin(); + const loadTimeout = window.setTimeout(() => { + if (settled || disposed) return; + setFailed(true); + finish(); + }, TEXTURE_LOAD_TIMEOUT_MS); + loader.load( url, (tex) => { + window.clearTimeout(loadTimeout); if (disposed) { tex.dispose(); finish(); @@ -620,17 +630,19 @@ function usePaintingTexture(url: string | null) { loaded = tex; tex.colorSpace = THREE.SRGBColorSpace; tex.anisotropy = 4; - // Upload under "Loading paintings…" so first turn into culled walls does not hitch. - try { - gl.initTexture(tex); - } catch { - // Still release the load counter if GPU upload fails. - } + // Release the hall overlay counter before GPU upload — large halls + // (50+ works) otherwise stay on "Loading paintings…" for a long time. finish(); if (!disposed) setTexture(tex); + try { + if (!disposed) gl.initTexture(tex); + } catch { + // Upload can fail after context loss; texture still usable later. + } }, undefined, () => { + window.clearTimeout(loadTimeout); finish(); if (!disposed) setFailed(true); } @@ -638,6 +650,7 @@ function usePaintingTexture(url: string | null) { return () => { disposed = true; + window.clearTimeout(loadTimeout); finish(); loaded?.dispose(); setTexture(null); @@ -1628,8 +1641,6 @@ function EnvironmentGate({ return <>{children}; } -const SHADER_WARM_TIMEOUT_MS = 4000; - /** Compile all scene materials (incl. culled doors) before dismissing the loading overlay. */ function WarmHallGpu({ enabled, @@ -1959,6 +1970,7 @@ export default function VirtualGallery(props: Props) { useEffect(() => { setEnvReady(false); setShadersWarmed(false); + setTexturesPending(0); }, [hallKey, glEpoch]); useEffect(() => { @@ -1974,10 +1986,10 @@ export default function VirtualGallery(props: Props) { // If shader warm-up never settles, dismiss the overlay anyway. useEffect(() => { - if (shadersWarmed || !canvasReady || !envReady || texturesPending > 0) return; + if (shadersWarmed || !canvasReady || !envReady) return; const t = window.setTimeout(() => setShadersWarmed(true), SHADER_WARM_TIMEOUT_MS + 1000); return () => window.clearTimeout(t); - }, [shadersWarmed, canvasReady, envReady, texturesPending, hallKey, glEpoch, hallIndex]); + }, [shadersWarmed, canvasReady, envReady, hallKey, glEpoch, hallIndex]); const layout = useMemo(() => { if (isWingedHall && movementHalls.length > 0) { @@ -1986,16 +1998,17 @@ export default function VirtualGallery(props: Props) { return buildHallLayout(paintings, periods); }, [isWingedHall, movementHalls, hallIndex, paintings, periods]); + // Do not block the hall on every painting texture — large artists (e.g. Duccio, 50+) + // otherwise sit on "Loading paintings…" for a long time. Textures keep loading after. const gallerySceneLoading = active && !glLost && - (!canvasReady || !envReady || texturesPending > 0 || !shadersWarmed); + (!canvasReady || !envReady || !shadersWarmed); const galleryLoadingMessage = canvasReady && texturesPending > 0 ? 'Loading paintings…' : 'Loading gallery…'; - const warmGpuEnabled = - canvasReady && envReady && texturesPending === 0 && !shadersWarmed; + const warmGpuEnabled = canvasReady && envReady && !shadersWarmed; const computedWindows = useMemo(() => { if (!isMovement || !interiorStyle || !('hallIndex' in layout)) return undefined; diff --git a/data/images/paintings/Duccio_Angel.jpg b/data/images/paintings/Duccio_Angel.jpg index 03e91af..01508d3 100644 Binary files a/data/images/paintings/Duccio_Angel.jpg and b/data/images/paintings/Duccio_Angel.jpg differ diff --git a/data/images/paintings/Duccio_Apostles_Philip_and_James_the_Elder.jpg b/data/images/paintings/Duccio_Apostles_Philip_and_James_the_Elder.jpg index 66685b1..0965ee8 100644 Binary files a/data/images/paintings/Duccio_Apostles_Philip_and_James_the_Elder.jpg and b/data/images/paintings/Duccio_Apostles_Philip_and_James_the_Elder.jpg differ diff --git a/data/images/paintings/Duccio_Cathedral.webp b/data/images/paintings/Duccio_Cathedral.webp new file mode 100644 index 0000000..b2e69ea Binary files /dev/null and b/data/images/paintings/Duccio_Cathedral.webp differ diff --git a/data/images/paintings/Duccio_Christ_Accused_by_the_Pharisees.jpg b/data/images/paintings/Duccio_Christ_Accused_by_the_Pharisees.jpg index cf0f3e0..0746430 100644 Binary files a/data/images/paintings/Duccio_Christ_Accused_by_the_Pharisees.jpg and b/data/images/paintings/Duccio_Christ_Accused_by_the_Pharisees.jpg differ diff --git a/data/images/paintings/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee.jpg b/data/images/paintings/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee.jpg index c93c8e6..fbdb0a2 100644 Binary files a/data/images/paintings/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee.jpg and b/data/images/paintings/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee.jpg differ diff --git a/data/images/paintings/Duccio_Christ_Before_Annas_top_Peter_Denying_Jesus_bottom.jpg b/data/images/paintings/Duccio_Christ_Before_Annas_top_Peter_Denying_Jesus_bottom.jpg new file mode 100644 index 0000000..21f14c5 Binary files /dev/null and b/data/images/paintings/Duccio_Christ_Before_Annas_top_Peter_Denying_Jesus_bottom.jpg differ diff --git a/data/images/paintings/Duccio_Christ_Washes_the_Apostles_Feet_top_Last_Supper_bottom.jpg b/data/images/paintings/Duccio_Christ_Washes_the_Apostles_Feet_top_Last_Supper_bottom.jpg new file mode 100644 index 0000000..c0c95d4 Binary files /dev/null and b/data/images/paintings/Duccio_Christ_Washes_the_Apostles_Feet_top_Last_Supper_bottom.jpg differ diff --git a/data/images/paintings/Duccio_Christ_and_the_Samaritan_Woman.jpg b/data/images/paintings/Duccio_Christ_and_the_Samaritan_Woman.jpg index c5dcf59..767aef6 100644 Binary files a/data/images/paintings/Duccio_Christ_and_the_Samaritan_Woman.jpg and b/data/images/paintings/Duccio_Christ_and_the_Samaritan_Woman.jpg differ diff --git a/data/images/paintings/Duccio_Christ_appearing_at_the_Apostles_table.jpg b/data/images/paintings/Duccio_Christ_appearing_at_the_Apostles_table.jpg new file mode 100644 index 0000000..242e402 Binary files /dev/null and b/data/images/paintings/Duccio_Christ_appearing_at_the_Apostles_table.jpg differ diff --git a/data/images/paintings/Duccio_Christ_before_Pilate_Again_top_Christ_Before_Herod_bottom.jpg b/data/images/paintings/Duccio_Christ_before_Pilate_Again_top_Christ_Before_Herod_bottom.jpg new file mode 100644 index 0000000..907dea8 Binary files /dev/null and b/data/images/paintings/Duccio_Christ_before_Pilate_Again_top_Christ_Before_Herod_bottom.jpg differ diff --git a/data/images/paintings/Duccio_Crucifix_workshop_Duccio.jpg b/data/images/paintings/Duccio_Crucifix_workshop_Duccio.jpg index c93c8e6..9624a60 100644 Binary files a/data/images/paintings/Duccio_Crucifix_workshop_Duccio.jpg and b/data/images/paintings/Duccio_Crucifix_workshop_Duccio.jpg differ diff --git a/data/images/paintings/Duccio_Disputation_with_the_Doctors.jpg b/data/images/paintings/Duccio_Disputation_with_the_Doctors.jpg index 5443eff..fda6692 100644 Binary files a/data/images/paintings/Duccio_Disputation_with_the_Doctors.jpg and b/data/images/paintings/Duccio_Disputation_with_the_Doctors.jpg differ diff --git a/data/images/paintings/Duccio_Dormition_of_the_Virgin.jpg b/data/images/paintings/Duccio_Dormition_of_the_Virgin.jpg index c93c8e6..19e44ba 100644 Binary files a/data/images/paintings/Duccio_Dormition_of_the_Virgin.jpg and b/data/images/paintings/Duccio_Dormition_of_the_Virgin.jpg differ diff --git a/data/images/paintings/Duccio_Entry_into_Jerusalem.jpg b/data/images/paintings/Duccio_Entry_into_Jerusalem.jpg index 05131d5..e2f750e 100644 Binary files a/data/images/paintings/Duccio_Entry_into_Jerusalem.jpg and b/data/images/paintings/Duccio_Entry_into_Jerusalem.jpg differ diff --git a/data/images/paintings/Duccio_Flagellation_top_Christ_Crowned_with_Thorns_bottom.jpg b/data/images/paintings/Duccio_Flagellation_top_Christ_Crowned_with_Thorns_bottom.jpg new file mode 100644 index 0000000..b92ac5e Binary files /dev/null and b/data/images/paintings/Duccio_Flagellation_top_Christ_Crowned_with_Thorns_bottom.jpg differ diff --git a/data/images/paintings/Duccio_Gualino_Madonna.jpg b/data/images/paintings/Duccio_Gualino_Madonna.jpg index a99eb3c..ce04758 100644 Binary files a/data/images/paintings/Duccio_Gualino_Madonna.jpg and b/data/images/paintings/Duccio_Gualino_Madonna.jpg differ diff --git a/data/images/paintings/Duccio_Madonna_and_Child_with_Saints.jpg b/data/images/paintings/Duccio_Madonna_and_Child_with_Saints.jpg index c93c8e6..abf7506 100644 Binary files a/data/images/paintings/Duccio_Madonna_and_Child_with_Saints.jpg and b/data/images/paintings/Duccio_Madonna_and_Child_with_Saints.jpg differ diff --git a/data/images/paintings/Duccio_Madonna_and_Child_with_Saints_Polyptych.jpg b/data/images/paintings/Duccio_Madonna_and_Child_with_Saints_Polyptych.jpg index 706d661..d58b7a2 100644 Binary files a/data/images/paintings/Duccio_Madonna_and_Child_with_Saints_Polyptych.jpg and b/data/images/paintings/Duccio_Madonna_and_Child_with_Saints_Polyptych.jpg differ diff --git a/data/images/paintings/Duccio_Madonna_di_Buonconvento.jpg b/data/images/paintings/Duccio_Madonna_di_Buonconvento.jpg index c93c8e6..0499b0f 100644 Binary files a/data/images/paintings/Duccio_Madonna_di_Buonconvento.jpg and b/data/images/paintings/Duccio_Madonna_di_Buonconvento.jpg differ diff --git a/data/images/paintings/Duccio_Madonna_di_Crevole.webp b/data/images/paintings/Duccio_Madonna_di_Crevole.webp new file mode 100644 index 0000000..6450c5d Binary files /dev/null and b/data/images/paintings/Duccio_Madonna_di_Crevole.webp differ diff --git a/data/images/paintings/Duccio_Madonna_with_Child_and_six_Angels.jpg b/data/images/paintings/Duccio_Madonna_with_Child_and_six_Angels.jpg index c93c8e6..6e8e3da 100644 Binary files a/data/images/paintings/Duccio_Madonna_with_Child_and_six_Angels.jpg and b/data/images/paintings/Duccio_Madonna_with_Child_and_six_Angels.jpg differ diff --git a/data/images/paintings/Duccio_Maest_-_Backside.jpg b/data/images/paintings/Duccio_Maest_-_Backside.jpg new file mode 100644 index 0000000..fb9b365 Binary files /dev/null and b/data/images/paintings/Duccio_Maest_-_Backside.jpg differ diff --git a/data/images/paintings/Duccio_Maest_.jpg b/data/images/paintings/Duccio_Maest_.jpg index b483a27..655c62e 100644 Binary files a/data/images/paintings/Duccio_Maest_.jpg and b/data/images/paintings/Duccio_Maest_.jpg differ diff --git a/data/images/paintings/Duccio_Maria_met_kind.jpg b/data/images/paintings/Duccio_Maria_met_kind.jpg index c93c8e6..eef18cc 100644 Binary files a/data/images/paintings/Duccio_Maria_met_kind.jpg and b/data/images/paintings/Duccio_Maria_met_kind.jpg differ diff --git a/data/images/paintings/Duccio_Massa_Marittima_Maest.jpg b/data/images/paintings/Duccio_Massa_Marittima_Maest.jpg new file mode 100644 index 0000000..b0203b3 Binary files /dev/null and b/data/images/paintings/Duccio_Massa_Marittima_Maest.jpg differ diff --git a/data/images/paintings/Duccio_Original_rose_window.webp b/data/images/paintings/Duccio_Original_rose_window.webp new file mode 100644 index 0000000..0346755 Binary files /dev/null and b/data/images/paintings/Duccio_Original_rose_window.webp differ diff --git a/data/images/paintings/Duccio_Pact_of_Judas_top_Christ_Taking_Leave_of_the_Apostles_bottom.jpg b/data/images/paintings/Duccio_Pact_of_Judas_top_Christ_Taking_Leave_of_the_Apostles_bottom.jpg new file mode 100644 index 0000000..5021aa4 Binary files /dev/null and b/data/images/paintings/Duccio_Pact_of_Judas_top_Christ_Taking_Leave_of_the_Apostles_bottom.jpg differ diff --git a/data/images/paintings/Duccio_Painted_crucifix.jpg b/data/images/paintings/Duccio_Painted_crucifix.jpg index c93c8e6..9624a60 100644 Binary files a/data/images/paintings/Duccio_Painted_crucifix.jpg and b/data/images/paintings/Duccio_Painted_crucifix.jpg differ diff --git a/data/images/paintings/Duccio_Pentecost.jpg b/data/images/paintings/Duccio_Pentecost.jpg index 23dfa4c..4a1d8af 100644 Binary files a/data/images/paintings/Duccio_Pentecost.jpg and b/data/images/paintings/Duccio_Pentecost.jpg differ diff --git a/data/images/paintings/Duccio_Prophet_Hosea.jpg b/data/images/paintings/Duccio_Prophet_Hosea.jpg index 6389ef6..4192202 100644 Binary files a/data/images/paintings/Duccio_Prophet_Hosea.jpg and b/data/images/paintings/Duccio_Prophet_Hosea.jpg differ diff --git a/data/images/paintings/Duccio_Road_to_Emmaus_top_Noli_me_tangere_bottom.jpg b/data/images/paintings/Duccio_Road_to_Emmaus_top_Noli_me_tangere_bottom.jpg new file mode 100644 index 0000000..e29d66c Binary files /dev/null and b/data/images/paintings/Duccio_Road_to_Emmaus_top_Noli_me_tangere_bottom.jpg differ diff --git a/data/images/paintings/Duccio_Rucellai_Madonna.jpg b/data/images/paintings/Duccio_Rucellai_Madonna.jpg index 6b45d15..1652dc1 100644 Binary files a/data/images/paintings/Duccio_Rucellai_Madonna.jpg and b/data/images/paintings/Duccio_Rucellai_Madonna.jpg differ diff --git a/data/images/paintings/Duccio_The_Annunciation.jpg b/data/images/paintings/Duccio_The_Annunciation.jpg index c93c8e6..6a2d122 100644 Binary files a/data/images/paintings/Duccio_The_Annunciation.jpg and b/data/images/paintings/Duccio_The_Annunciation.jpg differ diff --git a/data/images/paintings/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew.jpg b/data/images/paintings/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew.jpg index c93c8e6..b27d111 100644 Binary files a/data/images/paintings/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew.jpg and b/data/images/paintings/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew.jpg differ diff --git a/data/images/paintings/Duccio_The_Healing_of_the_Man_Born_Blind.jpg b/data/images/paintings/Duccio_The_Healing_of_the_Man_Born_Blind.jpg index 9ae4338..b4f3b1d 100644 Binary files a/data/images/paintings/Duccio_The_Healing_of_the_Man_Born_Blind.jpg and b/data/images/paintings/Duccio_The_Healing_of_the_Man_Born_Blind.jpg differ diff --git a/data/images/paintings/Duccio_The_Madonna_of_the_Franciscans.jpg b/data/images/paintings/Duccio_The_Madonna_of_the_Franciscans.jpg index 94f9f45..1f21c95 100644 Binary files a/data/images/paintings/Duccio_The_Madonna_of_the_Franciscans.jpg and b/data/images/paintings/Duccio_The_Madonna_of_the_Franciscans.jpg differ diff --git a/data/images/paintings/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel.jpg b/data/images/paintings/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel.jpg index b5c85e3..e4ad0c0 100644 Binary files a/data/images/paintings/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel.jpg and b/data/images/paintings/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel.jpg differ diff --git a/data/images/paintings/Duccio_The_Raising_of_Lazarus.jpg b/data/images/paintings/Duccio_The_Raising_of_Lazarus.jpg index 139c5a8..097e501 100644 Binary files a/data/images/paintings/Duccio_The_Raising_of_Lazarus.jpg and b/data/images/paintings/Duccio_The_Raising_of_Lazarus.jpg differ diff --git a/data/images/paintings/Duccio_The_Temptation_of_Christ_on_the_Mountain.jpg b/data/images/paintings/Duccio_The_Temptation_of_Christ_on_the_Mountain.jpg index fa0bc3c..77aa98f 100644 Binary files a/data/images/paintings/Duccio_The_Temptation_of_Christ_on_the_Mountain.jpg and b/data/images/paintings/Duccio_The_Temptation_of_Christ_on_the_Mountain.jpg differ diff --git a/data/images/paintings/Duccio_The_Transfiguration.jpg b/data/images/paintings/Duccio_The_Transfiguration.jpg index c93c8e6..442308b 100644 Binary files a/data/images/paintings/Duccio_The_Transfiguration.jpg and b/data/images/paintings/Duccio_The_Transfiguration.jpg differ diff --git a/data/images/paintings/Duccio_The_Wedding_at_Cana.jpg b/data/images/paintings/Duccio_The_Wedding_at_Cana.jpg index 6a76de6..2f153a5 100644 Binary files a/data/images/paintings/Duccio_The_Wedding_at_Cana.jpg and b/data/images/paintings/Duccio_The_Wedding_at_Cana.jpg differ diff --git a/data/images/paintings/Duccio_Three_Marys_at_the_Tomb_top_Descent_into_Hell_bottom.jpg b/data/images/paintings/Duccio_Three_Marys_at_the_Tomb_top_Descent_into_Hell_bottom.jpg new file mode 100644 index 0000000..3cb75fd Binary files /dev/null and b/data/images/paintings/Duccio_Three_Marys_at_the_Tomb_top_Descent_into_Hell_bottom.jpg differ diff --git a/data/images/paintings/Duccio_Triptych_Crucifixion_and_other_Scenes.jpg b/data/images/paintings/Duccio_Triptych_Crucifixion_and_other_Scenes.jpg new file mode 100644 index 0000000..8d98ae8 Binary files /dev/null and b/data/images/paintings/Duccio_Triptych_Crucifixion_and_other_Scenes.jpg differ diff --git a/data/images/paintings/Duccio_Triptych_the_Crucifixion_the_Redeemer_with_Angels_Saint_Nicholas_Saint_Gregory.png b/data/images/paintings/Duccio_Triptych_the_Crucifixion_the_Redeemer_with_Angels_Saint_Nicholas_Saint_Gregory.png new file mode 100644 index 0000000..4fc4d32 Binary files /dev/null and b/data/images/paintings/Duccio_Triptych_the_Crucifixion_the_Redeemer_with_Angels_Saint_Nicholas_Saint_Gregory.png differ diff --git a/data/images/paintings/Duccio_Virgin_and_Child_with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis.jpg b/data/images/paintings/Duccio_Virgin_and_Child_with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis.jpg new file mode 100644 index 0000000..1bac0ef Binary files /dev/null and b/data/images/paintings/Duccio_Virgin_and_Child_with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Angel_thumb.jpg b/data/images/paintings/thumbs/Duccio_Angel_thumb.jpg index fbb7f66..415b22c 100644 Binary files a/data/images/paintings/thumbs/Duccio_Angel_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Angel_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Apostles_Philip_and_James_the_Elder_thumb.jpg b/data/images/paintings/thumbs/Duccio_Apostles_Philip_and_James_the_Elder_thumb.jpg index 0a6b36f..1533ed7 100644 Binary files a/data/images/paintings/thumbs/Duccio_Apostles_Philip_and_James_the_Elder_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Apostles_Philip_and_James_the_Elder_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Cathedral_thumb.jpg b/data/images/paintings/thumbs/Duccio_Cathedral_thumb.jpg index 6e4f4a6..3c4e418 100644 Binary files a/data/images/paintings/thumbs/Duccio_Cathedral_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Cathedral_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Children_of_Bethlehem_killed_by_Herod_s_orders__Massacre_of_the_Innocents__thumb.jpg b/data/images/paintings/thumbs/Duccio_Children_of_Bethlehem_killed_by_Herod_s_orders__Massacre_of_the_Innocents__thumb.jpg new file mode 100644 index 0000000..205448d Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Children_of_Bethlehem_killed_by_Herod_s_orders__Massacre_of_the_Innocents__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_Accused_by_the_Pharisees_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_Accused_by_the_Pharisees_thumb.jpg index 1dbad94..b8e4e5d 100644 Binary files a/data/images/paintings/thumbs/Duccio_Christ_Accused_by_the_Pharisees_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Christ_Accused_by_the_Pharisees_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee_thumb.jpg index 0b8a41f..cd8f803 100644 Binary files a/data/images/paintings/thumbs/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Christ_Appears_to_the_Disciples_on_the_Mountain_in_Galilee_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_Before_Annas__top___Peter_Denying_Jesus__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_Before_Annas__top___Peter_Denying_Jesus__bottom__thumb.jpg new file mode 100644 index 0000000..6e4f4a6 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Christ_Before_Annas__top___Peter_Denying_Jesus__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_Before_Annas_top_Peter_Denying_Jesus_bottom_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_Before_Annas_top_Peter_Denying_Jesus_bottom_thumb.jpg index 6e4f4a6..d9718d6 100644 Binary files a/data/images/paintings/thumbs/Duccio_Christ_Before_Annas_top_Peter_Denying_Jesus_bottom_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Christ_Before_Annas_top_Peter_Denying_Jesus_bottom_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_Washes_the_Apostles_Feet_top_Last_Supper_bottom_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_Washes_the_Apostles_Feet_top_Last_Supper_bottom_thumb.jpg index e2fff98..2f85708 100644 Binary files a/data/images/paintings/thumbs/Duccio_Christ_Washes_the_Apostles_Feet_top_Last_Supper_bottom_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Christ_Washes_the_Apostles_Feet_top_Last_Supper_bottom_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_Washes_the_Apostles__Feet__top___Last_Supper__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_Washes_the_Apostles__Feet__top___Last_Supper__bottom__thumb.jpg new file mode 100644 index 0000000..e2fff98 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Christ_Washes_the_Apostles__Feet__top___Last_Supper__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_and_the_Samaritan_Woman_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_and_the_Samaritan_Woman_thumb.jpg index 6e7e25f..30b3353 100644 Binary files a/data/images/paintings/thumbs/Duccio_Christ_and_the_Samaritan_Woman_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Christ_and_the_Samaritan_Woman_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_appearing_at_the_Apostles__table_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_appearing_at_the_Apostles__table_thumb.jpg new file mode 100644 index 0000000..0b8a41f Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Christ_appearing_at_the_Apostles__table_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_appearing_at_the_Apostles_table_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_appearing_at_the_Apostles_table_thumb.jpg index 0b8a41f..5832471 100644 Binary files a/data/images/paintings/thumbs/Duccio_Christ_appearing_at_the_Apostles_table_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Christ_appearing_at_the_Apostles_table_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_before_Pilate_Again__top___Christ_Before_Herod__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_before_Pilate_Again__top___Christ_Before_Herod__bottom__thumb.jpg new file mode 100644 index 0000000..d82320d Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Christ_before_Pilate_Again__top___Christ_Before_Herod__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Christ_before_Pilate_Again_top_Christ_Before_Herod_bottom_thumb.jpg b/data/images/paintings/thumbs/Duccio_Christ_before_Pilate_Again_top_Christ_Before_Herod_bottom_thumb.jpg index d82320d..2b4b210 100644 Binary files a/data/images/paintings/thumbs/Duccio_Christ_before_Pilate_Again_top_Christ_Before_Herod_bottom_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Christ_before_Pilate_Again_top_Christ_Before_Herod_bottom_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Crucifix_workshop_Duccio_thumb.jpg b/data/images/paintings/thumbs/Duccio_Crucifix_workshop_Duccio_thumb.jpg index 0b8a41f..34ba5bb 100644 Binary files a/data/images/paintings/thumbs/Duccio_Crucifix_workshop_Duccio_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Crucifix_workshop_Duccio_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Disputation_with_the_Doctors_thumb.jpg b/data/images/paintings/thumbs/Duccio_Disputation_with_the_Doctors_thumb.jpg index 32b7ab6..6433c37 100644 Binary files a/data/images/paintings/thumbs/Duccio_Disputation_with_the_Doctors_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Disputation_with_the_Doctors_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Dormition_of_the_Virgin_thumb.jpg b/data/images/paintings/thumbs/Duccio_Dormition_of_the_Virgin_thumb.jpg index 0b8a41f..af45e55 100644 Binary files a/data/images/paintings/thumbs/Duccio_Dormition_of_the_Virgin_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Dormition_of_the_Virgin_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Entry_into_Jerusalem_thumb.jpg b/data/images/paintings/thumbs/Duccio_Entry_into_Jerusalem_thumb.jpg index 973167f..9c704e4 100644 Binary files a/data/images/paintings/thumbs/Duccio_Entry_into_Jerusalem_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Entry_into_Jerusalem_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Flagellation__top___Christ_Crowned_with_Thorns__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Flagellation__top___Christ_Crowned_with_Thorns__bottom__thumb.jpg new file mode 100644 index 0000000..662bad5 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Flagellation__top___Christ_Crowned_with_Thorns__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Flagellation_top_Christ_Crowned_with_Thorns_bottom_thumb.jpg b/data/images/paintings/thumbs/Duccio_Flagellation_top_Christ_Crowned_with_Thorns_bottom_thumb.jpg index 662bad5..bd0c2e9 100644 Binary files a/data/images/paintings/thumbs/Duccio_Flagellation_top_Christ_Crowned_with_Thorns_bottom_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Flagellation_top_Christ_Crowned_with_Thorns_bottom_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Gualino_Madonna_thumb.jpg b/data/images/paintings/thumbs/Duccio_Gualino_Madonna_thumb.jpg index c938e22..1762e43 100644 Binary files a/data/images/paintings/thumbs/Duccio_Gualino_Madonna_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Gualino_Madonna_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_Polyptych_thumb.jpg b/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_Polyptych_thumb.jpg index 6f371ea..cf02793 100644 Binary files a/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_Polyptych_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_Polyptych_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_thumb.jpg b/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_thumb.jpg index 0b8a41f..58b3baa 100644 Binary files a/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Madonna_and_Child_with_Saints_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Madonna_di_Buonconvento_thumb.jpg b/data/images/paintings/thumbs/Duccio_Madonna_di_Buonconvento_thumb.jpg index 0b8a41f..2b2caa5 100644 Binary files a/data/images/paintings/thumbs/Duccio_Madonna_di_Buonconvento_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Madonna_di_Buonconvento_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Madonna_di_Crevole_thumb.jpg b/data/images/paintings/thumbs/Duccio_Madonna_di_Crevole_thumb.jpg index 9db8ee4..a908778 100644 Binary files a/data/images/paintings/thumbs/Duccio_Madonna_di_Crevole_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Madonna_di_Crevole_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Madonna_with_Child_and_six_Angels_thumb.jpg b/data/images/paintings/thumbs/Duccio_Madonna_with_Child_and_six_Angels_thumb.jpg index 0b8a41f..d6a8fc9 100644 Binary files a/data/images/paintings/thumbs/Duccio_Madonna_with_Child_and_six_Angels_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Madonna_with_Child_and_six_Angels_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Maest_-_Backside_thumb.jpg b/data/images/paintings/thumbs/Duccio_Maest_-_Backside_thumb.jpg index 32b7ab6..fb67121 100644 Binary files a/data/images/paintings/thumbs/Duccio_Maest_-_Backside_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Maest_-_Backside_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Maest__-_Backside_thumb.jpg b/data/images/paintings/thumbs/Duccio_Maest__-_Backside_thumb.jpg new file mode 100644 index 0000000..32b7ab6 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Maest__-_Backside_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Maest__thumb.jpg b/data/images/paintings/thumbs/Duccio_Maest__thumb.jpg index 3159c93..fe49009 100644 Binary files a/data/images/paintings/thumbs/Duccio_Maest__thumb.jpg and b/data/images/paintings/thumbs/Duccio_Maest__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Maria_met_kind_thumb.jpg b/data/images/paintings/thumbs/Duccio_Maria_met_kind_thumb.jpg index 0b8a41f..b71bda1 100644 Binary files a/data/images/paintings/thumbs/Duccio_Maria_met_kind_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Maria_met_kind_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Massa_Marittima_Maest__thumb.jpg b/data/images/paintings/thumbs/Duccio_Massa_Marittima_Maest__thumb.jpg new file mode 100644 index 0000000..0805fdd Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Massa_Marittima_Maest__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Massa_Marittima_Maest_thumb.jpg b/data/images/paintings/thumbs/Duccio_Massa_Marittima_Maest_thumb.jpg index 0805fdd..e5eb9c7 100644 Binary files a/data/images/paintings/thumbs/Duccio_Massa_Marittima_Maest_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Massa_Marittima_Maest_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Original_rose_window_thumb.jpg b/data/images/paintings/thumbs/Duccio_Original_rose_window_thumb.jpg new file mode 100644 index 0000000..039013d Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Original_rose_window_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Pact_of_Judas__top___Christ_Taking_Leave_of_the_Apostles__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Pact_of_Judas__top___Christ_Taking_Leave_of_the_Apostles__bottom__thumb.jpg new file mode 100644 index 0000000..71dd748 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Pact_of_Judas__top___Christ_Taking_Leave_of_the_Apostles__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Pact_of_Judas_top_Christ_Taking_Leave_of_the_Apostles_bottom_thumb.jpg b/data/images/paintings/thumbs/Duccio_Pact_of_Judas_top_Christ_Taking_Leave_of_the_Apostles_bottom_thumb.jpg index 71dd748..d2eec86 100644 Binary files a/data/images/paintings/thumbs/Duccio_Pact_of_Judas_top_Christ_Taking_Leave_of_the_Apostles_bottom_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Pact_of_Judas_top_Christ_Taking_Leave_of_the_Apostles_bottom_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Painted_crucifix_thumb.jpg b/data/images/paintings/thumbs/Duccio_Painted_crucifix_thumb.jpg index 0b8a41f..34ba5bb 100644 Binary files a/data/images/paintings/thumbs/Duccio_Painted_crucifix_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Painted_crucifix_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Pentecost_thumb.jpg b/data/images/paintings/thumbs/Duccio_Pentecost_thumb.jpg index 6e4f4a6..f60c966 100644 Binary files a/data/images/paintings/thumbs/Duccio_Pentecost_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Pentecost_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Prophet_Hosea_thumb.jpg b/data/images/paintings/thumbs/Duccio_Prophet_Hosea_thumb.jpg index d231900..89db334 100644 Binary files a/data/images/paintings/thumbs/Duccio_Prophet_Hosea_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Prophet_Hosea_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Road_to_Calvalry__top___Pilate_Washes_His_Hands__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Road_to_Calvalry__top___Pilate_Washes_His_Hands__bottom__thumb.jpg new file mode 100644 index 0000000..5a5e461 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Road_to_Calvalry__top___Pilate_Washes_His_Hands__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Road_to_Emmaus__top___Noli_me_tangere__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Road_to_Emmaus__top___Noli_me_tangere__bottom__thumb.jpg new file mode 100644 index 0000000..dafbcae Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Road_to_Emmaus__top___Noli_me_tangere__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Road_to_Emmaus_top_Noli_me_tangere_bottom_thumb.jpg b/data/images/paintings/thumbs/Duccio_Road_to_Emmaus_top_Noli_me_tangere_bottom_thumb.jpg index dafbcae..9c8c139 100644 Binary files a/data/images/paintings/thumbs/Duccio_Road_to_Emmaus_top_Noli_me_tangere_bottom_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Road_to_Emmaus_top_Noli_me_tangere_bottom_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Rucellai_Madonna_thumb.jpg b/data/images/paintings/thumbs/Duccio_Rucellai_Madonna_thumb.jpg index 633f852..b1d4c5b 100644 Binary files a/data/images/paintings/thumbs/Duccio_Rucellai_Madonna_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Rucellai_Madonna_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Annunciation_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Annunciation_thumb.jpg index 0b8a41f..b05a842 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Annunciation_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Annunciation_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew_thumb.jpg index 0b8a41f..aef2bd2 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Calling_of_the_Apostles_Peter_and_Andrew_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Healing_of_the_Man_Born_Blind_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Healing_of_the_Man_Born_Blind_thumb.jpg index 84e7505..b0a0635 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Healing_of_the_Man_Born_Blind_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Healing_of_the_Man_Born_Blind_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Madonna_of_the_Franciscans_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Madonna_of_the_Franciscans_thumb.jpg index 01d94d4..95f1698 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Madonna_of_the_Franciscans_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Madonna_of_the_Franciscans_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel_thumb.jpg index 2f6e37c..28c837f 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Nativity_with_the_Prophets_Isaiah_and_Ezekiel_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Raising_of_Lazarus_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Raising_of_Lazarus_thumb.jpg index 2132e89..08cae7c 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Raising_of_Lazarus_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Raising_of_Lazarus_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Temptation_of_Christ_on_the_Mountain_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Temptation_of_Christ_on_the_Mountain_thumb.jpg index 986279d..a933079 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Temptation_of_Christ_on_the_Mountain_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Temptation_of_Christ_on_the_Mountain_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Transfiguration_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Transfiguration_thumb.jpg index 0b8a41f..5e1772c 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Transfiguration_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Transfiguration_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_The_Wedding_at_Cana_thumb.jpg b/data/images/paintings/thumbs/Duccio_The_Wedding_at_Cana_thumb.jpg index 8ee3253..7f8f0f3 100644 Binary files a/data/images/paintings/thumbs/Duccio_The_Wedding_at_Cana_thumb.jpg and b/data/images/paintings/thumbs/Duccio_The_Wedding_at_Cana_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Three_Marys_at_the_Tomb__top___Descent_into_Hell__bottom__thumb.jpg b/data/images/paintings/thumbs/Duccio_Three_Marys_at_the_Tomb__top___Descent_into_Hell__bottom__thumb.jpg new file mode 100644 index 0000000..96f7817 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Three_Marys_at_the_Tomb__top___Descent_into_Hell__bottom__thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Three_Marys_at_the_Tomb_top_Descent_into_Hell_bottom_thumb.jpg b/data/images/paintings/thumbs/Duccio_Three_Marys_at_the_Tomb_top_Descent_into_Hell_bottom_thumb.jpg index 96f7817..da19a6f 100644 Binary files a/data/images/paintings/thumbs/Duccio_Three_Marys_at_the_Tomb_top_Descent_into_Hell_bottom_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Three_Marys_at_the_Tomb_top_Descent_into_Hell_bottom_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Triptych_Crucifixion_and_other_Scenes_thumb.jpg b/data/images/paintings/thumbs/Duccio_Triptych_Crucifixion_and_other_Scenes_thumb.jpg index 0b8a41f..b5bdeff 100644 Binary files a/data/images/paintings/thumbs/Duccio_Triptych_Crucifixion_and_other_Scenes_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Triptych_Crucifixion_and_other_Scenes_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Triptych__Crucifixion_and_other_Scenes_thumb.jpg b/data/images/paintings/thumbs/Duccio_Triptych__Crucifixion_and_other_Scenes_thumb.jpg new file mode 100644 index 0000000..0b8a41f Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Triptych__Crucifixion_and_other_Scenes_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Triptych__the_Crucifixion__the_Redeemer_with_Angels__Saint_Nicholas__Saint_Gregory_thumb.jpg b/data/images/paintings/thumbs/Duccio_Triptych__the_Crucifixion__the_Redeemer_with_Angels__Saint_Nicholas__Saint_Gregory_thumb.jpg new file mode 100644 index 0000000..0b8a41f Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Triptych__the_Crucifixion__the_Redeemer_with_Angels__Saint_Nicholas__Saint_Gregory_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Triptych_the_Crucifixion_the_Redeemer_with_Angels_Saint_Nicholas_Saint_Gregory_thumb.jpg b/data/images/paintings/thumbs/Duccio_Triptych_the_Crucifixion_the_Redeemer_with_Angels_Saint_Nicholas_Saint_Gregory_thumb.jpg index 0b8a41f..b9f3c39 100644 Binary files a/data/images/paintings/thumbs/Duccio_Triptych_the_Crucifixion_the_Redeemer_with_Angels_Saint_Nicholas_Saint_Gregory_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Triptych_the_Crucifixion_the_Redeemer_with_Angels_Saint_Nicholas_Saint_Gregory_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Virgin_and_Child__with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis_thumb.jpg b/data/images/paintings/thumbs/Duccio_Virgin_and_Child__with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis_thumb.jpg new file mode 100644 index 0000000..4e9a236 Binary files /dev/null and b/data/images/paintings/thumbs/Duccio_Virgin_and_Child__with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis_thumb.jpg differ diff --git a/data/images/paintings/thumbs/Duccio_Virgin_and_Child_with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis_thumb.jpg b/data/images/paintings/thumbs/Duccio_Virgin_and_Child_with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis_thumb.jpg index 4e9a236..1c88546 100644 Binary files a/data/images/paintings/thumbs/Duccio_Virgin_and_Child_with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis_thumb.jpg and b/data/images/paintings/thumbs/Duccio_Virgin_and_Child_with_Scenes_from_the_Lives_of_Christ_and_Saint_Francis_thumb.jpg differ diff --git a/db/migrate-sync-timestamps.sql b/db/migrate-sync-timestamps.sql index e754886..ce21271 100644 --- a/db/migrate-sync-timestamps.sql +++ b/db/migrate-sync-timestamps.sql @@ -21,9 +21,15 @@ BEGIN 'paintings', 'painting_influences', 'painting_influence_sources', - 'painting_annotations' + 'painting_annotations', + 'tour_stops' ] LOOP + -- Skip tables not created yet (e.g. tour_stops before migrate-tours.sql). + IF to_regclass(format('public.%I', tbl)) IS NULL THEN + CONTINUE; + END IF; + EXECUTE format( 'ALTER TABLE %I ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now()', tbl diff --git a/db/migrate-tours.sql b/db/migrate-tours.sql index 759064e..e19c914 100644 --- a/db/migrate-tours.sql +++ b/db/migrate-tours.sql @@ -20,9 +20,13 @@ CREATE TABLE IF NOT EXISTS tour_stops ( painting_id INTEGER NOT NULL REFERENCES paintings(id) ON DELETE CASCADE, sort_order INTEGER NOT NULL DEFAULT 0, body TEXT NOT NULL DEFAULT '', + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (tour_id, painting_id) ); +-- Existing DBs created before updated_at: migrate-sync-timestamps.sql also adds it. +ALTER TABLE tour_stops ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now(); + CREATE INDEX IF NOT EXISTS tour_stops_tour_idx ON tour_stops (tour_id, sort_order); CREATE INDEX IF NOT EXISTS tour_stops_painting_idx ON tour_stops (painting_id); @@ -38,3 +42,8 @@ DROP TRIGGER IF EXISTS tours_updated_at ON tours; CREATE TRIGGER tours_updated_at BEFORE UPDATE ON tours FOR EACH ROW EXECUTE PROCEDURE tours_touch_updated_at(); + +DROP TRIGGER IF EXISTS tour_stops_updated_at ON tour_stops; +CREATE TRIGGER tour_stops_updated_at + BEFORE UPDATE ON tour_stops + FOR EACH ROW EXECUTE PROCEDURE tours_touch_updated_at(); diff --git a/infra/scripts/harmonize.ps1 b/infra/scripts/harmonize.ps1 index ea9dafe..7b2671c 100644 --- a/infra/scripts/harmonize.ps1 +++ b/infra/scripts/harmonize.ps1 @@ -104,7 +104,9 @@ function Get-HarmonizeSteps { if ($null -ne $harmonize.schemaChanged) { $schemaChanged = [bool]$harmonize.schemaChanged } elseif ($null -ne $Raw.schemaChanged) { $schemaChanged = [bool]$Raw.schemaChanged } - $steps = @{ + # Only these keys are orchestrated. Ignore release-profile keys (restoreProd, etc.) + # when falling back to devtoprod.config.json. + $steps = [ordered]@{ backupDev = $true backupProd = $true schema = $schemaChanged @@ -113,10 +115,21 @@ function Get-HarmonizeSteps { verify = $false } - $cfgSteps = $harmonize.steps + $allowed = @($steps.Keys) + $cfgSteps = $null + if ($null -ne $Raw.harmonize -and $null -ne $Raw.harmonize.steps) { + $cfgSteps = $Raw.harmonize.steps + } + elseif ($null -eq $Raw.harmonize -and $null -eq $Raw.profile -and $null -ne $Raw.steps) { + # Dedicated harmonize.config.json (no release "profile") + $cfgSteps = $Raw.steps + } + if ($null -ne $cfgSteps) { - foreach ($key in $cfgSteps.PSObject.Properties.Name) { - $steps[$key] = [bool]$cfgSteps.$key + foreach ($key in $allowed) { + if ($null -ne $cfgSteps.$key) { + $steps[$key] = [bool]$cfgSteps.$key + } } } diff --git a/scripts/db-env.js b/scripts/db-env.js index c7e93d6..765ec8e 100644 --- a/scripts/db-env.js +++ b/scripts/db-env.js @@ -9,8 +9,9 @@ const DEV_DB_NAME = 'gallery_dev'; const PROD_DB_NAME = 'gallery_prod'; const LEGACY_DB_NAMES = ['Gallery', 'gallery']; -function loadEnvFile(filePath) { - if (!fs.existsSync(filePath)) return; +function parseEnvFile(filePath) { + const out = {}; + if (!fs.existsSync(filePath)) return out; const content = fs.readFileSync(filePath, 'utf8'); for (const line of content.split(/\r?\n/)) { const trimmed = line.trim(); @@ -25,22 +26,35 @@ function loadEnvFile(filePath) { ) { value = value.slice(1, -1); } + out[key] = value; + } + return out; +} + +/** Load into process.env without overwriting keys already set. */ +function loadEnvFile(filePath) { + const parsed = parseEnvFile(filePath); + for (const [key, value] of Object.entries(parsed)) { if (process.env[key] === undefined) process.env[key] = value; } } -function pgConfigFromEnv() { - const host = process.env.DB_HOST || '192.168.10.122'; - const port = Number(process.env.DB_PORT || 5432); - const user = process.env.DB_USER; - const password = process.env.DB_PASSWORD; - const database = process.env.DB_NAME; +function pgConfigFromMap(env) { + const host = env.DB_HOST || '192.168.10.122'; + const port = Number(env.DB_PORT || 5432); + const user = env.DB_USER; + const password = env.DB_PASSWORD; + const database = env.DB_NAME; if (!user || !password || !database) { throw new Error('DB_USER, DB_PASSWORD, and DB_NAME are required'); } return { host, port, user, password, database }; } +function pgConfigFromEnv() { + return pgConfigFromMap(process.env); +} + function assertProdDatabase(dbName) { if (!dbName.endsWith(PROD_DB_SUFFIX) && dbName !== PROD_DB_NAME) { throw new Error( @@ -61,16 +75,22 @@ function assertDevDatabase(dbName) { return dbName; } +/** + * File values win over process.env so loading prod after dev (or vice versa) + * in the same process cannot leave the wrong DB_NAME stuck. + */ function loadDevPgConfig() { - loadEnvFile(path.join(rootDir, '.env')); - const config = pgConfigFromEnv(); + const filePath = path.join(rootDir, '.env'); + const fileEnv = parseEnvFile(filePath); + const config = pgConfigFromMap({ ...process.env, ...fileEnv }); assertDevDatabase(config.database); return config; } function loadProdPgConfig() { - loadEnvFile(path.join(rootDir, 'infra', 'docker', '.env.prod')); - const config = pgConfigFromEnv(); + const filePath = path.join(rootDir, 'infra', 'docker', '.env.prod'); + const fileEnv = parseEnvFile(filePath); + const config = pgConfigFromMap({ ...process.env, ...fileEnv }); assertProdDatabase(config.database); return config; } @@ -93,6 +113,7 @@ module.exports = { PROD_DB_NAME, LEGACY_DB_NAMES, rootDir, + parseEnvFile, loadEnvFile, pgConfigFromEnv, assertProdDatabase,