Updated vpn_status.html to handle silent API failures without showing an error.

This commit is contained in:
2025-08-30 22:31:26 +00:00
parent b9d2d762b6
commit 7875b06fd6
5 changed files with 93 additions and 6 deletions
+2 -1
View File
@@ -18,7 +18,8 @@ SERIAL_PASSWORDS = {
"3M1D2211Z3": "EP5G!f15878b4af20", "3M1D10146B": "EP5G!076689528baf", "3M1D2211Z3": "EP5G!f15878b4af20", "3M1D10146B": "EP5G!076689528baf",
"3M1D10146G": "EP5G!c3b0072cabf5", "3M1D2211Z1": "EP5G!65b22ae8617a", "3M1D10146G": "EP5G!c3b0072cabf5", "3M1D2211Z1": "EP5G!65b22ae8617a",
"3M1D19125H": "EP5G!da3c04fde559", "3M1D19125G": "EP5G!b73f98633108", "3M1D19125H": "EP5G!da3c04fde559", "3M1D19125G": "EP5G!b73f98633108",
"3M1D19125F": "EP5G!e61201fb9234", "3M1D1R16M4": "EP5G!ca439b544329" "3M1D19125F": "EP5G!e61201fb9234", "3M1D1R16M4": "EP5G!ca439b544329",
"3M1D10146F": "EP5G!d1343c34875d"
} }
def list_home_network_keys(host_ip, token): def list_home_network_keys(host_ip, token):
View File
+1 -1
View File
@@ -16,7 +16,7 @@
"machine_id": "7ebd37b3c5a44ff7acafc84fa3af449d", "machine_id": "7ebd37b3c5a44ff7acafc84fa3af449d",
"num_cpu": 4, "num_cpu": 4,
"virtualization": "kvm", "virtualization": "kvm",
"target_host_ip": "100.93.1.43", "target_host_ip": "100.92.0.238",
"mgmt": { "mgmt": {
"cidr": "192.168.105.156/24", "cidr": "192.168.105.156/24",
"gw": "192.168.105.1" "gw": "192.168.105.1"
-2
View File
@@ -12,8 +12,6 @@
<select class="form-select" id="dashboard-select"> <select class="form-select" id="dashboard-select">
<option selected>Triton</option> <option selected>Triton</option>
<option>Star</option> <option>Star</option>
<option>Bluebonnet</option>
<option>Lonestar</option>
<option>Production</option> <option>Production</option>
<option>Test (future)</option> <option>Test (future)</option>
</select> </select>
+90 -2
View File
@@ -12,8 +12,6 @@
<select class="form-select" id="dashboard-select"> <select class="form-select" id="dashboard-select">
<option selected>Triton</option> <option selected>Triton</option>
<option>Star</option> <option>Star</option>
<option>Bluebonnet</option>
<option>Lonestar</option>
<option>Production</option> <option>Production</option>
<option>Test (future)</option> <option>Test (future)</option>
</select> </select>
@@ -29,7 +27,28 @@
<h4>Results</h4> <h4>Results</h4>
<div id="spinner" class="d-none spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div> <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> <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>
<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 %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}
@@ -76,5 +95,74 @@
actionLink.innerHTML = `<i class="bi bi-arrow-clockwise"></i> Restart`; 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> </script>
{% endblock %} {% endblock %}