Compare commits
No commits in common. "main" and "contenu" have entirely different histories.
8 changed files with 60 additions and 1339 deletions
32
.github/workflows/auto-retry.yml
vendored
32
.github/workflows/auto-retry.yml
vendored
|
|
@ -1,32 +0,0 @@
|
|||
name: Auto-Retry on Timeout
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Deploy to Cloudflare Pages"]
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
retry-job:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'failure' || github.event.workflow_run.conclusion == 'cancelled' }}
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Relancer le workflow (Retry)
|
||||
if: ${{ github.event.workflow_run.run_attempt < 3 }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "Previous run failed. Attempting rerun #${{ github.event.workflow_run.run_attempt }}..."
|
||||
|
||||
# Use 'run rerun' to increment the attempt counter properly
|
||||
# Add --repo to fix the "not a git repository" error
|
||||
gh run rerun ${{ github.event.workflow_run.id }} \
|
||||
--repo ${{ github.repository }} \
|
||||
--failed
|
||||
|
||||
- name: Stop Retrying
|
||||
if: ${{ github.event.workflow_run.run_attempt >= 3 }}
|
||||
run: echo "Max attempts reached (3). Stopping to prevent loop."
|
||||
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
|
|
@ -11,7 +11,6 @@ on:
|
|||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
|
|
|
|||
1256
package-lock.json
generated
1256
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -24,7 +24,6 @@
|
|||
"dependencies": {
|
||||
"@docusaurus/core": "^3.9.2",
|
||||
"@docusaurus/preset-classic": "^3.9.2",
|
||||
"@docusaurus/theme-mermaid": "^3.9.2",
|
||||
"@easyops-cn/docusaurus-search-local": "^0.52.1",
|
||||
"clsx": "^2.1.1",
|
||||
"docusaurus-plugin-image-zoom": "^3.0.1",
|
||||
|
|
|
|||
|
|
@ -2,16 +2,8 @@ const Parser = require('rss-parser');
|
|||
const { XMLParser } = require('fast-xml-parser');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
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]);
|
||||
}
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
|
|
@ -21,15 +13,13 @@ module.exports = function (context, options) {
|
|||
console.log('[RSS Aggregator] Récupération des flux RSS...');
|
||||
|
||||
const parser = new Parser({
|
||||
timeout: PER_REQUEST_TIMEOUT_MS,
|
||||
requestOptions: {
|
||||
agent: false
|
||||
},
|
||||
timeout: 10000,
|
||||
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');
|
||||
|
||||
|
|
@ -39,6 +29,7 @@ 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;
|
||||
|
||||
|
|
@ -59,26 +50,22 @@ 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...`);
|
||||
|
||||
let globalTimedOut = false;
|
||||
const globalTimeoutId = setTimeout(() => {
|
||||
globalTimedOut = true;
|
||||
console.warn('[RSS Aggregator] Timeout global atteint (600s), arrêt du traitement.');
|
||||
}, GLOBAL_TIMEOUT_MS);
|
||||
|
||||
// Traitement par lots de 5 flux en parallèle pour ne pas surcharger
|
||||
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 withTimeout(parser.parseURL(feedInfo.xmlUrl), PER_REQUEST_TIMEOUT_MS);
|
||||
const feed = await parser.parseURL(feedInfo.xmlUrl);
|
||||
|
||||
// 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;
|
||||
|
|
@ -101,8 +88,7 @@ module.exports = function (context, options) {
|
|||
allItems.push(...batchResults.flat());
|
||||
}
|
||||
|
||||
clearTimeout(globalTimeoutId);
|
||||
|
||||
// Grouper par catégorie et trier
|
||||
const groupedByCategory = new Map();
|
||||
|
||||
allItems.forEach((item) => {
|
||||
|
|
@ -112,6 +98,7 @@ 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,
|
||||
|
|
@ -133,9 +120,13 @@ 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);
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ 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,
|
||||
|
|
@ -78,22 +77,9 @@ module.exports = function pluginUnifiedTags(context, options) {
|
|||
});
|
||||
}
|
||||
|
||||
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 allTags = Array.from(tagsMap.values()).sort((a, b) =>
|
||||
a.label.localeCompare(b.label)
|
||||
);
|
||||
|
||||
const tagsByLetter = {};
|
||||
allTags.forEach((tag) => {
|
||||
|
|
@ -104,19 +90,40 @@ module.exports = function pluginUnifiedTags(context, options) {
|
|||
tagsByLetter[firstLetter].push(tag);
|
||||
});
|
||||
|
||||
setGlobalData({allTags, tagsByLetter});
|
||||
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);
|
||||
|
||||
const locale = context.i18n.currentLocale;
|
||||
const baseUrl = locale === context.i18n.defaultLocale ? '/' : `/${locale}/`;
|
||||
|
||||
sortedTagsData.forEach((tagData, tagKey) => {
|
||||
Array.from(tagsMap.entries()).forEach(([tagKey, tagData]) => {
|
||||
addRoute({
|
||||
path: `${baseUrl}tags/${tagKey}`,
|
||||
component: '@site/src/theme/TagPage',
|
||||
exact: true,
|
||||
props: {
|
||||
tagKey,
|
||||
tagData,
|
||||
tagKey: tagKey,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ 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 (
|
||||
<Layout>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
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';
|
||||
|
|
@ -26,12 +27,22 @@ interface TagData {
|
|||
items: TagItem[];
|
||||
}
|
||||
|
||||
interface TagPageProps {
|
||||
tagKey: string;
|
||||
tagData: TagData;
|
||||
interface TagsMapData {
|
||||
[tagKey: string]: TagData;
|
||||
}
|
||||
|
||||
export default function TagPage({tagKey, tagData}: TagPageProps): JSX.Element {
|
||||
interface UnifiedTagsData {
|
||||
tagsMap: TagsMapData;
|
||||
}
|
||||
|
||||
interface TagPageProps {
|
||||
tagKey: string;
|
||||
}
|
||||
|
||||
export default function TagPage(props: TagPageProps): JSX.Element {
|
||||
const {tagsMap} = usePluginData('docusaurus-plugin-unified-tags') as UnifiedTagsData;
|
||||
const tagData = tagsMap[props.tagKey];
|
||||
|
||||
if (!tagData) {
|
||||
return (
|
||||
<Layout>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue