mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-11 17:29:37 +02:00
Problèmes résolus: ✅ Flux RSS ne s'affichaient pas → LEFT JOIN + COALESCE ✅ Pas de mise à jour automatique → Scheduler toutes les 5min ✅ Application lente → Cache serveur + client Scheduler RSS (services/rss-scheduler.js): - Mise à jour automatique des flux toutes les 5 minutes - Première exécution 5 secondes après démarrage - Gestion parallèle de tous les flux activés - Logs détaillés pour debugging - Protection contre les exécutions multiples simultanées - Limite de 20 articles par flux pour performance Routes RSS optimisées: - Cache serveur 30 secondes pour /api/rss/articles - Invalidation cache après fetch manuel - LEFT JOIN au lieu de INNER JOIN (flux sans articles) - COALESCE pour titre fallback - Logs améliorés avec compteurs Frontend vanilla JS: - Auto-refresh RSS toutes les 30 secondes - Cache settings client 1 minute - Synchronisation avec scheduler serveur - console.debug au lieu de console.error pour auto-refresh Frontend React: - Auto-refresh RSS toutes les 30 secondes - loadRssData() séparée pour refresh indépendant - useEffect cleanup proper (clearInterval) - État lastUpdate pour tracking Server.js: - Démarrage scheduler au startup - Log confirmation démarrage Performances: - Moins de requêtes DB grâce au cache - Refresh automatique sans action utilisateur - Articles toujours à jour (max 30s de délai) - Support de beaucoup de flux RSS sans ralentissement
137 lines
4.1 KiB
JavaScript
137 lines
4.1 KiB
JavaScript
// Service de mise à jour automatique des flux RSS
|
|
const Parser = require('rss-parser');
|
|
const { getAll, getOne, runQuery } = require('../config/database');
|
|
const logger = require('../config/logger');
|
|
|
|
const parser = new Parser({
|
|
timeout: 10000,
|
|
headers: {
|
|
'User-Agent': 'NoteFlow RSS Reader'
|
|
}
|
|
});
|
|
|
|
let isRunning = false;
|
|
|
|
/**
|
|
* Récupérer et mettre à jour tous les flux RSS activés
|
|
*/
|
|
async function fetchAllFeeds() {
|
|
if (isRunning) {
|
|
logger.info('Fetch RSS déjà en cours, skip...');
|
|
return;
|
|
}
|
|
|
|
isRunning = true;
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
logger.info('🔄 Début de la mise à jour des flux RSS...');
|
|
|
|
// Récupérer tous les flux activés
|
|
const feeds = await getAll('SELECT * FROM rss_feeds WHERE enabled = 1');
|
|
|
|
if (!feeds || feeds.length === 0) {
|
|
logger.info('Aucun flux RSS activé');
|
|
isRunning = false;
|
|
return;
|
|
}
|
|
|
|
logger.info(`Mise à jour de ${feeds.length} flux RSS...`);
|
|
|
|
let totalArticles = 0;
|
|
let totalErrors = 0;
|
|
|
|
// Traiter chaque flux
|
|
for (const feed of feeds) {
|
|
try {
|
|
logger.info(`Fetch: ${feed.url}`);
|
|
|
|
// Parser le flux
|
|
const parsedFeed = await parser.parseURL(feed.url);
|
|
|
|
// Mettre à jour le titre et description du flux si nécessaire
|
|
await runQuery(
|
|
'UPDATE rss_feeds SET title = ?, description = ?, last_fetched_at = CURRENT_TIMESTAMP WHERE id = ?',
|
|
[parsedFeed.title || feed.url, parsedFeed.description || '', feed.id]
|
|
);
|
|
|
|
// Ajouter les articles (limiter à 20 par flux pour ne pas surcharger)
|
|
const items = parsedFeed.items.slice(0, 20);
|
|
|
|
for (const item of items) {
|
|
try {
|
|
// Vérifier si l'article existe déjà
|
|
const existing = await getOne('SELECT id FROM rss_articles WHERE link = ?', [item.link]);
|
|
|
|
if (!existing) {
|
|
await runQuery(
|
|
'INSERT INTO rss_articles (feed_id, title, link, description, pub_date, content) VALUES (?, ?, ?, ?, ?, ?)',
|
|
[
|
|
feed.id,
|
|
item.title || 'Sans titre',
|
|
item.link || '',
|
|
item.contentSnippet || item.description || '',
|
|
item.pubDate || item.isoDate || new Date().toISOString(),
|
|
item.content || item['content:encoded'] || ''
|
|
]
|
|
);
|
|
totalArticles++;
|
|
}
|
|
} catch (articleError) {
|
|
// Ignorer les articles en double ou invalides
|
|
logger.debug(`Article ignoré: ${articleError.message}`);
|
|
}
|
|
}
|
|
|
|
logger.info(`✓ ${feed.title || feed.url}: ${items.length} articles traités`);
|
|
|
|
} catch (feedError) {
|
|
totalErrors++;
|
|
logger.error(`✗ Erreur fetch ${feed.url}:`, feedError.message);
|
|
}
|
|
}
|
|
|
|
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
logger.info(`✅ Mise à jour terminée: ${totalArticles} nouveaux articles, ${totalErrors} erreurs (${duration}s)`);
|
|
|
|
} catch (error) {
|
|
logger.error('Erreur lors de la mise à jour automatique des flux RSS:', error);
|
|
} finally {
|
|
isRunning = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Démarrer le scheduler (toutes les 5 minutes)
|
|
*/
|
|
function startScheduler() {
|
|
logger.info('📰 Scheduler RSS démarré (mise à jour toutes les 5 minutes)');
|
|
|
|
// Première exécution immédiate
|
|
setTimeout(() => {
|
|
fetchAllFeeds().catch(err => {
|
|
logger.error('Erreur lors de la première mise à jour RSS:', err);
|
|
});
|
|
}, 5000); // Attendre 5 secondes après le démarrage du serveur
|
|
|
|
// Ensuite toutes les 5 minutes
|
|
setInterval(() => {
|
|
fetchAllFeeds().catch(err => {
|
|
logger.error('Erreur lors de la mise à jour RSS:', err);
|
|
});
|
|
}, 5 * 60 * 1000); // 5 minutes
|
|
}
|
|
|
|
/**
|
|
* Fetch manuel (utilisé par la route API)
|
|
*/
|
|
async function manualFetch() {
|
|
return await fetchAllFeeds();
|
|
}
|
|
|
|
module.exports = {
|
|
startScheduler,
|
|
manualFetch,
|
|
fetchAllFeeds
|
|
};
|