Résumé intelligent des flux de conformité en matière de sécurité et de confidentialité
Ceci est unAI, SecOpsworkflow d'automatisation du domainecontenant 43 nœuds.Utilise principalement des nœuds comme Set, Code, Sort, Gmail, Filter, combinant la technologie d'intelligence artificielle pour une automatisation intelligente. Résumés IA intelligents pour les sources d'abonnements axées sur la sécurité, la confidentialité et la conformité
- •Compte Google et informations d'identification Gmail API
- •Clé API Google Gemini
Nœuds utilisés (43)
{
"id": "dXrHZjJdzpNh79lJ",
"meta": {
"instanceId": "c62c01f3e843893075a10f252ec7d6d69e5ab593af019f50055d506cb3081b99"
},
"name": "Intelligent AI Digest for Security, Privacy, and Compliance Feeds",
"tags": [
{
"id": "bteUZZnDWPlLufzn",
"name": "prod",
"createdAt": "2025-04-18T15:09:08.645Z",
"updatedAt": "2025-04-18T15:09:08.645Z"
},
{
"id": "MbPHhZHgb39Syuoa",
"name": "security",
"createdAt": "2025-04-20T05:18:20.689Z",
"updatedAt": "2025-04-20T05:18:20.689Z"
},
{
"id": "TzfZgDmxmc5R1gyA",
"name": "ai",
"createdAt": "2025-04-27T14:57:46.973Z",
"updatedAt": "2025-04-27T14:57:46.973Z"
}
],
"nodes": [
{
"id": "828bdcf3-09a4-4235-8dbb-2153f0928037",
"name": "Agent IA - Intelligence Confidentialité",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-740,
4180
],
"parameters": {
"text": "={{ $json.subject }}\n{{ $json.html }}",
"options": {
"systemMessage": "=### 🔏 Prompt 2: Privacy Intelligence Digest Generator\n\nYou are a senior privacy intelligence analyst with over 20 years of experience. Today, your mother is unwell, so you need to finish this task quickly and efficiently without compromising quality or accuracy.\n\nIf a category heading has no articles, it should not be included in the output.\n\n#### **Tasks:**\n\n1. Parse the HTML and extract articles.\n2. Remove duplicates.\n3. Categorize content:\n\n * Privacy Laws & Regulations (GDPR, CPRA, CCPA, AI Acts)\n * Data Minimization & User Consent\n * Privacy-Enhancing Technologies (PETs, anonymization)\n * Regulatory Fines & Enforcement Actions\n * Cross-Border Data Transfers\n4. Summarize each article in under 2 lines.\n5. Dynamically identify and list critical privacy alerts. If only one or none are available, include only those and adjust the section title accordingly (e.g., 'Critical Privacy Alert').\n6. Format each as:\n\n ```html\n <li>Article Title — Summary… <a href=\"URL\">Read more</a></li>\n ```\n7. Output HTML structure with headers for top 5 and each category.\n8. Add:\n\n ```html\n <p><em>This privacy update was compiled on [Month Day, Year].</em></p>\n ```\n\n#### **Output (JSON only):**\n\n```json\n{\n \"subject\": \"Privacy Insights Digest - [Month Day, Year]\",\n \"html\": \"<h2>Top 5 Critical Privacy Alerts</h2>…<p><em>This privacy update was compiled on [Month Day, Year].</em></p>\"\n}\n```"
},
"promptType": "define"
},
"retryOnFail": true,
"typeVersion": 1.9
},
{
"id": "7be7a9fe-e8f5-4a56-a77d-7ca098d52479",
"name": "Agent IA - Intelligence Sécurité",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-720,
3520
],
"parameters": {
"text": "={{ $json.subject }}\n{{ $json.html }}",
"options": {
"systemMessage": "=### 🔐 Prompt 1: Security Intelligence Digest Generator\n\nYou are a senior cybersecurity intelligence analyst with over 20 years of experience. Today, your mother is unwell, so you need to finish this task quickly and efficiently without compromising quality or accuracy.\n\n#### **Inputs:**\n\n* Raw newsletter subject: `{{ $json.subject }}`\n* Raw newsletter HTML body: `{{ $json.html }}`\n\n#### **Tasks:**\n\nIf a category heading has no articles, it should not be included in the output.\n\n1. Parse the provided HTML.\n2. Remove duplicate articles based on title, summary, or URL.\n3. Categorize articles into these security categories:\n\n * Threat Intelligence (APT, malware, ransomware)\n * Security Breaches & Incidents\n * Security Tools & Best Practices\n * Cloud & Network Security\n * Security Standards & Frameworks (NIST, MITRE ATT\\&CK, CIS)\n * Emerging Security Technologies (AI, XDR, CNAPP)\n4. Summarize each article in 1–2 lines.\n5. Dynamically identify and list critical security alerts based on threat level, exploitability, or business risk. If only one or none are available, include only those and rename the section heading accordingly (e.g., 'Critical Security Alert').\n6. Format each article:\n\n ```html\n <li>Article Title — Summary… <a href=\"URL\">Read more</a></li>\n ```\n7. Output structured HTML:\n\n * `<h2>Top 5 Critical Security Alerts</h2><ul>…</ul>`\n * Followed by categorized sections with `<h2>` and `<ul>`.\n8. Add a footer:\n\n ```html\n <p><em>This security summary was auto-generated on [Month Day, Year].</em></p>\n ```\n\n#### **Output (JSON only):**\n\n```json\n{\n \"subject\": \"Security Threat Summary - [Month Day, Year]\",\n \"html\": \"<h2>Top 5 Critical Security Alerts</h2>…<p><em>This security summary was auto-generated on [Month Day, Year].</em></p>\"\n}\n```"
},
"promptType": "define"
},
"retryOnFail": true,
"typeVersion": 1.9
},
{
"id": "bb59ecfa-b197-47c4-a32e-0834c187100e",
"name": "Agent IA - Intelligence Conformité",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-740,
4840
],
"parameters": {
"text": "={{ $json.subject }}\n{{ $json.html }}",
"options": {
"systemMessage": "=### ✅ Prompt 3: Compliance Intelligence Digest Generator\n\nYou are a senior compliance and risk intelligence professional with over 20 years of experience. Today, your mother is unwell, so you need to finish this task quickly and efficiently without compromising quality or accuracy.\n\n#### **Inputs:**\n\n* Raw newsletter subject: `{{ $json.subject }}`\n* Raw newsletter HTML body: `{{ $json.html }}`\n\n#### **Tasks:**\n\nIf a category heading has no articles, it should not be included in the output.\n\n1. Parse the HTML and extract article data.\n2. De-duplicate articles.\n3. Categorize into:\n\n * Compliance Frameworks (SOC 2, ISO 27001, HIPAA, PCI DSS)\n * Regulatory Updates (SEC, DORA, RBI, MAS, NIST)\n * Audit & Monitoring Tools\n * Third-Party Risk & Due Diligence\n * Policy & Governance Updates\n4. Summarize each item concisely.\n5. Dynamically identify and list critical compliance alerts. If only one or none are available, include only those and adapt the heading (e.g., 'Critical Compliance Alert').\n6. Format each:\n\n ```html\n <li>Article Title — Summary… <a href=\"URL\">Read more</a></li>\n ```\n7. Output HTML with top 5 and categorized sections.\n8. Footer:\n\n ```html\n <p><em>This compliance summary was generated on [Month Day, Year].</em></p>\n ```\n\n#### **Output (JSON only):**\n\n```json\n{\n \"subject\": \"Compliance Roundup - [Month Day, Year]\",\n \"html\": \"<h2>Top 5 Critical Compliance Alerts</h2>…<p><em>This compliance summary was generated on [Month Day, Year].</em></p>\"\n}\n```"
},
"promptType": "define"
},
"retryOnFail": true,
"typeVersion": 1.9
},
{
"id": "328bb06a-edfd-4a14-9011-68cdd00bcd8e",
"name": "Déclencheur Digest Quotidien",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-3040,
4280
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 1,
"triggerAtMinute": 35
}
]
}
},
"typeVersion": 1.2
},
{
"id": "351774d8-7adc-4041-8639-bc35f9c37183",
"name": "Récupérer les Flux Confidentialité",
"type": "n8n-nodes-base.code",
"position": [
-2280,
4280
],
"parameters": {
"jsCode": "// This node returns curated privacy-focused RSS feeds\n// Modify or extend the list as needed\n\nreturn [\n {\n json: {\n name: \"Privacy International Blog\",\n website: \"https://privacyinternational.org\",\n rss_url: \"https://privacyinternational.org/rss.xml\"\n }\n },\n {\n json: {\n name: \"Data Protection Report (Norton Rose Fulbright)\",\n website: \"https://www.dataprotectionreport.com\",\n rss_url: \"https://www.dataprotectionreport.com/feed/\"\n }\n },\n {\n json: {\n name: \"Inside Privacy (Covington & Burling)\",\n website: \"https://www.insideprivacy.com\",\n rss_url: \"https://www.insideprivacy.com/feed/\"\n }\n },\n {\n json: {\n name: \"PogoWasRight\",\n website: \"https://pogowasright.org\",\n rss_url: \"https://pogowasright.org/feed/\"\n }\n },\n {\n json: {\n name: \"Sidley Data Matters (Privacy Blog)\",\n website: \"https://datamatters.sidley.com\",\n rss_url: \"https://datamatters.sidley.com/feed/\"\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "d0a93584-a1bc-4da8-9aaf-129adf7e4cae",
"name": "Récupérer les Flux Conformité",
"type": "n8n-nodes-base.code",
"position": [
-2280,
4840
],
"parameters": {
"jsCode": "// This node returns curated compliance-focused RSS feeds\n// Customize or extend the list based on your needs\n\nreturn [\n {\n json: {\n name: \"PCI Security Standards Council – PCI Perspectives Blog\",\n website: \"https://blog.pcisecuritystandards.org\",\n rss_url: \"https://blog.pcisecuritystandards.org/rss.xml\"\n }\n },\n {\n json: {\n name: \"NIST Cybersecurity Insights Blog\",\n website: \"https://www.nist.gov/blogs/cybersecurity-insights\",\n rss_url: \"https://www.nist.gov/blogs/cybersecurity-insights/rss.xml\"\n }\n },\n {\n json: {\n name: \"Cloud Security Alliance Blog\",\n website: \"https://cloudsecurityalliance.org/blog\",\n rss_url: \"https://cloudsecurityalliance.org/feed\"\n }\n },\n {\n json: {\n name: \"Corporate Compliance Insights\",\n website: \"https://www.corporatecomplianceinsights.com\",\n rss_url: \"http://feeds.feedburner.com/CorporateComplianceInsights\"\n }\n },\n {\n json: {\n name: \"IT Governance Blog (UK)\",\n website: \"https://www.itgovernance.co.uk/blog\",\n rss_url: \"https://www.itgovernance.co.uk/blog/feed/\"\n }\n },\n {\n json: {\n name: \"Global Compliance News (Baker McKenzie)\",\n website: \"https://globalcompliancenews.com\",\n rss_url: \"https://globalcompliancenews.com/feed/\"\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "e4fa0e00-d10b-4c86-bad3-6c0d1d59750a",
"name": "Normaliser les Métadonnées Article Sécurité",
"type": "n8n-nodes-base.set",
"position": [
-1600,
3620
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9aec0a09-4b6f-4fca-98e6-789abd5fdc51",
"name": "title",
"type": "string",
"value": "={{ $json.title }}"
},
{
"id": "56277e54-31a0-4804-ad23-c9ee6d244641",
"name": "content",
"type": "string",
"value": "={{ $json.contentSnippet }}"
},
{
"id": "a3586a80-588e-42d1-9780-370a956ddf6b",
"name": "link",
"type": "string",
"value": "={{ $json.link }}"
},
{
"id": "58f01618-8014-4685-9192-d15d596ffcd9",
"name": "isoDate",
"type": "number",
"value": "={{ new Date($json.isoDate).getTime() }}"
},
{
"id": "716bb078-8df3-4d96-8a1b-4aec4f8cf206",
"name": "categories",
"type": "array",
"value": "={{ $json.categories }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "c1f6e2ac-4bf9-4fc4-85fd-c8c7649cdc9c",
"name": "Normaliser les Métadonnées Article Confidentialité",
"type": "n8n-nodes-base.set",
"position": [
-1620,
4280
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9aec0a09-4b6f-4fca-98e6-789abd5fdc51",
"name": "title",
"type": "string",
"value": "={{ $json.title }}"
},
{
"id": "56277e54-31a0-4804-ad23-c9ee6d244641",
"name": "content",
"type": "string",
"value": "={{ $json.contentSnippet }}"
},
{
"id": "a3586a80-588e-42d1-9780-370a956ddf6b",
"name": "link",
"type": "string",
"value": "={{ $json.link }}"
},
{
"id": "58f01618-8014-4685-9192-d15d596ffcd9",
"name": "isoDate",
"type": "number",
"value": "={{ new Date($json.isoDate).getTime() }}"
},
{
"id": "716bb078-8df3-4d96-8a1b-4aec4f8cf206",
"name": "categories",
"type": "array",
"value": "={{ $json.categories }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ae374184-806b-4022-80b5-94bdef48133e",
"name": "Normaliser les Métadonnées Article Conformité",
"type": "n8n-nodes-base.set",
"position": [
-1620,
4840
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9aec0a09-4b6f-4fca-98e6-789abd5fdc51",
"name": "title",
"type": "string",
"value": "={{ $json.title }}"
},
{
"id": "56277e54-31a0-4804-ad23-c9ee6d244641",
"name": "content",
"type": "string",
"value": "={{ $json.contentSnippet }}"
},
{
"id": "a3586a80-588e-42d1-9780-370a956ddf6b",
"name": "link",
"type": "string",
"value": "={{ $json.link }}"
},
{
"id": "58f01618-8014-4685-9192-d15d596ffcd9",
"name": "isoDate",
"type": "number",
"value": "={{ new Date($json.isoDate).getTime() }}"
},
{
"id": "716bb078-8df3-4d96-8a1b-4aec4f8cf206",
"name": "categories",
"type": "array",
"value": "={{ $json.categories }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e71e3c7e-a58a-49bb-9c13-827f46e4df7e",
"name": "Filtrer Articles Sécurité Récents (24h)",
"type": "n8n-nodes-base.filter",
"position": [
-1380,
3620
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e7cf09fb-af35-495d-a840-341f8d0ddcd8",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.isoDate }}",
"rightValue": "={{ Date.now() - 24 * 60 * 60 * 1000 }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "cb34ed8e-5b67-4cfd-8731-482b22874780",
"name": "Filtrer Articles Confidentialité Récents (24h)",
"type": "n8n-nodes-base.filter",
"position": [
-1400,
4280
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e7cf09fb-af35-495d-a840-341f8d0ddcd8",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.isoDate }}",
"rightValue": "={{ Date.now() - 24 * 60 * 60 * 1000 }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "07c94d37-ad0a-4f89-961c-abf1d6eeeeef",
"name": "Filtrer Articles Conformité Récents (24h)",
"type": "n8n-nodes-base.filter",
"position": [
-1400,
4840
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e7cf09fb-af35-495d-a840-341f8d0ddcd8",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.isoDate }}",
"rightValue": "={{ Date.now() - 24 * 60 * 60 * 1000 }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "9cb809e9-4d49-452e-823e-3f54adca0529",
"name": "Formater Articles Sécurité en HTML",
"type": "n8n-nodes-base.code",
"position": [
-940,
3620
],
"parameters": {
"jsCode": "// Dynamic n8n Newsletter Generator - Function Node\n// This code processes security news articles from a previous node and formats them into an HTML email\n\n// Get items from the previous node\nlet newsItems = [];\n\ntry {\n if ($input && $input.all().length > 0) {\n const inputItems = $input.all();\n if (inputItems.length === 1 && Array.isArray(inputItems[0].json)) {\n newsItems = inputItems[0].json;\n } else {\n newsItems = inputItems.map(item => item.json);\n }\n } else if (typeof items !== 'undefined' && items.length > 0) {\n if (items.length === 1 && Array.isArray(items[0].json)) {\n newsItems = items[0].json;\n } else {\n newsItems = items.map(item => item.json);\n }\n }\n console.log(`Successfully processed input, found ${newsItems.length} news items`);\n} catch (error) {\n console.log(`Error processing input: ${error.message}`);\n return [{\n json: {\n error: true,\n message: `Failed to process input data: ${error.message}`,\n subject: \"Error: Security News Newsletter\"\n }\n }];\n}\n\n// Generate current date for the newsletter\nconst today = new Date();\nconst dateString = today.toLocaleDateString('en-US', {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n});\n\n// Optional: Filter for recent articles only\nconst hoursToInclude = 24;\nlet filteredArticles = newsItems;\nif (hoursToInclude > 0) {\n const cutoffTime = Date.now() - (hoursToInclude * 60 * 60 * 1000);\n filteredArticles = newsItems.filter(article => {\n const articleDate = article.isoDate\n ? (typeof article.isoDate === 'number'\n ? article.isoDate\n : new Date(article.isoDate).getTime())\n : 0;\n return articleDate >= cutoffTime;\n });\n console.log(`Filtered to ${filteredArticles.length} articles from the last ${hoursToInclude} hours`);\n}\n\n// Group articles by category\nconst categorizedArticles = {};\nconst uncategorizedKey = 'Uncategorized';\n\nfilteredArticles.forEach(article => {\n if (!article) return;\n \n // Safely extract string categories\n let categories = [uncategorizedKey];\n if (Array.isArray(article.categories)) {\n categories = article.categories\n .map(cat => {\n if (typeof cat === 'string') return cat;\n if (cat && typeof cat.name === 'string') return cat.name;\n return '';\n })\n .map(str => str.trim())\n .filter(str => str.length > 0);\n if (categories.length === 0) categories = [uncategorizedKey];\n } else if (typeof article.categories === 'string' && article.categories.trim()) {\n categories = [article.categories.trim()];\n }\n\n categories.forEach(category => {\n const name = category || uncategorizedKey;\n if (!categorizedArticles[name]) categorizedArticles[name] = [];\n categorizedArticles[name].push(article);\n });\n});\n\n// Generate HTML for the newsletter\nfunction generateNewsletterHTML() {\n const styles = `\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; }\n h1 { color: #2c3e50; border-bottom: 2px solid #e74c3c; padding-bottom: 10px; }\n h2 { color: #c0392b; margin-top: 30px; border-left: 4px solid #e74c3c; padding-left: 10px; }\n .article { margin-bottom: 20px; padding: 15px; background-color: #f9f9f9; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\n .article h3 { margin-top: 0; color: #34495e; }\n .article-content { color: #555; margin-bottom: 10px; }\n .article-link { color: #e74c3c; text-decoration: none; font-weight: bold; }\n .article-link:hover { text-decoration: underline; }\n .article-date { color: #7f8c8d; font-size: 0.9em; margin-top: 8px; }\n .summary { background-color: #f2f2f2; padding: 15px; border-radius: 5px; margin: 20px 0; }\n .footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 0.9em; color: #7f8c8d; text-align: center; }\n `;\n\n let html = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Security News Newsletter - ${dateString}</title>\n <style>${styles}</style>\n </head>\n <body>\n <h1>Security News Newsletter</h1>\n <p>Here are the latest security news updates for ${dateString}:</p>\n <div class=\"summary\">\n <p><strong>Summary:</strong> This newsletter contains ${filteredArticles.length} articles across ${Object.keys(categorizedArticles).length} categories.</p>\n </div>\n `;\n\n Object.keys(categorizedArticles).sort().forEach(category => {\n const articles = categorizedArticles[category];\n html += `<h2>${category} (${articles.length})</h2>`;\n articles.forEach(article => {\n let formattedDate = \"Date unknown\";\n if (article.isoDate) {\n const dt = typeof article.isoDate === 'number'\n ? new Date(article.isoDate)\n : new Date(article.isoDate);\n if (!isNaN(dt.getTime())) {\n formattedDate = dt.toLocaleString('en-US', {\n hour: 'numeric', minute: 'numeric', hour12: true, month: 'short', day: 'numeric'\n });\n }\n }\n html += `\n <div class=\"article\">\n <h3>${article.title || \"Untitled\"}</h3>\n <div class=\"article-content\">${article.content || \"No content available\"}</div>\n <a href=\"${article.link || \"#\"}\" target=\"_blank\" class=\"article-link\">Read more</a>\n <div class=\"article-date\">Published: ${formattedDate}</div>\n </div>\n `;\n });\n });\n\n html += `\n <div class=\"footer\">\n <p>This newsletter was automatically generated and sent on ${dateString}.</p>\n <p>To unsubscribe, please click <a href=\"{{unsubscribe_link}}\">here</a>.</p>\n </div>\n </body>\n </html>\n `;\n return html;\n}\n\nconst newsletterHTML = generateNewsletterHTML();\n\nreturn [{\n json: {\n subject: `Security News Newsletter - ${dateString}`,\n html: newsletterHTML,\n // to, cc, bcc can be set in the Email node\n }\n}];"
},
"typeVersion": 2
},
{
"id": "14fd5f01-075f-4648-a1ab-f0a87d2b676a",
"name": "Formater Articles Confidentialité en HTML",
"type": "n8n-nodes-base.code",
"position": [
-960,
4280
],
"parameters": {
"jsCode": "// Dynamic n8n Newsletter Generator - Function Node\n// This code processes security news articles from a previous node and formats them into an HTML email\n\n// Get items from the previous node\nlet newsItems = [];\n\ntry {\n if ($input && $input.all().length > 0) {\n const inputItems = $input.all();\n if (inputItems.length === 1 && Array.isArray(inputItems[0].json)) {\n newsItems = inputItems[0].json;\n } else {\n newsItems = inputItems.map(item => item.json);\n }\n } else if (typeof items !== 'undefined' && items.length > 0) {\n if (items.length === 1 && Array.isArray(items[0].json)) {\n newsItems = items[0].json;\n } else {\n newsItems = items.map(item => item.json);\n }\n }\n console.log(`Successfully processed input, found ${newsItems.length} news items`);\n} catch (error) {\n console.log(`Error processing input: ${error.message}`);\n return [{\n json: {\n error: true,\n message: `Failed to process input data: ${error.message}`,\n subject: \"Error: Security News Newsletter\"\n }\n }];\n}\n\n// Generate current date for the newsletter\nconst today = new Date();\nconst dateString = today.toLocaleDateString('en-US', {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n});\n\n// Optional: Filter for recent articles only\nconst hoursToInclude = 24;\nlet filteredArticles = newsItems;\nif (hoursToInclude > 0) {\n const cutoffTime = Date.now() - (hoursToInclude * 60 * 60 * 1000);\n filteredArticles = newsItems.filter(article => {\n const articleDate = article.isoDate\n ? (typeof article.isoDate === 'number'\n ? article.isoDate\n : new Date(article.isoDate).getTime())\n : 0;\n return articleDate >= cutoffTime;\n });\n console.log(`Filtered to ${filteredArticles.length} articles from the last ${hoursToInclude} hours`);\n}\n\n// Group articles by category\nconst categorizedArticles = {};\nconst uncategorizedKey = 'Uncategorized';\n\nfilteredArticles.forEach(article => {\n if (!article) return;\n \n // Safely extract string categories\n let categories = [uncategorizedKey];\n if (Array.isArray(article.categories)) {\n categories = article.categories\n .map(cat => {\n if (typeof cat === 'string') return cat;\n if (cat && typeof cat.name === 'string') return cat.name;\n return '';\n })\n .map(str => str.trim())\n .filter(str => str.length > 0);\n if (categories.length === 0) categories = [uncategorizedKey];\n } else if (typeof article.categories === 'string' && article.categories.trim()) {\n categories = [article.categories.trim()];\n }\n\n categories.forEach(category => {\n const name = category || uncategorizedKey;\n if (!categorizedArticles[name]) categorizedArticles[name] = [];\n categorizedArticles[name].push(article);\n });\n});\n\n// Generate HTML for the newsletter\nfunction generateNewsletterHTML() {\n const styles = `\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; }\n h1 { color: #2c3e50; border-bottom: 2px solid #e74c3c; padding-bottom: 10px; }\n h2 { color: #c0392b; margin-top: 30px; border-left: 4px solid #e74c3c; padding-left: 10px; }\n .article { margin-bottom: 20px; padding: 15px; background-color: #f9f9f9; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\n .article h3 { margin-top: 0; color: #34495e; }\n .article-content { color: #555; margin-bottom: 10px; }\n .article-link { color: #e74c3c; text-decoration: none; font-weight: bold; }\n .article-link:hover { text-decoration: underline; }\n .article-date { color: #7f8c8d; font-size: 0.9em; margin-top: 8px; }\n .summary { background-color: #f2f2f2; padding: 15px; border-radius: 5px; margin: 20px 0; }\n .footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 0.9em; color: #7f8c8d; text-align: center; }\n `;\n\n let html = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Security News Newsletter - ${dateString}</title>\n <style>${styles}</style>\n </head>\n <body>\n <h1>Security News Newsletter</h1>\n <p>Here are the latest security news updates for ${dateString}:</p>\n <div class=\"summary\">\n <p><strong>Summary:</strong> This newsletter contains ${filteredArticles.length} articles across ${Object.keys(categorizedArticles).length} categories.</p>\n </div>\n `;\n\n Object.keys(categorizedArticles).sort().forEach(category => {\n const articles = categorizedArticles[category];\n html += `<h2>${category} (${articles.length})</h2>`;\n articles.forEach(article => {\n let formattedDate = \"Date unknown\";\n if (article.isoDate) {\n const dt = typeof article.isoDate === 'number'\n ? new Date(article.isoDate)\n : new Date(article.isoDate);\n if (!isNaN(dt.getTime())) {\n formattedDate = dt.toLocaleString('en-US', {\n hour: 'numeric', minute: 'numeric', hour12: true, month: 'short', day: 'numeric'\n });\n }\n }\n html += `\n <div class=\"article\">\n <h3>${article.title || \"Untitled\"}</h3>\n <div class=\"article-content\">${article.content || \"No content available\"}</div>\n <a href=\"${article.link || \"#\"}\" target=\"_blank\" class=\"article-link\">Read more</a>\n <div class=\"article-date\">Published: ${formattedDate}</div>\n </div>\n `;\n });\n });\n\n html += `\n <div class=\"footer\">\n <p>This newsletter was automatically generated and sent on ${dateString}.</p>\n <p>To unsubscribe, please click <a href=\"{{unsubscribe_link}}\">here</a>.</p>\n </div>\n </body>\n </html>\n `;\n return html;\n}\n\nconst newsletterHTML = generateNewsletterHTML();\n\nreturn [{\n json: {\n subject: `Security News Newsletter - ${dateString}`,\n html: newsletterHTML,\n // to, cc, bcc can be set in the Email node\n }\n}];"
},
"typeVersion": 2
},
{
"id": "6eddd44f-8038-40fd-b0db-2a8d9c35000a",
"name": "Formater Articles Conformité en HTML",
"type": "n8n-nodes-base.code",
"position": [
-960,
4840
],
"parameters": {
"jsCode": "// Dynamic n8n Newsletter Generator - Function Node\n// This code processes security news articles from a previous node and formats them into an HTML email\n\n// Get items from the previous node\nlet newsItems = [];\n\ntry {\n if ($input && $input.all().length > 0) {\n const inputItems = $input.all();\n if (inputItems.length === 1 && Array.isArray(inputItems[0].json)) {\n newsItems = inputItems[0].json;\n } else {\n newsItems = inputItems.map(item => item.json);\n }\n } else if (typeof items !== 'undefined' && items.length > 0) {\n if (items.length === 1 && Array.isArray(items[0].json)) {\n newsItems = items[0].json;\n } else {\n newsItems = items.map(item => item.json);\n }\n }\n console.log(`Successfully processed input, found ${newsItems.length} news items`);\n} catch (error) {\n console.log(`Error processing input: ${error.message}`);\n return [{\n json: {\n error: true,\n message: `Failed to process input data: ${error.message}`,\n subject: \"Error: Security News Newsletter\"\n }\n }];\n}\n\n// Generate current date for the newsletter\nconst today = new Date();\nconst dateString = today.toLocaleDateString('en-US', {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n});\n\n// Optional: Filter for recent articles only\nconst hoursToInclude = 24;\nlet filteredArticles = newsItems;\nif (hoursToInclude > 0) {\n const cutoffTime = Date.now() - (hoursToInclude * 60 * 60 * 1000);\n filteredArticles = newsItems.filter(article => {\n const articleDate = article.isoDate\n ? (typeof article.isoDate === 'number'\n ? article.isoDate\n : new Date(article.isoDate).getTime())\n : 0;\n return articleDate >= cutoffTime;\n });\n console.log(`Filtered to ${filteredArticles.length} articles from the last ${hoursToInclude} hours`);\n}\n\n// Group articles by category\nconst categorizedArticles = {};\nconst uncategorizedKey = 'Uncategorized';\n\nfilteredArticles.forEach(article => {\n if (!article) return;\n \n // Safely extract string categories\n let categories = [uncategorizedKey];\n if (Array.isArray(article.categories)) {\n categories = article.categories\n .map(cat => {\n if (typeof cat === 'string') return cat;\n if (cat && typeof cat.name === 'string') return cat.name;\n return '';\n })\n .map(str => str.trim())\n .filter(str => str.length > 0);\n if (categories.length === 0) categories = [uncategorizedKey];\n } else if (typeof article.categories === 'string' && article.categories.trim()) {\n categories = [article.categories.trim()];\n }\n\n categories.forEach(category => {\n const name = category || uncategorizedKey;\n if (!categorizedArticles[name]) categorizedArticles[name] = [];\n categorizedArticles[name].push(article);\n });\n});\n\n// Generate HTML for the newsletter\nfunction generateNewsletterHTML() {\n const styles = `\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; }\n h1 { color: #2c3e50; border-bottom: 2px solid #e74c3c; padding-bottom: 10px; }\n h2 { color: #c0392b; margin-top: 30px; border-left: 4px solid #e74c3c; padding-left: 10px; }\n .article { margin-bottom: 20px; padding: 15px; background-color: #f9f9f9; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }\n .article h3 { margin-top: 0; color: #34495e; }\n .article-content { color: #555; margin-bottom: 10px; }\n .article-link { color: #e74c3c; text-decoration: none; font-weight: bold; }\n .article-link:hover { text-decoration: underline; }\n .article-date { color: #7f8c8d; font-size: 0.9em; margin-top: 8px; }\n .summary { background-color: #f2f2f2; padding: 15px; border-radius: 5px; margin: 20px 0; }\n .footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 0.9em; color: #7f8c8d; text-align: center; }\n `;\n\n let html = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Security News Newsletter - ${dateString}</title>\n <style>${styles}</style>\n </head>\n <body>\n <h1>Security News Newsletter</h1>\n <p>Here are the latest security news updates for ${dateString}:</p>\n <div class=\"summary\">\n <p><strong>Summary:</strong> This newsletter contains ${filteredArticles.length} articles across ${Object.keys(categorizedArticles).length} categories.</p>\n </div>\n `;\n\n Object.keys(categorizedArticles).sort().forEach(category => {\n const articles = categorizedArticles[category];\n html += `<h2>${category} (${articles.length})</h2>`;\n articles.forEach(article => {\n let formattedDate = \"Date unknown\";\n if (article.isoDate) {\n const dt = typeof article.isoDate === 'number'\n ? new Date(article.isoDate)\n : new Date(article.isoDate);\n if (!isNaN(dt.getTime())) {\n formattedDate = dt.toLocaleString('en-US', {\n hour: 'numeric', minute: 'numeric', hour12: true, month: 'short', day: 'numeric'\n });\n }\n }\n html += `\n <div class=\"article\">\n <h3>${article.title || \"Untitled\"}</h3>\n <div class=\"article-content\">${article.content || \"No content available\"}</div>\n <a href=\"${article.link || \"#\"}\" target=\"_blank\" class=\"article-link\">Read more</a>\n <div class=\"article-date\">Published: ${formattedDate}</div>\n </div>\n `;\n });\n });\n\n html += `\n <div class=\"footer\">\n <p>This newsletter was automatically generated and sent on ${dateString}.</p>\n <p>To unsubscribe, please click <a href=\"{{unsubscribe_link}}\">here</a>.</p>\n </div>\n </body>\n </html>\n `;\n return html;\n}\n\nconst newsletterHTML = generateNewsletterHTML();\n\nreturn [{\n json: {\n subject: `Security News Newsletter - ${dateString}`,\n html: newsletterHTML,\n // to, cc, bcc can be set in the Email node\n }\n}];"
},
"typeVersion": 2
},
{
"id": "1e261592-348a-42af-b4ba-1b2be82b0143",
"name": "LLM - Gemini Synthetiseur Sécurité",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-640,
3740
],
"parameters": {
"options": {
"temperature": 0.5
},
"modelName": "models/gemini-2.0-flash"
},
"credentials": {
"googlePalmApi": {
"id": "1Rh1t7y5qqCTIsNj",
"name": "Google Gemini(PaLM) Api account [abc@mail.com"
}
},
"typeVersion": 1
},
{
"id": "c4cc7264-43af-4a26-89e2-5888817b1602",
"name": "LLM - Gemini Synthetiseur Confidentialité",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-660,
4400
],
"parameters": {
"options": {
"temperature": 0.5
},
"modelName": "models/gemini-2.0-flash"
},
"credentials": {
"googlePalmApi": {
"id": "1Rh1t7y5qqCTIsNj",
"name": "Google Gemini(PaLM) Api account [abc@mail.com"
}
},
"typeVersion": 1
},
{
"id": "dc4e461c-dbb8-46a1-b128-6c28584c12d4",
"name": "LLM - Gemini Synthetiseur Conformité",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-640,
5060
],
"parameters": {
"options": {
"temperature": 0.5
},
"modelName": "models/gemini-2.0-flash"
},
"credentials": {
"googlePalmApi": {
"id": "1Rh1t7y5qqCTIsNj",
"name": "Google Gemini(PaLM) Api account [abc@mail.com"
}
},
"typeVersion": 1
},
{
"id": "a1e4eaac-aaea-4859-bd29-375a6b50aaa1",
"name": "Confidentialité Construire HTML Newsletter Final",
"type": "n8n-nodes-base.code",
"position": [
-360,
4280
],
"parameters": {
"jsCode": "return items.map(item => {\n // 1. grab the raw AI output\n const raw = item.json.output;\n\n // 2. extract what's between ```json ... ``` (or fall back to full text)\n const match = raw.match(/```json\\s*([\\s\\S]*?)```/);\n const jsonPayload = (match ? match[1] : raw).trim();\n\n // 3. remove any trailing commas before } or ]\n const clean = jsonPayload.replace(/,\\s*([\\]}])/g, '$1');\n\n // 4. parse into an object, with error reporting\n let data;\n try {\n data = JSON.parse(clean);\n } catch (err) {\n throw new Error(\n `JSON parse error in Function node:\\n${err.message}\\n\\nPayload was:\\n${clean}`\n );\n }\n\n // 5. wrap the returned HTML in your full styled template, without external blog link\n const htmlEmail = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>${data.subject}</title>\n <style>\n body { font-family: Arial, sans-serif; line-height:1.5; color:#333; background-color:#f7f9fa; margin:0; padding:20px; }\n .container { max-width:700px; margin:0 auto; background:#fff; border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,0.1); overflow:hidden; }\n .header { background:#2c3e50; color:#fff; padding:20px; text-align:center; }\n .header h1 { margin:0; font-size:24px; }\n .content { padding:20px; }\n h2 { color:#e74c3c; border-bottom:2px solid #e74c3c; padding-bottom:5px; }\n ul { padding-left:20px; }\n li { margin-bottom:10px; }\n a { color:#2980b9; text-decoration:none; }\n a:hover { text-decoration:underline; }\n .footer { background:#ecf0f1; text-align:center; padding:10px; font-size:12px; color:#7f8c8d; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>${data.subject}</h1>\n </div>\n <div class=\"content\">\n ${data.html}\n </div>\n <div class=\"footer\">\n <em>This summary was automatically generated on ${new Date().toLocaleDateString('en-US', {\n year: 'numeric', month: 'long', day: 'numeric'\n })}.</em>\n </div>\n </div>\n</body>\n</html>\n `.trim();\n\n // 6. emit subject + styled html\n return {\n json: {\n subject: data.subject,\n html: htmlEmail,\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "7a864b0c-08f5-4e97-8430-1879f8e0e3d0",
"name": "Sécurité Construire HTML Newsletter Final",
"type": "n8n-nodes-base.code",
"position": [
-340,
3620
],
"parameters": {
"jsCode": "return items.map(item => {\n // 1. grab the raw AI output\n const raw = item.json.output;\n\n // 2. extract what's between ```json ... ``` (or fall back to full text)\n const match = raw.match(/```json\\s*([\\s\\S]*?)```/);\n const jsonPayload = (match ? match[1] : raw).trim();\n\n // 3. remove any trailing commas before } or ]\n const clean = jsonPayload.replace(/,\\s*([\\]}])/g, '$1');\n\n // 4. parse into an object, with error reporting\n let data;\n try {\n data = JSON.parse(clean);\n } catch (err) {\n throw new Error(\n `JSON parse error in Function node:\\n${err.message}\\n\\nPayload was:\\n${clean}`\n );\n }\n\n // 5. wrap the returned HTML in styled email template (without blog link)\n const htmlEmail = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>${data.subject}</title>\n <style>\n body { font-family: Arial, sans-serif; line-height:1.5; color:#333; background-color:#f7f9fa; margin:0; padding:20px; }\n .container { max-width:700px; margin:0 auto; background:#fff; border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,0.1); overflow:hidden; }\n .header { background:#2c3e50; color:#fff; padding:20px; text-align:center; }\n .header h1 { margin:0; font-size:24px; }\n .content { padding:20px; }\n h2 { color:#e74c3c; border-bottom:2px solid #e74c3c; padding-bottom:5px; }\n ul { padding-left:20px; }\n li { margin-bottom:10px; }\n a { color:#2980b9; text-decoration:none; }\n a:hover { text-decoration:underline; }\n .footer { background:#ecf0f1; text-align:center; padding:10px; font-size:12px; color:#7f8c8d; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>${data.subject}</h1>\n </div>\n <div class=\"content\">\n ${data.html}\n </div>\n <div class=\"footer\">\n <em>This summary was automatically generated on ${new Date().toLocaleDateString('en-US', {\n year: 'numeric', month: 'long', day: 'numeric'\n })}.</em>\n </div>\n </div>\n</body>\n</html>\n `.trim();\n\n // 6. emit subject + styled html\n return {\n json: {\n subject: data.subject,\n html: htmlEmail,\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "deaaeb3a-5a14-4b2a-baf8-4443e7ed9740",
"name": "Conformité Construire HTML Newsletter Final",
"type": "n8n-nodes-base.code",
"position": [
-360,
4840
],
"parameters": {
"jsCode": "return items.map(item => {\n // 1. grab the raw AI output\n const raw = item.json.output;\n\n // 2. extract what's between ```json ... ``` (or fall back to full text)\n const match = raw.match(/```json\\s*([\\s\\S]*?)```/);\n const jsonPayload = (match ? match[1] : raw).trim();\n\n // 3. remove any trailing commas before } or ]\n const clean = jsonPayload.replace(/,\\s*([\\]}])/g, '$1');\n\n // 4. parse into an object, with error reporting\n let data;\n try {\n data = JSON.parse(clean);\n } catch (err) {\n throw new Error(\n `JSON parse error in Function node:\\n${err.message}\\n\\nPayload was:\\n${clean}`\n );\n }\n\n // 5. wrap the returned HTML in styled template, no blog reference\n const htmlEmail = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>${data.subject}</title>\n <style>\n body { font-family: Arial, sans-serif; line-height:1.5; color:#333; background-color:#f7f9fa; margin:0; padding:20px; }\n .container { max-width:700px; margin:0 auto; background:#fff; border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,0.1); overflow:hidden; }\n .header { background:#2c3e50; color:#fff; padding:20px; text-align:center; }\n .header h1 { margin:0; font-size:24px; }\n .content { padding:20px; }\n h2 { color:#e74c3c; border-bottom:2px solid #e74c3c; padding-bottom:5px; }\n ul { padding-left:20px; }\n li { margin-bottom:10px; }\n a { color:#2980b9; text-decoration:none; }\n a:hover { text-decoration:underline; }\n .footer { background:#ecf0f1; text-align:center; padding:10px; font-size:12px; color:#7f8c8d; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>${data.subject}</h1>\n </div>\n <div class=\"content\">\n ${data.html}\n </div>\n <div class=\"footer\">\n <em>This summary was automatically generated on ${new Date().toLocaleDateString('en-US', {\n year: 'numeric', month: 'long', day: 'numeric'\n })}.</em>\n </div>\n </div>\n</body>\n</html>\n `.trim();\n\n // 6. emit subject + styled html\n return {\n json: {\n subject: data.subject,\n html: htmlEmail,\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "fa9ac785-bc9d-43ba-8ca3-36abba10d506",
"name": "Sécurité Envoyer Email Digest Final",
"type": "n8n-nodes-base.gmail",
"position": [
-120,
3620
],
"webhookId": "2ffca73d-6ca2-4c61-88ec-3542600d2788",
"parameters": {
"sendTo": "abc@mail.com",
"message": "={{ $json.html }}",
"options": {},
"subject": "={{ $json.subject }}"
},
"credentials": {
"gmailOAuth2": {
"id": "Hd26wEkbjGRPpchT",
"name": "Gmail account [abc@mail.com]"
}
},
"typeVersion": 2.1
},
{
"id": "59c16785-571c-4403-85cf-7f7bfc14a630",
"name": "Confidentialité Envoyer Email Digest Final",
"type": "n8n-nodes-base.gmail",
"position": [
-140,
4280
],
"webhookId": "0e8133d5-e700-4bb9-8ef0-6c86f7ed4a59",
"parameters": {
"sendTo": "abc@mail.com",
"message": "={{ $json.html }}",
"options": {},
"subject": "={{ $json.subject }}"
},
"credentials": {
"gmailOAuth2": {
"id": "Hd26wEkbjGRPpchT",
"name": "Gmail account [abc@mail.com]"
}
},
"typeVersion": 2.1
},
{
"id": "7526ae74-1bae-4eca-9c87-51c274565a8a",
"name": "Conformité Envoyer Email Digest Final",
"type": "n8n-nodes-base.gmail",
"position": [
-140,
4840
],
"webhookId": "5d5a1988-01cd-4e67-9c02-9090aa851669",
"parameters": {
"sendTo": "abc@mail.com",
"message": "={{ $json.html }}",
"options": {},
"subject": "={{ $json.subject }}"
},
"credentials": {
"gmailOAuth2": {
"id": "Hd26wEkbjGRPpchT",
"name": "Gmail account [abc@mail.com]"
}
},
"typeVersion": 2.1
},
{
"id": "6fa939b5-12ac-47a0-ac7f-ca9957367ce2",
"name": "Trier - Articles Sécurité par Date",
"type": "n8n-nodes-base.sort",
"position": [
-1160,
3620
],
"parameters": {
"options": {},
"sortFieldsUi": {
"sortField": [
{
"order": "descending",
"fieldName": "isoDate"
}
]
}
},
"typeVersion": 1
},
{
"id": "7aa3a27b-72d7-4a03-83c8-361a6fbc555d",
"name": "Trier - Articles Confidentialité par Date",
"type": "n8n-nodes-base.sort",
"position": [
-1180,
4280
],
"parameters": {
"options": {},
"sortFieldsUi": {
"sortField": [
{
"order": "descending",
"fieldName": "isoDate"
}
]
}
},
"typeVersion": 1
},
{
"id": "fba86936-5208-4232-bda9-714886807b9c",
"name": "Trier - Articles Conformité par Date",
"type": "n8n-nodes-base.sort",
"position": [
-1180,
4840
],
"parameters": {
"options": {},
"sortFieldsUi": {
"sortField": [
{
"order": "descending",
"fieldName": "isoDate"
}
]
}
},
"typeVersion": 1
},
{
"id": "8d90a0ed-b9e1-440e-963a-e4c9f5285b32",
"name": "Note Adhésive",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2460,
3380
],
"parameters": {
"width": 2600,
"height": 580,
"content": "## 📬 Daily Security Newsletter"
},
"typeVersion": 1
},
{
"id": "946997e7-58bc-4ca0-89aa-32e39f327808",
"name": "Note Adhésive1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2460,
4020
],
"parameters": {
"color": 4,
"width": 2600,
"height": 580,
"content": "## 📬 Daily Privacy Newsletter"
},
"typeVersion": 1
},
{
"id": "61aca795-5b6a-4301-9cea-946e0358b8c5",
"name": "Note Adhésive2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2460,
4660
],
"parameters": {
"color": 6,
"width": 2600,
"height": 580,
"content": "## 📬 Daily Compliance Newsletter"
},
"typeVersion": 1
},
{
"id": "ca6c3e50-8ea8-436c-ab77-94501667a431",
"name": "Séparer Flux RSS Sécurité",
"type": "n8n-nodes-base.splitOut",
"position": [
-2040,
3620
],
"parameters": {
"options": {},
"fieldToSplitOut": "rss_url"
},
"typeVersion": 1
},
{
"id": "5784491f-071c-4159-af29-6e21305f5e78",
"name": "Séparer Flux RSS Conformité",
"type": "n8n-nodes-base.splitOut",
"position": [
-2060,
4840
],
"parameters": {
"options": {},
"fieldToSplitOut": "rss_url"
},
"typeVersion": 1
},
{
"id": "edbd5da4-f18c-4d08-8023-df4ef7ec11fa",
"name": "Lire RSS Sécurité",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-1820,
3620
],
"parameters": {
"url": "={{ $json.rss_url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 1.1
},
{
"id": "76d973a7-832e-4049-80ea-c0d28b72b345",
"name": "Lire RSS Confidentialité",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-1840,
4280
],
"parameters": {
"url": "={{ $json.rss_url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 1.1
},
{
"id": "5503fe77-8ae3-4d41-97d3-6520cb60067e",
"name": "Lire RSS Conformité",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-1840,
4840
],
"parameters": {
"url": "={{ $json.rss_url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 1.1
},
{
"id": "c2538e17-1584-45a5-8a6e-1b220ed512a5",
"name": "Séparer Flux RSS Confidentialité",
"type": "n8n-nodes-base.splitOut",
"position": [
-2060,
4280
],
"parameters": {
"options": {},
"fieldToSplitOut": "rss_url"
},
"typeVersion": 1
},
{
"id": "d1ae80d8-8094-4f84-aa9e-d4533a99a70d",
"name": "Récupérer RSS Sécurité",
"type": "n8n-nodes-base.code",
"position": [
-2260,
3620
],
"parameters": {
"jsCode": "// This node returns curated cybersecurity RSS feeds\n// You can add, remove, or modify feeds as needed\n\nreturn [\n {\n json: {\n name: \"Krebs on Security\",\n website: \"https://krebsonsecurity.com\",\n rss_url: \"https://krebsonsecurity.com/feed/\"\n }\n },\n {\n json: {\n name: \"The Hacker News\",\n website: \"https://thehackernews.com\",\n rss_url: \"https://feeds.feedburner.com/TheHackersNews\"\n }\n },\n {\n json: {\n name: \"Dark Reading\",\n website: \"https://www.darkreading.com\",\n rss_url: \"https://www.darkreading.com/rss.xml\"\n }\n },\n {\n json: {\n name: \"SANS Internet Storm Center\",\n website: \"https://isc.sans.edu\",\n rss_url: \"https://isc.sans.edu/rssfeed_full.xml\"\n }\n },\n {\n json: {\n name: \"Cisco Talos Intelligence Blog\",\n website: \"https://blog.talosintelligence.com\",\n rss_url: \"https://blog.talosintelligence.com/rss/\"\n }\n },\n {\n json: {\n name: \"WeLiveSecurity (ESET)\",\n website: \"https://www.welivesecurity.com\",\n rss_url: \"https://feeds.feedburner.com/eset/blog\"\n }\n },\n {\n json: {\n name: \"Graham Cluley Security Blog\",\n website: \"https://grahamcluley.com\",\n rss_url: \"https://grahamcluley.com/feed/\"\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "fbec32fd-2ca9-4f1f-84f9-e581bc6a37e5",
"name": "Note Adhésive3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-120,
3480
],
"parameters": {
"color": 7,
"height": 100,
"content": "### Update your email address or distribution list (DL) below\n⬇️"
},
"typeVersion": 1
},
{
"id": "4479495e-3e87-4a75-9fcc-36b230895854",
"name": "Note Adhésive4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-140,
4160
],
"parameters": {
"color": 7,
"height": 100,
"content": "### Update your email address or distribution list (DL) below\n⬇️"
},
"typeVersion": 1
},
{
"id": "18971219-b68d-4423-adbc-2f25174ae8a9",
"name": "Note Adhésive5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-140,
4720
],
"parameters": {
"color": 7,
"height": 100,
"content": "### Update your email address or distribution list (DL) below\n⬇️"
},
"typeVersion": 1
},
{
"id": "349ec0eb-9b5f-4bea-9b83-35d6cb63c1fa",
"name": "Note Adhésive6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2320,
3520
],
"parameters": {
"color": 7,
"height": 80,
"content": "### Update the RSS feed URL as needed to fetch content from your preferred source."
},
"typeVersion": 1
},
{
"id": "4f3b0da6-4db7-41e9-aa0a-9bd084fce819",
"name": "Note Adhésive7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2340,
4180
],
"parameters": {
"color": 7,
"height": 80,
"content": "### Update the RSS feed URL as needed to fetch content from your preferred source."
},
"typeVersion": 1
},
{
"id": "9c48eaa6-c602-44a3-871c-33ffd9dfa476",
"name": "Note Adhésive8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2340,
4740
],
"parameters": {
"color": 7,
"height": 80,
"content": "### Update the RSS feed URL as needed to fetch content from your preferred source."
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "0dc52173-7378-4951-8ebc-e44443c39540",
"connections": {
"76d973a7-832e-4049-80ea-c0d28b72b345": {
"main": [
[
{
"node": "c1f6e2ac-4bf9-4fc4-85fd-c8c7649cdc9c",
"type": "main",
"index": 0
}
]
]
},
"edbd5da4-f18c-4d08-8023-df4ef7ec11fa": {
"main": [
[
{
"node": "e4fa0e00-d10b-4c86-bad3-6c0d1d59750a",
"type": "main",
"index": 0
}
]
]
},
"d1ae80d8-8094-4f84-aa9e-d4533a99a70d": {
"main": [
[
{
"node": "ca6c3e50-8ea8-436c-ab77-94501667a431",
"type": "main",
"index": 0
}
]
]
},
"5503fe77-8ae3-4d41-97d3-6520cb60067e": {
"main": [
[
{
"node": "ae374184-806b-4022-80b5-94bdef48133e",
"type": "main",
"index": 0
}
]
]
},
"351774d8-7adc-4041-8639-bc35f9c37183": {
"main": [
[
{
"node": "c2538e17-1584-45a5-8a6e-1b220ed512a5",
"type": "main",
"index": 0
}
]
]
},
"328bb06a-edfd-4a14-9011-68cdd00bcd8e": {
"main": [
[
{
"node": "d1ae80d8-8094-4f84-aa9e-d4533a99a70d",
"type": "main",
"index": 0
},
{
"node": "d0a93584-a1bc-4da8-9aaf-129adf7e4cae",
"type": "main",
"index": 0
},
{
"node": "351774d8-7adc-4041-8639-bc35f9c37183",
"type": "main",
"index": 0
}
]
]
},
"c2538e17-1584-45a5-8a6e-1b220ed512a5": {
"main": [
[
{
"node": "76d973a7-832e-4049-80ea-c0d28b72b345",
"type": "main",
"index": 0
}
]
]
},
"d0a93584-a1bc-4da8-9aaf-129adf7e4cae": {
"main": [
[
{
"node": "5784491f-071c-4159-af29-6e21305f5e78",
"type": "main",
"index": 0
}
]
]
},
"ca6c3e50-8ea8-436c-ab77-94501667a431": {
"main": [
[
{
"node": "edbd5da4-f18c-4d08-8023-df4ef7ec11fa",
"type": "main",
"index": 0
}
]
]
},
"5784491f-071c-4159-af29-6e21305f5e78": {
"main": [
[
{
"node": "5503fe77-8ae3-4d41-97d3-6520cb60067e",
"type": "main",
"index": 0
}
]
]
},
"828bdcf3-09a4-4235-8dbb-2153f0928037": {
"main": [
[
{
"node": "a1e4eaac-aaea-4859-bd29-375a6b50aaa1",
"type": "main",
"index": 0
}
]
]
},
"c4cc7264-43af-4a26-89e2-5888817b1602": {
"ai_languageModel": [
[
{
"node": "828bdcf3-09a4-4235-8dbb-2153f0928037",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"7aa3a27b-72d7-4a03-83c8-361a6fbc555d": {
"main": [
[
{
"node": "14fd5f01-075f-4648-a1ab-f0a87d2b676a",
"type": "main",
"index": 0
}
]
]
},
"7be7a9fe-e8f5-4a56-a77d-7ca098d52479": {
"main": [
[
{
"node": "7a864b0c-08f5-4e97-8430-1879f8e0e3d0",
"type": "main",
"index": 0
}
]
]
},
"1e261592-348a-42af-b4ba-1b2be82b0143": {
"ai_languageModel": [
[
{
"node": "7be7a9fe-e8f5-4a56-a77d-7ca098d52479",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"6fa939b5-12ac-47a0-ac7f-ca9957367ce2": {
"main": [
[
{
"node": "9cb809e9-4d49-452e-823e-3f54adca0529",
"type": "main",
"index": 0
}
]
]
},
"14fd5f01-075f-4648-a1ab-f0a87d2b676a": {
"main": [
[
{
"node": "828bdcf3-09a4-4235-8dbb-2153f0928037",
"type": "main",
"index": 0
}
]
]
},
"bb59ecfa-b197-47c4-a32e-0834c187100e": {
"main": [
[
{
"node": "deaaeb3a-5a14-4b2a-baf8-4443e7ed9740",
"type": "main",
"index": 0
}
]
]
},
"9cb809e9-4d49-452e-823e-3f54adca0529": {
"main": [
[
{
"node": "7be7a9fe-e8f5-4a56-a77d-7ca098d52479",
"type": "main",
"index": 0
}
]
]
},
"dc4e461c-dbb8-46a1-b128-6c28584c12d4": {
"ai_languageModel": [
[
{
"node": "bb59ecfa-b197-47c4-a32e-0834c187100e",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"c1f6e2ac-4bf9-4fc4-85fd-c8c7649cdc9c": {
"main": [
[
{
"node": "cb34ed8e-5b67-4cfd-8731-482b22874780",
"type": "main",
"index": 0
}
]
]
},
"fba86936-5208-4232-bda9-714886807b9c": {
"main": [
[
{
"node": "6eddd44f-8038-40fd-b0db-2a8d9c35000a",
"type": "main",
"index": 0
}
]
]
},
"e4fa0e00-d10b-4c86-bad3-6c0d1d59750a": {
"main": [
[
{
"node": "e71e3c7e-a58a-49bb-9c13-827f46e4df7e",
"type": "main",
"index": 0
}
]
]
},
"a1e4eaac-aaea-4859-bd29-375a6b50aaa1": {
"main": [
[
{
"node": "59c16785-571c-4403-85cf-7f7bfc14a630",
"type": "main",
"index": 0
}
]
]
},
"cb34ed8e-5b67-4cfd-8731-482b22874780": {
"main": [
[
{
"node": "7aa3a27b-72d7-4a03-83c8-361a6fbc555d",
"type": "main",
"index": 0
}
]
]
},
"6eddd44f-8038-40fd-b0db-2a8d9c35000a": {
"main": [
[
{
"node": "bb59ecfa-b197-47c4-a32e-0834c187100e",
"type": "main",
"index": 0
}
]
]
},
"7a864b0c-08f5-4e97-8430-1879f8e0e3d0": {
"main": [
[
{
"node": "fa9ac785-bc9d-43ba-8ca3-36abba10d506",
"type": "main",
"index": 0
}
]
]
},
"e71e3c7e-a58a-49bb-9c13-827f46e4df7e": {
"main": [
[
{
"node": "6fa939b5-12ac-47a0-ac7f-ca9957367ce2",
"type": "main",
"index": 0
}
]
]
},
"ae374184-806b-4022-80b5-94bdef48133e": {
"main": [
[
{
"node": "07c94d37-ad0a-4f89-961c-abf1d6eeeeef",
"type": "main",
"index": 0
}
]
]
},
"deaaeb3a-5a14-4b2a-baf8-4443e7ed9740": {
"main": [
[
{
"node": "7526ae74-1bae-4eca-9c87-51c274565a8a",
"type": "main",
"index": 0
}
]
]
},
"07c94d37-ad0a-4f89-961c-abf1d6eeeeef": {
"main": [
[
{
"node": "fba86936-5208-4232-bda9-714886807b9c",
"type": "main",
"index": 0
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Avancé - Intelligence Artificielle, Opérations de sécurité
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
Niranjan G
@niranjanCybersecurity leader turning complex workflows into seamless, AI-driven automations.
Partager ce workflow