luci-app-cloudflared: add Tunnels status page
authorSergey Ponomarev <stokito@gmail.com>
Sat, 3 Feb 2024 20:06:36 +0000 (22:06 +0200)
committerPaul Donald <newtwen@gmail.com>
Thu, 8 Feb 2024 13:42:00 +0000 (14:42 +0100)
The page allows to see if the tunnel has connections.
This can be used for a basic troubleshooting without opening the Cloudflare dashboard.

Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
(cherry picked from commit b8a4328fcfa77621258d210dffc091353fcb1989)

applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js [new file with mode: 0644]
applications/luci-app-cloudflared/po/templates/cloudflared.pot
applications/luci-app-cloudflared/root/usr/share/luci/menu.d/luci-app-cloudflared.json
applications/luci-app-cloudflared/root/usr/share/rpcd/acl.d/luci-app-cloudflared.json

diff --git a/applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js b/applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js
new file mode 100644 (file)
index 0000000..28acf44
--- /dev/null
@@ -0,0 +1,86 @@
+/* This is free software, licensed under the Apache License, Version 2.0
+ *
+ * Copyright (C) 2024 Sergey Ponomarev <stokito@gmail.com>
+ */
+
+'use strict';
+'require view';
+'require fs';
+
+function listTunnels() {
+       let command = '/usr/bin/cloudflared';
+       let commandArgs = ['tunnel', 'list', '-o', 'json'];
+       return fs.exec(command, commandArgs).then(function (res) {
+               if (res.code === 0) {
+                       return JSON.parse(res.stdout);
+               } else {
+                       throw new Error(res.stdout + ' ' + res.stderr);
+               }
+       });
+}
+
+
+return view.extend({
+       handleSaveApply: null,
+       handleSave: null,
+       handleReset: null,
+
+       load: function () {
+               return Promise.all([
+                       listTunnels()
+               ]);
+       },
+
+       render: function (data) {
+               var tunnels = data[0];
+
+               var tunnelsElList = [];
+               for (var tunnel of tunnels) {
+                       var connectionsSection = [];
+                       if (tunnel.connections.length > 0) {
+                               var connectionsElList = [];
+                               for (let connection of tunnel.connections) {
+                                       var dateOpenedAt = new Date(connection.opened_at).toLocaleString();
+                                       connectionsElList.push(
+                                               E('tr', [
+                                                       E('td', connection.id),
+                                                       E('td', connection.origin_ip),
+                                                       E('td', dateOpenedAt),
+                                                       E('td', connection.colo_name)
+                                               ])
+                                       );
+                               }
+
+                               connectionsSection = [
+                                       E('h5', _('Connections')),
+                                       E('table', {'class': 'table cbi-section-table'}, [
+                                               E('thead', [
+                                                       E('tr', {'class': 'tr table-titles'}, [
+                                                               E('th', {'class': 'th'}, 'ID'),
+                                                               E('th', {'class': 'th'}, _('Origin IP')),
+                                                               E('th', {'class': 'th'}, _('Opened At')),
+                                                               E('th', {'class': 'th'}, _('Data center')),
+                                                       ]),
+                                               ]),
+                                               E('tbody', connectionsElList)
+                                       ])
+                               ];
+                       } else {
+                               connectionsSection = [E('em', _('No connections'))];
+                       }
+
+                       var tunnelEl = E('div', [
+                                       E('h4', tunnel.name),
+                                       E('span', 'ID '),
+                                       E('span', tunnel.id),
+                                       E('div', connectionsSection)
+                               ]
+                       );
+                       tunnelsElList.push(tunnelEl);
+               }
+               return E([], [
+                       E('h2', {'class': 'section-title'}, _('Tunnels')),
+                       E('div', {'id': 'tunnels'}, tunnelsElList),
+               ]);
+       }
+});
\ No newline at end of file
index ce4f3c6f966f04b4f52272e1048f8ca0abdb5101..8f1d4d54289116e6b834aa30fc3b1bf3316aab55 100644 (file)
@@ -35,12 +35,20 @@ msgstr ""
 msgid "Configuration"
 msgstr ""
 
+#: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js:55
+msgid "Connections"
+msgstr ""
+
 #: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/config.js:54
 msgid ""
 "Create and manage your network on the <a %s>Cloudflare Zero Trust</a> "
 "dashboard."
 msgstr ""
 
+#: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js:62
+msgid "Data center"
+msgstr ""
+
 #: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/config.js:113
 msgid "Debug"
 msgstr ""
@@ -91,10 +99,14 @@ msgstr ""
 msgid "Log Direction:"
 msgstr ""
 
-#: applications/luci-app-cloudflared/root/usr/share/luci/menu.d/luci-app-cloudflared.json:22
+#: applications/luci-app-cloudflared/root/usr/share/luci/menu.d/luci-app-cloudflared.json:30
 msgid "Logs"
 msgstr ""
 
+#: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js:69
+msgid "No connections"
+msgstr ""
+
 #: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/config.js:36
 msgid "Not Running"
 msgstr ""
@@ -103,6 +115,14 @@ msgstr ""
 msgid "Obtain a certificate <a %s>here</a>."
 msgstr ""
 
+#: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js:61
+msgid "Opened At"
+msgstr ""
+
+#: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js:60
+msgid "Origin IP"
+msgstr ""
+
 #: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/config.js:102
 msgid "Region"
 msgstr ""
@@ -138,6 +158,11 @@ msgstr ""
 msgid "Token"
 msgstr ""
 
+#: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/tunnels.js:82
+#: applications/luci-app-cloudflared/root/usr/share/luci/menu.d/luci-app-cloudflared.json:22
+msgid "Tunnels"
+msgstr ""
+
 #: applications/luci-app-cloudflared/htdocs/luci-static/resources/view/cloudflared/log.js:33
 msgid "Unable to read the interface info from /var/log/cloudflared.log."
 msgstr ""
index 08c9a2d3b0e0e2e910e0e9e31e5536b7bf8daaed..4177afa6beb37988386a68a969e212c55ad93861 100644 (file)
                        "path": "cloudflared/config"
                }
        },
+       "admin/vpn/cloudflared/tunnels": {
+               "title": "Tunnels",
+               "order": 20,
+               "action": {
+                       "type": "view",
+                       "path": "cloudflared/tunnels"
+               }
+       },
        "admin/vpn/cloudflared/log": {
                "title": "Logs",
-               "order": 20,
+               "order": 30,
                "action": {
                        "type": "view",
                        "path": "cloudflared/log"
index 82a9a89a216a477665bc9bca831bb79757e15a8a..3013b35dbacf8f8958c8cf020b6be6bb4c3673c1 100644 (file)
@@ -6,7 +6,10 @@
                        "ubus": {
                                "service": [ "list" ]
                        },
-                       "file": [ "/var/log/cloudflared.log" ]
+                       "file": {
+                               "/var/log/cloudflared.log": [ "read" ],
+                               "/usr/bin/cloudflared *": [ "exec" ]
+                       }
                },
                "write": {
                        "uci": [ "cloudflared" ],