diff --git a/web/src/components/AudioConfigPopover.vue b/web/src/components/AudioConfigPopover.vue index 7d1590e6..3e97890e 100644 --- a/web/src/components/AudioConfigPopover.vue +++ b/web/src/components/AudioConfigPopover.vue @@ -20,6 +20,7 @@ import { } from '@/components/ui/select' import { Volume2, RefreshCw, Loader2 } from 'lucide-vue-next' import { audioApi, configApi } from '@/api' +import { useConfigStore } from '@/stores/config' import { useSystemStore } from '@/stores/system' import { getUnifiedAudio } from '@/composables/useUnifiedAudio' @@ -37,6 +38,7 @@ const emit = defineEmits<{ }>() const { t } = useI18n() +const configStore = useConfigStore() const systemStore = useSystemStore() const unifiedAudio = getUnifiedAudio() @@ -87,9 +89,9 @@ async function loadDevices() { // Initialize from current config function initializeFromCurrent() { - const audio = systemStore.audio + const audio = configStore.audio if (audio) { - audioEnabled.value = audio.available && audio.streaming + audioEnabled.value = audio.enabled selectedDevice.value = audio.device || '' selectedQuality.value = (audio.quality as 'voice' | 'balanced' | 'high') || 'balanced' } @@ -104,12 +106,10 @@ async function applyConfig() { try { // Update config - await configApi.update({ - audio: { - enabled: audioEnabled.value, - device: selectedDevice.value, - quality: selectedQuality.value, - }, + await configStore.updateAudio({ + enabled: audioEnabled.value, + device: selectedDevice.value, + quality: selectedQuality.value, }) // If enabled and device is selected, try to start audio stream @@ -151,12 +151,19 @@ async function applyConfig() { // Watch popover open state watch(() => props.open, (isOpen) => { - if (isOpen) { - if (devices.value.length === 0) { - loadDevices() - } - initializeFromCurrent() + if (!isOpen) return + + if (devices.value.length === 0) { + loadDevices() } + + configStore.refreshAudio() + .then(() => { + initializeFromCurrent() + }) + .catch(() => { + initializeFromCurrent() + }) }) diff --git a/web/src/components/HidConfigPopover.vue b/web/src/components/HidConfigPopover.vue index 9f640692..f8a9d080 100644 --- a/web/src/components/HidConfigPopover.vue +++ b/web/src/components/HidConfigPopover.vue @@ -22,7 +22,9 @@ import { import { MousePointer, Move, Loader2, RefreshCw } from 'lucide-vue-next' import HelpTooltip from '@/components/HelpTooltip.vue' import { configApi } from '@/api' -import { useSystemStore } from '@/stores/system' +import { useConfigStore } from '@/stores/config' +import { HidBackend } from '@/types/generated' +import type { HidConfigUpdate } from '@/types/generated' const props = defineProps<{ open: boolean @@ -35,7 +37,7 @@ const emit = defineEmits<{ }>() const { t } = useI18n() -const systemStore = useSystemStore() +const configStore = useConfigStore() const DEFAULT_MOUSE_MOVE_SEND_INTERVAL_MS = 16 @@ -72,7 +74,7 @@ watch(showCursor, (newValue, oldValue) => { }) // HID Device Settings (requires apply) -const hidBackend = ref<'otg' | 'ch9329' | 'none'>('none') +const hidBackend = ref(HidBackend.None) const devicePath = ref('') const baudrate = ref(9600) @@ -89,9 +91,9 @@ const buttonText = computed(() => t('actionbar.hidConfig')) // Available device paths based on backend type const availableDevicePaths = computed(() => { - if (hidBackend.value === 'ch9329') { + if (hidBackend.value === HidBackend.Ch9329) { return serialDevices.value - } else if (hidBackend.value === 'otg') { + } else if (hidBackend.value === HidBackend.Otg) { // For OTG, we show UDC devices return udcDevices.value.map(udc => ({ path: udc.name, @@ -124,9 +126,17 @@ function initializeFromCurrent() { showCursor.value = storedCursor // Initialize HID device settings from system state - const hid = systemStore.hid + const hid = configStore.hid if (hid) { - hidBackend.value = (hid.backend as 'otg' | 'ch9329' | 'none') || 'none' + hidBackend.value = hid.backend || HidBackend.None + if (hidBackend.value === HidBackend.Ch9329) { + devicePath.value = hid.ch9329_port || '' + baudrate.value = hid.ch9329_baudrate || 9600 + } else if (hidBackend.value === HidBackend.Otg) { + devicePath.value = hid.otg_udc || '' + } else { + devicePath.value = '' + } } } @@ -136,10 +146,8 @@ function toggleMouseMode() { emit('update:mouseMode', newMode) // Update backend config - configApi.update({ - hid: { - mouse_absolute: newMode === 'absolute', - }, + configStore.updateHid({ + mouse_absolute: newMode === 'absolute', }).catch(_e => { console.info('[HidConfig] Failed to update mouse mode') toast.error(t('config.updateFailed')) @@ -162,7 +170,11 @@ function handleThrottleChange(value: number[] | undefined) { // Handle backend change function handleBackendChange(backend: unknown) { if (typeof backend !== 'string') return - hidBackend.value = backend as 'otg' | 'ch9329' | 'none' + if (backend === HidBackend.Otg || backend === HidBackend.Ch9329 || backend === HidBackend.None) { + hidBackend.value = backend + } else { + return + } // Clear device path when changing backend devicePath.value = '' @@ -189,18 +201,18 @@ function handleBaudrateChange(rate: unknown) { async function applyHidConfig() { applying.value = true try { - const config: Record = { + const config: HidConfigUpdate = { backend: hidBackend.value, } - if (hidBackend.value === 'ch9329') { + if (hidBackend.value === HidBackend.Ch9329) { config.ch9329_port = devicePath.value config.ch9329_baudrate = baudrate.value - } else if (hidBackend.value === 'otg') { + } else if (hidBackend.value === HidBackend.Otg) { config.otg_udc = devicePath.value } - await configApi.update({ hid: config }) + await configStore.updateHid(config) toast.success(t('config.applied')) @@ -215,14 +227,20 @@ async function applyHidConfig() { // Watch open state watch(() => props.open, (isOpen) => { - if (isOpen) { - // Load devices on first open - if (serialDevices.value.length === 0) { - loadDevices() - } - // Initialize from current config - initializeFromCurrent() + if (!isOpen) return + + // Load devices on first open + if (serialDevices.value.length === 0) { + loadDevices() } + + configStore.refreshHid() + .then(() => { + initializeFromCurrent() + }) + .catch(() => { + initializeFromCurrent() + }) }) @@ -331,15 +349,15 @@ watch(() => props.open, (isOpen) => { - USB OTG - CH9329 (Serial) - {{ t('common.disabled') }} + USB OTG + CH9329 (Serial) + {{ t('common.disabled') }} -
+
() const { t } = useI18n() -const systemStore = useSystemStore() +const configStore = useConfigStore() const router = useRouter() // Device list @@ -64,7 +64,7 @@ const loadingCodecs = ref(false) // Backend list const backends = ref([]) -const currentEncoderBackend = ref('auto') +const currentEncoderBackend = computed(() => configStore.stream?.encoder || 'auto') // Browser supported codecs (WebRTC receive capabilities) const browserSupportedCodecs = ref>(new Set()) @@ -197,11 +197,11 @@ const applyingBitrate = ref(false) // Current config from store const currentConfig = computed(() => ({ - device: systemStore.stream?.device || '', - format: systemStore.stream?.format || '', - width: systemStore.stream?.resolution?.[0] || 1920, - height: systemStore.stream?.resolution?.[1] || 1080, - fps: systemStore.stream?.targetFps || 30, + device: configStore.video?.device || '', + format: configStore.video?.format || '', + width: configStore.video?.width || 1920, + height: configStore.video?.height || 1080, + fps: configStore.video?.fps || 30, })) // Button display text - simplified to just show label @@ -303,19 +303,6 @@ async function loadCodecs() { } } -// Load current encoder backend from config -async function loadEncoderBackend() { - try { - const config = await configApi.get() - // Access nested stream.encoder - const streamConfig = config.stream as { encoder?: string } | undefined - currentEncoderBackend.value = streamConfig?.encoder || 'auto' - } catch (e) { - console.info('[VideoConfig] Failed to load encoder backend config') - currentEncoderBackend.value = 'auto' - } -} - // Navigate to settings page (video tab) function goToSettings() { router.push('/settings?tab=video') @@ -440,14 +427,12 @@ async function applyVideoConfig() { applying.value = true try { - await configApi.update({ - video: { - device: selectedDevice.value, - format: selectedFormat.value, - width, - height, - fps: selectedFps.value, - }, + await configStore.updateVideo({ + device: selectedDevice.value, + format: selectedFormat.value, + width, + height, + fps: selectedFps.value, }) toast.success(t('config.applied')) @@ -463,26 +448,32 @@ async function applyVideoConfig() { // Watch open state watch(() => props.open, (isOpen) => { - if (isOpen) { - // Detect browser codec support on first open - if (browserSupportedCodecs.value.size === 0) { - detectBrowserCodecSupport() - } - // Load devices on first open - if (devices.value.length === 0) { - loadDevices() - } - // Load codecs and backends on first open - if (codecs.value.length === 0) { - loadCodecs() - } - // Load encoder backend config - loadEncoderBackend() - // Initialize from current config - initializeFromCurrent() - } else { + if (!isOpen) { isDirty.value = false + return } + + // Detect browser codec support on first open + if (browserSupportedCodecs.value.size === 0) { + detectBrowserCodecSupport() + } + // Load devices on first open + if (devices.value.length === 0) { + loadDevices() + } + // Load codecs and backends on first open + if (codecs.value.length === 0) { + loadCodecs() + } + + Promise.all([ + configStore.refreshVideo(), + configStore.refreshStream(), + ]).then(() => { + initializeFromCurrent() + }).catch(() => { + initializeFromCurrent() + }) }) // Sync selected values when backend config changes (e.g., auto format switch on mode change) diff --git a/web/src/stores/config.ts b/web/src/stores/config.ts new file mode 100644 index 00000000..2d0eb79a --- /dev/null +++ b/web/src/stores/config.ts @@ -0,0 +1,506 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { + authConfigApi, + atxConfigApi, + audioConfigApi, + hidConfigApi, + msdConfigApi, + rustdeskConfigApi, + streamConfigApi, + videoConfigApi, + webConfigApi, +} from '@/api' +import type { + AtxConfig, + AtxConfigUpdate, + AudioConfig, + AudioConfigUpdate, + AuthConfig, + AuthConfigUpdate, + HidConfig, + HidConfigUpdate, + MsdConfig, + MsdConfigUpdate, + StreamConfigResponse, + StreamConfigUpdate, + VideoConfig, + VideoConfigUpdate, + WebConfig, + WebConfigUpdate, +} from '@/types/generated' +import type { + RustDeskConfigResponse as ApiRustDeskConfigResponse, + RustDeskConfigUpdate as ApiRustDeskConfigUpdate, + RustDeskStatusResponse as ApiRustDeskStatusResponse, + RustDeskPasswordResponse as ApiRustDeskPasswordResponse, +} from '@/api' + +function normalizeErrorMessage(error: unknown): string { + if (error instanceof Error) return error.message + if (typeof error === 'string') return error + return 'Unknown error' +} + +export const useConfigStore = defineStore('config', () => { + const auth = ref(null) + const video = ref(null) + const audio = ref(null) + const hid = ref(null) + const msd = ref(null) + const stream = ref(null) + const web = ref(null) + const atx = ref(null) + const rustdeskConfig = ref(null) + const rustdeskStatus = ref(null) + const rustdeskPassword = ref(null) + + const authLoading = ref(false) + const videoLoading = ref(false) + const audioLoading = ref(false) + const hidLoading = ref(false) + const msdLoading = ref(false) + const streamLoading = ref(false) + const webLoading = ref(false) + const atxLoading = ref(false) + const rustdeskLoading = ref(false) + + const authError = ref(null) + const videoError = ref(null) + const audioError = ref(null) + const hidError = ref(null) + const msdError = ref(null) + const streamError = ref(null) + const webError = ref(null) + const atxError = ref(null) + const rustdeskError = ref(null) + + let authPromise: Promise | null = null + let videoPromise: Promise | null = null + let audioPromise: Promise | null = null + let hidPromise: Promise | null = null + let msdPromise: Promise | null = null + let streamPromise: Promise | null = null + let webPromise: Promise | null = null + let atxPromise: Promise | null = null + let rustdeskPromise: Promise | null = null + let rustdeskStatusPromise: Promise | null = null + let rustdeskPasswordPromise: Promise | null = null + + async function refreshAuth() { + if (authLoading.value && authPromise) return authPromise + authLoading.value = true + authError.value = null + const request = authConfigApi.get() + .then((response) => { + auth.value = response + return response + }) + .catch((error) => { + authError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + authLoading.value = false + authPromise = null + }) + + authPromise = request + return request + } + + async function refreshVideo() { + if (videoLoading.value && videoPromise) return videoPromise + videoLoading.value = true + videoError.value = null + const request = videoConfigApi.get() + .then((response) => { + video.value = response + return response + }) + .catch((error) => { + videoError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + videoLoading.value = false + videoPromise = null + }) + + videoPromise = request + return request + } + + async function refreshAudio() { + if (audioLoading.value && audioPromise) return audioPromise + audioLoading.value = true + audioError.value = null + const request = audioConfigApi.get() + .then((response) => { + audio.value = response + return response + }) + .catch((error) => { + audioError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + audioLoading.value = false + audioPromise = null + }) + + audioPromise = request + return request + } + + async function refreshHid() { + if (hidLoading.value && hidPromise) return hidPromise + hidLoading.value = true + hidError.value = null + const request = hidConfigApi.get() + .then((response) => { + hid.value = response + return response + }) + .catch((error) => { + hidError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + hidLoading.value = false + hidPromise = null + }) + + hidPromise = request + return request + } + + async function refreshMsd() { + if (msdLoading.value && msdPromise) return msdPromise + msdLoading.value = true + msdError.value = null + const request = msdConfigApi.get() + .then((response) => { + msd.value = response + return response + }) + .catch((error) => { + msdError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + msdLoading.value = false + msdPromise = null + }) + + msdPromise = request + return request + } + + async function refreshStream() { + if (streamLoading.value && streamPromise) return streamPromise + streamLoading.value = true + streamError.value = null + const request = streamConfigApi.get() + .then((response) => { + stream.value = response + return response + }) + .catch((error) => { + streamError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + streamLoading.value = false + streamPromise = null + }) + + streamPromise = request + return request + } + + async function refreshWeb() { + if (webLoading.value && webPromise) return webPromise + webLoading.value = true + webError.value = null + const request = webConfigApi.get() + .then((response) => { + web.value = response + return response + }) + .catch((error) => { + webError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + webLoading.value = false + webPromise = null + }) + + webPromise = request + return request + } + + async function refreshAtx() { + if (atxLoading.value && atxPromise) return atxPromise + atxLoading.value = true + atxError.value = null + const request = atxConfigApi.get() + .then((response) => { + atx.value = response + return response + }) + .catch((error) => { + atxError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + atxLoading.value = false + atxPromise = null + }) + + atxPromise = request + return request + } + + async function refreshRustdeskConfig() { + if (rustdeskLoading.value && rustdeskPromise) return rustdeskPromise + rustdeskLoading.value = true + rustdeskError.value = null + const request = rustdeskConfigApi.get() + .then((response) => { + rustdeskConfig.value = response + return response + }) + .catch((error) => { + rustdeskError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + rustdeskLoading.value = false + rustdeskPromise = null + }) + + rustdeskPromise = request + return request + } + + async function refreshRustdeskStatus() { + if (rustdeskLoading.value && rustdeskStatusPromise) return rustdeskStatusPromise + rustdeskLoading.value = true + rustdeskError.value = null + const request = rustdeskConfigApi.getStatus() + .then((response) => { + rustdeskStatus.value = response + rustdeskConfig.value = response.config + return response + }) + .catch((error) => { + rustdeskError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + rustdeskLoading.value = false + rustdeskStatusPromise = null + }) + + rustdeskStatusPromise = request + return request + } + + async function refreshRustdeskPassword() { + if (rustdeskLoading.value && rustdeskPasswordPromise) return rustdeskPasswordPromise + rustdeskLoading.value = true + rustdeskError.value = null + const request = rustdeskConfigApi.getPassword() + .then((response) => { + rustdeskPassword.value = response + return response + }) + .catch((error) => { + rustdeskError.value = normalizeErrorMessage(error) + throw error + }) + .finally(() => { + rustdeskLoading.value = false + rustdeskPasswordPromise = null + }) + + rustdeskPasswordPromise = request + return request + } + + function ensureAuth() { + if (auth.value) return Promise.resolve(auth.value) + return refreshAuth() + } + + function ensureVideo() { + if (video.value) return Promise.resolve(video.value) + return refreshVideo() + } + + function ensureAudio() { + if (audio.value) return Promise.resolve(audio.value) + return refreshAudio() + } + + function ensureHid() { + if (hid.value) return Promise.resolve(hid.value) + return refreshHid() + } + + function ensureMsd() { + if (msd.value) return Promise.resolve(msd.value) + return refreshMsd() + } + + function ensureStream() { + if (stream.value) return Promise.resolve(stream.value) + return refreshStream() + } + + function ensureWeb() { + if (web.value) return Promise.resolve(web.value) + return refreshWeb() + } + + function ensureAtx() { + if (atx.value) return Promise.resolve(atx.value) + return refreshAtx() + } + + function ensureRustdeskConfig() { + if (rustdeskConfig.value) return Promise.resolve(rustdeskConfig.value) + return refreshRustdeskConfig() + } + + async function updateAuth(update: AuthConfigUpdate) { + const response = await authConfigApi.update(update) + auth.value = response + return response + } + + async function updateVideo(update: VideoConfigUpdate) { + const response = await videoConfigApi.update(update) + video.value = response + return response + } + + async function updateAudio(update: AudioConfigUpdate) { + const response = await audioConfigApi.update(update) + audio.value = response + return response + } + + async function updateHid(update: HidConfigUpdate) { + const response = await hidConfigApi.update(update) + hid.value = response + return response + } + + async function updateMsd(update: MsdConfigUpdate) { + const response = await msdConfigApi.update(update) + msd.value = response + return response + } + + async function updateStream(update: StreamConfigUpdate) { + const response = await streamConfigApi.update(update) + stream.value = response + return response + } + + async function updateWeb(update: WebConfigUpdate) { + const response = await webConfigApi.update(update) + web.value = response + return response + } + + async function updateAtx(update: AtxConfigUpdate) { + const response = await atxConfigApi.update(update) + atx.value = response + return response + } + + async function updateRustdesk(update: ApiRustDeskConfigUpdate) { + const response = await rustdeskConfigApi.update(update) + rustdeskConfig.value = response + return response + } + + async function regenerateRustdeskId() { + const response = await rustdeskConfigApi.regenerateId() + rustdeskConfig.value = response + return response + } + + async function regenerateRustdeskPassword() { + const response = await rustdeskConfigApi.regeneratePassword() + rustdeskConfig.value = response + return response + } + + return { + auth, + video, + audio, + hid, + msd, + stream, + web, + atx, + rustdeskConfig, + rustdeskStatus, + rustdeskPassword, + authLoading, + videoLoading, + audioLoading, + hidLoading, + msdLoading, + streamLoading, + webLoading, + atxLoading, + rustdeskLoading, + authError, + videoError, + audioError, + hidError, + msdError, + streamError, + webError, + atxError, + rustdeskError, + refreshAuth, + refreshVideo, + refreshAudio, + refreshHid, + refreshMsd, + refreshStream, + refreshWeb, + refreshAtx, + refreshRustdeskConfig, + refreshRustdeskStatus, + refreshRustdeskPassword, + ensureAuth, + ensureVideo, + ensureAudio, + ensureHid, + ensureMsd, + ensureStream, + ensureWeb, + ensureAtx, + ensureRustdeskConfig, + updateAuth, + updateVideo, + updateAudio, + updateHid, + updateMsd, + updateStream, + updateWeb, + updateAtx, + updateRustdesk, + regenerateRustdeskId, + regenerateRustdeskPassword, + } +}) diff --git a/web/src/stores/index.ts b/web/src/stores/index.ts index 4e46eda9..638a5468 100644 --- a/web/src/stores/index.ts +++ b/web/src/stores/index.ts @@ -1,2 +1,3 @@ export { useAuthStore } from './auth' +export { useConfigStore } from './config' export { useSystemStore } from './system' diff --git a/web/src/views/ConsoleView.vue b/web/src/views/ConsoleView.vue index 27287d2a..b4c19bae 100644 --- a/web/src/views/ConsoleView.vue +++ b/web/src/views/ConsoleView.vue @@ -3,6 +3,7 @@ import { ref, onMounted, onUnmounted, computed, watch, nextTick } from 'vue' import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' import { useSystemStore } from '@/stores/system' +import { useConfigStore } from '@/stores/config' import { useAuthStore } from '@/stores/auth' import { useWebSocket } from '@/composables/useWebSocket' import { useConsoleEvents } from '@/composables/useConsoleEvents' @@ -59,6 +60,7 @@ import { setLanguage } from '@/i18n' const { t, locale } = useI18n() const router = useRouter() const systemStore = useSystemStore() +const configStore = useConfigStore() const authStore = useAuthStore() const { connected: wsConnected, networkError: wsNetworkError } = useWebSocket() const hidWs = useHidWebSocket() @@ -134,6 +136,15 @@ let accumulatedDelta = { x: 0, y: 0 } // For relative mode: accumulate deltas be // Cursor visibility (from localStorage, updated via storage event) const cursorVisible = ref(localStorage.getItem('hidShowCursor') !== 'false') +function syncMouseModeFromConfig() { + const mouseAbsolute = configStore.hid?.mouse_absolute + if (typeof mouseAbsolute !== 'boolean') return + const nextMode: 'absolute' | 'relative' = mouseAbsolute ? 'absolute' : 'relative' + if (mouseMode.value !== nextMode) { + mouseMode.value = nextMode + } +} + // Virtual keyboard state const virtualKeyboardVisible = ref(false) const virtualKeyboardAttached = ref(true) @@ -1787,6 +1798,9 @@ onMounted(async () => { // 4. 其他初始化 await systemStore.startStream().catch(() => {}) await systemStore.fetchAllStates() + await configStore.refreshHid().then(() => { + syncMouseModeFromConfig() + }).catch(() => {}) window.addEventListener('keydown', handleKeyDown) window.addEventListener('keyup', handleKeyUp) @@ -1800,6 +1814,10 @@ onMounted(async () => { window.addEventListener('hidMouseSendIntervalChanged', handleMouseSendIntervalChange as EventListener) window.addEventListener('storage', handleMouseSendIntervalStorage) + watch(() => configStore.hid?.mouse_absolute, () => { + syncMouseModeFromConfig() + }) + // Pointer Lock event listeners document.addEventListener('pointerlockchange', handlePointerLockChange) document.addEventListener('pointerlockerror', handlePointerLockError) diff --git a/web/src/views/SettingsView.vue b/web/src/views/SettingsView.vue index 4e66f17a..374a4b60 100644 --- a/web/src/views/SettingsView.vue +++ b/web/src/views/SettingsView.vue @@ -2,20 +2,14 @@ import { ref, computed, onMounted, watch } from 'vue' import { useI18n } from 'vue-i18n' import { useSystemStore } from '@/stores/system' +import { useConfigStore } from '@/stores/config' import { useAuthStore } from '@/stores/auth' import { authApi, - authConfigApi, configApi, streamApi, - videoConfigApi, - streamConfigApi, - hidConfigApi, - msdConfigApi, atxConfigApi, extensionsApi, - rustdeskConfigApi, - webConfigApi, systemApi, type EncoderBackendInfo, type AuthConfig, @@ -80,6 +74,7 @@ import { const { t, locale } = useI18n() const systemStore = useSystemStore() +const configStore = useConfigStore() const authStore = useAuthStore() // Settings state @@ -271,7 +266,6 @@ const config = ref({ } as OtgHidFunctions, msd_enabled: false, msd_dir: '', - network_port: 8080, encoder_backend: 'auto', // STUN/TURN settings stun_server: '', @@ -583,7 +577,7 @@ async function saveConfig() { // Video 配置(包括编码器和 WebRTC/STUN/TURN 设置) if (activeSection.value === 'video') { savePromises.push( - videoConfigApi.update({ + configStore.updateVideo({ device: config.value.video_device || undefined, format: config.value.video_format || undefined, width: config.value.video_width, @@ -593,7 +587,7 @@ async function saveConfig() { ) // 同时保存 Stream/Encoder 和 STUN/TURN 配置 savePromises.push( - streamConfigApi.update({ + configStore.updateStream({ encoder: config.value.encoder_backend as any, stun_server: config.value.stun_server || undefined, turn_server: config.value.turn_server || undefined, @@ -642,12 +636,12 @@ async function saveConfig() { hidUpdate.otg_profile = config.value.hid_otg_profile hidUpdate.otg_functions = { ...config.value.hid_otg_functions } } - savePromises.push(hidConfigApi.update(hidUpdate)) + savePromises.push(configStore.updateHid(hidUpdate)) if (config.value.msd_enabled !== desiredMsdEnabled) { config.value.msd_enabled = desiredMsdEnabled } savePromises.push( - msdConfigApi.update({ + configStore.updateMsd({ enabled: desiredMsdEnabled, }) ) @@ -656,7 +650,7 @@ async function saveConfig() { // MSD 配置 if (activeSection.value === 'msd') { savePromises.push( - msdConfigApi.update({ + configStore.updateMsd({ msd_dir: config.value.msd_dir || undefined, }) ) @@ -677,10 +671,10 @@ async function loadConfig() { try { // 并行加载所有域配置 const [video, stream, hid, msd] = await Promise.all([ - videoConfigApi.get(), - streamConfigApi.get(), - hidConfigApi.get(), - msdConfigApi.get(), + configStore.refreshVideo(), + configStore.refreshStream(), + configStore.refreshHid(), + configStore.refreshMsd(), ]) config.value = { @@ -702,7 +696,6 @@ async function loadConfig() { } as OtgHidFunctions, msd_enabled: msd.enabled || false, msd_dir: msd.msd_dir || '', - network_port: 8080, // 从旧 API 加载 encoder_backend: stream.encoder || 'auto', // STUN/TURN settings stun_server: stream.stun_server || '', @@ -723,14 +716,6 @@ async function loadConfig() { otgSerialNumber.value = hid.otg_descriptor.serial_number || '' } - // 加载 web config(仍使用旧 API) - try { - const fullConfig = await configApi.get() - const web = fullConfig.web as any || {} - config.value.network_port = web.http_port || 8080 - } catch (e) { - console.warn('Failed to load web config:', e) - } } catch (e) { console.error('Failed to load config:', e) } finally { @@ -763,7 +748,7 @@ async function loadBackends() { async function loadAuthConfig() { authConfigLoading.value = true try { - authConfig.value = await authConfigApi.get() + authConfig.value = await configStore.refreshAuth() } catch (e) { console.error('Failed to load auth config:', e) } finally { @@ -774,10 +759,9 @@ async function loadAuthConfig() { async function saveAuthConfig() { authConfigLoading.value = true try { - await authConfigApi.update({ + authConfig.value = await configStore.updateAuth({ single_user_allow_multiple_sessions: authConfig.value.single_user_allow_multiple_sessions, }) - await loadAuthConfig() } catch (e) { console.error('Failed to save auth config:', e) } finally { @@ -912,7 +896,7 @@ function removeEasytierPeer(index: number) { // ATX management functions async function loadAtxConfig() { try { - const config = await atxConfigApi.get() + const config = await configStore.refreshAtx() atxConfig.value = { enabled: config.enabled, power: { ...config.power }, @@ -937,7 +921,7 @@ async function saveAtxConfig() { loading.value = true saved.value = false try { - await atxConfigApi.update({ + await configStore.updateAtx({ enabled: atxConfig.value.enabled, power: { driver: atxConfig.value.power.driver, @@ -981,10 +965,8 @@ function getAtxDevicesForDriver(driver: string): string[] { async function loadRustdeskConfig() { rustdeskLoading.value = true try { - const [config, status] = await Promise.all([ - rustdeskConfigApi.get(), - rustdeskConfigApi.getStatus(), - ]) + const status = await configStore.refreshRustdeskStatus() + const config = status.config rustdeskConfig.value = config rustdeskStatus.value = status rustdeskLocalConfig.value = { @@ -1002,7 +984,7 @@ async function loadRustdeskConfig() { async function loadRustdeskPassword() { try { - rustdeskPassword.value = await rustdeskConfigApi.getPassword() + rustdeskPassword.value = await configStore.refreshRustdeskPassword() } catch (e) { console.error('Failed to load RustDesk password:', e) } @@ -1060,7 +1042,7 @@ function removeBindAddress(index: number) { // Web server config functions async function loadWebServerConfig() { try { - const config = await webConfigApi.get() + const config = await configStore.refreshWeb() webServerConfig.value = config applyBindStateFromConfig(config) } catch (e) { @@ -1078,7 +1060,7 @@ async function saveWebServerConfig() { https_enabled: webServerConfig.value.https_enabled, bind_addresses: effectiveBindAddresses.value, } - const updated = await webConfigApi.update(update) + const updated = await configStore.updateWeb(update) webServerConfig.value = updated applyBindStateFromConfig(updated) showRestartDialog.value = true @@ -1117,7 +1099,7 @@ async function saveRustdeskConfig() { 21116, ) const relayServer = normalizeRustdeskServer(rustdeskLocalConfig.value.relay_server, 21117) - await rustdeskConfigApi.update({ + await configStore.updateRustdesk({ enabled: rustdeskLocalConfig.value.enabled, rendezvous_server: rendezvousServer, relay_server: relayServer, @@ -1139,7 +1121,7 @@ async function regenerateRustdeskId() { if (!confirm(t('extensions.rustdesk.confirmRegenerateId'))) return rustdeskLoading.value = true try { - await rustdeskConfigApi.regenerateId() + await configStore.regenerateRustdeskId() await loadRustdeskConfig() await loadRustdeskPassword() } catch (e) { @@ -1153,7 +1135,7 @@ async function regenerateRustdeskPassword() { if (!confirm(t('extensions.rustdesk.confirmRegeneratePassword'))) return rustdeskLoading.value = true try { - await rustdeskConfigApi.regeneratePassword() + await configStore.regenerateRustdeskPassword() await loadRustdeskConfig() await loadRustdeskPassword() } catch (e) { @@ -1167,7 +1149,7 @@ async function startRustdesk() { rustdeskLoading.value = true try { // Enable and save config to start the service - await rustdeskConfigApi.update({ enabled: true }) + await configStore.updateRustdesk({ enabled: true }) rustdeskLocalConfig.value.enabled = true await loadRustdeskConfig() } catch (e) { @@ -1181,7 +1163,7 @@ async function stopRustdesk() { rustdeskLoading.value = true try { // Disable and save config to stop the service - await rustdeskConfigApi.update({ enabled: false }) + await configStore.updateRustdesk({ enabled: false }) rustdeskLocalConfig.value.enabled = false await loadRustdeskConfig() } catch (e) {