mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-22 22:48:09 +02:00
feat(facebook): renouvellement automatique des jetons de page
Un jeton de page collé à la main meurt au bout de 60 jours — ou du jour au lendemain si le mot de passe change, si le compte perd son rôle d'admin ou si Meta révoque l'autorisation — et on ne l'apprenait qu'à la première publication en échec : le contrôle quotidien existant se contentait de marquer la page « expired ». On reprend la chaîne recommandée par Meta, comme dans PleinR : autorisation OAuth, échange du code contre un jeton utilisateur longue durée (~60 j), puis dérivation des jetons de page — qui n'expirent pas. Le jeton utilisateur est conservé chiffré sur chaque page : c'est lui qui permet au TokenManager de régénérer un jeton révoqué sans intervention humaine, et de se prolonger lui-même avant échéance. - facebook_config : App ID / App Secret de l'application, saisis dans les paramètres (chiffrés en base) ou par variables d'environnement. - /api/facebook : configuration, connexion OAuth, callback qui crée ou met à jour les pages, contrôle d'une page à la demande, contrôle global. - TokenManager : /debug_token pour connaître l'état réel du jeton, puis renouvellement automatique 15 jours avant l'échéance ou dès révocation ; repli sur le contrôle historique tant que l'application n'est pas configurée ou si Facebook est injoignable, pour ne pas passer l'UI au rouge sur une panne réseau. Le cron passe à deux fois par jour. - Les comptes Instagram professionnels rattachés à une page reçoivent le même traitement, puisqu'ils publient avec le jeton de cette page. - L'UI montre l'état constaté (jeton valide, bientôt expiré, révoqué), l'erreur à corriger et un bouton de contrôle immédiat ; un bandeau signale les pages à reconnecter. - /api/pages ne renvoie plus aucun jeton au navigateur, seulement un indicateur de renouvellement automatique. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qns6Gcpd95bCHWUxySh4Ew
This commit is contained in:
13 files changed
+1385
-249
No files matched your search
+20
-3
@@ -18,6 +18,7 @@ import { reelsRouter } from "./routes/reels";
|
||||
import { remotionRouter } from "./routes/remotion";
|
||||
import { externalRouter } from "./routes/external";
|
||||
import { tiktokRouter } from "./routes/tiktok";
|
||||
import { facebookRouter } from "./routes/facebook";
|
||||
import { legalRouter } from "./routes/legal";
|
||||
import { insertAudioTrackSchema } from "@shared/schema";
|
||||
import * as musicMetadata from "music-metadata";
|
||||
@@ -475,6 +476,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
|
||||
// TikTok Routes (OAuth multi-comptes) — avant le routeur Reels monté sur /api
|
||||
app.use("/api/tiktok", requireAuth, tiktokRouter);
|
||||
app.use("/api/facebook", requireAuth, facebookRouter);
|
||||
|
||||
// Reels & Music Routes
|
||||
app.use("/api", requireAuth, reelsRouter);
|
||||
@@ -1553,6 +1555,19 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
});
|
||||
|
||||
// Social pages
|
||||
|
||||
/**
|
||||
* Vue client d'une page : sans jetons, avec un indicateur de renouvellement
|
||||
* automatique (vrai quand un jeton utilisateur longue durée est enregistré).
|
||||
*/
|
||||
function toClientPage(page: SocialPage) {
|
||||
const { accessToken, userAccessToken, refreshToken, ...safe } = page;
|
||||
return {
|
||||
...safe,
|
||||
autoRenew: page.platform === 'tiktok' ? !!refreshToken : !!userAccessToken,
|
||||
};
|
||||
}
|
||||
|
||||
app.get("/api/pages", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const user = req.user as User;
|
||||
@@ -1572,7 +1587,9 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
pages = await storage.getUserAccessiblePages(userId);
|
||||
}
|
||||
|
||||
res.json(pages);
|
||||
// Les jetons n'ont rien à faire dans le navigateur : on ne renvoie que
|
||||
// l'information dont l'UI a besoin, dont la capacité à se renouveler seule.
|
||||
res.json(pages.map(toClientPage));
|
||||
} catch (error) {
|
||||
console.error("Error fetching pages:", error);
|
||||
res.status(500).json({ error: "Failed to fetch pages" });
|
||||
@@ -1602,7 +1619,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
tokenExpiresAt
|
||||
});
|
||||
const page = await storage.createSocialPage(pageData);
|
||||
res.json(page);
|
||||
res.json(toClientPage(page));
|
||||
} catch (error) {
|
||||
console.error("Error creating page:", error);
|
||||
res.status(500).json({ error: "Failed to create page" });
|
||||
@@ -1638,7 +1655,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
|
||||
const updatedPage = await storage.updateSocialPage(pageId, pageData);
|
||||
res.json(updatedPage);
|
||||
res.json(toClientPage(updatedPage));
|
||||
} catch (error) {
|
||||
console.error("Error updating page:", error);
|
||||
res.status(500).json({ error: "Failed to update page" });
|
||||
|
||||
Reference in new issue
Block a user