Add password authentication

This commit is contained in:
Unheard0840
2026-08-29 09:15:14 +02:00
parent 6648bf0662
commit ab1f479ffd
6 changed files with 271 additions and 27 deletions
+103 -4
View File
@@ -100,6 +100,28 @@
color: #fca5a5;
}
.login {
max-width: 420px;
margin: 80px auto;
}
.login input {
box-sizing: border-box;
width: 100%;
padding: 12px 14px;
border: 1px solid #4b5563;
border-radius: 8px;
background: #111827;
color: #f9fafb;
font: inherit;
margin-top: 8px;
}
.login button {
width: 100%;
margin-top: 16px;
}
button {
margin-top: 16px;
padding: 10px 16px;
@@ -118,10 +140,31 @@
</head>
<body>
<main>
<h1>Munin Bitcoin</h1>
<p class="subtitle">Bitcoin watch-only wallet monitoring and label management</p>
<section id="login" class="card login">
<h1>Munin Bitcoin</h1>
<p class="subtitle">Sign in to access Munin Bitcoin.</p>
<section class="card">
<form id="login-form">
<label for="password">Admin password</label>
<input
id="password"
name="password"
type="password"
autocomplete="current-password"
required
/>
<button type="submit">Sign in</button>
</form>
<p id="login-error" class="error"></p>
</section>
<section id="dashboard" hidden>
<h1>Munin Bitcoin</h1>
<p class="subtitle">Bitcoin watch-only wallet monitoring and label management</p>
<section class="card">
<div class="row">
<span class="label">Munin service</span>
<span id="service-status" class="status">
@@ -168,6 +211,7 @@
<button type="button" onclick="loadStatus()">Refresh status</button>
<p id="error" class="error"></p>
</section>
</section>
</main>
@@ -177,17 +221,55 @@
element.querySelector('span:last-child').textContent = text;
}
async function showLogin() {
document.getElementById('login').hidden = false;
document.getElementById('dashboard').hidden = true;
document.getElementById('password').focus();
}
async function showDashboard() {
document.getElementById('login').hidden = true;
document.getElementById('dashboard').hidden = false;
}
async function login(password) {
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'same-origin',
body: JSON.stringify({ password }),
});
if (!response.ok) {
throw new Error('Invalid password');
}
await showDashboard();
await loadStatus();
}
async function loadStatus() {
const errorElement = document.getElementById('error');
errorElement.textContent = '';
try {
const response = await fetch('/api/status');
const response = await fetch('/api/status', {
credentials: 'same-origin',
});
if (response.status === 401) {
await showLogin();
return;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
await showDashboard();
const status = await response.json();
setStatus(
@@ -250,6 +332,23 @@
}
}
document.getElementById('login-form').addEventListener('submit', async (event) => {
event.preventDefault();
const passwordInput = document.getElementById('password');
const loginError = document.getElementById('login-error');
loginError.textContent = '';
try {
await login(passwordInput.value);
passwordInput.value = '';
} catch {
loginError.textContent = 'Invalid password.';
passwordInput.select();
}
});
loadStatus();
setInterval(loadStatus, 10000);
</script>