Shows real-time stats with color-coded indicators:
- Redis: version, memory, hit rate, ops/sec, evicted keys
- PostgreSQL: version, db size, connections, cache hit ratio, dead tuples
Uses MESSENGER_TRANSPORT_DSN for Redis auth (works in dev and prod).
Accessible via /admin/infra with nav link.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Configure Redis DB 2 as Symfony cache adapter
- Cache Meilisearch search results for 5 minutes (invalidated on writes)
- Cache admin dashboard stats for 10 minutes
- Add invalidateSearchCache() called after each Meilisearch write
- Update tests to support cache mock injection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Standalone installable PWA with:
- JWT login via /api/auth/login
- Event list from /api/live/events
- QR code camera scanning (html5-qrcode library)
- Scan results with accepted/refused state and ticket details
- Auto token refresh on expiry
- Offline caching via service worker
- Dark theme optimized for outdoor scanning
- Vibration feedback on scan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admin can now view the current logo and upload a new one via the
organizer edit form. Uses VichUploader with the existing organizer_logo
mapping. Adds test with fixture image.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fetches charges_enabled and payouts_enabled from Stripe API for each
organizer with a connected account and updates the local database.
Also adds retrieveAccountStatus() to StripeService for testability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add toggle online/offline and delete routes in AdminController
- Add action buttons (En ligne, Modifier, Supprimer) in admin events template
- Bypass requireEventOwnership and requireStripeReady for ROLE_ROOT so admin can edit any event
- Add Meilisearch healthcheck and depends_on in messenger service (prod + dev)
- Add tests for all new admin routes and ROLE_ROOT bypass
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add nullable debt field to User entity with addDebt/reduceDebt helpers
- On refund webhook: add refunded amount to organizer debt
- On dispute webhook (charge.dispute.created): add disputed amount to debt
- OrderController: if organizer has debt > 0, payment goes to main Stripe
account instead of connected account, debt reduced on payment success
- Display debt amount on organizer dashboard with warning message
- Add dispute notification email template
- Migration for debt column on user table
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add billing fields to User (isBilling, billingAmount, billingState,
billingStripeSubscriptionId) and OrganizerInvitation (billingAmount)
- Registration: organizer gets billingState="poor" (pending review)
- Admin approval: sets isBilling=true, billingAmount from form, state="good"
- Invitation: billingAmount from invitation, if 0 then isBilling=false
- ROLE_ROOT accounts: billing free (amount=0, state="good")
- Block Stripe Connect creation and all organizer features if state is
"poor" or "suspendu"
- Hide Stripe configuration section if billing not settled
- Add billing checkout via Stripe subscription with success route
- Webhooks: checkout.session.completed activates billing,
invoice.payment_failed and customer.subscription.deleted suspend
account and disable online events
- Show billing alert on /mon-compte with amount and subscribe button
- Display billing info in invitation email and landing page
- Add email templates for billing activated/failed/cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Hide organizer tabs (events, subaccounts, payouts) if Stripe not ready
- Redirect organizer tab content and all organizer routes to /mon-compte
- Add requireStripeReady() guard on all ROLE_ORGANIZER routes
- Force default tab to 'tickets' when Stripe is not validated
- Update test fixtures: approved organizers get Stripe enabled by default
- Add tests for blocked tabs and blocked event creation without Stripe
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ApiSandboxController: reduce scan() returns from 4 to 3 via ternary
- ApiDocController: add MIME_JSON constant, extract buildInsomniaRequest()
and buildInsomniaBody() to reduce cognitive complexity
- Store sessions in Redis to fix SSO disconnect with 2 PHP replicas
(round-robin load balancing caused session loss on filesystem storage)
- Configure session cookie: 24h lifetime, secure auto, samesite lax
- Replace Caddy analytics proxies (/stats/*, /assets/perf.js, /sperf)
with direct URLs to tools-security.esy-web.dev and cloudflareinsights.com
- Update JS tests for new direct analytics URLs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ansible: healthcheck via PHP container (curl from php, not libretranslate)
- Ansible: exit 0 if LibreTranslate not ready (don't block deploy)
- Ansible: ignore_errors on translation step (non-blocking)
- AccountControllerTest: add testEventQrCode (PNG response) and testEventQrCodeDeniedForOtherUser (403)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Public event page:
- Share buttons: X (Twitter), Facebook, Instagram (copy link), TikTok (copy link), copy link
- Buttons use url_encode for share URLs with event title + URL
- Instagram/TikTok copy to clipboard (no direct share URL support)
- Consistent brutal design with aria-labels
Organizer dashboard:
- Share X, Facebook, copy link buttons per event in events list
- QR code download button per event
- Route /mon-compte/evenement/{id}/qrcode: generates 400px PNG QR code via Endroid
- QR code points to public event URL, downloaded as qrcode-{slug}.png
JS module:
- assets/modules/share.js: initShare() handles data-share-copy buttons
- Copies URL to clipboard, shows checkmark for 1.5s then restores icon
- 4 tests (no buttons, copy, checkmark restore, multiple buttons)
Social icons already displayed via _social_icons.html.twig component
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AuditLog entity: action, entityType, entityId, data (JSON), performedBy, ipAddress
- AuditService: logs actions with current user and IP
- Audit on: order_created, order_paid, order_cancelled, order_refunded
- Admin /admin/logs: paginated table with action badges, details, user, IP
- Navigation link 'Logs' in admin header
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CsrfProtectionSubscriber: auto-injects hidden _csrf_token in HTML responses,
auto-verifies on POST requests
- Excludes: webhooks, JSON APIs, login (has its own CSRF)
- 9 tests covering all cases (GET, excluded, JSON, no token, invalid, valid, inject, non-HTML)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- OrganizerInvitation entity: companyName, firstName, lastName, email,
message, status (sent/opened/accepted/refused), unique token (64 hex chars)
- Admin route /admin/organisateurs/inviter: form + invitation list with status
- Button "Inviter un organisateur" on admin organizers page
- Email with accept/refuse links using unique token
- Public route /invitation/{token}/{action}: accept or refuse without auth
- Response page: confirmation message for accept/refuse
- Migration, PHPStan config, 7 entity tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- downloadTicket: order not found, ticket not found, success with mock PDF
- invoice: success with mock PDF (paid order)
- create: zero qty items filtered out
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- create: not found, empty cart, guest, logged user, invalid billet,
not buyable, exceeds quantity
- guest: not found, renders, empty fields, submit success, redirects if user set
- payment: not found, redirects if no name, renders with Stripe,
404 without Stripe account
- success: not found, renders, failed status, succeeded with mock
- publicOrder: not found, renders
- invoice: not found, not paid returns 404
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Retrieve PaymentIntent on success redirect, save payment_method, card_brand, card_last4
- Display payment info on /ma-commande page (card type + last 4 digits)
- Add "Il est recommande d'arriver en avance" to practical info on ticket PDF
- Migration for payment_method, card_brand, card_last4 columns
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>