Réseaux : contrôle réel de validité des jetons et outils d'inspection

Un jeton de page Facebook n'a pas de date d'expiration, donc `expiryStatus`
renvoyait « never » et aucune alerte ne pouvait se déclencher. Mais il meurt
quand même : mot de passe du compte changé, rôle d'administrateur perdu sur la
page, autorisation retirée, révocation par Meta. On ne l'apprenait qu'au moment
où une publication échouait.

- `checkTokenHealth()` interroge la plateforme (`/me` côté Graph, les ACL
  d'organisation côté LinkedIn) et rend un verdict motivé.
- L'écran Réseaux sociaux contrôle à chaque affichage : pastille « Jeton
  expiré », bandeau rouge et réponse brute de la plateforme quand il est mort,
  ligne verte de confirmation sinon.
- Le verdict est persisté (`social_accounts.last_check_*`) : le tableau de bord
  alerte via `tokenHealthCached()` (fenêtre de 6 h) sans appeler la plateforme à
  chaque affichage, et croise ce signal avec la dernière tentative de
  publication refusée pour motif d'autorisation.
- Bloc « Outils pour inspecter un jeton » dans l'aide de chaque réseau :
  débogueur de jeton, outil de jetons d'accès, explorateur d'API Graph,
  documentation des jetons de longue durée — en précisant que le bouton
  Connecter fait déjà l'échange court → longue durée → jeton de page, et qu'il
  n'y a aucun jeton à copier à la main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
This commit is contained in:
Claude committed 2026-09-06 10:43:11 +00:00
1 parent 17511a73c3
commit 81b99ed5fb
8 files changed
+1618 -16

No files matched your search

+5
View File
@@ -155,6 +155,11 @@ export const socialAccounts = pgTable("social_accounts", {
targetName: varchar("target_name", { length: 200 }),
connectedById: integer("connected_by_id").references(() => users.id, { onDelete: "set null" }),
connectedAt: timestamp("connected_at", { withTimezone: true }),
// Dernier contrôle de validité auprès de la plateforme : permet au tableau de
// bord d'alerter sans refaire un appel réseau à chaque affichage.
lastCheckAt: timestamp("last_check_at", { withTimezone: true }),
lastCheckOk: boolean("last_check_ok"),
lastCheckError: text("last_check_error"),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
});