Commit Graph

241 Commits

Author SHA1 Message Date
Serreau Jovann
0cf1160853 Reduce AccountController to 20 methods, remove unused AdminOrdersController constant
- 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>
2026-04-03 09:35:26 +02:00
Serreau Jovann
bc31040d81 Run php-cs-fixer: fix line endings and code style
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:40:15 +02:00
Serreau Jovann
7ec4e95a05 Extract duplicated slug regex into SLUG_PATTERN constant in HomeController
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:35:46 +02:00
Serreau Jovann
e04d615c6d Split AccountController (55 methods) and AdminController (30 methods) into smaller controllers
AccountController split into 3:
- AccountController (20 public methods): dashboard, settings, Stripe,
  sub-accounts, event CRUD, exports, payouts
- AccountEventCatalogController (13 public methods): categories, billets,
  design, event toggles, QR code
- AccountEventOperationsController (9 public methods): invitations,
  accreditations, ticket ops, attestation, order cancel/refund

AdminController split into 2:
- AdminController (20 public methods): dashboard, users, buyers,
  organizers, invitations, infra
- AdminOrdersController (10 public methods): orders, events, exports,
  logs, analytics

Shared helpers extracted to AccountEventOwnershipTrait (requireEventOwnership,
requireStripeReady). All route paths and names unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:28:47 +02:00
Serreau Jovann
83f2f40a91 Add missing test coverage for MeilisearchService, AnalyticsCryptoService, AccountController and AdminController
- 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>
2026-04-01 20:19:52 +02:00
Serreau Jovann
25354f9052 Fix decodeAndVerifyHash to have only 2 returns by merging base64 decode and pipe check guards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:02:01 +02:00
Serreau Jovann
1b3371cb7f Add comprehensive test coverage for AttestationController, LegalController, AdminController, AccountController and AnalyticsEvent entity
- 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>
2026-04-01 18:41:18 +02:00
Serreau Jovann
626510e692 Add force validate button in admin orders + fix Stripe Connect account in sync
- 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>
2026-04-01 14:22:56 +02:00
Serreau Jovann
02519dcfa8 Add pending orders reconciliation to stripe:sync command
- 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>
2026-04-01 14:14:29 +02:00
Serreau Jovann
381acd603e Split Stripe webhooks into 2 endpoints: insta (payments) and leger (Connect)
- /stripe/webhook → /webhooks/stripe/insta (paiements, payouts, disputes, subscriptions)
- /stripe/webhook/connect → /webhooks/stripe/leger (gestion comptes Connect)
- Rename env vars: STRIPE_WEBHOOK_SECRET → STRIPE_WEBHOOK_SECRET_INSTA,
  STRIPE_WEBHOOK_SECRET_CONNECT → STRIPE_WEBHOOK_SECRET_LEGER
- Update StripeService, CsrfProtectionSubscriber, vault, env files and all tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 14:07:49 +02:00
Serreau Jovann
97ef920514 Ignore CSP violations from browser userscripts (source-file: user-script)
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>
2026-04-01 11:33:09 +02:00
Serreau Jovann
2f2da97f68 Fix code style: add blank line before constructor in AttestationController
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 08:58:07 +02:00
Serreau Jovann
3a40de1ba0 Reduce return counts and fix code smells across controllers and services
- 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>
2026-03-28 20:23:08 +01:00
Serreau Jovann
0df78f75ae Fix self-referencing constant NO_SNAPSHOT_MSG in AdminController
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>
2026-03-28 19:06:51 +01:00
Serreau Jovann
ea50f8e740 Add POST /api/account/lookup route for account lookup by email
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>
2026-03-28 19:03:15 +01:00
Serreau Jovann
f2f8b31d6e Reduce method returns and cognitive complexity across controllers
- 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>
2026-03-26 22:22:41 +01:00
Serreau Jovann
5062f356f1 Extract DQL_EXCLUDE_INVITATIONS and NO_SNAPSHOT_MSG constants in AdminController
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 22:20:36 +01:00
Serreau Jovann
25564e4244 Fix all PHPStan errors: add missing iterable types and fix CacheInterface::clear()
- Add @param array<string, mixed> to AnalyticsController::createVisitor()
- Add @param/@return array<string, mixed> to AnalyticsCryptoService encrypt/decrypt
- Add @param array<string, mixed>|null to InfraService calcCpuPercent/calcMemory
- Merge duplicate docblocks on InfraService::calcMemory()
- Use intersection type CacheInterface&CacheItemPoolInterface for MeilisearchService cache

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:57:21 +01:00
Serreau Jovann
af6cea00a8 Fix PHPStan errors and SonarQube warnings
- Remove redundant && $fix checks in MeilisearchConsistencyCommand (always true after early return)
- Add @param PHPDoc for Attestation::__construct $payload parameter
- Extract PDF_SUFFIX constant to avoid duplicated ".pdf"" literal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:53:48 +01:00
Serreau Jovann
d77ee25bcf Apply PHP CS Fixer code style fixes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:40:52 +01:00
Serreau Jovann
e0245afca5 Refactor AccountController: extract constants, reduce returns and cognitive complexity
- Add DQL_EXCLUDE_INVITATIONS, DQL_BB_EXCLUDE_INVITATIONS, CONTENT_TYPE_PDF constants
- Reduce createAccreditation from 4 to 3 returns by combining validations
- Extract collectAttestationBillets, buildAttestationStats, buildAttestationTicketDetails
  from eventAttestation to reduce cognitive complexity from 18 to under 15
