luci-app-ocserv: fix status page and users view bugs
authorJoshua Klinesmith <redacted>
Mon, 30 Mar 2026 22:09:44 +0000 (18:09 -0400)
committerPaul Donald <redacted>
Mon, 30 Mar 2026 22:12:31 +0000 (00:12 +0200)
- Fix duplicate "Active OpenConnect Users" header on status page by
  returning the table directly like other status widgets
- Fix colspan mismatch in users table (13 columns, was 10)
- Fix undefined tx/rx in user list by mapping occtl JSON fields
- Switch status widget to occtl --json and add Tx/Rx columns

Fixes: #8439
Signed-off-by: Joshua Klinesmith <redacted>
Co-Authored-By: Claude Opus 4.6 (1M context) <redacted>
applications/luci-app-ocserv/htdocs/luci-static/resources/view/ocserv/users.js
applications/luci-app-ocserv/htdocs/luci-static/resources/view/status/include/80_ocserv.js

index 2cb4e5690a4eac97ead3b3d344f884645294955f..d37c2c1ca6e4c2a40c0365c680ca089cd5223d72 100644 (file)
@@ -68,7 +68,9 @@ return L.view.extend({
                        device: entry?.device,
                        time: entry?.time || entry['connected-at'],
                        cipher: entry?.cipher,
-                       status: entry?.status
+                       status: entry?.status,
+                       tx: entry?._TX || entry?.TX || entry?.tx,
+                       rx: entry?._RX || entry?.RX || entry?.rx
                };
        },
 
@@ -151,7 +153,7 @@ return L.view.extend({
                        if (users.length === 0) {
                                table.appendChild(
                                        E('div', { 'class': 'tr placeholder' }, [
-                                               E('div', { 'class': 'td', 'colspan': 10 }, 
+                                               E('div', { 'class': 'td', 'colspan': 13 },
                                                        E('em', _('Collecting data...')))
                                        ])
                                );
index 615ba9234647ea90936cdbd171ca037c706addc3..6a886574435345d7874f20fdc911483d8bf22e79 100644 (file)
@@ -39,9 +39,8 @@ return baseclass.extend({
                        
                        return users;
                } catch (e) {
-                       // Fall back to text parsing for non-JSON output
-                       console.warn('JSON parsing failed, falling back to text parsing:', e.message);
-                       return this.parseUsersText(output);
+                       console.error('Failed to parse JSON:', e.message);
+                       return users;
                }
        },
 
@@ -56,35 +55,12 @@ return baseclass.extend({
                        device: entry?.device,
                        time: entry?.time || entry['connected-at'],
                        cipher: entry?.cipher,
-                       status: entry?.status
+                       status: entry?.status,
+                       tx: entry?._TX || entry?.TX || entry?.tx,
+                       rx: entry?._RX || entry?.RX || entry?.rx
                };
        },
 
-       parseUsersText: function(output) {
-               const users = [];
-               if (!output) return users;
-
-               const lines = output.split('\n');
-               for (let line of lines) {
-                       // Parse: id user group vpn_ip ip device time cipher status
-                       const match = line.match(/^\s*(\d+)\s+([-_\w]+)\s+([().*\-_\w]+)\s+([:.\-_\w]+)\s+([:.\-_\w]+)\s+([:.\-_\w]+)\s+([:.\-_\w]+)\s+([():.\-_\w]+)\s+([:.\-_\w]+)/);
-                       if (match) {
-                               users.push({
-                                       id: match[1],
-                                       user: match[2],
-                                       group: match[3],
-                                       vpn_ip: match[4],
-                                       ip: match[5],
-                                       device: match[6],
-                                       time: match[7],
-                                       cipher: match[8],
-                                       status: match[9]
-                               });
-                       }
-               }
-               return users;
-       },
-
        handleDisconnect: function(id) {
                return L.resolveDefault(
                        fs.exec('/usr/bin/occtl', ['disconnect', 'id', id]),
@@ -100,7 +76,7 @@ return baseclass.extend({
 
        load: function() {
                return L.resolveDefault(
-                       fs.exec('/usr/bin/occtl', ['show', 'users']).then(res => res.stdout),
+                       fs.exec('/usr/bin/occtl', ['--json', 'show', 'users']).then(res => res.stdout),
                        ''
                );
        },
@@ -118,6 +94,8 @@ return baseclass.extend({
                                E('div', { 'class': 'th' }, _('Time')),
                                E('div', { 'class': 'th' }, _('Cipher')),
                                E('div', { 'class': 'th' }, _('Status')),
+                               E('div', { 'class': 'th' }, _('Tx')),
+                               E('div', { 'class': 'th' }, _('Rx')),
                                E('div', { 'class': 'th' }, '\u00a0')
                        ])
                ]);
@@ -141,7 +119,9 @@ return baseclass.extend({
                                                E('div', { 'class': 'td' }, user.time),
                                                E('div', { 'class': 'td' }, user.cipher),
                                                E('div', { 'class': 'td' }, user.status),
-                                               E('div', { 'class': 'td' }, 
+                                               E('div', { 'class': 'td' }, user.tx),
+                                               E('div', { 'class': 'td' }, user.rx),
+                                               E('div', { 'class': 'td' },
                                                        E('button', {
                                                                'class': 'cbi-button cbi-button-remove',
                                                                'click': L.bind(this.handleDisconnect, this, user.id)
@@ -151,9 +131,6 @@ return baseclass.extend({
                        }
                }
 
-               return E('div', { 'class': 'cbi-section' }, [
-                       E('legend', _('Active OpenConnect Users')),
-                       table
-               ]);
+               return table;
        }
 });
git clone https://git.99rst.org/PROJECT