feat: complete telegram cms system with workflows and deployment guide
- Add ULTIMATE-Telegram-CMS-COMPLETE.json with all commands - Add Docker Event workflows with Gitea integration - Add comprehensive deployment guide for fresh installs - Add quick reference and testing checklist - Include all n8n workflow exports Commands: /start, /list, /search, /stats, /preview, /publish, /delete, .review Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
372
n8n-docker-workflow-extended.json
Normal file
372
n8n-docker-workflow-extended.json
Normal file
@@ -0,0 +1,372 @@
|
||||
{
|
||||
"name": "Docker Event (Extended)",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "docker-event",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [0, 0],
|
||||
"id": "webhook-main",
|
||||
"name": "Webhook"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const data = $input.first().json;\n\nconst container = data.container ?? data.body?.container ?? '';\nconst image = data.image ?? data.body?.image ?? '';\nconst timestamp = data.timestamp ?? data.body?.timestamp ?? '';\n\nconst slug = container.toLowerCase().replace(/[^a-z0-9]+/g, '-');\nconst serviceName = container.replace(/[-_]/g, ' ');\n\n// Detect project type\nlet projectType = 'selfhosted';\nif (image.includes('denshooter') || image.includes('dk0')) {\n projectType = 'own';\n} else if (container.match(/^(act-|gitea-actions-|runner-)/)) {\n projectType = 'cicd';\n}\n\n// Extract repo from image for own projects\nlet repo = null;\nif (projectType === 'own') {\n const match = image.match(/([^/]+):(\\w+)/);\n if (match) repo = match[1];\n}\n\nreturn [{\n json: {\n container,\n image,\n serviceName,\n timestamp,\n slug,\n projectType,\n repo\n }\n}];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [220, 0],
|
||||
"id": "parse-context",
|
||||
"name": "Parse Context"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://cms.dk0.dev/items/projects?filter[slug][_eq]={{ $json.slug }}&limit=1",
|
||||
"authentication": "predefinedCredentialType",
|
||||
"nodeCredentialType": "httpBearerAuth",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [440, 0],
|
||||
"id": "search-slug",
|
||||
"name": "Check if Exists"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.data.length }}",
|
||||
"rightValue": "0",
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.3,
|
||||
"position": [660, 0],
|
||||
"id": "if-new",
|
||||
"name": "If New"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"rules": {
|
||||
"values": [
|
||||
{
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": ""
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $('Parse Context').item.json.projectType }}",
|
||||
"rightValue": "own",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"renameOutput": true,
|
||||
"outputKey": "Own Project"
|
||||
},
|
||||
{
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": ""
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $('Parse Context').item.json.projectType }}",
|
||||
"rightValue": "cicd",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"renameOutput": true,
|
||||
"outputKey": "CI/CD (Ignore)"
|
||||
},
|
||||
{
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": ""
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $('Parse Context').item.json.projectType }}",
|
||||
"rightValue": "selfhosted",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"renameOutput": true,
|
||||
"outputKey": "Self-Hosted"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.switch",
|
||||
"typeVersion": 3.2,
|
||||
"position": [880, 0],
|
||||
"id": "switch-type",
|
||||
"name": "Switch Type"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://git.dk0.dev/api/v1/repos/denshooter/{{ $('Parse Context').item.json.repo }}/commits?limit=1",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [1100, -200],
|
||||
"id": "get-commits",
|
||||
"name": "Get Last Commit",
|
||||
"credentials": {
|
||||
"httpHeaderAuth": {
|
||||
"id": "gitea-token",
|
||||
"name": "Gitea API"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://git.dk0.dev/api/v1/repos/denshooter/{{ $('Parse Context').item.json.repo }}/contents/README.md",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [1100, -80],
|
||||
"id": "get-readme",
|
||||
"name": "Get README"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const ctx = $('Parse Context').first().json;\nconst commits = $('Get Last Commit').first().json;\nconst readme = $('Get README').first().json;\n\n// Get commit data\nconst commit = Array.isArray(commits) ? commits[0] : commits;\nconst commitMsg = commit?.commit?.message || 'No recent commits';\nconst commitAuthor = commit?.commit?.author?.name || 'Unknown';\n\n// Decode README (base64)\nlet readmeText = '';\ntry {\n const content = readme?.content || readme?.data?.content;\n if (content) {\n readmeText = Buffer.from(content, 'base64').toString('utf8');\n // First 500 chars\n readmeText = readmeText.substring(0, 500).replace(/\\n/g, ' ').trim();\n } else {\n readmeText = 'No README available';\n }\n} catch (e) {\n readmeText = 'No README available';\n}\n\nconsole.log('Commit:', commitMsg);\nconsole.log('README excerpt:', readmeText.substring(0, 100));\n\nreturn [{\n json: {\n container: ctx.container,\n image: ctx.image,\n slug: ctx.slug,\n repo: ctx.repo,\n commitMsg,\n commitAuthor,\n readmeExcerpt: readmeText\n }\n}];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1320, -140],
|
||||
"id": "merge-git-data",
|
||||
"name": "Merge Git Data"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "145931600",
|
||||
"text": "={{ \n'🚀 Neuer Deploy: ' + $json.container + '\\n' +\n'📦 ' + $json.image + '\\n\\n' +\n'📝 Letzter Commit:\\n' + $json.commitMsg + '\\n' +\n'👤 ' + $json.commitAuthor + '\\n\\n' +\n'📄 README:\\n' + $json.readmeExcerpt + '...\\n\\n' +\n'Was ist das Highlight?' \n}}",
|
||||
"additionalFields": {
|
||||
"replyMarkup": "inlineKeyboard",
|
||||
"inlineKeyboard": {
|
||||
"rows": [
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"text": "✍️ Selbst beschreiben",
|
||||
"callbackData": "={{ 'manual:' + $json.slug }}"
|
||||
},
|
||||
{
|
||||
"text": "🤖 Auto-generieren",
|
||||
"callbackData": "={{ 'auto:' + $json.slug }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"text": "❌ Ignorieren",
|
||||
"callbackData": "={{ 'ignore:' + $json.slug }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.2,
|
||||
"position": [1540, -140],
|
||||
"id": "telegram-ask",
|
||||
"name": "Ask via Telegram"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"model": "openrouter/free",
|
||||
"options": {}
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
|
||||
"typeVersion": 1,
|
||||
"position": [1540, 160],
|
||||
"id": "openrouter-model",
|
||||
"name": "OpenRouter Chat Model"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"promptType": "define",
|
||||
"text": "=Du bist ein technischer Autor für dk0.dev.\n\nNeuer Self-Hosted Service:\nContainer: {{ $('Parse Context').item.json.container }}\nImage: {{ $('Parse Context').item.json.image }}\n\nErstelle eine Portfolio-Beschreibung:\n- Was macht die App\n- Warum Self-Hosting besser ist als Cloud\n- Wie sie in die Infrastruktur integriert ist\n\nAntworte NUR als JSON:\n{\n \"title_en\": \"Titel\",\n \"title_de\": \"Titel\",\n \"description_en\": \"4-6 Sätze\",\n \"description_de\": \"4-6 Sätze\",\n \"content_en\": \"2-3 Absätze Markdown\",\n \"content_de\": \"2-3 Absätze Markdown\",\n \"category\": \"selfhosted\",\n \"technologies\": [\"Docker\", \"...\"]\n}"
|
||||
},
|
||||
"type": "@n8n/n8n-nodes-langchain.chainLlm",
|
||||
"typeVersion": 1.9,
|
||||
"position": [1320, 80],
|
||||
"id": "ai-selfhosted",
|
||||
"name": "AI: Self-Hosted"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const raw = $input.first().json.text ?? \"\";\nconst match = raw.match(/\\{[\\s\\S]*\\}/);\nif (!match) throw new Error(\"No JSON found\");\nconst ai = JSON.parse(match[0]);\nreturn [{ json: ai }];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1540, 80],
|
||||
"id": "parse-json-selfhosted",
|
||||
"name": "Parse JSON"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const ai = $input.first().json;\nconst ctx = $('Parse Context').first().json;\n\nconst body = {\n slug: ctx.slug,\n status: \"draft\",\n featured: false,\n title: ai.title_en,\n category: ai.category,\n technologies: ai.technologies,\n tags: ai.technologies,\n date: new Date().toISOString().slice(0, 10),\n translations: {\n create: [\n {\n languages_code: \"en-US\",\n title: ai.title_en,\n description: ai.description_en,\n content: ai.content_en\n },\n {\n languages_code: \"de-DE\",\n title: ai.title_de,\n description: ai.description_de,\n content: ai.content_de\n }\n ]\n }\n};\n\nconst response = await this.helpers.httpRequest({\n method: \"POST\",\n url: \"https://cms.dk0.dev/items/projects\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": \"Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB\"\n },\n body\n});\n\nreturn [{ json: response }];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1760, 80],
|
||||
"id": "add-to-directus-selfhosted",
|
||||
"name": "Add to Directus"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "145931600",
|
||||
"text": "={{ \n'🆕 Self-Hosted Service: ' + $('Parse Context').first().json.serviceName + '\\n\\n' +\n'📝 ' + $json.data.title + '\\n\\n' +\n'Status: Draft erstellt (ID: ' + $json.data.id + ')\\n\\n' +\n'/publishproject' + $json.data.id + ' — Veröffentlichen\\n' + \n'/deleteproject' + $json.data.id + ' — Löschen' \n}}",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.2,
|
||||
"position": [1980, 80],
|
||||
"id": "telegram-notify-selfhosted",
|
||||
"name": "Notify Selfhosted"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "{ \"success\": true, \"message\": \"CI/CD container ignored\" }",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.5,
|
||||
"position": [1100, 200],
|
||||
"id": "respond-ignore",
|
||||
"name": "Respond (Ignore)"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "{ \"success\": true }",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.5,
|
||||
"position": [2200, 0],
|
||||
"id": "respond-success",
|
||||
"name": "Respond"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "{ \"success\": true, \"message\": \"Project already exists\" }",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.5,
|
||||
"position": [880, 200],
|
||||
"id": "respond-exists",
|
||||
"name": "Respond (Exists)"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [[{ "node": "Parse Context", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse Context": {
|
||||
"main": [[{ "node": "Check if Exists", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Check if Exists": {
|
||||
"main": [[{ "node": "If New", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"If New": {
|
||||
"main": [
|
||||
[{ "node": "Switch Type", "type": "main", "index": 0 }],
|
||||
[{ "node": "Respond (Exists)", "type": "main", "index": 0 }]
|
||||
]
|
||||
},
|
||||
"Switch Type": {
|
||||
"main": [
|
||||
[{ "node": "Get Last Commit", "type": "main", "index": 0 }],
|
||||
[{ "node": "Respond (Ignore)", "type": "main", "index": 0 }],
|
||||
[{ "node": "AI: Self-Hosted", "type": "main", "index": 0 }]
|
||||
]
|
||||
},
|
||||
"Get Last Commit": {
|
||||
"main": [[{ "node": "Get README", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Get README": {
|
||||
"main": [[{ "node": "Merge Git Data", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Merge Git Data": {
|
||||
"main": [[{ "node": "Ask via Telegram", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Ask via Telegram": {
|
||||
"main": [[{ "node": "Respond", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"OpenRouter Chat Model": {
|
||||
"ai_languageModel": [[{ "node": "AI: Self-Hosted", "type": "ai_languageModel", "index": 0 }]]
|
||||
},
|
||||
"AI: Self-Hosted": {
|
||||
"main": [[{ "node": "Parse JSON", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Parse JSON": {
|
||||
"main": [[{ "node": "Add to Directus", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Add to Directus": {
|
||||
"main": [[{ "node": "Notify Selfhosted", "type": "main", "index": 0 }]]
|
||||
},
|
||||
"Notify Selfhosted": {
|
||||
"main": [[{ "node": "Respond", "type": "main", "index": 0 }]]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"id": "docker-event-extended"
|
||||
}
|
||||
Reference in New Issue
Block a user