Commit Graph

33 Commits

Author SHA1 Message Date
Serreau Jovann
eb884faea1 Align prod PHP container UID with bot user (1001) and harden restore.sh
Some checks failed
CI / sonarqube (push) Failing after 52s
Sur le serveur prod, l'utilisateur 'bot' (cree par Ansible) a UID 1001
alors que l'utilisateur 'appuser' du conteneur PHP etait hardcode a UID 1000,
ce qui causait des erreurs 'Unable to write in var/cache/prod' apres restore
ou deploiement: les fichiers du host appartenaient a bot:bot (1001) et
appuser (1000) ne pouvait pas y ecrire.

docker/php/prod/Dockerfile:
  - appuser UID/GID passe de 1000 a 1001 pour matcher bot
  - dev/Dockerfile reste a 1000 (les users dev sont generalement 1000)

ansible/deploy.yml:
  - chown des dossiers public/uploads/* et var/payouts passe de 1000 a 1001

restore.sh:
  - Nouvelle option --owner USER:GRP (defaut: bot:bot, override via RESTORE_OWNER)
  - Chown automatique en fin de restore sur var/, public/uploads/, config/cert/, .env.local
  - Purge automatique de var/cache (sera reconstruit par PHP)
  - Verification root au demarrage (sudo requis pour chown)
  - Verification de l'existence du user et group cibles
  - Option --skip-chown pour bypass (deconseille)
  - Etapes post-migration mises a jour (ajout de make build_prod en premier)

Procedure de migration sur le serveur apres ce commit:
  cd /var/www/e-ticket
  git pull origin master
  make build_prod              # rebuild image PHP avec UID 1001
  make stop_prod
  sudo chown -R bot:bot var public/uploads config/cert .env.local
  sudo rm -rf var/cache
  make start_prod
  make clear_prod

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:04:14 +02:00
Serreau Jovann
3b4b51e3f2 Clear Symfony + Redis cache before migrations in deploy
Prevents stale Doctrine L2 cache and app cache from causing issues
after schema changes. Clears both filesystem cache and Redis pool.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:50:35 +01:00
Serreau Jovann
98b0b41064 Use SECRET_ANALYTICS env var, regenerated at each deployment
- New SECRET_ANALYTICS variable replaces kernel.secret for analytics
- Ansible generates a random 32-char secret at each deploy
- Endpoint token and encryption key change with every deployment
- Existing sessions will get new visitor_id after deploy (expected)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:27:05 +01:00
Serreau Jovann
d2fb17bf50 Add app:infra:snapshot command, page reads from var/infra.json
The /admin/infra page was slow because Docker stats API blocks per container.
Now a cron (every 5min) generates var/infra.json via app:infra:snapshot,
and the page reads the static JSON file instantly.
Mount Docker socket in cron container for snapshot access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:19:26 +01:00
Serreau Jovann
7a370b1e02 Fix Docker socket access: add docker GID to PHP container group
The PHP container user needs the docker group to read the socket.
Uses DOCKER_GID env var in dev (defaults to 989) and dynamic GID
detection via Ansible stat in prod.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:53:28 +01:00
Serreau Jovann
fd1162b7af Add Stripe sync cron (every 6h) to Ansible deploy playbook
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 09:58:14 +01:00
Serreau Jovann
8223e0b954 Fix indentation in deploy.yml after LibreTranslate removal
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:49:30 +01:00
Serreau Jovann
6dfffac457 Remove LibreTranslate tasks from deploy, add SESSION_HANDLER_DSN to prod env
- Remove Start/Wait/Translate LibreTranslate tasks from deploy.yml
- Add SESSION_HANDLER_DSN with Redis in env.local.j2 for prod sessions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:46:47 +01:00
Serreau Jovann
82c90f5b8b Fix LibreTranslate deploy: healthcheck from PHP container, ignore_errors, add QR code tests
- 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>
2026-03-23 18:12:18 +01:00
Serreau Jovann
acccd4a0c4 Fix LibreTranslate timeout: increase wait to 3min, add Ansible healthcheck wait
- TranslateCommand: increase waitForApi retries from 30 to 90 (3 minutes total)
- Ansible deploy: add explicit healthcheck wait step (60×5s = 5min max) before translation
- First launch downloads ~2-4GB of language models, needs more time

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:10:54 +01:00
Serreau Jovann
42d06dd49f Add LibreTranslate auto-translation, improve test coverage, fix code duplication
Translation system:
- Add LibreTranslate container (dev + prod), CPU-only, no port exposed, FR/EN/ES/DE/IT
- Create app:translate command: reads *.fr.yaml, translates incrementally, preserves placeholders
- Makefile: make trans / make trans_prod (stops container after translation)
- Ansible: start libretranslate -> translate -> stop during deploy
- Prod container restart: "no" (only runs during deploy)
- .gitignore: ignore generated *.en/es/de/it.yaml files
- 11 tests for TranslateCommand (API unreachable, empty, incremental, obsolete keys, placeholders, fallback)

Test coverage improvements:
- OrderController: event ended (400), invalid cart JSON, invalid email, stock zero (4 new tests)
- AccountController: finance stats all statuses (paid/pending/refunded/cancelled), soldCounts (2 new tests)
- JS cart: checkout without error elements, hide error on retry, stock polling edge cases (singular, no label, qty zero, unknown billet) (8 new tests)
- JS editor: comment node sanitization (1 new test)
- JS tabs: missing panel, generated id, parent null, click no-panel (5 new tests)

Code duplication fixes:
- MeilisearchConsistencyCommand: extract diffAndReport() method (was duplicated 3x)
- Email templates: extract _order_items_table.html.twig partial (shared by notification + cancelled)
- SonarQube: exclude src/Entity/** from CPD (getters/setters duplication)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 11:44:13 +01:00
Serreau Jovann
04927ec988 Complete TASK_CHECKUP: security, UX, tests, coverage, accessibility, config externalization
Billetterie:
- Partial refund support (STATUS_PARTIALLY_REFUNDED, refundedAmount field, migration)
- Race condition fix: PESSIMISTIC_WRITE lock on stock decrement in transaction
- Idempotency key on PaymentIntent::create, reuse existing PI if stripeSessionId set
- Disable checkout when event ended (server 400 + template hide)
- Webhook deduplication via cache (24h TTL on stripe event.id)
- Email validation (filter_var) in OrderController guest flow
- JSON cart validation (structure check before processing)
- Invitation expiration after 7 days (isExpired method + landing page message)
- Stripe Checkout fallback when JS fails to load (noscript + redirect)

Config externalization:
- Move Stripe fees (STRIPE_FEE_RATE, STRIPE_FEE_FIXED) and admin email (ADMIN_EMAIL) to .env/services.yaml
- Replace all hardcoded contact@e-cosplay.fr across 13 files
- MailerService: getAdminEmail()/getAdminFrom(), default $from=null resolves to admin

UX & Accessibility:
- ARIA tabs: role=tablist/tab/tabpanel, aria-selected, keyboard nav (arrows, Home, End)
- aria-label on cart +/- buttons and editor toolbar buttons
- tabindex=0 on editor toolbar buttons for keyboard access
- data-confirm handler in app.js (was only in admin.js)
- Cart error feedback on checkout failure
- Billet designer save feedback (loading/success/error states)
- Stock polling every 30s with rupture/low stock badges
- Back to event link on payment page

Security:
- HTML sanitizer: BLOCKED_TAGS list (script, style, iframe, svg, etc.) - content fully removed
- Stripe polling timeout (15s max) with fallback redirect
- Rate limiting on public order access (20/5min)
- .catch() on all fetch() calls (sortable, billet-designer)

Tests (92% PHP, 100% JS lines):
- PCOV added to dev Dockerfile
- Test DB setup: .env.test with DATABASE_URL, Redis auth, Meilisearch key
- Rate limiter disabled in test env
- Makefile: test_db_setup, test_db_reset, run_test_php, run_test_coverage_php/js
- New tests: InvitationFlowTest (21), AuditServiceTest (4), ExportServiceTest (9), InvoiceServiceTest (4)
- New tests: SuspendedUserSubscriberTest, RateLimiterSubscriberTest, MeilisearchServiceTest
- New tests: Stripe webhook payment_failed (6) + charge.refunded (6)
- New tests: BilletBuyer refund, User suspended, OrganizerInvitation expiration
- JS tests: stock polling (6), data-confirm (2), copy-url restore (1), editor ARIA (2), XSS (9), tabs keyboard (9)
- ESLint + PHP CS Fixer: 0 errors
- SonarQube exclusions aligned with vitest coverage config

Infra:
- Meilisearch consistency command (app:meilisearch:check-consistency --fix) + cron daily 3am
- MeilisearchService: getAllDocumentIds(), listIndexes()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 11:14:06 +01:00
Serreau Jovann
61200adc74 Add stock management, order notifications, webhooks, expiration cron, and billet type validation
- Decrement billet quantity after purchase in BilletOrderService::generateOrderTickets
- Block purchase when stock is exhausted (quantity <= 0) in OrderController::buildOrderItems
- Add organizer email notification on new order (order_notification_orga template)
- Add organizer email notification on cancel/refund (order_cancelled_orga template)
- Add ExpirePendingOrdersCommand (app:orders:expire-pending) cron every 5min via Ansible
  - Cancels pending orders older than 30 minutes, restores stock, invalidates tickets
  - Includes BilletBuyerRepository::findExpiredPending query method
  - 3 unit tests covering: no expired orders, stock restoration, unlimited billets
- Add payment_intent.payment_failed webhook: cancels order, logs audit, emails buyer
- Add charge.refunded webhook: sets order to refunded, invalidates tickets, notifies orga and buyer
- Validate billet type (billet/reservation_brocante/vote) against organizer offer
  - getAllowedBilletTypes: gratuit=billet only, basic/sur-mesure=all types
  - Server-side validation in hydrateBilletFromRequest, UI filtering in templates
- Update TASK_CHECKUP.md: all Billetterie & Commandes items now complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 00:12:30 +01:00
Serreau Jovann
207e985821 Add Messenger monitor command, uploads backup, hourly cron
- MonitorMessengerCommand: checks failed messages, emails admin with details
- Backup script: add /public/uploads tar.gz alongside DB dump
- Ansible: cron every hour for messenger monitor
- TASK_CHECKUP: mark infrastructure tasks done

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:55:35 +01:00
Serreau Jovann
30eceeccf9 Add database backup cron every 30 minutes via Ansible
- backup.sh.j2: pg_dump via Docker, gzip, 7 days retention
- deploy.yml: create backup dir, deploy script, configure cron
- TASK_CHECKUP: mark backup as done

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:52:52 +01:00
Serreau Jovann
0dd89d1b54 Add pwa_dev and pwa_prod commands using pwa:compile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 18:59:58 +01:00
Serreau Jovann
f652665a06 Remove asset-map:compile commands, PWA bundle works dynamically via Twig
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 18:59:24 +01:00
Serreau Jovann
759436d850 Fix PWA: remove pwa:create (not exists), use asset-map:compile, fix favicon path
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 18:56:38 +01:00
Serreau Jovann
689883b434 Add favicon, PWA commands (pwa_dev/pwa_prod), and prepare PWA deployment
- Add favicon.png link and apple-touch-icon in base.html.twig
- Add theme-color meta tag (#fabf04)
- Add pwa_dev and pwa_prod Makefile commands
- Add PWA asset generation step in Ansible playbook after cache clear
- Update Caddy static paths for favicon.png, marker.png, manifest.json, sw.js

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 18:53:36 +01:00
Serreau Jovann
acc9e2a5b8 Add S/MIME private key to vault and deploy cert directory in Ansible playbook
- Store smime_private_key in encrypted vault
- Add playbook tasks: create cert directory + deploy private key with 0600 permissions
- Certificate public already in git at config/cert/certificate.pem

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:38:02 +01:00
Serreau Jovann
9420bbf357 Remove Supervisor config from Ansible playbook, messenger runs via Docker
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:30:41 +01:00
Serreau Jovann
764f596771 Fix pgbouncer config permissions: 0644 so container can read mounted files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:22:25 +01:00
Serreau Jovann
f8de531973 Fix DB wait: use shell loop instead of inline PHP to avoid Ansible escaping issues
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:21:15 +01:00
Serreau Jovann
e154df7ae1 Run composer/bun commands on host instead of Docker exec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:18:29 +01:00
Serreau Jovann
3e9875a8a6 Run composer/bun commands via Docker exec instead of host in Ansible playbook
- composer install, bun install, bun run build now run inside php container
- Reorder: start containers first, then install dependencies via docker exec
- All php bin/console and dependency commands go through Docker

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:17:06 +01:00
Serreau Jovann
8dfaf0c9c8 Fix DB connectivity: remove host network, add wait for database before migration
- Remove unused host network from docker-compose-prod template
- Add 30s wait loop for pgbouncer connectivity before running migrations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:12:04 +01:00
Serreau Jovann
de945add75 Fix PgBouncer auth: template pgbouncer.ini and userlist.txt with vault db_password
- Create pgbouncer.ini.j2 with auth_type plain (no MD5 hash needed)
- Create userlist.txt.j2 with vault db_password
- Add Ansible tasks to deploy both files before docker-compose
- Mount pgbouncer files as read-only in compose template

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:07:16 +01:00
Serreau Jovann
6b61df7597 Move docker-compose-prod.yml template generation before build step
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:31:07 +01:00
Serreau Jovann
d11c0cbea4 Convert docker-compose-prod.yml to Ansible template with vault secrets
- Create docker-compose-prod.yml.j2 with vault variables (db_password, redis_password, meilisearch_master_key)
- Add template deployment step in playbook after stop containers
- Remove docker-compose-prod.yml from git tracking (now generated by Ansible)
- Add docker-compose-prod.yml to .gitignore

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:26:38 +01:00
Serreau Jovann
2dc4326b66 Add build_prod step to Ansible playbook before pulling images
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:23:55 +01:00
Serreau Jovann
12eadc95a9 Simplify Ansible playbook: remove Cloudflare tasks, add git pull and direct dependency install
- Remove entire Cloudflare configuration play (DNS, SSL, HSTS, bot management)
- Replace make install_prod with direct git pull, composer install, bun install, bun run build
- Add Docker image pull step before container restart
- Keep server deployment: env, containers, migrations, cache, uploads, Caddy, Supervisor

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:05:07 +01:00
Serreau Jovann
198d684fb8 Add organizer pages, SEO breadcrumbs, Open Graph, homepage redesign, and infrastructure updates
- Add public organizers list page (/organisateurs) with neo-brutalist card grid, social icons, and logo display
- Add organizer detail page (/organisateur/{id}-{slug}) with company info, SIRET, email, address, social links, and events placeholder
- Add slug-based URLs with 301 redirect on wrong slug, getSlug() method on User entity
- Add "Voir les evenements" button on organizer cards linking to detail page
- Add JSON-LD BreadcrumbList to all 17 pages that were missing breadcrumbs (login, forgot_password, register_success, email_verified, legal/*, attestation/*, account/*)
- Add Open Graph meta tags (og:title, og:description, og:image, og:type, og:locale, og:site_name) in base.html.twig with automatic inheritance from title/description blocks
- Add og:image with organizer logo on detail page
- Update sitemap: add /organisateurs to sitemap-main, generate organizer detail URLs in sitemap-orgas with logo images
- Update navbar to highlight "Organisateurs" on detail pages
- Redesign homepage with hero section, marquee, stats counters, how-it-works, and CTA sections
- Add Tailwind v4 @source "../templates" directive to app.scss and admin.scss
- Migrate Flysystem from S3 to local storage (uploads/events, uploads/logos)
- Update Liip Imagine config with FormatExtensionResolver for webp conversion
- Add User entity social fields (website, facebook, instagram, twitter, tiktok), logo upload (Vich), __serialize/__unserialize for session safety
- Add account page settings tab with profile, logo upload, and social media for organizers
- Add Stripe Connect status display and sub-account management in account page
- Delete WebpExtensionSubscriber (replaced by FormatExtensionResolver)
- Add migration for social fields and logo columns
- Add deploy.yml chmod tasks for uploads directories
- Add HomeController tests (detail success, wrong slug redirect, 404 cases)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 10:44:31 +01:00
Serreau Jovann
46a84a9f9a Merge deploy playbooks, add env.local template, update MailerService
- Fuse deploy-caddy.yml and cloudflare.yml into deploy.yml
- Add env.local.j2 template for production secrets
- Vault: add all production secrets
- Workflow: single deploy.yml playbook
- MailerService: rewrite with S/MIME signing, email tracking, unsubscribe
- ngrok-sync: run as root for .env.local write access
- Fix domain references to ticket.e-cosplay.fr

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 21:24:31 +01:00