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
    1 Posts
    27 Views
    No one has replied
  • 2 Votes
    1 Posts
    29 Views
    No one has replied
  • 1 Votes
    3 Posts
    107 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.

  • 5 Votes
    5 Posts
    98 Views
    L

    I would like to be able to leave a note alongside a particular application backup, too.

  • A decent CRM

    Moved Discuss
    16
    1 Votes
    16 Posts
    555 Views
    marcusquinnM

    @zonzonzon It's the best FOSS you'll find. Don't know your other experience, but you can pretty much get Espo to do anything you please. If you need accounting done for you, though, Odoo is also a consideration, and their SaaS pricing ain't too bad for what you get.

  • 1 Votes
    1 Posts
    40 Views
    No one has replied
  • App store not showing - unable to connect calendar

    Unsolved Cal.com
    15
    0 Votes
    15 Posts
    278 Views
    32463

    Sadly, this is the way cal.com works. When you self-host it, you need to provision the apps you want your users to be able to use, which cannot be done after the initial install it seems. It might be worth asking upstream in their Github about options do amend this via CLI or otherwise.

  • 16 Votes
    20 Posts
    948 Views
    jdaviescoatesJ

    NocoDB does nice forms.

  • 0 Votes
    1 Posts
    10 Views
    No one has replied
  • 2 Votes
    28 Posts
    1k Views
    L

    If only we could mark this "Solved - Permanently"!

  • Add a Text Field notes per apps

    Locked Solved Feature Requests
    2
    2 Votes
    2 Posts
    29 Views
    girishG

    @jrl-abstract27 Dup of https://forum.cloudron.io/topic/11559/possibility-to-add-app-notes-for-admins-operators-users/ .

    We have already implemented this for next release. Feel free to comment in the other thread, please.

  • GitLab - Package updates

    Pinned GitLab
    162
    1 Votes
    162 Posts
    9k Views
    girishG

    [1.90.0]

    Update GitLab to 16.11.1 Release announcement Autocomplete support for links to wiki pages Sidebar for metadata on the project overview page Option to cancel a pipeline immediately if any jobs fails Custom webhook headers Test project hooks with the REST API Group comment templates
  • Invidious - Package Updates

    Pinned Invidious
    10
    0 Votes
    10 Posts
    278 Views
    girishG

    [1.3.0]

    Update Invidious to v2.20240427 Full changelog Videos: Use android test suite client (#4650, thanks @SamantazFox) Trending: Un-nest category if this is the only one (#4600, thanks @ChunkyProgrammer) Comments: Add support for new format (#4576, thanks @ChunkyProgrammer) API: Add bitrate to formatStreams too (#4590, thanks @absidue) API: Add 'authorVerified' field on recommended videos (#4562, thanks @ChunkyProgrammer) Videos: Add support for new likes format (#4462, thanks @ChunkyProgrammer) Proxy: Handle non-200 HTTP codes on DASH manifests (#4429, thanks @absidue)
  • 4 Votes
    18 Posts
    482 Views
    girishG

    1.5.7 blocked by https://github.com/LibreTranslate/LibreTranslate/issues/614

  • Ghost - Package Updates

    Pinned Ghost
    379
    1 Votes
    379 Posts
    27k Views
    girishG

    [4.82.4]

    Update Ghost to 5.82.4 Full changelog
  • JupyterHub - Package Updates

    Pinned JupyterHub
    49
    0 Votes
    49 Posts
    1k Views
    girishG

    [1.45.3]

    Update datascience notebook image to lab-4.1.6
  • AppStore returns 424

    Solved Support
    22
    1 Votes
    22 Posts
    206 Views
    osoboO

    @girish Sorry for the delayed response. My server is also hosted in Singapore (by Hostinger). I've noted a couple of seconds delay to open Cloudron's app store but it is definitively back. Thank you again for the support 🙂 !

  • Monitor incoming port 25

    Discuss
    3
    1 Votes
    3 Posts
    58 Views
    T

    @girish
    Thanks, that makes sense. Have setup external monitoring for port 25. Appreciate the speedy response as always.

  • backup fails

    Solved Support
    8
    1 Votes
    8 Posts
    126 Views
    A

    no sorry. Just coincidentally having a similar issue with backups, at the same time I think. I can see why you'd think that, I kind of jumped into the conversation.

  • 1 Votes
    3 Posts
    28 Views
    girishG

    I have changed the error message now to this:

    image.png