refactor: extract shared utilities and standardize page patterns
- Add fmtBytes() and csvToArr() helpers to hoover/helpers.js - Replace inline async patterns with ActionButton/ConfirmDelete in wireguard.js - Convert addDomain/editDomain to QuickModal + apiSubmit in proxy.js - Convert settingsModal handlers to formAction in certs.js - Remove redundant synced handling from dhcp.js apply button - Add onComplete callback to ConfirmDelete (fixes users.js onRefresh bug) - Fix passkeys.js ActionCell/Table usage (invalid component API) - Remove duplicate fmtBytes from dashboard.js
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/** WireGuard page — tunnel & peer management. */
|
||||
import { html, PageHeader, Badge, StatusDot, Empty, Table, ServiceStatus, renderGuard, esc, enc, $val, apiFetch, toast, openModal, closeModal, formModal, definePage, getModel, modelFetch, ActionButton, ActionCell, MonoText, ActionGroup, QuickModal, downloadBlob, formAction, ApplyConfirm, qrSVG } from '/static/hoover/index.js';
|
||||
import { html, PageHeader, Badge, StatusDot, Empty, Table, ServiceStatus, renderGuard, esc, enc, $val, apiFetch, toast, openModal, closeModal, formModal, definePage, getModel, modelFetch, ActionButton, ActionCell, ConfirmDelete, MonoText, ActionGroup, QuickModal, downloadBlob, formAction, ApplyConfirm, qrSVG, csvToArr } from '/static/hoover/index.js';
|
||||
|
||||
/* ── LAN detection helper ────────────────────────────────────── */
|
||||
function getLanSubnets() {
|
||||
@@ -22,11 +22,7 @@ function getLanSubnets() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Allowed IPs helper ──────────────────────────────────────── */
|
||||
function parseAllowedIps(value) {
|
||||
if (!value || !value.trim()) return [];
|
||||
return value.split(',').map(s => s.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
|
||||
/* ── Color helpers ────────────────────────────────────────────── */
|
||||
function classColor(classKey) {
|
||||
@@ -63,7 +59,7 @@ const addPeer = QuickModal({
|
||||
} else if (preset === 'none') {
|
||||
allowed_ips = [];
|
||||
} else if (preset === 'custom') {
|
||||
allowed_ips = parseAllowedIps($val('wg-allowed'));
|
||||
allowed_ips = csvToArr($val('wg-allowed'));
|
||||
} else {
|
||||
allowed_ips = ['0.0.0.0/0'];
|
||||
}
|
||||
@@ -241,7 +237,7 @@ function settingsModal(wireguardData, state) {
|
||||
handler: formAction(async () => {
|
||||
const port = parseInt($val('wg-port'), 10);
|
||||
if (isNaN(port) || port < 1 || port > 65535) throw 'Invalid port';
|
||||
const addresses = ($val('wg-addrs') || '').split(',').map(s => s.trim()).filter(Boolean);
|
||||
const addresses = csvToArr($val('wg-addrs'));
|
||||
if (!addresses.length) throw 'At least one address required';
|
||||
const body = {
|
||||
interface: {
|
||||
@@ -342,43 +338,6 @@ function editClassModal(key, cls, peerCount) {
|
||||
});
|
||||
}
|
||||
|
||||
async function initClassKeys(classKey) {
|
||||
const resp = await apiFetch('/api/wireguard/classes/keys/' + enc(classKey), {
|
||||
method: 'POST',
|
||||
});
|
||||
if (!resp.ok) {
|
||||
toast(resp.error || 'Failed to generate keys', 'error');
|
||||
return;
|
||||
}
|
||||
toast('Keys generated for class "' + classKey + '"', 'success');
|
||||
modelFetch('wireguard');
|
||||
}
|
||||
|
||||
async function deleteAccessClass(key) {
|
||||
if (!confirm(`Delete access class '${key}'?`)) return;
|
||||
const resp = await apiFetch('/api/wireguard/classes', {
|
||||
method: 'DELETE',
|
||||
body: { key },
|
||||
});
|
||||
if (!resp.ok) {
|
||||
toast(resp.error || 'Failed to delete class', 'error');
|
||||
return;
|
||||
}
|
||||
toast('Class deleted', 'success');
|
||||
modelFetch('wireguard');
|
||||
}
|
||||
|
||||
async function toggleClassTunnel(classKey, isUp) {
|
||||
const url = '/api/wireguard/classes/' + enc(classKey) + '/' + (isUp ? 'down' : 'up');
|
||||
const resp = await apiFetch(url, { method: 'POST' });
|
||||
if (!resp.ok) {
|
||||
toast(resp.error || 'Failed', 'error');
|
||||
return;
|
||||
}
|
||||
toast('Tunnel ' + (isUp ? 'stopped' : 'started'), 'success');
|
||||
modelFetch('wireguard');
|
||||
}
|
||||
|
||||
/* ── Access Classes Section ──────────────────────────────────── */
|
||||
function renderAccessClasses(config, status) {
|
||||
const classes = config?.access_classes || {};
|
||||
@@ -414,13 +373,18 @@ function renderAccessClasses(config, status) {
|
||||
</td>
|
||||
<td>
|
||||
${!hasKeys
|
||||
? html`<button class="btn btn-sm btn-warning" onClick=${() => initClassKeys(k)} title="Generate keys">Keys</button>`
|
||||
? html`<${ActionButton} url=${'/api/wireguard/classes/keys/' + enc(k)} label="Keys"
|
||||
cls="btn btn-sm btn-warning" successMsg=${'Keys generated for ' + esc(k)} refresh="wireguard" />`
|
||||
: ''}
|
||||
<button class="btn btn-sm btn-outline" onClick=${() => editClassModal(k, v, pCount)}>Edit</button>
|
||||
<button class="btn btn-sm btn-outline" onClick=${() => toggleClassTunnel(k, isUp)}>${isUp ? 'Stop' : 'Start'}</button>
|
||||
<${ActionButton} url=${'/api/wireguard/classes/' + enc(k) + '/' + (isUp ? 'down' : 'up')}
|
||||
cls="btn btn-sm btn-outline" labelOn="Stop" labelOff="Start" condition=${isUp}
|
||||
successMsg=${isUp ? 'Tunnel stopped' : 'Tunnel started'} refresh="wireguard" />
|
||||
${(pCount > 0)
|
||||
? html`<button class="btn btn-sm btn-outline" disabled title="Peers reference this class">Delete</button>`
|
||||
: html`<button class="btn btn-sm btn-outline" onClick=${() => deleteAccessClass(k)}>Delete</button>`}
|
||||
: html`<${ConfirmDelete} url=${'/api/wireguard/classes'} body=${{ key: k }}
|
||||
deleteKey=${k} message=${'Delete access class ' + esc(k) + '?'} success="Class deleted"
|
||||
refresh="wireguard" label="Delete" />`}
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
@@ -528,10 +492,12 @@ export default definePage({
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Subnet: ${esc(v.subnet || '-')}</span>
|
||||
<span>LAN: ${v.lan_access ? 'Yes' : 'No'}</span>
|
||||
<span>Keys: ${classHasKeys(v) ? 'Ready' : html`<button class="btn btn-xs btn-warning" onClick=${() => initClassKeys(k)}>Generate</button>`}</span>
|
||||
<span>Keys: ${classHasKeys(v) ? 'Ready' : html`<${ActionButton} url=${'/api/wireguard/classes/keys/' + enc(k)} label="Generate" cls="btn btn-xs btn-warning" successMsg=${'Keys generated'} refresh="wireguard" />`}</span>
|
||||
</div>
|
||||
<div style="margin-top: 4px;">
|
||||
<button class="btn btn-xs btn-outline" onClick=${() => toggleClassTunnel(k, isUp)}>${isUp ? 'Stop' : 'Start'}</button>
|
||||
<${ActionButton} url=${'/api/wireguard/classes/' + enc(k) + '/' + (isUp ? 'down' : 'up')}
|
||||
cls="btn btn-xs btn-outline" labelOn="Stop" labelOff="Start" condition=${isUp}
|
||||
successMsg=${isUp ? 'Stopped' : 'Started'} refresh="wireguard" />
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
Reference in New Issue
Block a user