- Remove unused $totalRevenue, duplicate $label, and securityKey from attestation details

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:39:20 +01:00
Serreau Jovann
02cce5608c Fix MeilisearchConsistencyCommand: remove unused variable, redundant array_values, always-true condition
- Remove unused $indexes variable (listIndexes() kept as health check)
- Make $fixMissing parameter required (always passed), remove && $fixMissing check
- Remove redundant array_values() on array_map result (already a list)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:36:14 +01:00
Serreau Jovann
176b70650b Add SRI integrity hashes for CDN scripts and replace md5 with xxh128 for cache keys
- Add integrity/crossorigin attributes to chart.js and html5-qrcode CDN scripts
- Replace md5() with hash('xxh128') for Meilisearch cache key generation (non-sensitive context)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:06:00 +01:00
Serreau Jovann
2d14075d54 Fix infra page: add missing error key for redis/postgres/pgbouncer defaults
Template accesses redis_global.error, postgres.error and pgbouncer.error
in the else branch when connected=false, but the key was not provided.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:27:52 +01:00
Serreau Jovann
e8d2744333 Fix infra page crash when snapshot missing: provide default server values
Template accesses server.hostname etc. but server was passed as empty array
when infra.json does not exist. Now provides all expected keys with '?' defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 18:13:26 +01:00
Serreau Jovann
58e139e261 Apply PHP CS Fixer code style fixes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:19:15 +01:00
Serreau Jovann
4caff2e032 Fix SonarQube warnings: reduce returns, extract constants, fix viewport
- Reduce requireStripeReady() from 4 returns to 2 by combining conditions
- Extract SCANNER_PATH constant in ScannerController to avoid duplicated "/scanner/" literal
- Remove user-scalable=no from scanner viewport meta (WCAG accessibility)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:16:36 +01:00
Serreau Jovann
d8508f3c96 Refactor MeilisearchConsistencyCommand: reduce cognitive complexity and extract constants
- Extract filterUsers() and createUserIndex() from checkUserIndex() to reduce cognitive complexity from 16 to under 15
- Add INDEXES_ENDPOINT constant to replace duplicated "/indexes" literal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:15:41 +01:00
Serreau Jovann
61946f724e Fix scanner service worker CDN URL and clean up MeilisearchConsistencyCommand
- Replace unpkg.com with cdn.jsdelivr.net in sw.js cache list
- Fix sw.js scope to /scanner/
- Remove unused $indexes parameter from checkAllIndexes()
- Extract duplicated " [%s] Index missing" literal to INDEX_MISSING_MSG constant

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:14:33 +01:00
Serreau Jovann
15616167d0 Add attestation system with digital signature, public verification, and detailed ticket listing
- Create Attestation entity with reference, signature hash (HMAC-SHA256), event, user, payload
- Add migration Version20260326180000 for attestation table
- Save each attestation in DB with unique signature for tamper-proof verification
- Add public route /attestation/ventes/r/{reference} for QR code verification (short URL)
- Keep fallback /attestation/ventes/{hash} route for base64-signed verification
- Public page shows "Attestation conforme" with signature proof, no detailed data
- QR code on PDF now uses short reference URL instead of full base64 hash (scannable)
- Increase QR code resolution to 300px for better readability
- Display verification URL on PDF next to QR code

