49 lines
4.0 KiB
JavaScript
49 lines
4.0 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
const loginForm = document.querySelector('#login-form');
|
|
const setupForm = document.querySelector('#setup-form');
|
|
const message = document.querySelector('#message');
|
|
function showMessage(text, kind = '') { message.textContent = text; message.className = kind; }
|
|
async function api(url, options = {}) {
|
|
const response = await fetch(url, { credentials: 'same-origin', ...options, headers: { 'Content-Type': 'application/json', ...(options.headers || {}) } });
|
|
const data = await response.json();
|
|
if (!response.ok || data.ok === false) throw new Error(data.error || 'Request failed.');
|
|
return data;
|
|
}
|
|
function values(form) { return Object.fromEntries(new FormData(form).entries()); }
|
|
async function initialize() {
|
|
const auth = await api('/api/auth/status');
|
|
if (auth.authenticated) { location.replace('/'); return; }
|
|
const status = await api('/api/setup/status');
|
|
if (status.configured) { loginForm.hidden = false; const authStatus = await api('/api/auth/status'); document.querySelector('#totp-row').hidden = !authStatus.totpRequired; loginForm.elements.totpCode.required = authStatus.totpRequired; document.querySelector('#intro').textContent = authStatus.totpRequired ? 'Sign in with your password and two-factor authentication.' : 'Sign in to your personal Chimera server.'; return; }
|
|
setupForm.hidden = false; setupForm.elements.language.value = status.defaultLanguage || 'en'; document.querySelector('#intro').textContent = 'Complete the one-time secure Docker configuration.';
|
|
document.querySelector('#data-status').textContent = `Data root: ${status.dataRoot} — ${status.volumes.filter(v => v.writable).length}/${status.volumes.length} volumes writable.`;
|
|
const roots = document.querySelector('#roots');
|
|
for (const volume of status.volumes) {
|
|
const label = document.createElement('label'); label.className = 'check';
|
|
const input = document.createElement('input'); input.type = 'checkbox'; input.name = 'allowedRoots'; input.value = volume.path; input.checked = true; input.disabled = !volume.writable;
|
|
label.append(input, document.createTextNode(` ${volume.path} ${volume.writable ? '' : '(read-only)'}`)); roots.append(label);
|
|
}
|
|
document.querySelector('#capabilities').textContent = Object.entries(status.capabilities.features).map(([name, feature]) => `${name}: ${feature.available ? 'available' : `unavailable — ${feature.reason}`}`).join('\n');
|
|
}
|
|
loginForm.addEventListener('submit', async event => {
|
|
event.preventDefault(); showMessage('Signing in…');
|
|
try { await api('/api/auth/login', { method: 'POST', body: JSON.stringify(values(loginForm)) }); location.replace('/'); }
|
|
catch (error) { showMessage(error.message, 'error'); }
|
|
});
|
|
document.querySelector('#test-ftp').addEventListener('click', async () => {
|
|
const data = values(setupForm); if (!data.ftpHost) { showMessage('Enter a PS5 host or IP first.', 'error'); return; }
|
|
showMessage('Testing FTP connection…');
|
|
try { await api('/api/setup/test-ftp', { method: 'POST', headers: { 'X-Chimera-Bootstrap-Token': data.bootstrapToken || '' }, body: JSON.stringify({ host: data.ftpHost, port: data.ftpPort, user: data.ftpUser, password: data.ftpPassword }) }); showMessage('FTP connection successful.', 'success'); }
|
|
catch (error) { showMessage(error.message, 'error'); }
|
|
});
|
|
setupForm.addEventListener('submit', async event => {
|
|
event.preventDefault(); showMessage('Saving setup securely…'); const data = values(setupForm);
|
|
data.allowedRoots = [...setupForm.querySelectorAll('input[name="allowedRoots"]:checked:not(:disabled)')].map(input => input.value);
|
|
data.safetyConfirmed = setupForm.elements.safetyConfirmed.checked;
|
|
try { await api('/api/setup', { method: 'POST', headers: { 'X-Chimera-Bootstrap-Token': data.bootstrapToken || '' }, body: JSON.stringify(data) }); location.replace('/'); }
|
|
catch (error) { showMessage(error.message, 'error'); }
|
|
});
|
|
initialize().catch(error => showMessage(error.message, 'error'));
|
|
})();
|