- .gitea/workflows/deploy.yml: the bot user on the new prod host has
fish as its login shell, which rejects bash syntax (set -e, VAR=...,
$(...), trap, process substitution). Wrap the entire deploy script
in `bash -c '...'` so fish only spawns a bash subprocess and the
script itself is parsed by bash.
- Forward DEPLOY_PATH alongside VAULT_PASS through appleboy/ssh-action
envs: so the bash subprocess inherits both, instead of interpolating
the secret directly into the rendered script (where masking would
collide with the cd argument).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- .gitea/workflows/deploy.yml: stop interpolating ANSIBLE_VAULT_PASSWORD
directly into the remote script (the runner masks the secret with ***
which broke the <(echo '...') process substitution at runtime)
- inject the password as VAULT_PASS through appleboy/ssh-action's
envs: forwarding so it never appears in the rendered script
- on the remote, write it to a mktemp file with chmod 600 and remove
the file via trap on EXIT, then point ansible-playbook
--vault-password-file at that temp file
- use printf '%s' instead of echo to avoid adding a stray newline to
the vault password
- add set -e so the script fails fast if any step errors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- .gitea/workflows/deploy.yml: replace manual ssh key setup + raw ssh
command with the appleboy/ssh-action@v1.0.0 action
- host, user, key and deploy path are now read from Gitea secrets
(SSH_HOST, SSH_USER, SSH_PRIVATE_KEY, DEPLOY_PATH) instead of being
hard-coded in the workflow
- ansible-playbook command and vault password file (process substitution
fed by ANSIBLE_VAULT_PASSWORD) are kept identical, only the transport
changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- .env, .env.test, ansible/env.local.j2: point SONARQUBE_URL to https://sn.e-cosplay.fr
- ansible/vault.yml, .env: rotate sonarqube_badge_token to new value
- .gitea/workflows/ci.yml, sonarqube.yml: remove OWASP Dependency-Check steps and force sonar.host.url via CLI args
- sonar-project.properties: drop dependencyCheck report paths
- .gitea/workflows/deploy.yml: switch SSH target from 34.90.187.4 to 152.228.222.133
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Remplace 'cp -p' par 'cp -rp' lors de la copie de config/cert/.
Sans l'option -r, cp omet le repertoire avec l'erreur:
cp: -r non specifie ; omission du repertoire 'config/cert/.'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
backup.sh genere un bundle de migration tar.gz autonome contenant:
- dump PostgreSQL gzippe (pg_dump --clean --if-exists --no-owner)
- archive public/uploads (events, billets, logos) repere via flysystem.yaml
- archive var/ (payouts, billets, invoices, unsubscribed.json)
- .env.local et config/cert/ (clef S/MIME), exclus si SKIP_SECRETS=1
- manifest.txt avec metadata (host, git commit/branche, date) et checksums SHA-256
restore.sh restaure un bundle sur un nouveau serveur:
- verifie les checksums SHA-256 avant toute action
- affiche un plan de restauration et demande confirmation (sauf --yes)
- options --skip-db, --skip-uploads, --skip-var, --skip-secrets
- cree des sauvegardes de securite avant ecrasement (.env.local, public/uploads, var/)
- verifie que le service db-master tourne avant restore PostgreSQL
- rappelle les etapes post-migration (migrate_prod, clear_prod)
Les deux scripts utilisent des variables d'env override (COMPOSE_FILE, DB_SERVICE,
DB_USER, DB_NAME) et s'appuient sur docker-compose-prod.yml par defaut.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove syncPendingOrders and its helpers (handleSucceeded, handleCancelled, handleFailed) from StripeSyncCommand
- Clean up unused dependencies (BilletOrderService, MailerService, AuditService, BilletBuyer)
- Add PESSIMISTIC_WRITE lock in handlePaymentIntentSucceeded to prevent duplicate ticket generation when Stripe sends concurrent webhook calls
- Update tests to match simplified command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The ping_threshold option is not a valid framework.mailer configuration key in Symfony Mailer.
This caused composer install to fail during cache:clear on deployment.
The option should be set as a DSN query parameter on the MAILER_DSN environment variable instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set ping_threshold to 10 seconds so Symfony checks and reconnects stale
SMTP connections before reuse, fixing "451 4.4.2 Timeout waiting for
data from client" errors from Amazon SES.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Creates a paid order with BilletOrder tickets before generating the
attestation, exercising the soldCounts loop (line 503) and ticket
details loop (lines 546-551) in AccountEventOperationsController.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move export, exportPdf, payoutPdf from AccountController to
AccountEventOperationsController (9 -> 12 methods)
- Remove getAllowedBilletTypes delegate from AccountController
- Update tests to reference AccountEventCatalogController for that method
- Remove unused DQL_EXCLUDE_INVITATIONS from AdminOrdersController
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Creates AnalyticsUniqId and AnalyticsEvent records in DB so the
daily chart aggregation loops (visitorsPerDay, pageviewsPerDay,
allDays merge, DateTimeInterface check) are fully exercised.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mock generateOrderTickets to create a real BilletOrder in DB so the
foreach loop setting isInvitation=true is exercised and verified.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add <thead>/<th> to rate limiting and error codes tables in doc.html.twig
- Ignore SonarQube css:S4662 rule on SCSS files (Tailwind v4 @source)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add proper <thead> with <th> headers to tables in email templates:
order_cancelled_orga, order_notification_orga, order_refunded,
organizer_invitation, payment_failed, scan_force_notification
- Add proper <thead> with <th> headers to tables in PDF templates:
attestation_ventes, billet, export_recap, invoice
- Fix testInfraPageWithSnapshotData: provide complete server data
(os, uptime, cpu, ram, disk, services, ssl) required by the template
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensures 100% coverage regardless of which provider (c8/istanbul/v8)
is used by the CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- MeilisearchServiceTest: add test for invalidateSearchCache()
- AnalyticsCryptoService: mark unreachable tryDecryptJsFormat guard
with @codeCoverageIgnore (decrypt already checks strlen >= 28)
- AccountControllerTest: add test for tickets search query (tq param)
- AdminControllerTest: add test for infra page with snapshot data file
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add test for navigator.language falsy branch
- Add test for retry getOrCreateVisitor failing on second attempt
- Mark unreachable defensive guards (encrypt/decrypt/send with null encKey)
with c8 ignore since they cannot be triggered via public API
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AttestationControllerTest: add required template keys (ref, organizer,
generatedAt, etc.) to test payloads so check_ventes.html.twig renders
- StripeSyncCommandTest: add getAccount() mock to event in
testPendingOrderFailedWithoutEmail so order is not skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The audit->log() call in handleFailed was not configured on the mock,
causing an exception caught by the try/catch which prevented setStatus
from being called.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PHPUnit strict mock validation rejects stdClass as return value for
retrievePaymentIntent which declares Stripe\PaymentIntent return type.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AttestationController: fix decodeAndVerifyHash to have max 3 returns, add 11 tests covering all routes (check, ventesRef, ventes) and all decodeAndVerifyHash branches (invalid base64, missing pipe, bad signature, bad JSON, valid hash with/without registered attestation), plus generateHash unit tests with unicode
- LegalController: add 6 tests for RGPD POST routes (rgpdAccess and rgpdDeletion) covering empty fields, data found, and no data found scenarios
- AdminController: add 10 tests for analytics page (all period filters + access denied) and orderTickets endpoint (single ticket PDF, multiple tickets ZIP, order not found, no tickets)
- AccountController: add 17 tests for downloadTicket (success/denied/404), resendTicket (success/denied/404), cancelTicket (success/denied/404), createAccreditation (staff/exposant/empty fields/no categories/invalid type), eventAttestation (with categories/billets/empty selection)
- AnalyticsEvent entity: new test file with 8 tests covering constructor defaults, all getters/setters, nullable fields, and fluent interface
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add POST /admin/commandes/{id}/forcer-validation to force validate pending
orders (generates tickets, sends emails, notifies organizer)
- Add "Forcer validation" button in orders template for pending orders
- Fix retrievePaymentIntent to query on organizer's Connect account
- Update stripe:sync to pass organizer stripeAccountId when checking payments
- Add 3 tests for force validation (pending, non-pending, not found)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add retrievePaymentIntent() to StripeService
- StripeSyncCommand now checks pending orders against Stripe API:
- succeeded: generates tickets, sends emails, notifies organizer
- canceled: marks order as cancelled + audit log
- requires_payment_method: marks as cancelled + audit + failure email
- other statuses: logs as still pending
- Add 13 tests covering accounts sync + all pending order scenarios
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 'user-script' to ignored source files in CspReportController to filter
out false positive CSP violations triggered by browser extensions/userscripts.
Add corresponding test case.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Test token validation (invalid token returns 404)
- Test request validation (missing body, missing 'd' field, invalid JSON return 400)
- Test decryption validation (invalid encrypted data returns 403)
- Test new visitor creation with full fields, optional fields, mobile/tablet UA
- Test page view dispatch with valid hash, default values
- Test page view rejection with invalid/missing hash (403)
- Test setUser dispatch with valid hash
- Test visitor UID format (UUID v4), IP hash, UA truncation, language truncation
- Test response hash is verifiable by crypto service
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add responsive breakpoints (sm/md) to event_detail.html.twig: adaptive titles, stacked ticket layout on mobile, reduced padding/spacing
- Add responsive breakpoints to order templates (guest, summary, public, payment, success): adaptive typography, padding, and layouts
- Fix BreadcrumbList JSON-LD: escape names with json_encode, remove item URL from last breadcrumb
- Update deploy.yml cron schedule from 3h/13h/19h/23h to 1h/22h
- Add <title> tags to rgpd_deletion.html.twig and rgpd_access.html.twig
- Add scope attributes to all <th> tags in rgpd_access.html.twig
- Replace deprecated width/cellpadding/cellspacing HTML attributes with CSS in scan_force_notification email
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ApiAccountController::lookup: reduce from 4 to 3 returns via ternary
- AttestationController::ventes: reduce from 5 to 2 returns by extracting
decodeAndVerifyHash() helper; add TPL_NOT_FOUND_VENTES constant for the
template literal duplicated 5 times
- AnalyticsCryptoService::decrypt: reduce from 4 to 2 returns by extracting
tryDecryptJsFormat() helper
- InfraService::fmtDuration: reduce from 4 to 1 return using match(true)
- InfraService: replace nested ternary with match(true) for SSL status
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The constant was incorrectly defined as self::NO_SNAPSHOT_MSG = self::NO_SNAPSHOT_MSG
causing a PHP fatal error. Replace with the actual string value.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New API endpoint secured by X-App-Secret header (no JWT auth required).
Accepts an email in the request body and returns the user's id and
stripeAccountId if present. Includes 6 unit tests covering all cases
(success, missing secret, invalid secret, missing email, user not found).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AnalyticsController::track: extract handleTrackData(), reduce from 7 to 3 returns
- ApiAuthController::ssoValidate: extract ssoError/ssoSuccess helpers, reduce from 6 to 3 returns
- ApiLiveController::scan: extract findTicketFromRequest(), reduce from 4 to 3 returns
- ApiLiveController::scanForce: flatten logic, reduce from 6 to 3 returns
- ApiLiveController::processScan: extract isAlwaysValidTicket, checkRefusal,
markScannedAndRespond, reduce cognitive complexity from 16 to under 15
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ArrayAdapter implements both CacheInterface and CacheItemPoolInterface,
matching the intersection type on the constructor parameter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>