Attestation PDF improvements:
- Rename "ATTESTATION DE VENTES" to "ATTESTATION"
- Add two modes: "Attestation detaillee" (with ticket list) and "Attestation simple" (certification only)
- Simple mode: certifies figures are valid, only paid billets/votes confirmed by Stripe count
- Detailed mode: adds full ticket listing with reference, order number, billet name, buyer name
- No amounts displayed in either mode
- Gold color scheme (#fabf04) for headers, borders, table headers, summary box
- Larger text in QR verification box for readability

Scanner: ROLE_ROOT buyer tickets always validate at scan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:13:40 +01:00
Serreau Jovann
822bf8915f Scanner: SSO login, 2 scan modes (camera/security key), sound feedback, order details, force validation, staff/exposant badges
- Add SSO login button to scanner PWA with Keycloak redirect flow via session state
- Add manual scan mode via security key (16 chars) alongside QR camera scan
- Add audio feedback: good (accepted), warning (already scanned), refused sounds
- Add unique scan counter per reference (no double counting same ticket)
- Add order details display in scan results (order number, email, total, items)
- Add force validation button for refused tickets (organizer/ROLE_ROOT only), sends email notification
- Add already_scanned warning only for same-day scans, exit_definitive only same day
- Staff and exposant tickets always validate regardless of state

API: ROLE_ROOT access to all events, categories, billets, and scan endpoints

- ROLE_ROOT bypasses ownership checks on all /api/live/* endpoints
- ROLE_ROOT can login via API (email/password and SSO)
- Scan API accepts securityKey parameter in addition to reference
- Scan response includes billetType, buyerEmail, and full order details with items

Event management: tickets tab, staff/exposant accreditations, attestation PDF

- Add Tickets tab listing all sold tickets with search, download PDF, resend email, cancel actions
- Add Staff/Exposant accreditation form in Invitations tab, generates dedicated non-buyable billet
- Add Attestation tab to generate sales certificate PDF with category/billet selection
- PDF billet template shows STAFF/EXPOSANT badge with distinct colors (black/purple)
- Exclude invitations from all financial stats (event stats, admin dashboard, organizer finances)
- Fix sold counts to exclude invitations in categories recap
- Use actual Stripe fee parameters instead of hardcoded values in commission calculations
- Add commission detail breakdown (E-Ticket + Stripe) in categories and stats tabs

Admin: download tickets for orders

- Add download button on admin orders page (single PDF or ZIP for multiple tickets)

Scanner PWA fixes: CSP (unpkg -> jsdelivr), service worker scope (/scanner/)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:50:42 +01:00
Serreau Jovann
832387876e Fix robots.txt: replace /track/ with /t/ for analytics endpoint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 13:01:30 +01:00
Serreau Jovann
19b973ebc0 Allow indexing of legal pages in robots.txt
Mentions legales, CGU, CGV, hebergement, cookies, RGPD, conformite
are now allowed for crawling instead of disallowed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 13:00:06 +01:00
Serreau Jovann
83583e0d3d Enable Doctrine L2 cache and add Redis cache pools
Doctrine Second Level Cache (NONSTRICT_READ_WRITE) on:
- Event, User, Category, Billet, BilletDesign
- Default region: 1h TTL, short_lived region: 5min TTL

Redis cache pools added:
- app.cache.events (30min) — for event listings
- app.cache.homepage (5min) — for homepage data
- doctrine.result_cache_pool — DQL result cache via Redis
- doctrine.system_cache_pool — metadata/query cache

All pools backed by Redis DB 2. Reduces DB queries significantly
for read-heavy pages (event listings, user profiles, categories).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:50:13 +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
a139f86b90 Make analytics endpoint dynamic: /t/{token} derived from APP_SECRET
The endpoint path is now /t/<8-char hash of APP_SECRET> instead of
static /t. Token is injected via data-e attribute on body, read by JS.
Server validates token on every hit, returns 404 if invalid.
Changes with each APP_SECRET = impossible to hardcode in a blocker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:22:59 +01:00
Serreau Jovann
d4c897fd89 Fix PostgreSQL SUBSTRING on timestamp: use CAST AS DATE with native SQL
SUBSTRING does not work on timestamp in PostgreSQL. Use native SQL
with CAST(created_at AS DATE) for daily chart aggregation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:15:06 +01:00
Serreau Jovann
375357ddde Add charts and bounce rate to admin Analytics, filter self-referrers
- Bar chart: visitors per day
- Line chart: pageviews per day (with fill)
- Bounce rate KPI with color coding (green/yellow/red)
- Filter out self-referrers (ticket.e-cosplay.fr, esyweb.local)
- Uses Chart.js via cdn.jsdelivr.net (already in CSP)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:14:33 +01:00
Serreau Jovann
efe967389d Add bounce rate to admin Analytics dashboard
Bounce rate = visitors with only 1 pageview / total visitors.
Color-coded: green <40%, yellow <60%, red >=60%.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:13:08 +01:00
Serreau Jovann
3945fbb0ef Send email when no RGPD data found, add DPO contact to PDFs
- Send confirmation email when no data found for access or deletion request
- Add DPO contact (DPO-167945, E-Cosplay) to both access and deletion PDFs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:11:25 +01:00
Serreau Jovann
c9a040804e Fix Uuid class not found: use native PHP UUID v4 generation
symfony/uid not installed, replace Uuid::v4() with random_int based
UUID v4 generation (RFC 4122 compliant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:07:42 +01:00
Serreau Jovann
ce1b153cc1 Fix AES-GCM format mismatch between JS Web Crypto and PHP openssl
Web Crypto API AES-GCM outputs: iv + ciphertext + tag (tag appended)
PHP openssl was using: iv + tag + ciphertext (tag in middle)

Now both use the same format: iv (12 bytes) + ciphertext + tag (16 bytes).
Decrypt tries JS format first, falls back to PHP format for compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 12:07:08 +01:00
Serreau Jovann
2ae28089d5 Add RGPD data access/deletion forms and admin Analytics dashboard
RGPD (/rgpd):
- Access form: search by IP, generate PDF with all visitor data, email it
- Deletion form: delete all visitor data by IP, generate attestation PDF
- Both forms pre-fill client IP, require email for response
- PDF templates with E-Cosplay branding, RGPD article references

Admin Analytics (/admin/analytics):
- KPIs: unique visitors, pageviews, pages/visitor
- Top pages and referrers tables
- Device type, browser, OS breakdowns
- Period filter: today, 7d, 30d, all

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:59:34 +01:00
Serreau Jovann
6438afadbf Add first-party analytics tracker with encrypted transmissions
Core system:
- AnalyticsUniqId entity (visitor identity with device/os/browser parsing)
- AnalyticsEvent entity (page views linked to visitor)
- POST /t endpoint with AES-256-GCM encrypted payloads
- HMAC-SHA256 visitor hash for anti-tampering
- Async processing via Messenger
- JS module: auto page_view tracking, setAuth for logged users
- Encryption key shared via data-k attribute on body
- setAuth only triggers when cookie consent is accepted
- Clean CSP: remove old tracker domains (Cloudflare, Umami)

100% first-party, no cookies, invisible to adblockers, RGPD-friendly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:07 +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
8db44017d2 Redesign admin Infra page: full-screen 4-column layout with Docker stats
Complete rewrite of /admin/infra into 4 columns:
- Col 1 (Serveur): CPU, RAM, Disk, System, Services (Caddy, Docker, SSL cert)
- Col 2 (Containers): All Docker containers with CPU%, RAM, state via Docker API
- Col 3 (Redis): Global stats + per-DB (Messenger, Sessions, Cache)
- Col 4 (PostgreSQL): Instance stats + PgBouncer pools/stats

Extract all infra logic into InfraService. Mount Docker socket (read-only)
in PHP container for container stats. Check SSL cert expiry and Caddy status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:51:04 +01:00
Serreau Jovann
ae338bb74a Fix PgBouncer stats: fallback to pgbouncer:6432 when DATABASE_URL points to postgres directly
If DATABASE_URL uses port 5432 (direct postgres), the PgBouncer stats
connection falls back to pgbouncer:6432. Fixes error when .env.local
overrides DATABASE_URL to bypass pgbouncer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:42:53 +01:00
Serreau Jovann
08433321d0 Add Server stats (CPU, RAM, Disk, System) to admin Infra page
First row shows host-level stats from /proc: CPU model, cores, load
average with charge %, RAM total/used/available with usage %, disk
total/used/free with usage %, hostname, OS and uptime. All color-coded
green <70%, yellow <90%, red >=90%.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:41:41 +01:00
Serreau Jovann
d7cecfe9ef Fix PgBouncer admin console: enable PDO emulated prepares
PgBouncer admin console does not support extended query protocol
(prepared statements). Setting PDO::ATTR_EMULATE_PREPARES = true
forces PDO to use simple query protocol for SHOW commands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:40:21 +01:00
Serreau Jovann
7e53e3343b Add PHP container stats to admin Infra page
Shows per-container: hostname, PHP version, SAPI, uptime, CPU cores,
CPU usage % (sampled from cgroup), load averages (1/5/15m), RAM used/
total/free with usage %. Color-coded: green <70%, yellow <90%, red >=90%.
Reads from cgroup v2 (fallback v1) and /proc for container-level stats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:38:33 +01:00