import { ref } from 'vue' import { defineStore } from 'pinia' import { useApiFetch } from '../../composables/useApi' const CLOUD_BACKEND_IDS = ['anthropic', 'openai'] export interface Backend { id: string; enabled: boolean; priority: number } export interface Service { name: string; port: number; running: boolean; note: string } export interface IntegrationField { key: string; label: string; type: string } export interface Integration { id: string; name: string; connected: boolean; tier_required: string; fields: IntegrationField[] } export const useSystemStore = defineStore('settings/system', () => { const backends = ref([]) const byokAcknowledged = ref([]) const byokPending = ref([]) // Private snapshot — NOT in return(). Closure-level only. let _preSaveSnapshot: Backend[] = [] const saving = ref(false) const saveError = ref(null) const loadError = ref(null) const services = ref([]) const emailConfig = ref>({}) const integrations = ref([]) const serviceErrors = ref>({}) const emailSaving = ref(false) const emailError = ref(null) // File paths + deployment const filePaths = ref>({}) const deployConfig = ref>({}) const filePathsSaving = ref(false) const deploySaving = ref(false) async function loadLlm() { loadError.value = null const { data, error } = await useApiFetch<{ backends: Backend[]; byok_acknowledged: string[] }>('/api/settings/system/llm') if (error) { loadError.value = 'Failed to load LLM config'; return } if (!data) return backends.value = data.backends ?? [] byokAcknowledged.value = data.byok_acknowledged ?? [] } async function trySave() { _preSaveSnapshot = JSON.parse(JSON.stringify(backends.value)) const newlyEnabled = backends.value .filter(b => CLOUD_BACKEND_IDS.includes(b.id) && b.enabled) .map(b => b.id) .filter(id => !byokAcknowledged.value.includes(id)) if (newlyEnabled.length > 0) { byokPending.value = newlyEnabled return // modal takes over } await _commitSave() } async function confirmByok() { saving.value = true saveError.value = null const { error } = await useApiFetch('/api/settings/system/llm/byok-ack', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ backends: byokPending.value }), }) if (error) { saving.value = false saveError.value = 'Failed to save acknowledgment — please try again.' return // leave modal open, byokPending intact } byokAcknowledged.value = [...byokAcknowledged.value, ...byokPending.value] byokPending.value = [] await _commitSave() } function cancelByok() { if (_preSaveSnapshot.length > 0) { backends.value = JSON.parse(JSON.stringify(_preSaveSnapshot)) } byokPending.value = [] _preSaveSnapshot = [] } async function _commitSave() { saving.value = true saveError.value = null const { error } = await useApiFetch('/api/settings/system/llm', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ backends: backends.value }), }) saving.value = false if (error) saveError.value = 'Save failed — please try again.' } async function loadServices() { const { data } = await useApiFetch('/api/settings/system/services') if (data) services.value = data } async function startService(name: string) { const { data, error } = await useApiFetch<{ok: boolean; output: string}>( `/api/settings/system/services/${name}/start`, { method: 'POST' } ) if (error || !data?.ok) { serviceErrors.value = { ...serviceErrors.value, [name]: data?.output ?? 'Start failed' } } else { serviceErrors.value = { ...serviceErrors.value, [name]: '' } await loadServices() } } async function stopService(name: string) { const { data, error } = await useApiFetch<{ok: boolean; output: string}>( `/api/settings/system/services/${name}/stop`, { method: 'POST' } ) if (error || !data?.ok) { serviceErrors.value = { ...serviceErrors.value, [name]: data?.output ?? 'Stop failed' } } else { serviceErrors.value = { ...serviceErrors.value, [name]: '' } await loadServices() } } async function loadEmail() { const { data } = await useApiFetch>('/api/settings/system/email') if (data) emailConfig.value = data } async function saveEmail() { emailSaving.value = true emailError.value = null const { error } = await useApiFetch('/api/settings/system/email', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailConfig.value), }) emailSaving.value = false if (error) emailError.value = 'Save failed — please try again.' } async function testEmail() { const { data } = await useApiFetch<{ok: boolean; error?: string}>( '/api/settings/system/email/test', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailConfig.value), } ) return data } async function loadIntegrations() { const { data } = await useApiFetch('/api/settings/system/integrations') if (data) integrations.value = data } async function loadFilePaths() { const { data } = await useApiFetch>('/api/settings/system/paths') if (data) filePaths.value = data } async function saveFilePaths() { filePathsSaving.value = true const { error } = await useApiFetch('/api/settings/system/paths', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(filePaths.value), }) filePathsSaving.value = false if (error) saveError.value = 'Failed to save file paths.' } async function loadDeployConfig() { const { data } = await useApiFetch>('/api/settings/system/deploy') if (data) deployConfig.value = data } async function saveDeployConfig() { deploySaving.value = true const { error } = await useApiFetch('/api/settings/system/deploy', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(deployConfig.value), }) deploySaving.value = false if (error) saveError.value = 'Failed to save deployment config.' } return { backends, byokAcknowledged, byokPending, saving, saveError, loadError, loadLlm, trySave, confirmByok, cancelByok, services, emailConfig, integrations, serviceErrors, emailSaving, emailError, filePaths, deployConfig, filePathsSaving, deploySaving, loadServices, startService, stopService, loadEmail, saveEmail, testEmail, loadIntegrations, loadFilePaths, saveFilePaths, loadDeployConfig, saveDeployConfig, } })