From f2f6e03777dc142a0b29598076bffc16a009c86c Mon Sep 17 00:00:00 2001 From: Tellsanguis Date: Mon, 2 Mar 2026 23:26:07 +0100 Subject: [PATCH] Optimisation plugins RSS et tags --- .../docusaurus-plugin-rss-aggregator/index.js | 42 +++++++++++----- .../docusaurus-plugin-unified-tags/index.js | 49 ++++++++----------- src/pages/tags/index.tsx | 2 - src/theme/TagPage.tsx | 15 +----- 4 files changed, 52 insertions(+), 56 deletions(-) diff --git a/plugins/docusaurus-plugin-rss-aggregator/index.js b/plugins/docusaurus-plugin-rss-aggregator/index.js index 71cf3ba..d059669 100644 --- a/plugins/docusaurus-plugin-rss-aggregator/index.js +++ b/plugins/docusaurus-plugin-rss-aggregator/index.js @@ -5,6 +5,16 @@ const path = require('path'); const https = require('https'); const http = require('http'); +const PER_REQUEST_TIMEOUT_MS = 20_000; +const GLOBAL_TIMEOUT_MS = 600_000; + +function withTimeout(promise, ms) { + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error(`Timeout après ${ms / 1000}s`)), ms) + ); + return Promise.race([promise, timeout]); +} + module.exports = function (context, options) { return { name: 'docusaurus-plugin-rss-aggregator', @@ -12,14 +22,19 @@ module.exports = function (context, options) { async loadContent() { console.log('[RSS Aggregator] Récupération des flux RSS...'); + const httpAgent = new http.Agent({ keepAlive: false }); + const httpsAgent = new https.Agent({ keepAlive: false }); + const parser = new Parser({ - timeout: 10000, + timeout: PER_REQUEST_TIMEOUT_MS, + requestOptions: { + agent: { http: httpAgent, https: httpsAgent } + }, customFields: { item: ['description', 'content:encoded'] } }); - // Lecture du fichier OPML const opmlPath = path.join(context.siteDir, 'static', 'veille-tech.opml'); const opmlText = fs.readFileSync(opmlPath, 'utf-8'); @@ -29,7 +44,6 @@ module.exports = function (context, options) { }); const opmlData = xmlParser.parse(opmlText); - // Extraction des flux depuis le fichier OPML const opmlFeeds = []; const outlines = opmlData.opml.body.outline; @@ -50,22 +64,26 @@ module.exports = function (context, options) { }); }); - // Récupération des flux RSS (articles des dernières 24h) const allItems = []; const now = Date.now(); const twentyFourHoursAgo = now - (24 * 60 * 60 * 1000); console.log(`[RSS Aggregator] Récupération de ${opmlFeeds.length} flux RSS...`); - // Traitement par lots de 5 flux en parallèle pour ne pas surcharger + let globalTimedOut = false; + const globalTimeoutId = setTimeout(() => { + globalTimedOut = true; + console.warn('[RSS Aggregator] Timeout global atteint (600s), arrêt du traitement.'); + }, GLOBAL_TIMEOUT_MS); + const batchSize = 5; for (let i = 0; i < opmlFeeds.length; i += batchSize) { + if (globalTimedOut) break; const batch = opmlFeeds.slice(i, i + batchSize); const batchPromises = batch.map(async (feedInfo) => { try { - const feed = await parser.parseURL(feedInfo.xmlUrl); + const feed = await withTimeout(parser.parseURL(feedInfo.xmlUrl), PER_REQUEST_TIMEOUT_MS); - // Filtrer les articles des dernières 24h const recentItems = feed.items.filter((item) => { const itemDate = new Date(item.pubDate || item.isoDate || ''); return itemDate.getTime() >= twentyFourHoursAgo; @@ -88,7 +106,10 @@ module.exports = function (context, options) { allItems.push(...batchResults.flat()); } - // Grouper par catégorie et trier + clearTimeout(globalTimeoutId); + httpAgent.destroy(); + httpsAgent.destroy(); + const groupedByCategory = new Map(); allItems.forEach((item) => { @@ -98,7 +119,6 @@ module.exports = function (context, options) { groupedByCategory.get(item.category).push(item); }); - // Trier les articles de chaque catégorie par date (plus récent en premier) const groups = Array.from(groupedByCategory.entries()) .map(([category, items]) => ({ category, @@ -120,13 +140,9 @@ module.exports = function (context, options) { async contentLoaded({ content, actions }) { const { setGlobalData } = actions; - // Écrire les données dans un fichier JSON statique const outputPath = path.join(context.siteDir, 'static', 'rss-feed-cache.json'); fs.writeFileSync(outputPath, JSON.stringify(content, null, 2)); - console.log(`[RSS Aggregator] Données écrites dans ${outputPath}`); - - // Rendre les données disponibles globalement setGlobalData(content); }, }; diff --git a/plugins/docusaurus-plugin-unified-tags/index.js b/plugins/docusaurus-plugin-unified-tags/index.js index 9d50abe..a785ffc 100644 --- a/plugins/docusaurus-plugin-unified-tags/index.js +++ b/plugins/docusaurus-plugin-unified-tags/index.js @@ -37,6 +37,7 @@ module.exports = function pluginUnifiedTags(context, options) { title: post.metadata.title, permalink: post.metadata.permalink, date: post.metadata.date, + _ts: new Date(post.metadata.date).getTime(), formattedDate: post.metadata.formattedDate, description: post.metadata.description, authors: post.metadata.authors, @@ -77,9 +78,22 @@ module.exports = function pluginUnifiedTags(context, options) { }); } - const allTags = Array.from(tagsMap.values()).sort((a, b) => - a.label.localeCompare(b.label) - ); + const sortedTagsData = new Map(); + tagsMap.forEach((tagData, tagKey) => { + const sortedItems = tagData.items + .sort((a, b) => { + if (a.type === 'blog' && b.type === 'blog') return b._ts - a._ts; + if (a.type === 'doc' && b.type === 'doc') return a.title.localeCompare(b.title); + return a.type === 'blog' ? -1 : 1; + }) + .map(({_ts, ...item}) => item); + + sortedTagsData.set(tagKey, {...tagData, items: sortedItems}); + }); + + const allTags = Array.from(tagsMap.values()) + .map(({items, ...tag}) => tag) + .sort((a, b) => a.label.localeCompare(b.label)); const tagsByLetter = {}; allTags.forEach((tag) => { @@ -90,40 +104,19 @@ module.exports = function pluginUnifiedTags(context, options) { tagsByLetter[firstLetter].push(tag); }); - const globalData = { - allTags, - tagsByLetter, - tagsMap: Object.fromEntries( - Array.from(tagsMap.entries()).map(([key, value]) => [ - key, - { - ...value, - items: value.items.sort((a, b) => { - if (a.type === 'blog' && b.type === 'blog') { - return new Date(b.date) - new Date(a.date); - } - if (a.type === 'doc' && b.type === 'doc') { - return a.title.localeCompare(b.title); - } - return a.type === 'blog' ? -1 : 1; - }) - } - ]) - ) - }; - - setGlobalData(globalData); + setGlobalData({allTags, tagsByLetter}); const locale = context.i18n.currentLocale; const baseUrl = locale === context.i18n.defaultLocale ? '/' : `/${locale}/`; - Array.from(tagsMap.entries()).forEach(([tagKey, tagData]) => { + sortedTagsData.forEach((tagData, tagKey) => { addRoute({ path: `${baseUrl}tags/${tagKey}`, component: '@site/src/theme/TagPage', exact: true, props: { - tagKey: tagKey, + tagKey, + tagData, }, }); }); diff --git a/src/pages/tags/index.tsx b/src/pages/tags/index.tsx index 4c35fc7..3603e5d 100644 --- a/src/pages/tags/index.tsx +++ b/src/pages/tags/index.tsx @@ -26,8 +26,6 @@ interface UnifiedTagsData { export default function TagsPage(): JSX.Element { const pluginData = usePluginData('docusaurus-plugin-unified-tags') as UnifiedTagsData; - console.log('Plugin data:', pluginData); - if (!pluginData || !pluginData.tagsByLetter) { return ( diff --git a/src/theme/TagPage.tsx b/src/theme/TagPage.tsx index 5e6de44..ae88a1c 100644 --- a/src/theme/TagPage.tsx +++ b/src/theme/TagPage.tsx @@ -1,7 +1,6 @@ import React from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; -import {usePluginData} from '@docusaurus/useGlobalData'; import Layout from '@theme/Layout'; import Translate, {translate} from '@docusaurus/Translate'; import Heading from '@theme/Heading'; @@ -27,22 +26,12 @@ interface TagData { items: TagItem[]; } -interface TagsMapData { - [tagKey: string]: TagData; -} - -interface UnifiedTagsData { - tagsMap: TagsMapData; -} - interface TagPageProps { tagKey: string; + tagData: TagData; } -export default function TagPage(props: TagPageProps): JSX.Element { - const {tagsMap} = usePluginData('docusaurus-plugin-unified-tags') as UnifiedTagsData; - const tagData = tagsMap[props.tagKey]; - +export default function TagPage({tagKey, tagData}: TagPageProps): JSX.Element { if (!tagData) { return (