console-admin
À quoi sert cette brique
Section intitulée « À quoi sert cette brique »console-admin est le back-office interne de Titreo E-Pay — l’outil d’administration utilisé par les équipes Titreo (pas par les marchands). Elle répond à une question simple : comment crée-t-on et administre-t-on les marchands de la plateforme sans toucher à la base de données à la main ?
Ses responsabilités, toutes de scope admin :
| Responsabilité | Détail |
|---|---|
| Marchands | Créer, lister, éditer (nom, webhook, TTL de session), activer/désactiver un marchand |
| Clés API | Émettre et révoquer les clés API d’un marchand (scopes restreints, jamais admin) |
| Configs PSP | Consulter et supprimer les configs PSP d’un marchand (en lecture surtout : ces clés sont saisies par le marchand depuis sa propre console) |
| Cartes cadeaux | Consulter et retirer les accès Titréo d’un marchand. Jamais de saisie à sa place |
| Audit | Lire les journaux d’audit, globalement ou filtrés par marchand |
| Transactions | Lister les sessions de tous les marchands, voir le détail, exporter en CSV |
| Journal | Lire le journal métier, tous marchands |
| Logs techniques | Explorer les logs et reconstituer la trace d’une requête ou d’un paiement |
| Téléchargements | Retrouver les paquets à installer chez un marchand, avec leur empreinte |
| Admins Titreo | Lister et créer les comptes admin qui ont accès à cette console |
À ne pas confondre avec console-merchant : celle-ci est destinée au marchand pour gérer ses clés et ses PSP ; console-admin est l’outil interne qui voit tous les marchands.
flowchart LR A["Équipe Titreo (navigateur)"] --> B["console-admin (Next.js, port 3400)"] B -->|"server actions / RSC, cookie tep_session"| C["API E-Pay (port 3000)"] C --> D["Firebird"] E["Marchand"] -.->|"sa propre console"| F["console-merchant"] F --> C
Stack et démarrage
Section intitulée « Stack et démarrage »D’après console-admin/package.json :
- Next.js 15 (App Router) + React 19
- Zod pour valider les entrées de formulaires côté serveur
- Tailwind CSS (config
console-admin/tailwind.config.ts, tokens dansconsole-admin/app/tokens.css) - Biome pour le lint/format
- Pas de base de données ni d’ORM ici : la console est sans état, tout passe par l’API.
Scripts npm réels (package.json) :
| Script | Effet |
|---|---|
dev |
next dev -p 3400 |
build |
next build |
start |
next start -p 3400 |
typecheck |
tsc --noEmit |
lint / lint:fix |
biome check . / biome check --write . |
La console tourne sur le port 3400. Variables d’environnement :
| Variable | Rôle | Lue dans |
|---|---|---|
TITREO_API_BASE_URL |
URL de l’API E-Pay, appels côté serveur uniquement. Défaut http://localhost:3000 |
console-admin/lib/config.ts |
GRAFANA_BASE_URL |
Grafana du client. Vide : aucun lien « Ouvrir dans Grafana » | console-admin/lib/config.ts |
TITREO_CDN_INTERNAL_URL |
Adresse par laquelle le serveur lit downloads/index.json (http://cdn en production). Absente : celle de TITREO_CDN_PUBLIC_URL |
console-admin/lib/downloads.ts |
TITREO_CDN_PUBLIC_URL |
Adresse portée par les liens de téléchargement (https://cdn.<domaine> en production) |
console-admin/lib/downloads.ts |
Les deux variables du CDN sont posées par deploy/docker-compose.prod.yml.
Un Dockerfile multi-étapes (deps → builder → runner) existe à la racine de la brique et expose le port 3400. Voir aussi l’environnement de dev local.
Structure des routes
Section intitulée « Structure des routes »L’arborescence App Router (console-admin/app/) est organisée autour de deux groupes protégés, (authed) et (wide) :
app/ page.tsx -> redirige vers /merchants login/page.tsx -> formulaire de connexion healthz/route.ts -> sonde de vivacité (authed)/ layout.tsx -> requireAdmin() + Sidebar transactions/ page.tsx -> sessions de tous les marchands [sessionId]/page.tsx -> détail d'une session, lien vers sa trace export/route.ts -> relais vers /v1/admin/sessions/export.csv journal/page.tsx -> journal métier merchants/ page.tsx -> liste des marchands (+ filtre actif/en attente) new/page.tsx -> création d'un marchand [id]/ page.tsx -> aperçu + édition du marchand psp-configs/page.tsx -> configs PSP du marchand (lecture + suppression) gift-configs/page.tsx -> accès cartes cadeaux du marchand (lecture + suppression) api-keys/page.tsx -> clés API du marchand (+ create-form) audit-logs/page.tsx -> journaux d'audit du marchand downloads/page.tsx -> paquets à installer chez un marchand admin-users/ page.tsx -> liste des admins Titreo new/page.tsx -> création d'un admin (+ form) settings/page.tsx -> compte admin courant + déconnexion (wide)/ layout.tsx -> requireAdmin() + Sidebar, sans colonne de lecture bornée logs/page.tsx -> logs techniques : explorateur et traceLa navigation latérale (console-admin/components/Sidebar.tsx) expose sept entrées : Transactions, Journal, Logs techniques, Marchands, Téléchargements, Admins Titreo, Paramètres. Les écrans par marchand sont atteints via cinq onglets (console-admin/components/MerchantTabs.tsx) : Aperçu, PSP configs, Cartes cadeaux, API keys, Audit logs.
Onglet Cartes cadeaux
Section intitulée « Onglet Cartes cadeaux »app/(authed)/merchants/[id]/gift-configs/page.tsx liste les accès Titréo du marchand : fournisseur, enseigne, clé masquée, statut, date de vérification, date de mise à jour. Un accès est « Actif » s’il est actif et vérifié.
Sans accès, la page le dit : la page de paiement de ce marchand ne propose pas les cartes cadeaux. Le bouton Supprimer appelle DELETE /v1/admin/merchants/:id/gift-configs/:provider. Il n’y a pas de formulaire de saisie ; pour poser des accès à la place d’un marchand, passer par tep gift set sur le serveur.
Page Logs techniques
Section intitulée « Page Logs techniques »app/(wide)/logs/page.tsx monte components/logs/LogsApp.tsx. Deux vues : Explorateur (barre de requête à pastilles, histogramme empilé par niveau avec sélection à la souris, facettes, suivi en direct toutes les 3 s, 500 lignes par page) et Trace (couloirs par acteur, silences repliés).
Tout est décrit sur Logs techniques : routes de l’API, syntaxe de la barre de requête, activation de la collecte, limites.
La fiche d’une transaction et le journal métier mènent à la trace (/logs?view=trace&session=…, /logs?view=trace&corr=…).
Page Téléchargements
Section intitulée « Page Téléchargements »app/(authed)/downloads/page.tsx lit downloads/index.json sur le CDN (lib/downloads.ts) et liste, par paquet, toutes les versions publiées : fichier, taille, empreinte SHA-256, date, lien. La plus récente porte le badge « Dernière ».
| Paquet | Installation |
|---|---|
| Module PrestaShop | Zip à déposer dans le back-office. Il porte les adresses de la plateforme |
| SDK JavaScript / TypeScript | Par l’adresse du fichier, sans registre npm |
| SDK PHP | Par Composer, en dépôt de type « package » |
L’app Shopify n’y figure pas : c’est un service hébergé, installé par son lien d’installation.
Si l’index est illisible — aucun paquet publié, ou CDN qui ne répond pas — la page le dit et renvoie à tep plugin publish. Voir Plugin PrestaShop.
Comment la console parle à l’API
Section intitulée « Comment la console parle à l’API »Toute la logique réseau est centralisée dans console-admin/lib/api.ts. Les pages sont des Server Components et les mutations des server actions ('use server') ; le navigateur ne contacte jamais l’API directement.
sequenceDiagram
participant U as "Navigateur"
participant C as "console-admin (server)"
participant A as "API E-Pay (3000)"
U->>C: "POST login (server action)"
C->>A: "POST /v1/auth/admin/login"
A-->>C: "Set-Cookie: tep_session=..."
C-->>U: "cookie httpOnly tep_session + redirect /merchants"
U->>C: "GET /merchants"
C->>A: "GET /v1/admin/merchants (Cookie: tep_session)"
A-->>C: "{ data, nextCursor, hasMore }"
C-->>U: "HTML rendu (RSC)"
Authentification
Section intitulée « Authentification »Le flux complet de session (console-admin/lib/actions/auth.ts + console-admin/lib/auth.ts) :
loginActionposte les identifiants surPOST /v1/auth/admin/login.- L’API renvoie un cookie
tep_session; la console le ré-émet côté serveur en cookiehttpOnly,sameSite: 'strict',secure: true. - À chaque rendu,
getCurrentUser()appelleGET /v1/auth/meet exigekind === 'admin'(requireAdmin()redirige sinon vers/login). logoutActionappellePOST /v1/auth/logoutet supprime le cookie.
Le cookie est le credential bearer de l’API : console-admin/lib/api.ts le réinjecte dans l’en-tête Cookie de chaque appel sortant.
Le même fichier pose un X-Correlation-Id sur chaque appel, un identifiant par appel. En cas d’erreur, ApiHttpError.correlationId reprend celui que l’API a renvoyé ; la page de logs l’affiche sous « Référence ». Voir Traçabilité.
Endpoints admin visés
Section intitulée « Endpoints admin visés »Tous les appels sont concentrés dans console-admin/lib/actions/*.ts. Les routes consommées (vérifiées présentes côté api/src/http/routes/) :
| Server action (fichier) | Méthode + endpoint API |
|---|---|
auth.ts |
POST /v1/auth/admin/login, POST /v1/auth/logout, GET /v1/auth/me |
merchants.ts |
GET/POST /v1/admin/merchants, GET/PATCH /v1/admin/merchants/{id} |
api-keys.ts |
GET/POST /v1/admin/merchants/{id}/api-keys, DELETE …/api-keys/{keyId} |
psp-configs.ts |
GET /v1/admin/merchants/{id}/psp-configs, DELETE …/psp-configs/{provider} |
gift-configs.ts |
GET /v1/admin/merchants/{id}/gift-configs, DELETE …/gift-configs/{provider} |
events.ts |
GET /v1/admin/events, GET /v1/admin/sessions, GET /v1/sessions/{id} |
logs.ts |
GET /v1/admin/logs, GET /v1/admin/logs/histogram, GET /v1/admin/logs/labels, GET /v1/admin/traces/{correlationId}, GET /v1/admin/sessions/{id}/trace |
audit-logs.ts |
GET /v1/audit-logs (filtres merchantId, action, limit, cursor) |
admin-users.ts |
GET/POST /v1/admin/users |
Le préfixe /v1/admin/ côté API porte les opérations réservées à l’administration. Le contrat de réponse en cas d’erreur ({ code, message }) est mappé sur ApiHttpError dans lib/api.ts puis affiché dans les formulaires. Voir les contrats inter-briques pour la forme exacte des enveloppes.
Sécurité
Section intitulée « Sécurité »Deux protections explicites valent d’être notées (détails dans Sécurité) :
- CSP par nonce (
console-admin/middleware.ts) :Content-Security-Policyconstruite par requête avec un nonce surscript-srcet'strict-dynamic'. Le middleware redirige aussi vers/logintoute route non publique sans cookietep_session. La politique part enReport-Only(basculerCSP_REPORT_ONLY=falsepour l’appliquer). - Scopes de clés API verrouillés (
console-admin/lib/actions/api-keys.ts) : la liste autorisée énumère explicitement six scopes (sessions:read,sessions:write,psp:read,psp:write,webhooks:read,webhooks:write) et exclut volontairement tout scopeadmin, pour qu’une clé liée à un marchand ne puisse jamais être tout-accès.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- console-merchant — la console jumelle côté marchand
- Backend — l’API qui sert les endpoints
/v1/admin/* - Logs techniques — la page
/logsen détail - Traçabilité — journal d’appels et corrélation
- Vue d’ensemble de l’écosystème — où se place cette brique