diff --git a/crowdsec-docs/docusaurus.config.ts b/crowdsec-docs/docusaurus.config.ts
index 5a6133218..ab2b8fbc7 100644
--- a/crowdsec-docs/docusaurus.config.ts
+++ b/crowdsec-docs/docusaurus.config.ts
@@ -6,6 +6,8 @@ import { themes } from "prism-react-renderer";
import tailwindPlugin from "./plugins/tailwind-config";
import { ctiApiSidebar, guidesSideBar, remediationSideBar } from "./sidebarsUnversioned";
+const extractPreprocessor = require("./plugins/extract-preprocessor");
+
const generateCurrentAndNextRedirects = (s) => [
{
from: `/docs/${s}`,
@@ -220,6 +222,7 @@ const config: Config = {
admonitions: true,
headingIds: true,
},
+ preprocessor: extractPreprocessor,
},
stylesheets: [
{
diff --git a/crowdsec-docs/plugins/extract-preprocessor.js b/crowdsec-docs/plugins/extract-preprocessor.js
new file mode 100644
index 000000000..aaa07affc
--- /dev/null
+++ b/crowdsec-docs/plugins/extract-preprocessor.js
@@ -0,0 +1,104 @@
+const fs = require('fs');
+const path = require('path');
+
+// --- CONFIGURATION ---
+// The directories to scan for snippets
+const DOCS_DIRS = ['./docs', './unversioned'];
+// ---------------------
+
+const snippetRegistry = new Map();
+let isIndexed = false;
+
+// Helper: Recursively find all .md/.mdx files
+const getAllFiles = (dirPath, arrayOfFiles = []) => {
+ if (!fs.existsSync(dirPath)) return arrayOfFiles;
+
+ const files = fs.readdirSync(dirPath);
+ files.forEach((file) => {
+ const fullPath = path.join(dirPath, file);
+ if (fs.statSync(fullPath).isDirectory()) {
+ getAllFiles(fullPath, arrayOfFiles);
+ } else if (file.endsWith('.md') || file.endsWith('.mdx')) {
+ arrayOfFiles.push(fullPath);
+ }
+ });
+ return arrayOfFiles;
+};
+
+// Helper: Extract Doc ID from Frontmatter
+const getDocId = (content, filename) => {
+ const idMatch = content.match(/^---\s+[\s\S]*?\nid:\s*(.*?)\s*[\n\r]/m);
+ if (idMatch && idMatch[1]) {
+ return idMatch[1].replace(/['"]/g, '').trim();
+ }
+ return filename;
+};
+
+// --- CORE LOGIC ---
+const buildIndex = () => {
+ if (isIndexed) return;
+ console.log('[ExtractPreprocessor] ⚡ Indexing snippets via Regex...');
+
+ const allFiles = [];
+ DOCS_DIRS.forEach(dir => getAllFiles(path.resolve(process.cwd(), dir), allFiles));
+
+ let count = 0;
+
+ // Regex to find:
CONTENT
+ // We use [\s\S]*? to match content across multiple lines (lazy match)
+ const extractRegex = /]*>([\s\S]*?)<\/div>/g;
+
+ allFiles.forEach(filePath => {
+ try {
+ const content = fs.readFileSync(filePath, 'utf8');
+ const filename = path.basename(filePath, path.extname(filePath));
+ const docId = getDocId(content, filename);
+
+ let match;
+ // Loop through all matches in the file
+ while ((match = extractRegex.exec(content)) !== null) {
+ let [fullTag, extractId, snippetContent] = match;
+
+ // Clean up the content (optional: trim leading/trailing newlines)
+ snippetContent = snippetContent.replace(/^\n+|\n+$/g, '');
+
+ // Generate Key: "docId:snippetId"
+ // If the ID already has a colon, assume user provided full ID
+ const key = extractId.includes(':') ? extractId : `${docId}:${extractId}`;
+
+ snippetRegistry.set(key, snippetContent);
+ console.log(`[ExtractPreprocessor] ⚡ Indexed snippet: ${key}`);
+ count++;
+ }
+ } catch (e) {
+ console.warn(`[ExtractPreprocessor] Failed to read ${filePath}`);
+ }
+ });
+
+ isIndexed = true;
+ console.log(`[ExtractPreprocessor] ⚡ Indexed ${count} snippets.`);
+};
+
+// This function is called by Docusaurus for EVERY markdown file
+const preprocessor = ({ filePath, fileContent }) => {
+ // 1. Ensure Index exists (runs once)
+ buildIndex();
+
+ // 2. Regex to find:
+ // Matches
OR
+ const copyRegex = /
\s*(?:<\/div>)?/g;
+
+ // 3. Replace with content
+ return fileContent.replace(copyRegex, (match, requestedId) => {
+ if (snippetRegistry.has(requestedId)) {
+ // Return the stored snippet content
+ return snippetRegistry.get(requestedId);
+ } else {
+ console.error(`[ExtractPreprocessor] ❌ Snippet not found: "${requestedId}" in ${path.basename(filePath)}`);
+ // Return an error message in the UI so you see it
+ return `> **Error: Snippet "${requestedId}" not found.**`;
+ }
+ });
+};
+
+module.exports = preprocessor;
\ No newline at end of file
diff --git a/crowdsec-docs/unversioned/troubleshooting/console_issues.md b/crowdsec-docs/unversioned/troubleshooting/console_issues.md
index 144d0cdc9..7140df999 100644
--- a/crowdsec-docs/unversioned/troubleshooting/console_issues.md
+++ b/crowdsec-docs/unversioned/troubleshooting/console_issues.md
@@ -14,6 +14,7 @@ This page lists all possible health check issues, their trigger conditions, and
- 🌟 **Bonus** : Optimization advises and upper tier recommendation with great return on value *(comming in next iterations of Stack Health)*
## Health Check Issues Overview
+
| Issue | Criticality | Summary | Resolution |
|-------|-------------|---------|------------|
@@ -27,6 +28,7 @@ This page lists all possible health check issues, their trigger conditions, and
| **Security Engine Offline** | 🔥 Critical | Security Engine has not reported to Console for 24+ hours | [Troubleshooting](/u/troubleshooting/issue_se_offline) |
| **Security Engine Too Many Alerts** | ⚠️ High | More than 250,000 alerts in 6 hours | [Troubleshooting](/u/troubleshooting/issue_se_too_many_alerts) |
+
## Issue Dependencies
Some issues are related and share common root causes:
diff --git a/crowdsec-docs/unversioned/troubleshooting/intro.md b/crowdsec-docs/unversioned/troubleshooting/intro.md
index 2d0f97695..36391ce7c 100644
--- a/crowdsec-docs/unversioned/troubleshooting/intro.md
+++ b/crowdsec-docs/unversioned/troubleshooting/intro.md
@@ -77,3 +77,7 @@ When using `cscli` to list your parsers, scenarios and collections, some might a
### Which information is sent to your services ?
See [CAPI documentation](/docs/next/central_api/intro).
+
+### stack Health issues list
+
+
\ No newline at end of file