Files
2026-05-07 12:30:51 -04:00

172 lines
7.6 KiB
HTML

{% extends "layout.html" %}
{% set active_page = 'vpn_status' %}
{% block title %}m2000 Status - {{ super() }}{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 id="page-title" class="mb-0">m2000 Status</h2>
</div>
<div class="row align-items-end">
<div class="col-md-4" id="dashboard-select-wrapper">
<label for="dashboard-select" class="form-label">HPE P5G Dashboard</label>
<select class="form-select" id="dashboard-select" {% if not dashboard_names %}disabled{% endif %}>
{% if dashboard_names %}
{% for name in dashboard_names %}
<option value="{{ name }}" {% if loop.first %}selected{% endif %}>{{ name }}</option>
{% endfor %}
{% else %}
<option value="">No dashboards configured</option>
{% endif %}
</select>
</div>
</div>
<hr>
<div id="vpn-view">
<p>List the status of all network links from the selected dashboard. From the results table, you can restart the OpenVPN service on a specific device.</p>
<p>Note that after a VPN connection is restarted it will take some time for the connection to re-establish. Click List m2000 status to refresh the list.</p>
<button class="btn btn-primary" id="listVpnsBtn">List m2000 Status</button>
</div>
<div class="mt-4">
<h4>Results</h4>
<div id="spinner" class="d-none spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div>
<div id="results-output" class="p-3"></div>
<!-- Serial and IPv6 subnet input on the same line, styled -->
<div class="mt-3">
<label class="form-label">Restart VPN by Serial & IPv6 Subnet</label>
<div class="input-group mb-2">
<input type="text" id="serial-input" class="form-control" placeholder="Serial Number"
style="background:#000;color:#fff;font-size:1.2em;border:2px solid #fff;width:10vw;">
<input type="text" id="ipv6-subnet-input" class="form-control ms-2" placeholder="IPv6 prefix"
style="background:#000;color:#fff;font-size:1.2em;border:2px solid #fff;width:10vw;">
<button class="btn btn-danger ms-2" id="restart-ipv6-btn" type="button">Restart VPN</button>
</div>
<div style="margin-top: 1.5em;"></div>
<div id="serial-password-result" class="mt-2"></div>
</div>
</div>
<style>
#serial-input::placeholder,
#ipv6-subnet-input::placeholder {
color: #6b7280 !important; /* Tailwind slate-500 */
opacity: 1;
font-weight: normal !important;
}
</style>
{% endblock %}
{% block extra_scripts %}
<script defer>
document.getElementById('listVpnsBtn').addEventListener('click', async () => {
const dashboard = document.getElementById('dashboard-select').value;
const devices = await apiCall('/api/m2000/list', { dashboard });
if (devices) {
let tableHtml = `<table class="table table-dark table-hover table-sm align-middle table-compact">
<thead><tr><th>Serial</th><th>Name</th><th>Subnet</th><th>Status</th><th class="text-center">Action</th></tr></thead>
<tbody>`;
devices.forEach(d => {
const statusClass = getStatusClass(d.status);
let actionHtml = '';
if (d.status === 'DEPLOYED') {
actionHtml = `<a href="#" class="badge bg-danger text-decoration-none restart-btn" data-serial="${d.serial}" data-subnet="${d.subnet}" data-bs-toggle="tooltip" title="Restart VPN"><i class="bi bi-arrow-clockwise"></i> Restart</a>`;
}
tableHtml += `<tr><td>${d.serial}</td><td>${d.name}</td><td>${d.subnet}</td><td><span class="badge ${statusClass}">${d.status}</span></td><td><div class="d-flex justify-content-center">${actionHtml}</div></td></tr>`;
});
tableHtml += '</tbody></table>';
resultsOutput.innerHTML = tableHtml;
initializeTooltips();
}
});
resultsOutput.addEventListener('click', async (event) => {
const actionLink = event.target.closest('.restart-btn');
if (actionLink) {
event.preventDefault();
const serial = actionLink.dataset.serial;
const subnet = actionLink.dataset.subnet;
const dashboard = document.getElementById('dashboard-select').value;
actionLink.disabled = true;
actionLink.innerHTML = `<span class="spinner-border spinner-border-sm"></span> Sending...`;
const result = await apiCall('/api/m2000/restart', { dashboard, serial, subnet }, false);
if (result) {
alert(result.message);
}
actionLink.disabled = false;
actionLink.innerHTML = `<i class="bi bi-arrow-clockwise"></i> Restart`;
}
});
// Helper to safely call API and handle non-JSON errors
async function apiCall(url, data, showAlert = true) {
try {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const text = await res.text();
try {
return JSON.parse(text);
} catch (e) {
if (showAlert) {
alert("Error: Server returned invalid response. Check server logs.");
}
return null;
}
} catch (err) {
if (showAlert) {
alert("Error: Could not reach server.");
}
return null;
}
}
document.getElementById('restart-ipv6-btn').addEventListener('click', async () => {
const serial = document.getElementById('serial-input').value.trim();
const subnet = document.getElementById('ipv6-subnet-input').value.trim();
const dashboard = document.getElementById('dashboard-select').value;
const btn = document.getElementById('restart-ipv6-btn');
const resultDiv = document.getElementById('serial-password-result');
// Validate input
if (!serial) {
alert('Please enter a Serial.');
return;
}
if (!subnet || !subnet.match(/^fd[0-9a-fA-F:]+\/\d+$/)) {
alert('Please enter a valid IPv6 subnet (e.g. fd14:6666::45:0/112).');
return;
}
btn.disabled = true;
btn.innerHTML = `<span class="spinner-border spinner-border-sm"></span> Sending...`;
resultDiv.innerHTML = '';
// Lookup password first
const pwResult = await apiCall('/api/m2000/lookup_password', { dashboard, serial }, false);
if (pwResult && pwResult.password) {
resultDiv.innerHTML = `<strong>Password:</strong> <code>${pwResult.password}</code>`;
} else if (pwResult && pwResult.error) {
resultDiv.innerHTML = `<span class="text-danger">Password not found.</span>`;
} else {
// Do not show anything if the API failed silently
resultDiv.innerHTML = '';
}
// Restart VPN
const result = await apiCall('/api/m2000/restart', { dashboard, serial, subnet }, false);
if (result && result.message) {
alert(result.message);
} else if (!result) {
alert("Error: Could not restart VPN. Check server logs.");
}
btn.disabled = false;
btn.innerHTML = 'Restart VPN';
});
</script>
{% endblock %}