Commit Graph

360 Commits

Author SHA1 Message Date
Paul Nothaft 2f2f405d9b Merge pull request #319 from the-luap/feat/prezip-and-photo-replace
fix: discussion #317 issues and #318 archive crash
2026-04-26 22:51:46 +02:00
Paul Nothaft 6cfff6f6a6 fix: address bugs and feature requests from discussion #317
- Share link: display and copy now use the absolute URL built from the
  current origin instead of the relative path stored in events.share_link.
  Added a Copy Link button to the events list (inline + dropdown).
- Detect dev tools default: event creation now reads the global
  enable_devtools_protection app setting instead of always falling back to
  the column default; admins who disable it globally get new events with
  it disabled too.
- Require password default: added a global "Require password by default"
  setting (event_default_require_password, default true), exposed via
  Settings -> Events. Create-event form initialises from it.
- Filter bar: added gallery_show_filter_bar setting and hide the search/
  sort row in the public gallery when off, or when the gallery has zero
  photos (fixes the empty-state UX from the screenshot).
- Theme picker unclickable on Create Event: memoised availableEventTypes
  so its identity is stable. The "auto-apply event-type recommended
  preset" effect was firing on every render due to the unstable array
  reference and silently overwriting the user's preset selection ~1ms
  after each click.
- Branding logo disappearing on theme change: handlePresetChange and
  handleThemeChange no longer wipe the existing logoUrl when a preset
  config (which carries no logoUrl) is applied; handleSave falls back to
  brandingSettings.logo_url. themeMutation now invalidates the
  admin-settings and public-settings caches so saved theme changes appear
  immediately.
