0bc7e2af17
Background: galleryOgService already serves OG/Twitter Card meta tags to social-crawler User-Agents (WhatsApp, Facebook, Slack, Telegram, Discord, ~21 in total) for /gallery/:slug URLs. Today the og:image is always the brand logo with the inline rationale "no protected photo content". #474 asked for a hero/cover photo preview. The trade-off is that any URL embedded in og:image is fetched unauthenticated by every link-preview crawler — so an opted-in image is effectively public to anyone the gallery URL is shared to. Ship as a per-event boolean, default FALSE, so existing galleries never start surfacing photos without explicit admin intent. Schema (migration 102): - events.og_image_share_enabled BOOLEAN NOT NULL DEFAULT FALSE. Backend: - galleryOgService.buildOgMetadata: when opt-in is on AND a hero_photo_id is set AND the photo has a generated thumbnail, emit og:image as /og/gallery/:slug/cover. Falls back to the brand logo on any miss (deleted hero, missing thumbnail, no opt-in) so a half-configured gallery still gets a polished preview rather than a broken-image src. - galleryOgService.handleGalleryOgCover: new public endpoint that streams the hero thumbnail. Validates slug shape, checks the opt-in flag + hero presence + thumbnail existence; returns 404 on any failure. ETag = thumbnail mtime + photo id so a regenerated thumb busts crawler caches. Cache-Control: public, max-age=300 (short — admins shouldn't wait an hour for a cover swap to land in chat previews). - server.js: mount the new GET /og/gallery/:slug/cover route. The existing nginx ^~ /og/gallery/ proxy block already covers it. - adminEvents.js: validator + persistence on POST + PUT. formatBoolean coercion so SQLite (0/1) and Postgres (boolean) both behave correctly. Frontend: - Event type + UpdateEventData carry og_image_share_enabled. - EventDetailsPage adds a checkbox under the HeroPhotoSelector, disabled when no hero photo is picked. Help text deliberately spells out the public-by-design consequence — admins shouldn't flip this on for a sensitive gallery without realising what they're sharing with link-preview crawlers. Tests: 8 new in galleryOgService.shareImage.test.js — pin the cover-vs-logo decision contract (3 cases) plus the defensive fallbacks (deleted hero, missing thumbnail) and the 404 contract on the cover endpoint (4 cases). The 404 tests assert that ensureThumbnail() is NOT called when opt-in is off, so a future refactor can't accidentally widen the unauthenticated cover endpoint to expose a hero the admin hasn't shared. i18n: en + de hand-translated; nl + pt + ru + fr machine-translated and flagged for native review per project convention.
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:
- The
initializeDatabase()function insrc/database/db.jswill create all necessary tables - Only migrations in the
/coredirectory will be run - This ensures a clean, optimized database schema
For Existing Deployments
If you're upgrading from an older version:
- All migrations (both core and legacy) will be run in sequence
- The migration system tracks which migrations have been applied
- 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.