Commit Graph
11 Commits
Author SHA1 Message Date
Paul Nothaft 038e84cae7 fix: dedupe parallel admin 401 redirects to /admin/login
Visiting /admin/dashboard while logged out caused a navigation storm:
the dashboard fires ~7 /api/admin/* queries on mount, each returns 401,
each axios interceptor call did `window.location.href = '/admin/login'`.

The path-based guard `currentPath.includes('/admin/login')` reads
`location.pathname` *synchronously* — but `location.href = …` is async,
so all 7 parallel handlers saw the still-old pathname and each fired a
fresh navigation. The browser logged 6+ ERR_ABORTED entries and the user
saw a flicker storm. Same shape would bite any admin page that fans out
queries on mount.

Add a module-level `adminLoginRedirectPending` flag set the moment we
kick off the first redirect; subsequent 401s in the same tick see it
and skip. Single navigation, clean transition to login.

Smoke spec 10-admin-redirect-loop locks the regression in by sampling
the URL across 5 ticks — if any tick lands somewhere other than
/admin/login, the spec fails.
2026-04-27 22:38:00 +02: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 665ce5a6e7 Fix issues #31 #33 #34 #35 #36 2025-10-12 21:03:07 +02:00
paul 8d6ddd257d Fix gallery login persistence and favorites (#29)
Test and Lint / frontend-test (push) Successful in 2m16s
Test and Lint / backend-test (push) Successful in 2m6s
2025-09-22 14:33:11 +02:00
paul 71e7179145 Harden auth cookies and fix native schema for event creation
Mirror to GitHub / mirror (push) Successful in 45s
Test and Lint / backend-test (push) Successful in 1m37s
Test and Lint / frontend-test (push) Successful in 2m8s
Version and Release / version-bump (push) Successful in 1m0s
Version and Release / trigger-drone (push) Successful in 3s
2025-09-18 15:59:26 +02:00
paulandClaude e91209f7cb fix: resolve multiple issues from GitHub issue #14
Mirror to GitHub / mirror (push) Successful in 46s
Test and Lint / backend-test (push) Successful in 1m54s
Test and Lint / frontend-test (push) Successful in 2m9s
Version and Release / version-bump (push) Successful in 1m5s
Version and Release / trigger-drone (push) Successful in 3s
- Fixed duplicate German translation for 'downloadSelected' button
- Added client_max_body_size configuration in nginx for file uploads
- Fixed date parsing in FeedbackModerationPanel to handle timestamps
- Fixed admin authentication context (req.admin vs req.user) in feedback routes
- Enhanced clipboard functionality with fallback for non-HTTPS contexts
- Fixed authentication token handling for numeric event IDs in uploads

These changes ensure comment moderation works properly, file uploads are configured correctly, and the UI handles all edge cases properly.

Fixes #14

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-09-01 22:56:44 +02:00
paulandClaude 1b4b497fdf chore: clean up codebase for production readiness
Mirror to GitHub / mirror (push) Successful in 44s
Test and Lint / backend-test (push) Successful in 1m42s
Test and Lint / frontend-test (push) Has been cancelled
Version and Release / version-bump (push) Has been cancelled
Version and Release / trigger-drone (push) Has been cancelled
- Remove all console.log/debug statements from production code
- Add NODE_ENV checks for development-only logging
- Remove test scripts (test-feedback, test-image-security, test-backup-*, test-restore)
- Remove one-time fix scripts (fix-temp-photos, fix-migration-state, mark-migration-applied)
- Remove sensitive files (.env.backup, ADMIN_CREDENTIALS.txt)
- Update package.json to remove references to deleted scripts
- Replace console statements with logger utility in backend
- Secure error boundaries to not expose stack traces in production

This makes the codebase production-ready with no debug output or test scripts.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-08-24 23:19:30 +02:00
paulandClaude 9dc3777985 CRITICAL FIX: prevent gallery pages redirecting to admin login
Users were being redirected from gallery pages to admin login due to
useLocalizedDate hook trying to fetch admin settings. Fixed by:

1. Added general_date_format to public settings endpoint
2. Created publicSettingsService for unauthenticated access
3. Updated useLocalizedDate to use public settings instead of admin
4. Fixed API interceptor to not redirect on public endpoint 401s
5. Added backups/ and test-archiver/ to .gitignore

This restores gallery access for all users.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-07-24 16:57:08 +02:00
paulandClaude 3dc013d7b1 CRITICAL FIX: Remove 403 from auth redirect logic to restore login
BREAKING ISSUE FIXED:
- 403 errors were triggering redirects, preventing login page from loading
- Public endpoints returning 403 were causing redirect loops

Changes:
- Removed 403 status from automatic redirect logic
- Only 401 (Unauthorized) now triggers login redirect
- 403 (Forbidden) errors are passed through without redirect

This fixes the critical issue where users couldn't access the login page
because public API calls were returning 403 and triggering redirects.

403 errors should be handled differently than 401:
- 401 = Missing/invalid auth (redirect to login)
- 403 = Forbidden (could be rate limit, IP block, etc - don't redirect)

🚨 Emergency fix for production

Co-Authored-By: Claude <[email protected]>
2025-07-24 16:57:07 +02:00
paulandClaude b2ae5f18ad fix: handle auth errors and JSON parsing in admin panel
- Added proper HTTP status check before JSON parsing in AnalyticsPage
  * Prevents "Unexpected token '<'" error when API returns HTML error pages
  * Throws proper error for non-OK responses

- Enhanced API error handling to treat 403 as auth failure
  * Both 401 and 403 now trigger redirect to login page
  * Clears expired admin tokens automatically
  * Prevents users from staying on admin pages with expired sessions

These fixes resolve:
1. JSON parse errors when fetching Umami config
2. 403 Forbidden errors not redirecting to login
3. Backend version display issues due to auth failures

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-07-24 16:57:07 +02:00
paulandClaude 1773ed5f95 Initial commit - Project start (July 17, 2025)
continuous-integration/drone/push Build is passing
Mirror to GitHub / mirror (push) Successful in 26s
Test and Lint / backend-test (push) Successful in 1m11s
Test and Lint / frontend-test (push) Successful in 2m28s
Version and Release / version-bump (push) Successful in 32s
Version and Release / trigger-drone (push) Has been skipped
Original: feat: enhance security logging and ensure rate limit blocks are properly tracked

- Add comprehensive logging for rate limit blocks with full request details
  - IP address (with proper proxy detection), user agent, headers, timestamps
  - Rate limit info (current count, limit, remaining, reset time)
  - Separate tracking for auth vs general endpoints

- Enhance authentication failure logging
  - JWT validation failures with detailed error info
  - Admin auth attempts without token
  - Failed token validation with user context
  - All events include IP, path, method, user agent

- Improve Winston logger configuration for production
  - Add automatic log rotation (10MB errors, 50MB combined)
  - Create separate security.log for auth/rate limit events
  - Ensure logs directory exists automatically
  - Add structured JSON format for log aggregation
  - Support container logging with LOG_TO_CONSOLE env var

- Create comprehensive documentation
  - Security logging guide with examples
  - Monitoring recommendations
  - Configuration reference

- Add test script to verify logging functionality

All rate limit settings remain configurable via admin panel:
- Window duration, max requests, auth limits
- Skip authenticated requests option
- Public endpoints only option

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-07-24 16:57:07 +02:00