2026-04-26 22:48:59 +02:00
github-actions[bot] 82adcd1f71 chore(beta): release 3.29.0-beta.0 2026-04-23 14:54:21 +00:00
Paul Nothaft e18afd3e6b feat: pre-zip download all and photo replacement by name (#312, #313)
Pre-zip downloads:
- Generate ZIP in background after photo mutations (upload/delete/watermark change)
- Serve cached zip with Content-Length for instant downloads and native progress bar
- Falls back to on-the-fly streaming when no cache exists yet
- Frontend uses browser-native download when zip is ready (no blob buffering)
- New downloadZipService with debounced regeneration and in-memory locking

Photo replacement:
- Admin upload form gets "Replace existing photos with same name" checkbox
- Matches by original_filename (case-insensitive) within the same event
- Preserves photo ID, position, feedback, category, and visibility
- Updates file, thumbnail, dimensions, EXIF capture date on replacement
- Ambiguous matches (multiple photos with same name) skip replacement with warning
- New photoReplacementService with findReplacementCandidate and replacePhoto
2026-04-23 16:49:31 +02:00
github-actions[bot] 89f86b9fe4 chore(beta): release 3.28.3-beta.0 2026-04-13 05:30:51 +00:00
github-actions[bot] 0a5b07de5d chore(beta): release 3.28.2-beta.0 2026-04-12 18:58:40 +00:00
Paul Nothaft b05c36ac81 Merge pull request #308 from the-luap/fix/welcome-message-and-guest-thumbnails
fix: display welcome message in gallery and fix guest thumbnail URLs (#306, #307)
2026-04-12 20:58:24 +02:00
Paul Nothaft 9323befdd9 fix: display welcome message in gallery and fix guest thumbnail URLs (#306, #307)
- Render welcome_message in gallery view for all non-fullpage layouts
  (grid, masonry, carousel, timeline, mosaic) as a centered banner
- Add /api prefix to thumbnail/photo URLs in adminGuests.js and
  adminPhotos.js so they route correctly through Nginx proxy
2026-04-12 20:58:02 +02:00
github-actions[bot] 623ab72916 chore(beta): release 3.28.1-beta.0 2026-04-12 08:06:08 +00:00
Paul Nothaft 3716ff5085 Merge pull request #304 from the-luap/fix/gallery-sort-direction-and-feedback-visibility
fix: apply sort direction in gallery and respect show_feedback_to_guests (#302, #303)
2026-04-12 10:05:52 +02:00
Paul Nothaft dffe057772 fix: apply sort direction in gallery view and respect show_feedback_to_guests (#302, #303)
- Gallery now respects the configured sort direction (asc/desc) from
  default_photo_sort setting instead of using hard-coded directions
- Photos endpoint zeroes out feedback fields (like_count, favorite_count,
  average_rating, comment_count, has_feedback) when show_feedback_to_guests
  is disabled, while still showing data to admin/client users
2026-04-12 10:05:24 +02:00
github-actions[bot] c303dd51e8 chore(beta): release 3.28.0-beta.0 2026-04-11 21:24:41 +00:00
Paul Nothaft 77f07e9329 fix: guest feedback flow bugs in Masonry grid and PhotoLightbox (#292)
Fixes two bugs reported on #292 after 3.27.0-beta.0 shipped:

1. Masonry grid showed no visual feedback after liking a photo.
   MasonryGalleryLayout's Like button had no liked-state plumbing —
   the Heart icon was a static <Heart> regardless of whether the user
   had liked the photo.

2. PhotoLightbox (fullscreen view) silently failed to like photos in
   guest identity mode. submitLike() and submitRating() never called
   ensureIdentity() before firing the API request, so the first
   interaction from a fresh session hit a 401 from the server instead
   of opening the name prompt.

Root causes:

1. MasonryGalleryLayout was missing the 'liked' state pattern that
   GridGalleryLayout already uses (likedPhotoIds Set in the parent,
   passed down as a `liked` prop, updated via onLikeSuccess callback).
   The bug was invisible in simple mode (no personal state) but
   surfaced immediately in guest mode where each guest expects to see
   confirmation of their own action.

2. PhotoLightbox's submit handlers were written before the guest
   identity context existed and only checked the legacy
   require_name_email flag. They were never updated when guest mode
   landed.

Also fixed: z-index conflict where the GuestNamePromptModal (z-50)
was sitting at the same level as PhotoLightbox (z-50), so when the
prompt opened over the lightbox, the fullscreen image intercepted
pointer events and the modal's Continue button was unclickable.
Bumped both guest modals to z-[60].

Changes:

- MasonryGalleryLayout.tsx
  - MasonryPhotoProps gains `liked?: boolean` + `onLikeSuccess?: () => void`.
  - Like button: red bg + filled white Heart icon when liked; aria-label
    toggles between "Like photo"/"Unlike photo"; aria-pressed mirrors state.
  - onClick wires onLikeSuccess() for optimistic UI in both guest-mode
    and simple-mode branches plus the FeedbackIdentityModal onSubmit path.
  - Parent layout holds `likedPhotoIds: Set<number>` and passes it to
    each MasonryPhoto (matches the GridGalleryLayout pattern).

- PhotoLightbox.tsx
  - Consumes useGuestIdentityOptional(); new `isGuestMode` flag.
  - submitLike() and submitRating() get a guest-mode branch that calls
    ensureIdentity() first and submits without body guest_name/email
    (server reads from the verified token).
  - Optimistic UI updates happen after successful submit in guest mode.

- GuestNamePromptModal.tsx, GuestRecoveryModal.tsx
  - z-50 → z-[60] so they render above PhotoLightbox.

Verified end-to-end against local Docker with Playwright MCP on event
168 (Masonry Columns Test layout):

- Fresh session, click Like in Masonry grid → name prompt opens, register,
  feedback persists with guest_id, Heart button turns red with
  aria-pressed and "Unlike photo" label. Subsequent likes on other
  photos also show red state. DB confirms feedback rows.

- Fresh session, open photo in lightbox BEFORE registering → click Like,
  the name prompt correctly opens on top of the lightbox, register,
  feedback persists. Rate 4 stars → works, average 4.0 (1) displayed
  in lightbox, ★ badge appears on toggle-feedback button, grid cell
  shows "1 likes" + "Rating: 4.0" indicators after closing lightbox.

- Backend DB: gallery_guests row created, photo_feedback rows have
  correct guest_id, server reads name from verified token (body values
  ignored).

Out of scope (documented in audit, not reported by the user, no
regression from guest mode): Mosaic/Carousel/Timeline have partial
optimistic-UI issues unrelated to this report; they pre-date guest
mode and behave the same in simple mode. Leaving alone per scope
discipline.
2026-04-11 14:42:28 +02:00
github-actions[bot] 95d8bc4065 chore(beta): release 3.27.0-beta.0 2026-04-11 06:29:51 +00:00
Paul Nothaft 3856ba25bb Merge pull request #295 from the-luap/feat/guest-selections
feat: guest selections with per-person identity (#292)
2026-04-11 08:27:31 +02:00
github-actions[bot] b0efd32f7a chore(beta): release 3.26.2-beta.0 2026-04-11 06:26:39 +00:00
Paul Nothaft ad4e5a7506 feat: guest selections with per-person identity (#292)
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.
2026-04-11 07:48:23 +02:00
Paul Nothaft d4b4dc628f fix: wire admin photo feedback filters into grid query (#293)
The Has Likes / Has Favorites / Has Comments checkboxes in the admin
Event > Photos tab updated local state but never affected the visible
photo grid, because the feedbackFilters state was only wired to the
export menu and the backend /admin/photos/:eventId/photos endpoint had
no support for these params.

Fixes:
- backend/src/routes/adminPhotos.js: extend GET /:eventId/photos to
  accept has_likes, has_favorites, has_comments, min_rating, and logic
  (AND/OR) query params and apply them via where-clause groups using
  the existing denormalized like_count/favorite_count/comment_count/
  average_rating columns.
- frontend/src/services/photos.service.ts: add hasLikes, hasFavorites,
  hasComments, minRating, logic to the PhotoFilters interface and
  append them as query params in getEventPhotos.
- frontend/src/pages/admin/EventDetailsPage.tsx: merge feedbackFilters
  into combinedPhotoFilters (via useMemo) and key the admin-event-photos
  query on it, so toggling any checkbox refetches with the new params.

Verified end-to-end against local Docker: seeded event with a known
feedback distribution and confirmed
- Has Likes → 4 photos
- Has Favorites → 3 photos
- Likes AND Favorites → 1 photo
- Likes OR Favorites → 6 photos
- Has Comments → 2 photos
- network requests carry the exact query params
2026-04-11 07:46:32 +02:00
github-actions[bot] 1f3b9c6712 chore(beta): release 3.26.1-beta.0 2026-04-09 14:27:12 +00:00
Paul Nothaft 147dc28440 fix: apply password change redirect fix to regular modal too (#263)
The redirect loop fix only covered MandatoryPasswordChangeModal.
The regular PasswordChangeModal (profile settings) had the same
issue — onSuccess updated React state but didn't handle the new
JWT cookie, causing the same redirect loop.

Also increase redirect delay from 500ms to 2000ms in both modals
so the success toast is visible before the page reloads.
2026-04-09 16:05:53 +02:00
github-actions[bot] ad64005a80 chore(beta): release 3.26.0-beta.0 2026-04-09 13:11:41 +00:00
Paul Nothaft 633d4a0f30 feat: sort photos by capture date with configurable default sort (#283)
Add per-event default photo sort setting with 6 options:
- Upload Date (Newest/Oldest First)
- Date Taken (Newest/Oldest First) — uses EXIF captured_at
- Filename (A-Z / Z-A)

Backend:
- Migration 077 adds default_photo_sort column to events table
- Event create/update handlers accept and validate the setting
- Gallery info endpoint returns default_photo_sort for frontend

Frontend:
- "Date Taken" added to gallery sort dropdown (alongside Date, Name,
  Size, Rating)
- Gallery initializes with event's default sort instead of hardcoded
  "date"
- "Default Photo Sort" dropdown in event create and edit forms
- Photos without EXIF dates fall back to upload date

i18n: All 5 locales (EN, DE, NL, PT, RU) updated with sort labels.

Closes #283
2026-04-09 15:10:51 +02:00
Paul Nothaft 835bdf5abb fix: resolve password change redirect loop and file watcher crash
#263: The mandatory password change modal updated React state before
the browser stored the new JWT cookie, causing a race condition where
the auth context checked the session with the old (invalidated) token.
Replace the state update with a full page redirect to /admin/dashboard
after a brief delay, ensuring the new cookie is applied cleanly.

#269: The file watcher service imported isVideoMimeType from
fileSecurityUtils where it doesn't exist. The function is exported
from videoProcessor. Fix the import path.

Closes #269
2026-04-09 13:54:23 +02:00
github-actions[bot] 97b1ae5b03 chore(beta): release 3.25.0-beta.0 2026-04-08 09:51:27 +00:00
Paul Nothaft 40332a71db feat: draft mode, admin branding, and workflow improvements
Draft Mode:
- Events are created as drafts by default — no email sent until published
- Add "Publish & Notify Client" button with confirmation dialog
- Draft banner with yellow styling on event details page
- Draft filter tab in events list
- Gallery middleware blocks public access to draft events
- Migration 076 adds is_draft column to events table

Admin Draft Preview:
- Admins can preview draft galleries via JWT preview token (?preview=)
- "View Gallery" link on drafts auto-appends preview token

Admin & Login Page Branding:
- Admin header uses configured company logo/name from branding settings
- Login page shows configured logo instead of hardcoded PicPeak
- Respects logo_display_mode (logo_only, text_only, logo_and_text)

OG Tag Branding:
- DynamicFavicon component updates OG meta tags and page title from
  branding settings

Editable Client Email:
- Customer email is now editable after event creation in edit mode

Branding Inheritance:
- New events inherit hero logo settings (visibility, size, position)
  from global branding configuration

Share Link Full Domain URL:
- New getFrontendBaseUrl() utility with DB fallback to general_site_url
- Used in email processor and share link service
2026-04-08 11:42:38 +02:00
Paul Nothaft 9ddd50f7e4 Merge pull request #266 from the-luap/security/pin-axios-version
security: pin axios to 1.14.0 — supply chain attack prevention
2026-04-05 18:40:47 +02:00
Paul Nothaft bec36fc99f security: pin axios to 1.14.0 to prevent supply chain attack
Axios versions 1.14.1 and 0.30.4 were compromised on March 31, 2026
with a RAT dropper (plain-crypto-js) attributed to North Korean threat
actor UNC1069/Sapphire Sleet. The malicious versions have been removed
from npm but our ^1.12.2 range could have pulled 1.14.1 on next install.

Pin to exact version 1.14.0 (latest safe release) in both frontend and
backend package.json and lock files to prevent any future resolution to
compromised versions.

References:
- https://github.com/axios/axios/issues/10604
- https://snyk.io/blog/axios-npm-package-compromised-supply-chain-attack-delivers-cross-platform/
2026-04-05 18:40:24 +02:00
github-actions[bot] 8614c2232c chore(beta): release 3.24.1-beta.0 2026-04-05 16:34:26 +00:00
github-actions[bot] 2b7c9b0138 chore(beta): release 3.24.0-beta.0 2026-04-04 21:33:29 +00:00
Paul Nothaft ee3f6ae13b feat: warn about low thumbnail resolution when selecting beta themes
Beta themes (Gallery Premium, Gallery Story) display thumbnails at
400-800px, but the default thumbnail size is 300x300px, causing visible
pixelation. Show an amber warning banner with a link to Thumbnail
Settings when a beta layout is active and thumbnails are below 500px.

Warning appears both in the preset selector and the layout selector
sections of the theme customizer.
2026-04-04 23:32:49 +02:00
github-actions[bot] 0a7a89045b chore(beta): release 3.23.0-beta.0 2026-04-04 20:13:49 +00:00
Paul Nothaft 8c5996e4ec Merge pull request #258 from the-luap/feat/email-template-translations
feat: multilingual email templates with translations table
2026-04-04 17:43:29 +02:00
Paul Nothaft f50d7c0c51 feat: multilingual email templates with translations table
Replace column-based email template languages (subject_en/subject_de) with
a normalized email_template_translations table where each language is a row.
This allows adding new languages without schema changes.

- Add migration 075 to create email_template_translations table, migrate
  existing EN/DE data, and seed NL/PT/RU for customer-facing templates
- Update processTemplate() to query translations table with fallback chain
  (requested lang -> en -> first available), with legacy column fallback
- Restructure admin email API to return/accept translations object format
- Update frontend EmailConfigPage with dynamic 5-language tabs, translation
  count badges, and copy-from-language feature for empty translations
- Add Dutch to default language dropdown in general settings
- Add Dutch to clientAccessI18n and password security messages in emails
- Expand email domain detection for NL/BE/BR/PT/RU domains
- Add email i18n keys (copyFrom, noTranslation, etc.) across all 5 locales
2026-04-04 17:43:01 +02:00
github-actions[bot] 85a4eb90fd chore(beta): release 3.22.0-beta.0 2026-03-25 21:36:31 +00:00
Paul Nothaft b54a80d251 feat: add Dutch (nl) locale and fix missing translation keys across all locales
Add complete Dutch translation (2054 keys) with Netherlands flag in the
language selector. Also synchronize all existing locales so every language
has the same set of keys: added 29 missing keys to EN/RU/PT and 95 missing
keys to DE (moderation, analytics, CSS templates, backup, events).
2026-03-25 22:35:57 +01:00
github-actions[bot] 2ac6c51fe5 chore(beta): release 3.21.1-beta.0 (#255)
Build and Push Docker Images / build-backend (push) Failing after 3m42s
Build and Push Docker Images / build-frontend (push) Failing after 3m41s
Build and Push Docker Images / summary (push) Successful in 3s
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-22 12:43:55 +01:00
Paul Nothaft 23cd9cb680 fix: address Shannon security assessment findings (37 vulnerabilities) (#254)
Remediate 4 Critical, 18 High, 10 Medium, and 5 Low vulnerabilities
identified in the Shannon security assessment (2026-03-20).

Critical fixes:
- Command injection via rsync SSH key path (INJ-VULN-01)
- Self-escalation to super_admin role (AUTHZ-VULN-11)
- Invite super_admin backdoor (AUTHZ-VULN-12)
- Handlebars SSTI in email templates (INJ-VULN-05)

Authentication hardening:
- Rate limit on share-link login (AUTH-VULN-01)
- X-Forwarded-For spoofing bypass (AUTH-VULN-02)
- reCAPTCHA fails closed when misconfigured (AUTH-VULN-03)
- Token revocation on admin/gallery logout (AUTH-VULN-04/05)
- Cookie Secure flag defaults true in production (AUTH-VULN-06)
- Remove JWT from admin login response body (AUTH-VULN-07)
- Timing-safe gallery slug validation (AUTH-VULN-09)
- Account lockout fails closed on DB error (AUTH-VULN-12)
- Session endpoint checks token revocation

Path traversal & file access:
- checksums endpoint path containment (INJ-VULN-03)
- manifest validate path containment (INJ-VULN-04)

XSS prevention:
- Block SVG data URIs in CSS sanitizer (XSS-VULN-01)
- Email preview iframe sandbox (XSS-VULN-02)
- SSR branding HTML escaping (XSS-VULN-03)
- User-Agent sanitization in feedback (XSS-VULN-04)

Authorization (IDOR):
- Event ownership middleware for all admin routes
- Cross-admin user profile read restriction (AUTHZ-VULN-10)

SSRF & infrastructure:
- Private IP validation for SMTP, S3, rsync hosts
- Replace inline JWT with standard adminAuth middleware
- CSRF Content-Type enforcement on mutating API endpoints
- CSP headers in nginx location blocks

Token revocation fix:
- Remove overly broad orWhere clause that invalidated all future tokens
- Allow empty-body POST requests (logout) in CSRF middleware

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-03-22 12:40:01 +01:00
github-actions[bot] a63f1a8dd9 chore(beta): release 3.21.0-beta.0 (#253)
Build and Push Docker Images / build-backend (push) Failing after 3m46s
Build and Push Docker Images / build-frontend (push) Failing after 3m47s
Build and Push Docker Images / summary (push) Successful in 2s
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-18 10:59:14 +01:00
Paul Nothaft ee46088985 feat: add per-gallery thumbnail scale setting (#172) (#251)
Add a thumbnailScale field (xs/sm/md/lg/xl) to gallery layout settings
that adjusts column counts for Grid, Masonry (columns mode), and Mosaic
layouts. Each scale maps to a column offset applied on top of the
layout's base columns, letting photographers control photo density.

- Add thumbnailScale to GalleryLayoutSettings type
- Apply scale offset in Grid, Masonry, and Mosaic layout components
- Add thumbnail scale dropdown to admin theme customizer
- Conditionally show dropdown only for applicable layouts
- Safelist dynamic grid-cols classes in Tailwind config
- Add i18n keys for EN, DE, PT, RU locales

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-03-18 10:44:07 +01:00
github-actions[bot] 3742d71535 chore(beta): release 3.20.1-beta.0 (#250)
Build and Push Docker Images / build-backend (push) Failing after 3m46s
Build and Push Docker Images / build-frontend (push) Failing after 3m46s
Build and Push Docker Images / summary (push) Successful in 3s
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-17 17:18:56 +01:00
Paul Nothaft 486239aeb9 fix: address beta feedback - gallery layout fixes, Russian locale, email logo (#249)
- Add Russian (Русский) to admin settings language dropdown
- Fix Premium layout hero using thumbnail instead of hero_url
- Hide "Uncategorized" section header in Story layout for uncategorized photos
- Add PhotoLightbox to Story layout so photo clicks open full-screen view
- Use full-res images in StoryPhotoCard instead of thumbnails
- Defer public gallery auto-login text until settings/locale are loaded
- Add validation and debug logging for email logo URL construction

Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>
2026-03-17 17:16:50 +01:00
github-actions[bot] f9889a93fb chore(beta): release 3.20.0-beta.0 2026-03-17 12:05:29 +00:00
Paul Nothaft e1b6e43e52 feat: photo visibility control with client access (#172)
Add two-tier gallery access system allowing clients (e.g., wedding couples) to
review and hide photos before the gallery is shared with guests.

Backend:
- Migration 074: add visibility column to photos, client_access_enabled/
  client_password_hash/client_share_token to events
- Client login endpoint (POST /auth/gallery/:slug/client-login) with bcrypt PIN
- Gallery photo list filters hidden photos for guests, shows all for clients
- Visibility toggle endpoints (single + bulk) for client access level
- Admin event CRUD supports client access fields
- Email template includes client access link + PIN (EN/DE/RU/PT)

Frontend:
- ClientAccessPage: PIN entry form at /gallery/:slug/client-access
- GalleryView: client mode banner, visibility counter, toggle controls
- GridGalleryLayout: eye/eye-off overlay per photo for clients
- AdminPhotoGrid: visibility badge, bulk Hide/Show buttons
- EventDetailsPage: Client Access settings section (toggle, PIN, link)
- CreateEventPage: client access toggle + PIN in event creation form
- GalleryAuthContext: accessLevel/isClient/clientLogin support
- New complete pt-BR locale (pt.json) with all translations
- Client access i18n keys for EN, DE, RU, PT
2026-03-17 13:04:41 +01:00
github-actions[bot] f5997892c4 chore(beta): release 3.19.2-beta.0 2026-03-16 21:35:09 +00:00
Paul Nothaft f3622396e7 fix(security): invalidate tokens on password change, enforce session timeout, fix role update
- Set password_changed_at when changing password via adminAuth route so
  existing JWT tokens are rejected by the auth middleware check
- Enforce session timeout on first request with unseen tokens by checking
  token iat against configured timeout (prevents bypass after server restart)
- Convert camelCase roleId/isActive to snake_case role_id/is_active in
  frontend updateUser service (fixes silent role update failures)

Resolves GHSA-rqg3-47p5-vgwg
2026-03-16 22:34:32 +01:00
github-actions[bot] 2618415aa1 chore(beta): release 3.19.1-beta.0 2026-03-16 21:23:14 +00:00
Paul Nothaft dfae2c2bc6 Merge pull request #241 from the-luap/fix/external-media-dimensions-and-email-colors
fix: external media dimensions, theme race condition, email color customization
2026-03-16 22:22:58 +01:00
Paul Nothaft bbeedd1888 fix: resolve external media dimensions, gallery theme race condition, and add email color customization
- Fix dimension repair for external media by using photoResolver instead of hardcoded paths
- Extract photo dimensions via Sharp during external media import
- Remove duplicate theme useEffect from GalleryView to prevent flash/revert race condition
- Pass event welcome_message to Story layout footer for per-event customization
- Add email_primary_color/email_secondary_color settings with admin UI color pickers
- Add i18n keys for email branding in all 4 locales (en, de, ru, pt)
2026-03-16 22:22:36 +01:00
github-actions[bot] 1468c459ba chore(beta): release 3.19.0-beta.0 2026-03-16 16:24:01 +00:00
Paul Nothaft 1fa222e9c4 feat: add photo cap per event and Portuguese (pt-BR) locale
- Add photo_cap column to events table (migration 074) to limit photos per event
- Enforce photo cap in upload route, returning 400 when limit exceeded
- Pass photo_cap through all event CRUD routes and frontend forms
- Add complete Portuguese (pt-BR) translation (2300+ strings)
- Register pt locale in i18n config, language selector, date formatting
- Add photoCap/photoCapHelp translation keys to all locale files (en, de, ru, pt)
2026-03-16 17:22:54 +01:00