Initial config with email service test
Build LinkLog Development Image / development-image (push) Successful in 11s
Build LinkLog Development Image / development-image (push) Successful in 11s
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
const resetForm = document.querySelector('#reset-password-form');
|
||||
const resetStatus = document.querySelector('#reset-password-status');
|
||||
const resetToken = new URLSearchParams(window.location.search).get('token');
|
||||
|
||||
resetForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const response = await fetch('/api/auth/reset-password', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({token: resetToken, password: new FormData(resetForm).get('password')}),
|
||||
});
|
||||
const result = await response.json();
|
||||
resetStatus.textContent = response.ok ? `${result.message} Redirecting...` : (result.detail || 'Reset failed.');
|
||||
resetStatus.style.color = response.ok ? '#166534' : '#b91c1c';
|
||||
if (response.ok) window.setTimeout(() => window.location.assign('/login'), 1000);
|
||||
});
|
||||
+106
-9
@@ -1,26 +1,123 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
document.querySelector('#setup-form').addEventListener('submit', async (event) => {
|
||||
const setupForm = document.querySelector('#setup-form');
|
||||
const testMailButton = document.querySelector('#test-mail-button');
|
||||
const completeSetupButton = document.querySelector('#complete-setup-button');
|
||||
const setupStatus = document.querySelector('#setup-status');
|
||||
const setupTimer = document.querySelector('#setup-timer');
|
||||
let nextAllowedAt = null;
|
||||
let timerHandle = null;
|
||||
|
||||
function updateTimer() {
|
||||
if (timerHandle) window.clearTimeout(timerHandle);
|
||||
if (!nextAllowedAt) {
|
||||
setupTimer.textContent = '';
|
||||
testMailButton.disabled = !setupForm.dataset.saved;
|
||||
return;
|
||||
}
|
||||
const seconds = Math.max(0, Math.ceil((nextAllowedAt - Date.now()) / 1000));
|
||||
if (seconds === 0) {
|
||||
nextAllowedAt = null;
|
||||
updateTimer();
|
||||
return;
|
||||
}
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
setupTimer.textContent = `Next test mail available in ${minutes ? `${minutes}m ` : ''}${seconds % 60}s.`;
|
||||
testMailButton.disabled = true;
|
||||
timerHandle = window.setTimeout(updateTimer, 1000);
|
||||
}
|
||||
|
||||
function applyStatus(result) {
|
||||
setupForm.dataset.saved = result.pending ? 'true' : '';
|
||||
testMailButton.disabled = !result.pending;
|
||||
const smtpDefaults = result.smtp_defaults || {};
|
||||
for (const [name, value] of Object.entries(smtpDefaults)) {
|
||||
const field = setupForm.elements[name];
|
||||
if (!field || field.value || field.type === 'checkbox') continue;
|
||||
field.value = value;
|
||||
}
|
||||
if (typeof smtpDefaults.smtp_use_tls === 'boolean') {
|
||||
setupForm.elements.smtp_use_tls.checked = smtpDefaults.smtp_use_tls;
|
||||
}
|
||||
if (result.next_allowed_at) nextAllowedAt = Date.parse(result.next_allowed_at);
|
||||
updateTimer();
|
||||
}
|
||||
|
||||
async function loadSetupStatus() {
|
||||
const response = await fetch('/api/setup/status');
|
||||
if (!response.ok) return;
|
||||
applyStatus(await response.json());
|
||||
}
|
||||
|
||||
setupForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const form = event.currentTarget;
|
||||
const status = document.querySelector('#setup-status');
|
||||
const values = Object.fromEntries(new FormData(form));
|
||||
values.smtp_port = Number(values.smtp_port);
|
||||
values.smtp_use_tls = form.elements.smtp_use_tls.checked;
|
||||
try {
|
||||
const response = await fetch('/api/setup', {
|
||||
const response = await fetch('/api/setup/configuration', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(values),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok) throw new Error(result.detail || 'Setup failed');
|
||||
status.textContent = result.message;
|
||||
status.style.color = '#94e2d5';
|
||||
form.replaceChildren(status);
|
||||
setupStatus.textContent = result.message;
|
||||
setupStatus.style.color = '#94e2d5';
|
||||
form.dataset.saved = 'true';
|
||||
testMailButton.disabled = false;
|
||||
} catch (error) {
|
||||
status.textContent = error.message;
|
||||
status.style.color = '#f38ba8';
|
||||
setupStatus.textContent = error.message;
|
||||
setupStatus.style.color = '#f38ba8';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
testMailButton.addEventListener('click', async () => {
|
||||
testMailButton.disabled = true;
|
||||
try {
|
||||
const response = await fetch('/api/setup/test-mail', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({email: setupForm.elements.email.value}),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok) {
|
||||
if (response.status === 429 && response.headers.get('Retry-After')) {
|
||||
nextAllowedAt = Date.now() + Number(response.headers.get('Retry-After')) * 1000;
|
||||
updateTimer();
|
||||
}
|
||||
throw new Error(result.detail || 'Test mail failed');
|
||||
}
|
||||
setupStatus.textContent = `${result.message} ${result.sends_remaining} sends remaining.`;
|
||||
setupStatus.style.color = '#94e2d5';
|
||||
if (result.next_allowed_at) nextAllowedAt = Date.parse(result.next_allowed_at);
|
||||
updateTimer();
|
||||
completeSetupButton.hidden = false;
|
||||
} catch (error) {
|
||||
setupStatus.textContent = error.message;
|
||||
setupStatus.style.color = '#f38ba8';
|
||||
if (!nextAllowedAt) testMailButton.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
completeSetupButton.addEventListener('click', async () => {
|
||||
const response = await fetch('/api/setup/complete', {method: 'POST'});
|
||||
const result = await response.json();
|
||||
if (!response.ok) {
|
||||
setupStatus.textContent = result.detail || 'Could not complete setup.';
|
||||
setupStatus.style.color = '#f38ba8';
|
||||
return;
|
||||
}
|
||||
setupStatus.style.color = '#94e2d5';
|
||||
setupStatus.textContent = '';
|
||||
setupStatus.append(document.createTextNode(`${result.message} `));
|
||||
const homeLink = document.createElement('a');
|
||||
homeLink.href = '/';
|
||||
homeLink.textContent = 'Home';
|
||||
setupStatus.append(homeLink);
|
||||
setupForm.replaceChildren(setupStatus);
|
||||
});
|
||||
|
||||
loadSetupStatus();
|
||||
Reference in New Issue
Block a user