Changeset 0.10.5 (#61)
This commit is contained in:
@@ -61,7 +61,7 @@ async function toggleUserRole(id, role) {
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
async function deleteUser(id, username) {
|
||||
if (!confirm(`Delete user "${username}"? This cannot be undone.`)) return;
|
||||
if (!await showConfirm(`Delete user "${username}"? This cannot be undone.`)) return;
|
||||
try { await API.adminDeleteUser(id); UI.toast('User deleted', 'success'); await UI.loadAdminUsers(); }
|
||||
catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
@@ -89,68 +89,14 @@ async function adminResetUserPassword(id, username) {
|
||||
);
|
||||
if (!pw) return;
|
||||
if (pw.length < 8) { UI.toast('Password must be at least 8 characters', 'warning'); return; }
|
||||
if (!confirm(`Final confirmation: Reset password AND destroy personal vault for "${username}"?`)) return;
|
||||
if (!await showConfirm(`Final confirmation: Reset password AND destroy personal vault for "${username}"?`)) return;
|
||||
try {
|
||||
await API.adminResetPassword(id, pw);
|
||||
UI.toast(`Password reset for ${username}. Vault destroyed.`, 'success');
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function deleteGlobalProvider(id) {
|
||||
if (!confirm('Delete this global provider?')) return;
|
||||
try { await API.adminDeleteGlobalConfig(id); UI.toast('Provider deleted', 'success'); await UI.loadAdminProviders(); }
|
||||
catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
var _editingProviderId = null;
|
||||
function editGlobalProvider(id) {
|
||||
const c = UI._providerCache?.[id];
|
||||
if (!c) return;
|
||||
_editingProviderId = id;
|
||||
const form = document.getElementById('adminAddProviderForm');
|
||||
form.style.display = '';
|
||||
document.getElementById('adminProvName').value = c.name || '';
|
||||
document.getElementById('adminProvType').value = c.provider || 'openai';
|
||||
document.getElementById('adminProvEndpoint').value = c.endpoint || '';
|
||||
document.getElementById('adminProvKey').value = '';
|
||||
document.getElementById('adminProvKey').placeholder = c.has_key ? '(unchanged)' : 'sk-...';
|
||||
document.getElementById('adminProvModel').value = c.model_default || '';
|
||||
document.getElementById('adminProvPrivate').checked = !!c.is_private;
|
||||
document.getElementById('adminCreateProvBtn').textContent = 'Update';
|
||||
}
|
||||
|
||||
async function createGlobalProvider() {
|
||||
try {
|
||||
const name = document.getElementById('adminProvName').value.trim();
|
||||
const prov = document.getElementById('adminProvType').value;
|
||||
const ep = document.getElementById('adminProvEndpoint').value.trim();
|
||||
const key = document.getElementById('adminProvKey').value;
|
||||
const model = document.getElementById('adminProvModel').value.trim();
|
||||
const isPrivate = document.getElementById('adminProvPrivate').checked;
|
||||
|
||||
if (_editingProviderId) {
|
||||
// Update mode
|
||||
const updates = {};
|
||||
if (name) updates.name = name;
|
||||
if (ep) updates.endpoint = ep;
|
||||
if (key) updates.api_key = key;
|
||||
updates.model_default = model;
|
||||
updates.is_private = isPrivate;
|
||||
await API.adminUpdateGlobalConfig(_editingProviderId, updates);
|
||||
UI.toast('Provider updated', 'success');
|
||||
} else {
|
||||
// Create mode
|
||||
if (!name || !ep || !key) { UI.toast('Name, endpoint, and API key required', 'warning'); return; }
|
||||
await API.adminCreateGlobalConfig(name, prov, ep, key, model, isPrivate);
|
||||
UI.toast('Provider added', 'success');
|
||||
}
|
||||
_editingProviderId = null;
|
||||
document.getElementById('adminAddProviderForm').style.display = 'none';
|
||||
document.getElementById('adminCreateProvBtn').textContent = 'Save';
|
||||
document.getElementById('adminProvKey').placeholder = 'sk-...';
|
||||
await UI.loadAdminProviders();
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
// ── Admin Provider / Role handlers — now managed by UI primitives in ui-admin.js ──
|
||||
|
||||
async function fetchAdminModels() {
|
||||
const hint = document.getElementById('adminModelsHint');
|
||||
@@ -194,64 +140,7 @@ async function bulkSetVisibility(visibility) {
|
||||
} catch (e) { UI.toast(e.message, 'error'); hint.textContent = 'Failed'; }
|
||||
}
|
||||
|
||||
// ── Admin Roles ────────────────────────────
|
||||
|
||||
async function adminRoleProviderChanged(role, slot) {
|
||||
const provId = document.getElementById(`role-${role}-${slot}-provider`)?.value;
|
||||
const modelSelect = document.getElementById(`role-${role}-${slot}-model`);
|
||||
if (!modelSelect) return;
|
||||
|
||||
modelSelect.innerHTML = '<option value="">— select model —</option>';
|
||||
if (!provId || !window._adminModelList) return;
|
||||
|
||||
// Filter by provider AND model type matching the role
|
||||
const typeForRole = role === 'embedding' ? 'embedding' : 'chat';
|
||||
const models = window._adminModelList.filter(m =>
|
||||
m.provider_config_id === provId &&
|
||||
(m.model_type || 'chat') === typeForRole
|
||||
);
|
||||
models.forEach(m => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = m.model_id;
|
||||
opt.textContent = m.display_name || m.model_id;
|
||||
modelSelect.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
async function adminSaveRole(role) {
|
||||
const status = document.getElementById(`role-${role}-status`);
|
||||
try {
|
||||
const getBinding = (slot) => {
|
||||
const prov = document.getElementById(`role-${role}-${slot}-provider`)?.value;
|
||||
const model = document.getElementById(`role-${role}-${slot}-model`)?.value;
|
||||
return (prov && model) ? { provider_config_id: prov, model_id: model } : null;
|
||||
};
|
||||
await API.adminUpdateRole(role, {
|
||||
primary: getBinding('primary'),
|
||||
fallback: getBinding('fallback')
|
||||
});
|
||||
if (status) { status.textContent = '✓ Saved'; status.style.color = 'var(--success)'; }
|
||||
// Reload to reflect saved state in dropdowns
|
||||
setTimeout(() => UI.loadAdminRoles(), 500);
|
||||
} catch (e) {
|
||||
if (status) { status.textContent = '✗ ' + e.message; status.style.color = 'var(--error)'; }
|
||||
}
|
||||
}
|
||||
|
||||
async function adminTestRole(role) {
|
||||
const status = document.getElementById(`role-${role}-status`);
|
||||
if (status) { status.textContent = 'Testing...'; status.style.color = 'var(--text-muted)'; }
|
||||
try {
|
||||
const result = await API.adminTestRole(role);
|
||||
if (status) {
|
||||
const fb = result.used_fallback ? ' (fallback)' : '';
|
||||
status.textContent = `✓ ${result.model}${fb}`;
|
||||
status.style.color = 'var(--success)';
|
||||
}
|
||||
} catch (e) {
|
||||
if (status) { status.textContent = '✗ ' + e.message; status.style.color = 'var(--error)'; }
|
||||
}
|
||||
}
|
||||
// ── Admin Roles — now managed by renderRoleConfig primitive in ui-admin.js ──
|
||||
|
||||
// ── User Model Preferences ──────────────────
|
||||
|
||||
@@ -284,7 +173,7 @@ async function bulkSetUserModelVisibility(show) {
|
||||
}
|
||||
|
||||
async function deleteUserPreset(id, name) {
|
||||
if (!confirm(`Delete preset "${name}"?`)) return;
|
||||
if (!await showConfirm(`Delete preset "${name}"?`)) return;
|
||||
try {
|
||||
await API.deleteUserPreset(id);
|
||||
UI.toast('Preset deleted');
|
||||
@@ -432,7 +321,7 @@ async function toggleAdminPreset(id, active) {
|
||||
}
|
||||
|
||||
async function deleteAdminPreset(id, name) {
|
||||
if (!confirm(`Delete preset "${name}"?`)) return;
|
||||
if (!await showConfirm(`Delete preset "${name}"?`)) return;
|
||||
try {
|
||||
await API.adminDeletePreset(id);
|
||||
UI.toast('Preset deleted', 'success');
|
||||
@@ -452,7 +341,7 @@ async function toggleTeamActive(id, active) {
|
||||
}
|
||||
|
||||
async function deleteTeam(id, name) {
|
||||
if (!confirm(`Delete team "${name}"? All members will be removed.`)) return;
|
||||
if (!await showConfirm(`Delete team "${name}"? All members will be removed.`)) return;
|
||||
try {
|
||||
await API.adminDeleteTeam(id);
|
||||
UI.toast('Team deleted');
|
||||
@@ -471,7 +360,7 @@ async function updateTeamMember(teamId, memberId, role) {
|
||||
}
|
||||
|
||||
async function removeTeamMember(teamId, memberId, email) {
|
||||
if (!confirm(`Remove ${email} from team?`)) return;
|
||||
if (!await showConfirm(`Remove ${email} from team?`)) return;
|
||||
try {
|
||||
await API.adminRemoveMember(teamId, memberId);
|
||||
UI.toast('Member removed');
|
||||
@@ -491,7 +380,7 @@ async function settingsUpdateTeamMember(teamId, memberId, role) {
|
||||
}
|
||||
|
||||
async function settingsRemoveTeamMember(teamId, memberId, email) {
|
||||
if (!confirm(`Remove ${email} from team?`)) return;
|
||||
if (!await showConfirm(`Remove ${email} from team?`)) return;
|
||||
try {
|
||||
await API.teamRemoveMember(teamId, memberId);
|
||||
UI.toast('Member removed');
|
||||
@@ -500,7 +389,7 @@ async function settingsRemoveTeamMember(teamId, memberId, email) {
|
||||
}
|
||||
|
||||
async function settingsDeleteTeamPreset(teamId, presetId, name) {
|
||||
if (!confirm(`Delete team preset "${name}"?`)) return;
|
||||
if (!await showConfirm(`Delete team preset "${name}"?`)) return;
|
||||
try {
|
||||
await API.teamDeletePreset(teamId, presetId);
|
||||
UI.toast('Preset deleted');
|
||||
@@ -509,36 +398,7 @@ async function settingsDeleteTeamPreset(teamId, presetId, name) {
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function settingsToggleTeamProvider(teamId, providerId, currentlyActive) {
|
||||
try {
|
||||
await API.teamUpdateProvider(teamId, providerId, { is_active: !currentlyActive });
|
||||
UI.toast(currentlyActive ? 'Provider deactivated' : 'Provider activated');
|
||||
await UI.loadTeamManageProviders(teamId);
|
||||
await fetchModels();
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function settingsDeleteTeamProvider(teamId, providerId, name) {
|
||||
if (!confirm(`Delete team provider "${name}"? This will remove all models from this provider for your team.`)) return;
|
||||
try {
|
||||
await API.teamDeleteProvider(teamId, providerId);
|
||||
UI.toast('Provider deleted');
|
||||
await UI.loadTeamManageProviders(teamId);
|
||||
await fetchModels();
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
function settingsEditTeamProvider(teamId, provId, name, endpoint, defaultModel, isPrivate) {
|
||||
UI._editingTeamProviderId = provId;
|
||||
document.getElementById('settingsTeamAddProvider').style.display = 'none';
|
||||
const form = document.getElementById('settingsTeamEditProvider');
|
||||
form.style.display = '';
|
||||
document.getElementById('teamProviderEditName').value = name;
|
||||
document.getElementById('teamProviderEditEndpoint').value = endpoint;
|
||||
document.getElementById('teamProviderEditKey').value = '';
|
||||
document.getElementById('teamProviderEditDefault').value = defaultModel;
|
||||
document.getElementById('teamProviderEditPrivate').checked = isPrivate;
|
||||
}
|
||||
// ── Team Provider handlers — now managed by UI primitives in ui-settings.js ──
|
||||
|
||||
// ── Admin Listeners (extracted from initListeners) ──
|
||||
|
||||
@@ -558,26 +418,12 @@ function _initAdminListeners() {
|
||||
document.getElementById('adminCancelUserBtn')?.addEventListener('click', () => { document.getElementById('adminAddUserForm').style.display = 'none'; });
|
||||
document.getElementById('adminCreateUserBtn')?.addEventListener('click', createAdminUser);
|
||||
|
||||
// Admin — providers
|
||||
// Admin — providers (form managed by primitives in loadAdminProviders)
|
||||
document.getElementById('adminAddProviderBtn')?.addEventListener('click', () => {
|
||||
_editingProviderId = null;
|
||||
document.getElementById('adminCreateProvBtn').textContent = 'Save';
|
||||
document.getElementById('adminProvKey').placeholder = 'sk-...';
|
||||
document.getElementById('adminProvName').value = '';
|
||||
document.getElementById('adminProvEndpoint').value = '';
|
||||
document.getElementById('adminProvKey').value = '';
|
||||
document.getElementById('adminProvModel').value = '';
|
||||
document.getElementById('adminProvPrivate').checked = false;
|
||||
const f = document.getElementById('adminAddProviderForm');
|
||||
if (UI._adminProvForm) UI._adminProvForm.setCreateMode();
|
||||
f.style.display = f.style.display === 'none' ? '' : 'none';
|
||||
});
|
||||
document.getElementById('adminCancelProvBtn')?.addEventListener('click', () => {
|
||||
document.getElementById('adminAddProviderForm').style.display = 'none';
|
||||
_editingProviderId = null;
|
||||
document.getElementById('adminCreateProvBtn').textContent = 'Save';
|
||||
document.getElementById('adminProvKey').placeholder = 'sk-...';
|
||||
});
|
||||
document.getElementById('adminCreateProvBtn')?.addEventListener('click', createGlobalProvider);
|
||||
|
||||
// Admin — models
|
||||
document.getElementById('adminFetchModelsBtn')?.addEventListener('click', fetchAdminModels);
|
||||
|
||||
Reference in New Issue
Block a user