안전 개인 정보 준수 구독 소스 지능 요약
고급
이것은AI, SecOps분야의자동화 워크플로우로, 43개의 노드를 포함합니다.주로 Set, Code, Sort, Gmail, Filter 등의 노드를 사용하며인공지능 기술을 결합하여 스마트 자동화를 구현합니다. 안전, 개인 정보 보호 및 준수를 위한 지능형 AI 요약
사전 요구사항
- •Google 계정 및 Gmail API 인증 정보
- •Google Gemini API Key
사용된 노드 (43)
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"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": "AI 에이전트 - 개인정보보호 인텔리전스",
"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": "AI 에이전트 - 보안 인텔리전스",
"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": "AI 에이전트 - 규정준수 인텔리전스",
"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": "데일리 다이제스트 트리거",
"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": "개인정보보호 피드 가져오기",
"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": "규정준수 피드 가져오기",
"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": "보안 아티클 메타데이터 정규화",
"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": "개인정보보호 아티클 메타데이터 정규화",
"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": "규정준수 아티클 메타데이터 정규화",
"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": "최근 보안 아티클 필터링 (24시간)",
"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": "최근 개인정보보호 아티클 필터링 (24시간)",
"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": "최근 규정준수 아티클 필터링 (24시간)",
"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": "보안 아티클 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": "개인정보보호 아티클 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": "규정준수 아티클 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 보안 요약기",
"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 개인정보보호 요약기",
"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 규정준수 요약기",
"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": "개인정보보호 최종 뉴스레터 HTML 생성",
"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": "보안 최종 뉴스레터 HTML 생성",
"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": "규정준수 최종 뉴스레터 HTML 생성",
"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": "보안 최종 다이제스트 이메일 전송",
"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": "개인정보보호 최종 다이제스트 이메일 전송",
"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": "규정준수 최종 다이제스트 이메일 전송",
"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": "보안 아티클 날짜순 정렬",
"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": "개인정보보호 아티클 날짜순 정렬",
"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": "규정준수 아티클 날짜순 정렬",
"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": "스티커 노트",
"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": "스티커 노트1",
"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": "스티커 노트2",
"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": "보안 RSS 분리",
"type": "n8n-nodes-base.splitOut",
"position": [
-2040,
3620
],
"parameters": {
"options": {},
"fieldToSplitOut": "rss_url"
},
"typeVersion": 1
},
{
"id": "5784491f-071c-4159-af29-6e21305f5e78",
"name": "규정준수 RSS 분리",
"type": "n8n-nodes-base.splitOut",
"position": [
-2060,
4840
],
"parameters": {
"options": {},
"fieldToSplitOut": "rss_url"
},
"typeVersion": 1
},
{
"id": "edbd5da4-f18c-4d08-8023-df4ef7ec11fa",
"name": "보안 RSS 읽기",
"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": "개인정보보호 RSS 읽기",
"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": "규정준수 RSS 읽기",
"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": "개인정보보호 RSS 분리",
"type": "n8n-nodes-base.splitOut",
"position": [
-2060,
4280
],
"parameters": {
"options": {},
"fieldToSplitOut": "rss_url"
},
"typeVersion": 1
},
{
"id": "d1ae80d8-8094-4f84-aa9e-d4533a99a70d",
"name": "보안 RSS 가져오기",
"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": "스티커 노트3",
"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": "스티커 노트4",
"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": "스티커 노트5",
"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": "스티커 노트6",
"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": "스티커 노트7",
"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": "스티커 노트8",
"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
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
고급 - 인공지능, 보안 운영
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
AI 기반 공급업체 정책 및 RSS 피드 분석 및 위험 점수 매기기
리스크 점수 통합 AI 공급업체 정책 및 RSS 피드 분석 시스템
If
Code
Sort
+
If
Code
Sort
29 노드Kamalraj
인공지능
시각화 참조 라이브러리에서 n8n 노드를 탐색
可视化 참조 라이브러리에서 n8n 노드를 탐색
If
Ftp
Set
+
If
Ftp
Set
113 노드I versus AI
기타
AI를 사용한 LinkedIn RSS 처리
Gemini AI 및 RSS 피드를 사용한 LinkedIn 뉴스 게시 자동화
If
Set
Code
+
If
Set
Code
43 노드Club de Inteligencia Artificial Politécnico CIAP
인공지능
지능형 Gmail 이메일 태그
AI 기반 내용 분류를 통한 지능형 Gmail 이메일 조직
If
Set
Code
+
If
Set
Code
18 노드Niranjan G
지원
GPT-4.1, Outlook 및 Mem.ai를 사용한 Microsoft Teams 회의 분석 자동화
GPT-4.1, Outlook 및 Mem.ai를 사용한 자동화된 Microsoft Teams 회의 분석
If
Set
Code
+
If
Set
Code
61 노드Wayne Simpson
인사
애자일 팀 스프린트 계획 자동화
OpenAI, Google 캘린더 및 Gmail을 사용한 애자일 팀용 스프린트 계획 자동화
If
Set
Code
+
If
Set
Code
52 노드Willemijn
제품
워크플로우 정보
난이도
고급
노드 수43
카테고리2
노드 유형11
저자
Niranjan G
@niranjanCybersecurity leader turning complex workflows into seamless, AI-driven automations.
외부 링크
n8n.io에서 보기 →
이 워크플로우 공유