Files
picpeak/backend/migrations
Paul Nothaft 41e1de7818 fix(email): repair and seed the gallery lifecycle templates
Correction: the reported premise held for only one of the three templates,
verified by running the core migration set against an empty database.

- expiration_warning is German-is-English on every fresh install, exactly as
  reported. Repaired with migration 194's pattern verbatim.
- gallery_expired and archive_complete are NOT German-is-English -- they do
  not exist at all. Their master rows are inserted only by migrations/legacy/
  010+020, which never run on a fresh install, so 075/099/106/108 seeded zero
  translations for them (they key off a master row that is not there). A
  fresh install's email_templates holds 17 keys and neither is among them.
  The consequence is worse than a translation gap: expirationChecker's
  sendGalleryExpiredEmails and archiveService's completion mail both hit
  "Email template not found", retry three times and die silently in
  email_queue on every expiry and every archive.

So 195 also seeds those two (master row + en/de translations + category),
but only when the master row is absent -- it never overwrites. English
follows legacy 028, which emailProcessor's own comments call the shipped
copy; German follows legacy 026's wording. Both are restructured into the
plain unstyled shape the other core-seeded templates use, so wrapEmailHtml's
configurable palette governs styling rather than hard-coded hex. The
support-contact line is wrapped in {{#if support_email}} because
getSupportEmail() can return ''.

196 adds the {{#if welcome_message}} block that nl/pt/ru/fr/es/sl already
have in gallery_created but en and de lack, so the photographer's personal
note was silently dropped for those two locales even though the value is
passed at send time. safeTemplateReplace does resolve {{#if}} before variable
substitution, so this is a real conditional -- there is a test rendering the
migrated body both ways. HTML body only, matching the other locales:
emailProcessor rewrites welcome_message through formatWelcomeMessage
(escape + nl2br) once for both bodies, so the text part would print literal
<br /> and &amp;.

Both migrations keep 194's conservative condition -- rewrite only while the
German is still byte-identical to English or empty -- so admin-edited and
legacy-translated installs are untouched. Idempotent, guarded, no-op down().

Known gap, documented in 195's header: the two newly seeded templates get
en/de only. nl/pt/ru/fr/es/sl fall back to en via processTemplate's fallback
chain, which is strictly better than today's hard failure but is not real
localisation.

Refs testplan REPORT.md B1, B2.
2026-09-02 09:43:10 +02:00
..

Database Migrations

This directory contains database migrations for the PicPeak photo sharing platform.

Directory Structure

/core

Essential migrations that are always run for new deployments. These include:

  • init.js - Initial database schema creation
  • Backup service tables (029-035)
  • Gallery feedback tables (033)
  • Pre-generated watermarks (061)

/legacy

Migrations needed only when upgrading from older versions. New deployments can skip these as the core schema already includes all necessary tables and columns.

For New Deployments

If you're deploying this application for the first time:

  1. The initializeDatabase() function in src/database/db.js will create all necessary tables
  2. Only migrations in the /core directory will be run
  3. This ensures a clean, optimized database schema

For Existing Deployments

If you're upgrading from an older version:

  1. All migrations (both core and legacy) will be run in sequence
  2. The migration system tracks which migrations have been applied
  3. Only new migrations will be executed

Running Migrations

# Development
npm run migrate

# Production
npm run migrate:prod

Note on Duplicate Migration Numbers

The legacy directory contains renamed duplicates:

  • 014_add_host_name_to_events_duplicate.js (was duplicate of 014)
  • 027_add_rate_limit_settings_duplicate.js (was duplicate of 027)

These have been renamed to avoid conflicts while preserving the migration history.