Commit Graph
4 Commits
Author SHA1 Message Date
Claude 81b99ed5fb 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
2026-09-06 10:43:11 +00:00
Claude 17511a73c3 Connexion réseaux : adresse de retour déduite de la requête, CSP sans upgrade en HTTP
Cliquer sur « Connecter » sans avoir enregistré l'URL publique du site
renvoyait une erreur, et la redirection retombait sur https://0.0.0.0 :
la directive upgrade-insecure-requests était posée même sur une page
servie en HTTP, le navigateur basculait donc la navigation vers un
https:// inexistant.

- `publicBaseUrl()` : réglage `site_public_url`, variables d'environnement,
  puis origine de la requête (X-Forwarded-Host / X-Forwarded-Proto). L'adresse
  de retour OAuth et le lien des publications l'utilisent ; `seo-server`
  s'appuie dessus au lieu de dupliquer la déduction.
- `src/lib/site-url.ts` : déduction pure et testée de l'origine, HTTP par
  défaut pour localhost et les IP nues, HTTPS pour un nom de domaine.
- Middleware : `upgrade-insecure-requests` uniquement quand la page est servie
  en HTTPS.
- Page Réseaux : l'adresse détectée est annoncée comme valeur par défaut, avec
  le rappel que Facebook et LinkedIn exigent du HTTPS sur un domaine.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
2026-09-06 10:11:06 +00:00
Claude e86c67c17e URL publique du site réglable depuis le backoffice
Connecter un réseau social imposait de poser NEXT_PUBLIC_SITE_URL dans
docker-compose et de redémarrer le conteneur. L'écran Réseaux sociaux porte
désormais le champ, pré-rempli avec l'adresse par laquelle l'administrateur
consulte la page.

- Nouveau réglage `site_public_url` ; `siteUrl()` lit la base puis retombe sur
  les variables d'environnement, donc les déploiements actuels ne bougent pas.
- `siteUrl()`, `redirectUri()`, `authorizeUrl()`, `promoLink()` et
  `buildPromoMessage()` deviennent asynchrones.
- Seule l'origine est conservée : le schéma est ajouté s'il manque, un chemin
  est retiré, et une adresse invalide revient sur l'écran avec un message au
  lieu d'une page d'erreur brute.
- `saveSiteSettings` saute cette clé : le formulaire Paramètres ne la contient
  pas et l'aurait écrasée à chaque enregistrement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
2026-07-25 08:28:04 +00:00
Claude 19d1104ab0 Connexion Facebook / LinkedIn en OAuth depuis le backoffice
Les jetons se posaient à la main dans docker-compose. Un écran Backend ›
Réseaux sociaux permet désormais de coller les identifiants de l'application,
cliquer Connecter, choisir la page, et ne plus y revenir.

- Table `social_accounts` : identifiants, jetons et cible par réseau. Tous les
  secrets sont chiffrés en AES-256-GCM (`src/lib/crypto.ts`), clé
  `SOCIAL_TOKEN_KEY` avec repli sur `AUTH_SECRET`.
- Routes `api/social/[network]/{connect,callback}` : `state` anti-CSRF en cookie
  httpOnly, échange du code, récupération des pages administrées. Aucun jeton ne
  transite par une URL — les cibles sont relistées côté serveur au moment de la
  sélection.
- Facebook : jeton utilisateur longue durée puis jeton de PAGE, qui n'expire
  pas. LinkedIn : jeton 60 jours, rafraîchi automatiquement si l'application a
  obtenu les jetons de rafraîchissement programmatiques.
- Faute de quoi le backoffice affiche la date d'expiration, un bandeau sur le
  tableau de bord à J-7 et un bouton Reconnecter.
- `social.ts` lit les identifiants via `social-accounts.ts` : base d'abord,
  variables d'environnement ensuite. Les installations existantes continuent de
  fonctionner sans modification.
- Le secret d'application n'est jamais renvoyé au navigateur : champ vide =
  valeur conservée.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
2026-07-24 23:00:08 +00:00