ad4e5a7506
Introduces a new "Per-guest selections" identity mode for event feedback, letting each visitor register under their own name so their likes/favorites/comments/ratings are tracked independently. Includes admin insights (list, per-guest detail, aggregate view, export) and advanced identity features (forget-me, email recovery, invite tokens, merge). New event-level setting - event_feedback_settings.identity_mode = 'simple' | 'guest' (default 'simple' → zero behavior change for existing events). - Admin UI radio under Feedback Settings to toggle per event. Root cause of the previous "all guests share state" bug - generateGuestIdentifier() was sha256(ip + userAgent), so every visitor on the same WiFi + similar device collided into one identity. - Now: when a verified guest JWT is present (x-guest-token header), req.guest.identifier takes precedence — per-person rate limits and per-person deduplication. Phase 1 — identity layer - Migration 078: new gallery_guests, guest_invites, guest_verification_ codes tables; identity_mode column + check constraint; nullable guest_id FK on photo_feedback. - New guest JWT type scoped to (eventId, guestId). - New middleware guestAuth.resolveGuest (non-blocking) + requireGuest. - POST /gallery/:slug/guest, GET /guest/me, DELETE /guest/me. - Gallery feedback route enforces guest identity in guest mode and reads name/email from the verified token (never from the body). - Frontend GuestIdentityContext + GuestNamePromptModal; axios interceptor injects x-guest-token on gallery API calls. - Feedback-only blocking: gallery opens freely, prompt only on first interactive feedback action. - Admin "Guests" tab (conditional on identity_mode='guest') with the AdminGuestsList component. Phase 2 — admin insights - GET /admin/events/:eventId/guests list + aggregated counts. - GET /admin/events/:eventId/guests/:guestId detail with per-type groupings; AdminGuestDetail modal with thumbnail grid + tabs. - GET /admin/events/:eventId/guests/aggregate sorted by distinct guest pick count; GuestSelectionsAggregate component. - Per-guest export (txt/csv/json) and bulk export-all ZIP. Phase 3 — polish - 3.1 Self-service forget-me link in gallery footer. - 3.2 Email-based identity recovery: POST /guest/recover sends a 6-digit code via the existing emailProcessor, POST /guest/verify exchanges it for a token (rate-limited, enumeration-safe). - 3.3 Admin invite tokens: pre-mint identities, share URLs with ?invite=, single-use redemption stripping the param from history. - 3.4 Admin merge endpoint reassigns feedback + soft-deletes sources. Shared helper - useGalleryFeedbackAction hook wraps the identity-check logic for inline like buttons across Masonry/Grid/Justified/Mosaic/Carousel/ Timeline/Premium layouts. Backwards compatibility - Existing events default to 'simple' after migration; behavior unchanged. - Legacy photo_feedback rows keep guest_id NULL; admin shows them in the generic feedback moderation view as before. - feedback_count denormalized stat now uses COALESCE(guest_id, guest_identifier) so per-guest counts are accurate without touching legacy rows. Verified end-to-end against local Docker - Migration clean on existing data. - Simple mode unchanged (no prompt, legacy flow). - Guest mode: Alice registers on click, tokens persist in sessionStorage, feedback rows carry guest_id. - Carol via invite link auto-redeems, sees Alice's "1 likes" badge. - Admin Guests tab shows both with correct counts; detail modal displays thumbnail grid with badges; aggregate view sorts by picker count (photo 227 = 2, others = 1); CSV/JSON export matches DB. - Merge Carol into Alice: feedback reassigned, Carol soft-deleted, Alice count = 4.
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.