Refactor backend and frontend cleanup paths
This commit is contained in:
@@ -61,7 +61,7 @@
|
||||
<input
|
||||
v-model="formByProvider[prov.provider_id].base_url"
|
||||
type="text"
|
||||
:placeholder="providerDefaultBaseUrl(prov.provider_id) || '(default)'"
|
||||
:placeholder="providerDefaultBaseUrl(prov) || '(default)'"
|
||||
class="block w-full rounded-lg px-3 py-2 text-sm border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
@@ -71,7 +71,7 @@
|
||||
<SearchableModelSelect
|
||||
v-model="formByProvider[prov.provider_id].model_name"
|
||||
:provider-id="prov.provider_id"
|
||||
:placeholder="providerDefaultModel(prov.provider_id)"
|
||||
:placeholder="providerDefaultModel(prov)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<input
|
||||
v-model.number="formByProvider[prov.provider_id].context_chars"
|
||||
type="number"
|
||||
:placeholder="String(providerDefaultContextChars(prov.provider_id))"
|
||||
:placeholder="String(providerDefaultContextChars(prov))"
|
||||
min="1000"
|
||||
max="2000000"
|
||||
class="block w-full rounded-lg px-3 py-2 text-sm border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-colors"
|
||||
@@ -131,8 +131,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Per-user AI provider assignment (existing — DO NOT MODIFY) ─────── -->
|
||||
|
||||
<div v-if="loading" class="bg-white rounded-xl border border-gray-200 p-8 text-center">
|
||||
<div class="flex items-center justify-center gap-2 text-gray-400 text-sm">
|
||||
<span class="animate-spin rounded-full border-2 border-current border-t-transparent w-4 h-4"></span>
|
||||
@@ -210,25 +208,6 @@ import AppIcon from '../../components/ui/AppIcon.vue'
|
||||
import SearchableModelSelect from '../../components/ui/SearchableModelSelect.vue'
|
||||
import BreadcrumbBar from '../../components/ui/BreadcrumbBar.vue'
|
||||
|
||||
|
||||
const _PROVIDER_DEFAULTS = {
|
||||
openai: { base_url: null, model: 'gpt-4o', context_chars: 120000 },
|
||||
anthropic: { base_url: null, model: 'claude-sonnet-4-6', context_chars: 180000 },
|
||||
gemini: { base_url: 'https://generativelanguage.googleapis.com/v1beta/openai/', model: 'gemini-2.0-flash', context_chars: 800000 },
|
||||
groq: { base_url: 'https://api.groq.com/openai/v1', model: 'llama-3.3-70b-versatile', context_chars: 128000 },
|
||||
xai: { base_url: 'https://api.x.ai/v1', model: 'grok-3-mini', context_chars: 128000 },
|
||||
deepseek: { base_url: 'https://api.deepseek.com', model: 'deepseek-chat', context_chars: 60000 },
|
||||
openrouter: { base_url: 'https://openrouter.ai/api/v1', model: 'anthropic/claude-3.5-sonnet', context_chars: 180000 },
|
||||
mistral: { base_url: 'https://api.mistral.ai/v1', model: 'mistral-large-latest', context_chars: 128000 },
|
||||
ollama: { base_url: 'http://host.docker.internal:11434/v1', model: 'llama3.2', context_chars: 8000 },
|
||||
lmstudio: { base_url: 'http://host.docker.internal:1234/v1', model: 'gemma-4-e4b-it', context_chars: 8000 },
|
||||
}
|
||||
|
||||
function providerDefaultBaseUrl(pid) { return _PROVIDER_DEFAULTS[pid]?.base_url || '' }
|
||||
function providerDefaultModel(pid) { return _PROVIDER_DEFAULTS[pid]?.model || '' }
|
||||
function providerDefaultContextChars(pid) { return _PROVIDER_DEFAULTS[pid]?.context_chars || 8000 }
|
||||
|
||||
|
||||
const systemProviders = ref([])
|
||||
const loadingSystem = ref(false)
|
||||
const systemError = ref(null)
|
||||
@@ -238,6 +217,18 @@ const testResults = reactive({})
|
||||
const savingProvider = ref(null)
|
||||
const testingProvider = ref(null)
|
||||
|
||||
function providerDefaultBaseUrl(provider) {
|
||||
return provider?.base_url || ''
|
||||
}
|
||||
|
||||
function providerDefaultModel(provider) {
|
||||
return provider?.model_name || ''
|
||||
}
|
||||
|
||||
function providerDefaultContextChars(provider) {
|
||||
return provider?.context_chars || 8000
|
||||
}
|
||||
|
||||
function toggleProvider(pid) {
|
||||
openProviderId.value = openProviderId.value === pid ? null : pid
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ function generateRandomPassword() {
|
||||
const lower = 'abcdefghijkmnpqrstuvwxyz'
|
||||
const digits = '23456789'
|
||||
const special = '!@#$%^&*'
|
||||
const charset = upper + lower + digits + special // 64 chars — 256 % 64 === 0, no modulo bias
|
||||
const charset = upper + lower + digits + special
|
||||
|
||||
const arr = new Uint8Array(16)
|
||||
crypto.getRandomValues(arr)
|
||||
@@ -396,62 +396,60 @@ function cancelDelete() {
|
||||
}
|
||||
|
||||
async function confirmDoDelete(id) {
|
||||
pendingAction[id] = true
|
||||
deleteError.value = null
|
||||
try {
|
||||
await runUserAction(id, async () => {
|
||||
await api.adminDeleteUser(id, deletePassword.value)
|
||||
users.value = users.value.filter(u => u.id !== id)
|
||||
cancelDelete()
|
||||
} catch (e) {
|
||||
}, (e) => {
|
||||
deleteError.value = e.message
|
||||
} finally {
|
||||
delete pendingAction[id]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function confirmDoDeactivate(id) {
|
||||
pendingAction[id] = true
|
||||
actionError.value = null
|
||||
try {
|
||||
await runUserAction(id, async () => {
|
||||
await api.adminDeactivateUser(id)
|
||||
const idx = users.value.findIndex(u => u.id === id)
|
||||
if (idx !== -1) {
|
||||
users.value[idx] = { ...users.value[idx], is_active: false }
|
||||
}
|
||||
updateUser(id, { is_active: false })
|
||||
confirmDeactivate.value = null
|
||||
} catch (e) {
|
||||
}, (e) => {
|
||||
actionError.value = e.message
|
||||
confirmDeactivate.value = null
|
||||
} finally {
|
||||
delete pendingAction[id]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function reactivate(id) {
|
||||
pendingAction[id] = true
|
||||
actionError.value = null
|
||||
try {
|
||||
await runUserAction(id, async () => {
|
||||
await api.adminReactivateUser(id)
|
||||
const idx = users.value.findIndex(u => u.id === id)
|
||||
if (idx !== -1) {
|
||||
users.value[idx] = { ...users.value[idx], is_active: true }
|
||||
}
|
||||
} catch (e) {
|
||||
updateUser(id, { is_active: true })
|
||||
}, (e) => {
|
||||
actionError.value = e.message
|
||||
})
|
||||
}
|
||||
|
||||
async function resetPassword(id) {
|
||||
actionError.value = null
|
||||
await runUserAction(id, () => api.adminResetUserPassword(id), (e) => {
|
||||
actionError.value = e.message
|
||||
})
|
||||
}
|
||||
|
||||
async function runUserAction(id, action, onError) {
|
||||
pendingAction[id] = true
|
||||
try {
|
||||
await action()
|
||||
} catch (e) {
|
||||
onError(e)
|
||||
} finally {
|
||||
delete pendingAction[id]
|
||||
}
|
||||
}
|
||||
|
||||
async function resetPassword(id) {
|
||||
pendingAction[id] = true
|
||||
actionError.value = null
|
||||
try {
|
||||
await api.adminResetUserPassword(id)
|
||||
} catch (e) {
|
||||
actionError.value = e.message
|
||||
} finally {
|
||||
delete pendingAction[id]
|
||||
function updateUser(id, patch) {
|
||||
const idx = users.value.findIndex(u => u.id === id)
|
||||
if (idx !== -1) {
|
||||
users.value[idx] = { ...users.value[idx], ...patch }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user