Cloudron makes it easy to run web apps like WordPress, Nextcloud, GitLab on your server. Find out more or install now.


Skip to content
  • 1 Votes
    3 Posts
    113 Views
    M

    Out of curiosity I've built what @luckow suggested, only with Cloudron API (notifications endpoint) -> n8n -> some magic -> ntfy.sh-app and it looks like this:

    f036453c-4190-48d2-bae2-33c5e16133b5-image.png

    Action gets triggered (either automatically or manually), n8n checks the cloudron api https://my.example1.com/api/v1/notifications (create a readonly access token in your profile and update the Bearer token with it) with the parameters acknowledged = false, so only unread notifications are collected.

    The code-node does the following (filteredTitles because I don't care about app-updates, only actual issues, can be adjusted of course):

    const notifications = items[0].json.notifications; const types = notifications.map(notification => notification.type); const titles = notifications.map(notification => notification.title); // Filter notifications to remove those with type "appUpdated" const filteredNotifications = notifications.filter(notification => notification.type != "appUpdated"); // Extract titles of the filtered notifications const filteredTitles = filteredNotifications.map(notification => notification.title); //return [{ json: { types, titles } }]; return [{ json: { filteredTitles } }];

    This will result in an array of current notifications, e.g.

    [ { "filteredTitles": [ "Email is not configured properly", "The app at cms.example.com ran out of memory." ] } ] The if-node checks if there's actual content. If it's empty, nothing will happen, if it's not, it will make a http request to your nfty instance and topic of your choice (saved credentials in n8n) and send the notifications:

    2ae64242-ff94-44d3-843d-fb0154436eed-image.png

    Since I was working with readonly tokens, I had no intentions of marking the notifications as read after sending out the message. So there's room for improvement, but I thought I would throw it in here as a PoC 🙂

    Complete code (My_Cloudron_workflow.json) is here:

    { "name": "My Cloudron workflow", "nodes": [ { "parameters": {}, "id": "0dcf1d60-f8e7-4ffe-be14-64399a5c924d", "name": "When clicking \"Test workflow\"", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ 240, 620 ] }, { "parameters": { "url": "https://my.example1.com/api/v1/notifications", "sendQuery": true, "queryParameters": { "parameters": [ { "name": "acknowledged", "value": "false" } ] }, "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "Bearer 123" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ {} ] }, "options": {} }, "id": "073de174-b658-480e-bb3c-6be103304bd9", "name": "EX1", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 460, 620 ], "alwaysOutputData": false }, { "parameters": { "url": "https://my.example2.com/api/v1/notifications", "sendQuery": true, "queryParameters": { "parameters": [ { "name": "acknowledged", "value": "false" } ] }, "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "Bearer 456" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ {} ] }, "options": {} }, "id": "c29eae02-51b5-417c-a103-d35b4bcb15e8", "name": "EX2", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 460, 780 ], "alwaysOutputData": false }, { "parameters": { "url": "https://my.example3.com/api/v1/notifications", "sendQuery": true, "queryParameters": { "parameters": [ { "name": "acknowledged", "value": "false" } ] }, "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "Bearer 789" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ {} ] }, "options": {} }, "id": "a00d01ae-fca9-41e8-b16f-3456459f414a", "name": "EX3", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 460, 460 ], "alwaysOutputData": false }, { "parameters": { "jsCode": "const notifications = items[0].json.notifications;\nconst types = notifications.map(notification => notification.type);\nconst titles = notifications.map(notification => notification.title);\n\n// Filter notifications to remove those with type \"appUpdated\"\nconst filteredNotifications = notifications.filter(notification => notification.type != \"appUpdated\");\n\n// Extract titles of the filtered notifications\nconst filteredTitles = filteredNotifications.map(notification => notification.title);\n\n//return [{ json: { types, titles } }];\nreturn [{ json: { filteredTitles } }];" }, "id": "fb776916-d0d4-46b6-8a7d-7de5694e650c", "name": "Code", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 620, 460 ] }, { "parameters": { "jsCode": "const notifications = items[0].json.notifications;\nconst types = notifications.map(notification => notification.type);\nconst titles = notifications.map(notification => notification.title);\n\n// Filter notifications to remove those with type \"appUpdated\"\nconst filteredNotifications = notifications.filter(notification => notification.type != \"appUpdated\");\n\n// Extract titles of the filtered notifications\nconst filteredTitles = filteredNotifications.map(notification => notification.title);\n\n//return [{ json: { types, titles } }];\nreturn [{ json: { filteredTitles } }];" }, "id": "968f2566-7983-46ef-b14a-3677a3a6fc88", "name": "Code1", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 620, 620 ] }, { "parameters": { "jsCode": "const notifications = items[0].json.notifications;\nconst types = notifications.map(notification => notification.type);\nconst titles = notifications.map(notification => notification.title);\n\n// Filter notifications to remove those with type \"appUpdated\"\nconst filteredNotifications = notifications.filter(notification => notification.type != \"appUpdated\");\n\n// Extract titles of the filtered notifications\nconst filteredTitles = filteredNotifications.map(notification => notification.title);\n\n//return [{ json: { types, titles } }];\nreturn [{ json: { filteredTitles } }];" }, "id": "5463ace4-ed1f-408a-a13c-a153fbda2ca3", "name": "Code2", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 620, 780 ] }, { "parameters": { "method": "POST", "url": "https://ntfy.nftyserver.com/cloudron-notifications", "authentication": "genericCredentialType", "genericAuthType": "httpBasicAuth", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Title", "value": "Current example3.com notifications" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "Title", "body": "={{ $json.filteredTitles.join(\"\\n\"); }}", "options": {} }, "id": "83bc0ca8-d7e1-4fb5-8dd9-0b3360f6763e", "name": "HTTP Request", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 940, 460 ], "credentials": { "httpBasicAuth": { "id": "5tUu1DjoNUxl0M5a", "name": "ntfy token" } } }, { "parameters": { "method": "POST", "url": "https://ntfy.nftyserver.com/cloudron-notifications", "authentication": "genericCredentialType", "genericAuthType": "httpBasicAuth", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Title", "value": "Current example1.com notifications" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "Title", "body": "={{ $json.filteredTitles.join(\"\\n\"); }}", "options": {} }, "id": "92815612-9381-444f-86b5-7c3c3cf8a371", "name": "HTTP Request1", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 940, 620 ], "credentials": { "httpBasicAuth": { "id": "5tUu1DjoNUxl0M5a", "name": "ntfy token" } } }, { "parameters": { "method": "POST", "url": "https://ntfy.nftyserver.com/cloudron-notifications", "authentication": "genericCredentialType", "genericAuthType": "httpBasicAuth", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Title", "value": "Current example2.com notifications" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "Title", "body": "={{ $json.filteredTitles.join(\"\\n\"); }}", "options": {} }, "id": "bc221e4f-8817-4c19-a74d-9333a4783319", "name": "HTTP Request2", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 940, 780 ], "credentials": { "httpBasicAuth": { "id": "5tUu1DjoNUxl0M5a", "name": "ntfy token" } } }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "43482a2f-b2b0-4f0a-b970-7b11e8f6ee03", "leftValue": "={{ $json.filteredTitles }}", "rightValue": "", "operator": { "type": "array", "operation": "notEmpty", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "id": "af3ebb14-32db-4afb-8c9d-84eb593eae96", "name": "If1", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [ 780, 620 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "1811bb6f-55ca-4b2d-9f6b-97c489ef4d21", "leftValue": "={{ $json.filteredTitles }}", "rightValue": "", "operator": { "type": "array", "operation": "notEmpty", "singleValue": true } } ], "combinator": "or" }, "options": {} }, "id": "e354d631-5daf-495d-a5e2-e0fb64ef212d", "name": "If2", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [ 780, 780 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "43482a2f-b2b0-4f0a-b970-7b11e8f6ee03", "leftValue": "={{ $json.filteredTitles }}", "rightValue": "", "operator": { "type": "array", "operation": "notEmpty", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "id": "a3fc1a27-f94d-4651-884f-34c683b0a2da", "name": "If", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [ 780, 460 ] } ], "pinData": {}, "connections": { "When clicking \"Test workflow\"": { "main": [ [ { "node": "EX1", "type": "main", "index": 0 }, { "node": "EX2", "type": "main", "index": 0 }, { "node": "EX3", "type": "main", "index": 0 } ] ] }, "EX1": { "main": [ [ { "node": "Code1", "type": "main", "index": 0 } ] ] }, "EX2": { "main": [ [ { "node": "Code2", "type": "main", "index": 0 } ] ] }, "EX3": { "main": [ [ { "node": "Code", "type": "main", "index": 0 } ] ] }, "Code1": { "main": [ [ { "node": "If1", "type": "main", "index": 0 } ] ] }, "Code2": { "main": [ [ { "node": "If2", "type": "main", "index": 0 } ] ] }, "Code": { "main": [ [ { "node": "If", "type": "main", "index": 0 } ] ] }, "If1": { "main": [ [ { "node": "HTTP Request1", "type": "main", "index": 0 } ] ] }, "If2": { "main": [ [ { "node": "HTTP Request2", "type": "main", "index": 0 } ] ] }, "If": { "main": [ [ { "node": "HTTP Request", "type": "main", "index": 0 } ] ] } }, "active": false, "settings": { "executionOrder": "v1" }, "versionId": "b1689af0-81c6-453c-a8aa-1d7c84d7a85e", "meta": { "templateCredsSetupCompleted": true, "instanceId": "8903125d64295093a384f52f480a1738308c71ada8db56c53efba4894aefbff2" }, "id": "I2Mq9Y8GOPIDIh8i", "tags": [] }

    I threw out a module last minute, so hopefully no oversights in there. Improvements are welcome! 😉

    TL;DR: Adjust the URLs to your Cloudrons, create and add readonly tokens, add a ntfy token to your n8n credentials and adjust the URL to your ntfy instance.