diff --git a/client/public/__manus__/version.json b/client/public/__manus__/version.json index 4a9c4c8..9eb04c1 100644 --- a/client/public/__manus__/version.json +++ b/client/public/__manus__/version.json @@ -1,4 +1,4 @@ { - "version": "d5ca5837", - "timestamp": 1782484339948 + "version": "138ac2fc", + "timestamp": 1782484729381 } \ No newline at end of file diff --git a/client/src/pages/RssFeeds.tsx b/client/src/pages/RssFeeds.tsx index 2941c75..10d1e25 100644 --- a/client/src/pages/RssFeeds.tsx +++ b/client/src/pages/RssFeeds.tsx @@ -539,127 +539,31 @@ function FeedCard({ // ─── Panneau de configuration ───────────────────────────────────────────────── -function SettingsPanel({ isAdmin }: { isAdmin: boolean }) { - const { data: settings, refetch } = trpc.rss.getSettings.useQuery(); - const saveMutation = trpc.rss.saveSettings.useMutation({ - onSuccess: () => { - toast.success("Paramètres RSS sauvegardés"); - refetch(); - }, - onError: (e) => toast.error("Erreur", { description: e.message }), - }); - - const [fetchMode, setFetchMode] = useState<"interval" | "scheduled">(settings?.fetchMode ?? "scheduled"); - const [fetchIntervalMinutes, setFetchIntervalMinutes] = useState(settings?.fetchIntervalMinutes ?? 360); - const [scheduledTime, setScheduledTime] = useState(settings?.scheduledTime ?? "06:00"); - const [autoFetchEnabled, setAutoFetchEnabled] = useState(settings?.autoFetchEnabled ?? true); - - // Sync state when settings load - if (settings && settings.fetchMode !== fetchMode && !saveMutation.isPending) { - setFetchMode(settings.fetchMode); - setFetchIntervalMinutes(settings.fetchIntervalMinutes); - setScheduledTime(settings.scheduledTime ?? "06:00"); - setAutoFetchEnabled(settings.autoFetchEnabled); - } - - const handleSave = () => { - saveMutation.mutate({ - fetchMode, - fetchIntervalMinutes, - scheduledTime, - autoFetchEnabled, - }); - }; +function SettingsPanel({ isAdmin: _isAdmin }: { isAdmin: boolean }) { + const { data: settings } = trpc.rss.getSettings.useQuery(); + const importTime = settings?.scheduledTime ?? "06:00"; return ( - Paramètres de lecture automatique + Planification de la lecture RSS - Configurez la fréquence et le mode de lecture des flux RSS. + La lecture des flux RSS est déclenchée automatiquement par le cron d’import quotidien. - - {/* Activation */} -
+ +
+
-

Lecture automatique

-

Active la récupération périodique des flux

-
- -
- - {/* Mode */} -
- -
- {(["scheduled", "interval"] as const).map((mode) => ( - - ))} +

Lecture automatique chaque jour à {importTime}

+

+ Pour modifier l’heure, rendez-vous dans Paramètres → Import & Planification. +

- - {/* Paramètre selon le mode */} - {fetchMode === "scheduled" ? ( -
- - setScheduledTime(e.target.value)} - disabled={!isAdmin} - className="w-36" - /> -
- ) : ( -
- - -
- )} - - {isAdmin && ( - - )}
); diff --git a/server/_core/index.ts b/server/_core/index.ts index d03d5b6..0ddb5e0 100644 --- a/server/_core/index.ts +++ b/server/_core/index.ts @@ -12,7 +12,7 @@ import { runFullImport } from "../importer"; import uploadRoutes from "../uploadRoutes"; import scheduledRoutes from "../scheduledRoutes"; import { ensureAdminExists } from "../localAuth"; -import { getSetting, getRssSettings } from "../db"; +import { getSetting } from "../db"; import { runRssFetch } from "../rssEngine"; function isPortAvailable(port: number): Promise { @@ -30,7 +30,7 @@ async function findAvailablePort(startPort: number = 3000): Promise { throw new Error(`No available port found starting from ${startPort}`); } -// ─── Tâche d'import quotidien ───────────────────────────────────────────────── +// ─── Tâche d'import quotidien + lecture RSS ────────────────────────────────── let cronJob: ReturnType | null = null; async function scheduleDailyImport() { @@ -45,85 +45,34 @@ async function scheduleDailyImport() { cronJob = cron.schedule(cronExpr, async () => { console.log(`[Cron] Import automatique démarré à ${new Date().toISOString()}`); try { + // 1. Import des fichiers Excel (Veille + AAP) const result = await runFullImport(); - console.log(`[Cron] Import terminé — Veille: +${result.veille.newRows} | AAP: +${result.aap.newRows}`); + console.log(`[Cron] Import Excel terminé — Veille: +${result.veille.newRows} | AAP: +${result.aap.newRows}`); } catch (e) { - console.error("[Cron] Erreur lors de l'import:", e); + console.error("[Cron] Erreur lors de l'import Excel:", e); } - }); - console.log(`[Cron] Import quotidien planifié à ${importTime} (${cronExpr})`); -} - -// ─── Planificateur RSS natif ────────────────────────────────────────────────── -let rssCronJob: ReturnType | null = null; - -/** - * Démarre (ou redémarre) le planificateur RSS en lisant la configuration - * depuis la table rss_settings. Peut être appelé au démarrage et à chaque - * modification des paramètres RSS via l'interface d'administration. - */ -export async function scheduleRssFetch() { - // Arrêter le cron existant s'il y en a un - if (rssCronJob) { - rssCronJob.stop(); - rssCronJob = null; - console.log("[RSS Cron] Planificateur RSS arrêté."); - } - - let settings = null; - try { - settings = await getRssSettings(); - } catch (e) { - // En cas d'erreur (ex: colonne manquante lors d'une migration), - // utiliser les valeurs par défaut et planifier un retry dans 2 minutes - console.warn("[RSS Cron] Impossible de lire les paramètres RSS, utilisation des valeurs par défaut:", (e as Error).message); - setTimeout(() => scheduleRssFetch(), 2 * 60 * 1000); - // Démarrer quand même avec les valeurs par défaut - settings = { autoFetchEnabled: true, fetchMode: "interval" as const, fetchIntervalMinutes: 60, scheduledTime: "06:00" }; - } - - if (!settings || !settings.autoFetchEnabled) { - console.log("[RSS Cron] Lecture automatique des flux RSS désactivée."); - return; - } - - let cronExpr: string; - - if (settings.fetchMode === "interval") { - // Mode intervalle : toutes les N minutes - const intervalMin = Math.max(5, settings.fetchIntervalMinutes ?? 60); - if (intervalMin < 60) { - cronExpr = `*/${intervalMin} * * * *`; - } else if (intervalMin % 60 === 0) { - const hours = intervalMin / 60; - cronExpr = `0 */${hours} * * *`; - } else { - cronExpr = `*/${intervalMin} * * * *`; - } - console.log(`[RSS Cron] Mode intervalle — toutes les ${intervalMin} minutes (${cronExpr})`); - } else { - // Mode planifié : heure fixe quotidienne - const scheduledTime = settings.scheduledTime ?? "06:00"; - const [hour, minute] = scheduledTime.split(":").map(Number); - cronExpr = `0 ${minute ?? 0} ${hour ?? 6} * * *`; - console.log(`[RSS Cron] Mode planifié — tous les jours à ${scheduledTime} (${cronExpr})`); - } - - rssCronJob = cron.schedule(cronExpr, async () => { - console.log(`[RSS Cron] Lecture des flux RSS démarrée à ${new Date().toISOString()}`); try { - const summary = await runRssFetch(); + // 2. Lecture des flux RSS + const rssSummary = await runRssFetch(); console.log( - `[RSS Cron] Lecture terminée — ${summary.totalFeeds} flux, ` + - `+${summary.totalNewItems} nouveaux articles, ` + - `${summary.errorFeeds} erreur(s)` + `[Cron] Lecture RSS terminée — ${rssSummary.totalFeeds} flux, ` + + `+${rssSummary.totalNewItems} nouveaux articles, ` + + `${rssSummary.errorFeeds} erreur(s)` ); } catch (e) { - console.error("[RSS Cron] Erreur lors de la lecture des flux:", e); + console.error("[Cron] Erreur lors de la lecture RSS:", e); } }); + console.log(`[Cron] Import quotidien + lecture RSS planifiés à ${importTime} (${cronExpr})`); +} - console.log("[RSS Cron] Planificateur RSS démarré."); +/** + * Stub conservé pour compatibilité avec les imports existants dans routers.ts. + * La lecture RSS est désormais intégrée dans scheduleDailyImport(). + */ +export async function scheduleRssFetch() { + // No-op : la lecture RSS est pilotée par le cron d'import quotidien (import_time) + console.log("[RSS Cron] Planificateur RSS indépendant désactivé — la lecture est pilotée par le cron d'import quotidien."); } async function startServer() { @@ -159,7 +108,6 @@ async function startServer() { try { await ensureAdminExists(); await scheduleDailyImport(); - await scheduleRssFetch(); } catch (e) { console.error("[Init] Erreur d'initialisation:", e); }