Logs de diagnostic et rapprochement SumUp ↔ bénévole
ci-cd / build-backend (push) Successful in 38s
ci-cd / build-frontend (push) Successful in 20s
ci-cd / deploy (push) Successful in 13s

- Une ligne de log par requête (route modèle, statut, durée, code d'erreur,
  compte), identifiant X-Request-ID repris dans chaque log et affiché à
  l'utilisateur sous une erreur, exceptions non gérées tracées (500 JSON)
- Journal d'accès d'uvicorn coupé : il exposait le jeton du formulaire public
- Logs métier : paiement SumUp, connexion, import, plannings, solveur
- Nom du bénévole dans la description du checkout SumUp
- Code et identifiant de transaction SumUp enregistrés au paiement
  (migration 0023), affichés sur la fiche et dans l'export Excel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AdamThomas
2026-10-12 18:54:35 +02:00
co-authored by Claude Opus 5.5
parent d722a81a87
commit d0fa1e109c
27 changed files with 798 additions and 16 deletions
+6 -2
View File
@@ -70,7 +70,11 @@ export function createHttpApi(baseUrl: string): PlanningApi {
? ((detail as { violations: [] }).violations ?? [])
: [];
if (res.status === 401 && !path.includes('/auth/')) notifyUnauthorized();
throw new ApiError(res.status, code, violations);
const requestId = res.headers.get('X-Request-ID');
if (res.status >= 500) {
console.error(`API ${res.status} ${path} — référence ${requestId ?? '?'}`, detail);
}
throw new ApiError(res.status, code, violations, requestId);
}
return body as T;
}
@@ -99,7 +103,7 @@ export function createHttpApi(baseUrl: string): PlanningApi {
// Corps non JSON (ex. erreur réseau) : garde le code générique.
}
if (res.status === 401) notifyUnauthorized();
throw new ApiError(res.status, code, []);
throw new ApiError(res.status, code, [], res.headers.get('X-Request-ID'));
}
return res.blob();
}
+4
View File
@@ -299,6 +299,8 @@ export function buildMockState(): MockState {
mecenat_country: null,
fee_payment_status: null,
fee_paid_at: null,
fee_transaction_code: null,
fee_transaction_id: null,
created_at: NOW,
updated_at: NOW,
});
@@ -316,6 +318,8 @@ export function buildMockState(): MockState {
fee_amount: mecenat ? 40 : 15,
fee_payment_status: 'PAID',
fee_paid_at: v.created_at,
fee_transaction_code: `TDEMO${String(i).padStart(5, '0')}`,
fee_transaction_id: `00000000-0000-4000-8000-${String(i).padStart(12, '0')}`,
...(mecenat
? { mecenat_address: '1 rue du Festival', mecenat_city: 'Rennes', mecenat_postal_code: '35000', mecenat_country: 'France' }
: {}),
+8
View File
@@ -244,6 +244,8 @@ function promotePending(pending: MockPendingSubmission): VolunteerRead {
mecenat_country: pending.mecenatCountry,
fee_payment_status: 'PAID',
fee_paid_at: now,
fee_transaction_code: `TMOCK${String(state.seq).padStart(5, '0')}`,
fee_transaction_id: `mock-transaction-${state.seq}`,
created_at: now,
updated_at: now,
};
@@ -1005,6 +1007,8 @@ export function createMockApi(): PlanningApi {
mecenat_country: null,
fee_payment_status: null,
fee_paid_at: null,
fee_transaction_code: null,
fee_transaction_id: null,
created_at: now,
updated_at: now,
};
@@ -1310,6 +1314,8 @@ export function createMockApi(): PlanningApi {
mecenat_country: null,
fee_payment_status: null,
fee_paid_at: null,
fee_transaction_code: null,
fee_transaction_id: null,
created_at: now,
updated_at: now,
};
@@ -1778,6 +1784,8 @@ export function createMockApi(): PlanningApi {
mecenat_country: null,
fee_payment_status: null,
fee_paid_at: null,
fee_transaction_code: null,
fee_transaction_id: null,
created_at: now,
updated_at: now,
};
+6
View File
@@ -117,6 +117,12 @@ export interface VolunteerRead {
fee_amount: number | null;
fee_payment_status: FeePaymentStatus | null;
fee_paid_at: string | null;
/** Transaction SumUp qui a réglé la cotisation : `fee_transaction_code` est
* le code affiché dans le tableau de bord SumUp (ex. `TEENSK4W2K`),
* `fee_transaction_id` l'identifiant technique. `null` tant que non payée
* (ou payée avant que l'appli ne l'enregistre). */
fee_transaction_code: string | null;
fee_transaction_id: string | null;
/** Coordonnées pour un reçu fiscal (cerfa) — seulement si payé via un type
* de paiement « mécénat ». */
mecenat_address: string | null;
+3
View File
@@ -233,6 +233,9 @@ export class ApiError extends Error {
public readonly status: number,
public readonly code: string,
public readonly violations: Violation[] = [],
/** En-tête `X-Request-ID` de la réponse : référence à communiquer pour
* retrouver l'erreur dans les logs du serveur (`app/request_logging.py`). */
public readonly requestId: string | null = null,
) {
super(code);
this.name = 'ApiError';