The admin model management (enable/disable, custom models, default override) had persistent rendering issues. Removed the UI panel — models are managed via the global model selector in the header, which works reliably. Backend API endpoints for model config are retained for future use.
575 lines
23 KiB
JavaScript
575 lines
23 KiB
JavaScript
// ============================================================
|
|
// ADMIN.JS — Admin panel: users, settings, stats
|
|
// ============================================================
|
|
|
|
(function() {
|
|
|
|
var loaded = false;
|
|
|
|
// Load admin panel when admin tab is activated
|
|
document.addEventListener('tabChanged', function(e) {
|
|
if (e.detail && e.detail.tab === 'admin') {
|
|
if (!loaded) { loadAdmin(); loaded = true; }
|
|
}
|
|
});
|
|
|
|
document.addEventListener('click', function(e) {
|
|
// Refresh button
|
|
if (e.target.closest('#btn-refresh-users')) {
|
|
loadUsers();
|
|
}
|
|
// Toggle registration
|
|
if (e.target.closest('#btn-toggle-reg')) {
|
|
toggleRegistration();
|
|
}
|
|
});
|
|
|
|
function loadAdmin() {
|
|
loadSettings();
|
|
loadUsers();
|
|
}
|
|
|
|
// ---- SETTINGS + STATS ----
|
|
|
|
function loadSettings() {
|
|
fetch('/api/admin/settings', { headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) return;
|
|
var stats = data.stats || {};
|
|
var settings = data.settings || {};
|
|
|
|
var el = document.getElementById('stat-users');
|
|
if (el) el.textContent = stats.totalUsers !== undefined ? stats.totalUsers : '—';
|
|
el = document.getElementById('stat-api-total');
|
|
if (el) el.textContent = stats.totalApiCalls !== undefined ? stats.totalApiCalls : '—';
|
|
el = document.getElementById('stat-api-today');
|
|
if (el) el.textContent = stats.todayApiCalls !== undefined ? stats.todayApiCalls : '—';
|
|
|
|
updateRegStatus(settings.registrationEnabled !== false);
|
|
})
|
|
.catch(function(err) { console.error('[Admin] Settings load failed:', err); });
|
|
}
|
|
|
|
function updateRegStatus(enabled) {
|
|
var text = document.getElementById('reg-status-text');
|
|
var btn = document.getElementById('btn-toggle-reg');
|
|
if (text) {
|
|
text.innerHTML = 'Registration is currently <strong style="color:' + (enabled ? 'var(--green)' : 'var(--red)') + '">' + (enabled ? '✅ Enabled' : '❌ Disabled') + '</strong>';
|
|
}
|
|
if (btn) btn.textContent = enabled ? 'Disable Registration' : 'Enable Registration';
|
|
window._adminRegEnabled = enabled;
|
|
}
|
|
|
|
function toggleRegistration() {
|
|
var newVal = !window._adminRegEnabled;
|
|
fetch('/api/admin/settings/registration', {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({ enabled: newVal })
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) {
|
|
updateRegStatus(newVal);
|
|
showToast('Registration ' + (newVal ? 'enabled' : 'disabled'), 'success');
|
|
} else {
|
|
showToast(data.error || 'Failed', 'error');
|
|
}
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
}
|
|
|
|
// ---- USERS ----
|
|
|
|
function loadUsers() {
|
|
var tbody = document.getElementById('admin-users-body');
|
|
if (!tbody) return;
|
|
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--g400);padding:20px;">Loading...</td></tr>';
|
|
|
|
fetch('/api/admin/users', { headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) { tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--red);padding:20px;">Failed to load users</td></tr>'; return; }
|
|
renderUsers(data.users || []);
|
|
})
|
|
.catch(function() { tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--red);padding:20px;">Request failed</td></tr>'; });
|
|
}
|
|
|
|
function renderUsers(users) {
|
|
var tbody = document.getElementById('admin-users-body');
|
|
if (!tbody) return;
|
|
|
|
var currentUser = JSON.parse(localStorage.getItem('ped_scribe_user') || '{}');
|
|
|
|
if (users.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--g400);padding:20px;">No users found</td></tr>';
|
|
return;
|
|
}
|
|
|
|
tbody.innerHTML = users.map(function(u) {
|
|
var isSelf = currentUser.id && u.id === currentUser.id;
|
|
var roleColor = u.role === 'admin' ? 'var(--blue)' : u.role === 'moderator' ? 'var(--purple)' : 'var(--g500)';
|
|
var statusColor = u.disabled ? 'var(--red)' : (u.email_verified ? 'var(--green)' : 'var(--amber)');
|
|
var statusText = u.disabled ? 'Disabled' : (u.email_verified ? 'Active' : 'Unverified');
|
|
var joined = u.created_at ? new Date(u.created_at).toLocaleDateString() : '—';
|
|
|
|
var actions = [];
|
|
if (!isSelf) {
|
|
if (!u.email_verified) {
|
|
actions.push('<button class="btn-sm btn-primary admin-action" data-action="verify" data-id="' + u.id + '" data-email="' + esc(u.email) + '">Verify</button>');
|
|
}
|
|
if (u.disabled) {
|
|
actions.push('<button class="btn-sm btn-success admin-action" data-action="enable" data-id="' + u.id + '" data-email="' + esc(u.email) + '">Enable</button>');
|
|
} else {
|
|
actions.push('<button class="btn-sm btn-ghost admin-action" data-action="disable" data-id="' + u.id + '" data-email="' + esc(u.email) + '">Disable</button>');
|
|
}
|
|
if (u.role === 'admin') {
|
|
actions.push('<button class="btn-sm btn-ghost admin-action" data-action="demote" data-id="' + u.id + '" data-email="' + esc(u.email) + '">Demote</button>');
|
|
} else {
|
|
actions.push('<button class="btn-sm btn-primary admin-action" data-action="promote" data-id="' + u.id + '" data-email="' + esc(u.email) + '">Make Admin</button>');
|
|
}
|
|
if (u.role !== 'moderator') {
|
|
actions.push('<button class="btn-sm btn-ghost admin-action" data-action="set-moderator" data-id="' + u.id + '" data-email="' + esc(u.email) + '" style="color:var(--purple);">Set Moderator</button>');
|
|
}
|
|
if (u.role === 'moderator') {
|
|
actions.push('<button class="btn-sm btn-ghost admin-action" data-action="set-user" data-id="' + u.id + '" data-email="' + esc(u.email) + '">Set User</button>');
|
|
}
|
|
actions.push('<button class="btn-sm btn-ghost admin-action" data-action="reset-pw" data-id="' + u.id + '" data-email="' + esc(u.email) + '">Reset PW</button>');
|
|
actions.push('<button class="btn-sm admin-action" style="background:var(--red-light);color:var(--red);" data-action="delete" data-id="' + u.id + '" data-email="' + esc(u.email) + '" data-name="' + esc(u.name) + '">Delete</button>');
|
|
} else {
|
|
actions.push('<span style="font-size:12px;color:var(--g400);">(you)</span>');
|
|
}
|
|
|
|
return '<tr>' +
|
|
'<td><div style="font-weight:600;font-size:13px;">' + esc(u.name) + '</div><div style="font-size:11px;color:var(--g500);">' + esc(u.email) + '</div></td>' +
|
|
'<td><span style="font-size:12px;font-weight:600;color:' + roleColor + ';">' + (u.role || 'user') + '</span></td>' +
|
|
'<td><span style="font-size:12px;color:' + statusColor + ';">' + statusText + '</span></td>' +
|
|
'<td style="font-size:12px;color:var(--g500);">' + joined + '</td>' +
|
|
'<td style="white-space:nowrap;">' + actions.join(' ') + '</td>' +
|
|
'</tr>';
|
|
}).join('');
|
|
|
|
// Bind action buttons
|
|
tbody.querySelectorAll('.admin-action').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
handleUserAction(btn.dataset.action, btn.dataset.id, btn.dataset.email, btn.dataset.name);
|
|
});
|
|
});
|
|
}
|
|
|
|
function handleUserAction(action, userId, email, name) {
|
|
switch (action) {
|
|
|
|
case 'verify':
|
|
if (!confirm('Verify email for ' + email + '?')) return;
|
|
adminPost('/api/admin/users/' + userId + '/verify', {}, function() {
|
|
showToast(email + ' verified', 'success'); loadUsers();
|
|
});
|
|
break;
|
|
|
|
case 'disable':
|
|
if (!confirm('Disable account for ' + email + '?')) return;
|
|
adminPost('/api/admin/users/' + userId + '/disable', {}, function() {
|
|
showToast(email + ' disabled', 'info'); loadUsers();
|
|
});
|
|
break;
|
|
|
|
case 'enable':
|
|
adminPost('/api/admin/users/' + userId + '/enable', {}, function() {
|
|
showToast(email + ' enabled', 'success'); loadUsers();
|
|
});
|
|
break;
|
|
|
|
case 'promote':
|
|
if (!confirm('Grant admin role to ' + email + '?')) return;
|
|
adminPost('/api/admin/users/' + userId + '/role', { role: 'admin' }, function() {
|
|
showToast(email + ' is now admin', 'success'); loadUsers();
|
|
});
|
|
break;
|
|
|
|
case 'demote':
|
|
if (!confirm('Remove admin role from ' + email + '?')) return;
|
|
adminPost('/api/admin/users/' + userId + '/role', { role: 'user' }, function() {
|
|
showToast(email + ' demoted to user', 'info'); loadUsers();
|
|
});
|
|
break;
|
|
|
|
case 'set-moderator':
|
|
if (!confirm('Set ' + email + ' as moderator? They will be able to manage Learning Hub content.')) return;
|
|
adminPost('/api/admin/users/' + userId + '/role', { role: 'moderator' }, function() {
|
|
showToast(email + ' is now a moderator', 'success'); loadUsers();
|
|
});
|
|
break;
|
|
|
|
case 'set-user':
|
|
if (!confirm('Set ' + email + ' to full user role?')) return;
|
|
adminPost('/api/admin/users/' + userId + '/role', { role: 'user' }, function() {
|
|
showToast(email + ' is now a user', 'success'); loadUsers();
|
|
});
|
|
break;
|
|
|
|
case 'reset-pw':
|
|
var newPw = prompt('New password for ' + email + ' (8+ characters):');
|
|
if (!newPw) return;
|
|
if (newPw.length < 8) { showToast('Password must be 8+ characters', 'error'); return; }
|
|
adminPost('/api/admin/users/' + userId + '/reset-password', { newPassword: newPw }, function() {
|
|
showToast('Password reset for ' + email, 'success');
|
|
});
|
|
break;
|
|
|
|
case 'delete':
|
|
if (!confirm('Permanently delete user ' + (name || email) + '?\n\nThis cannot be undone.')) return;
|
|
fetch('/api/admin/users/' + userId, { method: 'DELETE', headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) { showToast(email + ' deleted', 'info'); loadUsers(); }
|
|
else showToast(data.error || 'Delete failed', 'error');
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
break;
|
|
}
|
|
}
|
|
|
|
function adminPost(url, body, onSuccess) {
|
|
fetch(url, {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify(body)
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) { onSuccess(data); }
|
|
else showToast(data.error || 'Request failed', 'error');
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
}
|
|
|
|
function esc(str) {
|
|
if (!str) return '';
|
|
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
}
|
|
|
|
})();
|
|
|
|
// ============================================================
|
|
// ADMIN CMS — Announcements, Feature Flags, Email, AI Prompts
|
|
// ============================================================
|
|
(function() {
|
|
|
|
var cmsLoaded = false;
|
|
|
|
// Load CMS when admin tab is opened (via tabChanged event or click)
|
|
document.addEventListener('tabChanged', function(e) {
|
|
if (e.detail && e.detail.tab === 'admin') {
|
|
if (!cmsLoaded) { loadCms(); cmsLoaded = true; }
|
|
}
|
|
});
|
|
|
|
document.addEventListener('click', function(e) {
|
|
|
|
// Save buttons
|
|
if (e.target.closest('#btn-save-announcement')) saveAnnouncement();
|
|
if (e.target.closest('#btn-save-flags')) saveFlags();
|
|
if (e.target.closest('#btn-save-email')) saveEmail();
|
|
if (e.target.closest('#btn-test-email')) sendTestEmail();
|
|
if (e.target.closest('#btn-save-prompt')) savePrompt();
|
|
if (e.target.closest('#btn-reset-prompt')) resetPrompt();
|
|
if (e.target.closest('#btn-save-smtp')) saveSmtp();
|
|
if (e.target.closest('#btn-clear-smtp')) clearSmtp();
|
|
if (e.target.closest('#btn-save-auto-delete')) saveAutoDelete();
|
|
if (e.target.closest('#btn-reset-all-defaults')) resetAllDefaults();
|
|
});
|
|
|
|
// When email template selector changes, repopulate fields
|
|
document.addEventListener('change', function(e) {
|
|
if (e.target.id === 'cms-email-template') loadEmailFields(e.target.value);
|
|
if (e.target.id === 'cms-prompt-select') loadPromptText(e.target.value);
|
|
});
|
|
|
|
// ---- LOAD ALL CONFIG ----
|
|
|
|
function loadCms() {
|
|
fetch('/api/admin/config', { headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) return;
|
|
var cfg = {};
|
|
(data.config || []).forEach(function(row) { cfg[row.key] = row.value; });
|
|
|
|
// Announcement
|
|
var annEnabled = document.getElementById('cms-ann-enabled');
|
|
var annType = document.getElementById('cms-ann-type');
|
|
var annText = document.getElementById('cms-ann-text');
|
|
if (annEnabled) annEnabled.value = cfg['announcement.enabled'] || 'false';
|
|
if (annType) annType.value = cfg['announcement.type'] || 'info';
|
|
if (annText) annText.value = cfg['announcement.text'] || '';
|
|
|
|
// Feature flags
|
|
var flagReadAloud = document.getElementById('cms-flag-read-aloud');
|
|
var flagNextcloud = document.getElementById('cms-flag-nextcloud');
|
|
if (flagReadAloud) flagReadAloud.value = cfg['feature.read_aloud'] !== undefined ? cfg['feature.read_aloud'] : 'true';
|
|
if (flagNextcloud) flagNextcloud.value = cfg['feature.nextcloud'] !== undefined ? cfg['feature.nextcloud'] : 'true';
|
|
|
|
// Auto-delete setting
|
|
var autoDeleteDays = cfg['site.auto_delete_days'] || '7';
|
|
var el = document.getElementById('cms-auto-delete-days');
|
|
if (el) el.value = autoDeleteDays;
|
|
el = document.getElementById('admin-auto-delete-days');
|
|
if (el) el.textContent = autoDeleteDays;
|
|
|
|
// Email — load verify template by default
|
|
window._adminEmailConfig = cfg;
|
|
loadEmailFields('verify');
|
|
})
|
|
.catch(function(err) { console.error('[AdminCMS] Config load failed:', err); });
|
|
|
|
loadPromptList();
|
|
loadSmtp();
|
|
}
|
|
|
|
// ---- ANNOUNCEMENT ----
|
|
|
|
function saveAnnouncement() {
|
|
var enabled = document.getElementById('cms-ann-enabled').value;
|
|
var type = document.getElementById('cms-ann-type').value;
|
|
var text = document.getElementById('cms-ann-text').value;
|
|
|
|
Promise.all([
|
|
putConfig('announcement.enabled', enabled),
|
|
putConfig('announcement.type', type),
|
|
putConfig('announcement.text', text)
|
|
]).then(function() {
|
|
showToast('Announcement saved', 'success');
|
|
if (typeof loadAnnouncement === 'function') loadAnnouncement();
|
|
}).catch(function() { showToast('Save failed', 'error'); });
|
|
}
|
|
|
|
// ---- FEATURE FLAGS ----
|
|
|
|
function saveFlags() {
|
|
var readAloud = document.getElementById('cms-flag-read-aloud').value;
|
|
var nextcloud = document.getElementById('cms-flag-nextcloud').value;
|
|
|
|
Promise.all([
|
|
putConfig('feature.read_aloud', readAloud),
|
|
putConfig('feature.nextcloud', nextcloud)
|
|
]).then(function() {
|
|
showToast('Feature flags saved', 'success');
|
|
}).catch(function() { showToast('Save failed', 'error'); });
|
|
}
|
|
|
|
// ---- EMAIL TEMPLATES ----
|
|
|
|
function loadEmailFields(template) {
|
|
var cfg = window._adminEmailConfig || {};
|
|
var subject = document.getElementById('cms-email-subject');
|
|
var body = document.getElementById('cms-email-body');
|
|
if (subject) subject.value = cfg['email.' + template + '.subject'] || '';
|
|
if (body) body.value = cfg['email.' + template + '.body'] || '';
|
|
}
|
|
|
|
function saveEmail() {
|
|
var template = document.getElementById('cms-email-template').value;
|
|
var subject = document.getElementById('cms-email-subject').value;
|
|
var body = document.getElementById('cms-email-body').value;
|
|
|
|
Promise.all([
|
|
putConfig('email.' + template + '.subject', subject),
|
|
putConfig('email.' + template + '.body', body)
|
|
]).then(function() {
|
|
// Update local cache
|
|
if (!window._adminEmailConfig) window._adminEmailConfig = {};
|
|
window._adminEmailConfig['email.' + template + '.subject'] = subject;
|
|
window._adminEmailConfig['email.' + template + '.body'] = body;
|
|
showToast('Email template saved', 'success');
|
|
}).catch(function() { showToast('Save failed', 'error'); });
|
|
}
|
|
|
|
function sendTestEmail() {
|
|
var template = document.getElementById('cms-email-template').value;
|
|
var to = prompt('Send test email to (enter email address):');
|
|
if (!to || !to.trim()) return;
|
|
fetch('/api/admin/config/test-email', {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({ to: to.trim(), template: template })
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) showToast('Test email sent to ' + to, 'success');
|
|
else showToast(data.error || 'Send failed', 'error');
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
}
|
|
|
|
// ---- AI PROMPTS ----
|
|
|
|
function loadPromptList() {
|
|
fetch('/api/admin/config/prompts', { headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) return;
|
|
var sel = document.getElementById('cms-prompt-select');
|
|
if (!sel) return;
|
|
sel.innerHTML = '';
|
|
(data.prompts || []).forEach(function(p) {
|
|
var opt = document.createElement('option');
|
|
opt.value = p.key;
|
|
opt.textContent = p.key;
|
|
sel.appendChild(opt);
|
|
});
|
|
window._adminPrompts = {};
|
|
(data.prompts || []).forEach(function(p) { window._adminPrompts[p.key] = p.value; });
|
|
if (data.prompts && data.prompts.length > 0) {
|
|
loadPromptText(data.prompts[0].key);
|
|
}
|
|
})
|
|
.catch(function(err) { console.error('[AdminCMS] Prompts load failed:', err); });
|
|
}
|
|
|
|
function loadPromptText(key) {
|
|
var textarea = document.getElementById('cms-prompt-text');
|
|
if (!textarea) return;
|
|
textarea.value = (window._adminPrompts && window._adminPrompts[key]) || '';
|
|
}
|
|
|
|
function savePrompt() {
|
|
var sel = document.getElementById('cms-prompt-select');
|
|
var text = document.getElementById('cms-prompt-text');
|
|
if (!sel || !text) return;
|
|
var key = sel.value;
|
|
var value = text.value;
|
|
if (!key) return;
|
|
|
|
putConfig('prompt.' + key, value)
|
|
.then(function() {
|
|
if (!window._adminPrompts) window._adminPrompts = {};
|
|
window._adminPrompts[key] = value;
|
|
showToast('Prompt saved', 'success');
|
|
})
|
|
.catch(function() { showToast('Save failed', 'error'); });
|
|
}
|
|
|
|
function resetPrompt() {
|
|
var sel = document.getElementById('cms-prompt-select');
|
|
if (!sel || !sel.value) return;
|
|
var key = sel.value;
|
|
if (!confirm('Reset "' + key + '" to the hardcoded default? This cannot be undone.')) return;
|
|
|
|
fetch('/api/admin/config/prompts/' + key + '/reset', {
|
|
method: 'POST',
|
|
headers: getAuthHeaders()
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) {
|
|
if (!window._adminPrompts) window._adminPrompts = {};
|
|
window._adminPrompts[key] = data.value;
|
|
var textarea = document.getElementById('cms-prompt-text');
|
|
if (textarea) textarea.value = data.value;
|
|
showToast('Prompt reset to default', 'success');
|
|
} else {
|
|
showToast(data.error || 'Reset failed', 'error');
|
|
}
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
}
|
|
|
|
// ---- SMTP ----
|
|
|
|
function loadSmtp() {
|
|
fetch('/api/admin/config/smtp/status', { headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) return;
|
|
var badge = document.getElementById('smtp-source-badge');
|
|
if (badge) {
|
|
if (data.source === 'env') badge.textContent = 'Configured via env';
|
|
else if (data.source === 'database') badge.textContent = 'Configured in DB';
|
|
else { badge.textContent = 'Not configured'; badge.style.background = 'var(--red-light)'; badge.style.color = 'var(--red)'; }
|
|
}
|
|
var h = document.getElementById('cms-smtp-host'); if (h) h.value = data.host || '';
|
|
var p = document.getElementById('cms-smtp-port'); if (p) p.value = data.port || '587';
|
|
var u = document.getElementById('cms-smtp-user'); if (u) u.value = data.user || '';
|
|
var f = document.getElementById('cms-smtp-from'); if (f) f.value = data.from || '';
|
|
})
|
|
.catch(function() {});
|
|
}
|
|
|
|
function saveSmtp() {
|
|
var host = document.getElementById('cms-smtp-host').value.trim();
|
|
var port = document.getElementById('cms-smtp-port').value.trim();
|
|
var user = document.getElementById('cms-smtp-user').value.trim();
|
|
var pass = document.getElementById('cms-smtp-pass').value.trim();
|
|
var from = document.getElementById('cms-smtp-from').value.trim();
|
|
var secure = document.getElementById('cms-smtp-secure').value;
|
|
if (!host) { showToast('SMTP host required', 'error'); return; }
|
|
|
|
fetch('/api/admin/config/smtp', {
|
|
method: 'PUT',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({ host: host, port: port, user: user, pass: pass, from: from, secure: secure === 'true' })
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) { showToast('SMTP settings saved', 'success'); loadSmtp(); }
|
|
else showToast(data.error || 'Save failed', 'error');
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
}
|
|
|
|
function clearSmtp() {
|
|
if (!confirm('Clear DB SMTP settings? Env vars will be used if set.')) return;
|
|
fetch('/api/admin/config/smtp', { method: 'DELETE', headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) { showToast('SMTP DB settings cleared', 'info'); loadSmtp(); }
|
|
else showToast(data.error || 'Failed', 'error');
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
}
|
|
|
|
// ---- AUTO-DELETE / RESET ----
|
|
|
|
function saveAutoDelete() {
|
|
var days = document.getElementById('cms-auto-delete-days').value;
|
|
putConfig('site.auto_delete_days', days)
|
|
.then(function() {
|
|
var el = document.getElementById('admin-auto-delete-days');
|
|
if (el) el.textContent = days;
|
|
showToast('Auto-delete set to ' + days + ' days', 'success');
|
|
})
|
|
.catch(function() { showToast('Save failed', 'error'); });
|
|
}
|
|
|
|
function resetAllDefaults() {
|
|
if (!confirm('Reset all settings to factory defaults?\n\nAnnouncements, feature flags, email templates will be reset. SMTP and custom models are preserved.')) return;
|
|
fetch('/api/admin/config/reset-defaults', { method: 'POST', headers: getAuthHeaders() })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) {
|
|
showToast(data.message || 'Reset to defaults', 'success');
|
|
cmsLoaded = false;
|
|
loadCms();
|
|
} else showToast(data.error || 'Reset failed', 'error');
|
|
})
|
|
.catch(function() { showToast('Request failed', 'error'); });
|
|
}
|
|
|
|
// ---- HELPERS ----
|
|
|
|
function putConfig(key, value) {
|
|
return fetch('/api/admin/config/' + encodeURIComponent(key), {
|
|
method: 'PUT',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({ value: value })
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) throw new Error(data.error || 'Failed');
|
|
return data;
|
|
});
|
|
}
|
|
|
|
})();
|