- Neuer Endpoint GET/PUT /api/admin/server-settings - Liest appsettings.Production.json (Fallback: appsettings.json → Standardwert) - Schreibt immer in appsettings.Production.json - Definierte Settings: AnthropicToken, MaxIterations, PayloadLogging, PayloadLoggingGzip, KreaToken, KreaApiKey - Neue Frontend-Seite 'server-settings' (Admin-only) - Dynamisches Formular basierend auf Setting-Definitionen vom Backend - Password-Felder mit Eye-Toggle, Checkboxen für Booleans, Number-Inputs - Hinweis dass Neustart erforderlich ist - Dashboard: Neuer Link 'Servereinstellungen' für Admins - appsettings.Production.json vom Publish ausgeschlossen (CopyToPublishDirectory=Never)
146 lines
4.8 KiB
JavaScript
146 lines
4.8 KiB
JavaScript
// ── Server Settings Page (Admin) ──
|
|
|
|
registerPage('server-settings', {
|
|
async init() {
|
|
if (!currentUser || !currentUser.isAdmin) {
|
|
navigateTo('dashboard');
|
|
return;
|
|
}
|
|
|
|
var container = document.getElementById('serverSettingsFields');
|
|
var saveBtn = document.getElementById('serverSettingsSaveBtn');
|
|
var messageEl = document.getElementById('serverSettingsMessage');
|
|
var inputs = {};
|
|
|
|
function showMessage(text, isError) {
|
|
messageEl.textContent = text;
|
|
messageEl.className = 'profile-message ' + (isError ? 'profile-message-error' : 'profile-message-success');
|
|
messageEl.hidden = false;
|
|
if (!isError) setTimeout(function() { messageEl.hidden = true; }, 4000);
|
|
}
|
|
|
|
// Settings laden und Formular aufbauen
|
|
try {
|
|
var result = await apiGetServerSettings();
|
|
var settings = result.settings;
|
|
|
|
for (var i = 0; i < settings.length; i++) {
|
|
var s = settings[i];
|
|
var field = document.createElement('div');
|
|
field.className = 'form-field';
|
|
|
|
var label = document.createElement('label');
|
|
label.textContent = s.label;
|
|
label.setAttribute('for', 'ss-' + s.key);
|
|
field.appendChild(label);
|
|
|
|
if (s.inputType === 'toggle') {
|
|
var toggleLabel = document.createElement('label');
|
|
toggleLabel.className = 'toggle-label';
|
|
|
|
var cb = document.createElement('input');
|
|
cb.type = 'checkbox';
|
|
cb.id = 'ss-' + s.key;
|
|
cb.checked = !!s.value;
|
|
inputs[s.key] = cb;
|
|
|
|
var span = document.createElement('span');
|
|
span.textContent = s.value ? 'Aktiviert' : 'Deaktiviert';
|
|
|
|
cb.addEventListener('change', (function(sp, checkbox) {
|
|
return function() { sp.textContent = checkbox.checked ? 'Aktiviert' : 'Deaktiviert'; };
|
|
})(span, cb));
|
|
|
|
toggleLabel.appendChild(cb);
|
|
toggleLabel.appendChild(span);
|
|
field.appendChild(toggleLabel);
|
|
} else if (s.inputType === 'password') {
|
|
var wrapper = document.createElement('div');
|
|
wrapper.className = 'input-with-btn';
|
|
|
|
var inp = document.createElement('input');
|
|
inp.type = 'password';
|
|
inp.id = 'ss-' + s.key;
|
|
inp.value = s.value || '';
|
|
inp.placeholder = s.label;
|
|
inp.autocomplete = 'off';
|
|
inputs[s.key] = inp;
|
|
|
|
var toggleBtn = document.createElement('button');
|
|
toggleBtn.type = 'button';
|
|
toggleBtn.className = 'icon-btn';
|
|
toggleBtn.innerHTML = '<i class="fa fa-eye"></i>';
|
|
toggleBtn.addEventListener('click', (function(input, btn) {
|
|
return function() {
|
|
if (input.type === 'password') {
|
|
input.type = 'text';
|
|
btn.innerHTML = '<i class="fa fa-eye-slash"></i>';
|
|
} else {
|
|
input.type = 'password';
|
|
btn.innerHTML = '<i class="fa fa-eye"></i>';
|
|
}
|
|
};
|
|
})(inp, toggleBtn));
|
|
|
|
wrapper.appendChild(inp);
|
|
wrapper.appendChild(toggleBtn);
|
|
field.appendChild(wrapper);
|
|
} else if (s.inputType === 'number') {
|
|
var inp = document.createElement('input');
|
|
inp.type = 'number';
|
|
inp.id = 'ss-' + s.key;
|
|
inp.value = s.value != null ? s.value : '';
|
|
inp.placeholder = s.label;
|
|
inputs[s.key] = inp;
|
|
field.appendChild(inp);
|
|
} else {
|
|
var inp = document.createElement('input');
|
|
inp.type = 'text';
|
|
inp.id = 'ss-' + s.key;
|
|
inp.value = s.value || '';
|
|
inp.placeholder = s.label;
|
|
inputs[s.key] = inp;
|
|
field.appendChild(inp);
|
|
}
|
|
|
|
container.appendChild(field);
|
|
}
|
|
|
|
// Speichern
|
|
saveBtn.addEventListener('click', async function() {
|
|
saveBtn.disabled = true;
|
|
messageEl.hidden = true;
|
|
|
|
try {
|
|
var data = {};
|
|
for (var j = 0; j < settings.length; j++) {
|
|
var def = settings[j];
|
|
var input = inputs[def.key];
|
|
if (!input) continue;
|
|
|
|
if (def.type === 'bool') {
|
|
data[def.key] = input.checked;
|
|
} else if (def.type === 'int') {
|
|
data[def.key] = parseInt(input.value, 10) || 0;
|
|
} else {
|
|
data[def.key] = input.value;
|
|
}
|
|
}
|
|
|
|
var result = await apiSaveServerSettings(data);
|
|
showMessage(result.hint || 'Einstellungen gespeichert', false);
|
|
} catch (err) {
|
|
showMessage(err.message, true);
|
|
} finally {
|
|
saveBtn.disabled = false;
|
|
}
|
|
});
|
|
|
|
} catch (err) {
|
|
container.innerHTML = '<p class="form-error">Fehler beim Laden: ' + esc(err.message) + '</p>';
|
|
}
|
|
},
|
|
|
|
destroy() {}
|
|
});
|