diff --git a/frontend/bindings/voidraft/internal/services/migrationservice.ts b/frontend/bindings/voidraft/internal/services/migrationservice.ts index 1605694..3c5dd7a 100644 --- a/frontend/bindings/voidraft/internal/services/migrationservice.ts +++ b/frontend/bindings/voidraft/internal/services/migrationservice.ts @@ -50,13 +50,5 @@ export function ServiceShutdown(): Promise & { cancel(): void } { return $resultPromise; } -/** - * SetProgressBroadcaster 设置进度广播函数 - */ -export function SetProgressBroadcaster(broadcaster: any): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(3244071921, broadcaster) as any; - return $resultPromise; -} - // Private type creation functions const $$createType0 = $models.MigrationProgress.createFrom; diff --git a/frontend/src/composables/useWebSocket.ts b/frontend/src/composables/useWebSocket.ts deleted file mode 100644 index e967b99..0000000 --- a/frontend/src/composables/useWebSocket.ts +++ /dev/null @@ -1,423 +0,0 @@ -import { ref, onUnmounted, reactive, computed, watch, nextTick } from 'vue'; - -// 基础WebSocket消息接口 -interface WebSocketMessage { - type: string; - data: T; -} - -// 迁移进度接口(与后端保持一致) -interface MigrationProgress { - status: 'migrating' | 'completed' | 'failed'; - progress: number; // 0-100 - error?: string; -} - -// 连接状态枚举 -enum ConnectionState { - DISCONNECTED = 'disconnected', - CONNECTING = 'connecting', - CONNECTED = 'connected', - RECONNECTING = 'reconnecting', - ERROR = 'error' -} - -// WebSocket配置选项 -interface WebSocketOptions { - url?: string; - reconnectInterval?: number; - maxReconnectAttempts?: number; - debug?: boolean; - autoConnect?: boolean; - protocols?: string | string[]; - heartbeat?: { - enabled: boolean; - interval: number; - message: string; - }; -} - -// 消息处理器类型 -type MessageHandler = (data: T) => void; - -// 事件处理器映射 -interface EventHandlers { - [messageType: string]: MessageHandler[]; -} - -// 连接事件类型 -type ConnectionEventType = 'connect' | 'disconnect' | 'error' | 'reconnect'; -type ConnectionEventHandler = (event?: Event | CloseEvent | ErrorEvent) => void; - -export function useWebSocket(options: WebSocketOptions = {}) { - const { - url = 'ws://localhost:8899/ws/migration', - reconnectInterval = 3000, - maxReconnectAttempts = 10, - debug = false, - autoConnect = true, - protocols, - heartbeat = { enabled: false, interval: 30000, message: 'ping' } - } = options; - - // === 状态管理 === - const connectionState = ref(ConnectionState.DISCONNECTED); - const connectionError = ref(null); - const reconnectAttempts = ref(0); - const lastMessage = ref(null); - const messageHistory = ref([]); - - // 迁移进度状态(保持向后兼容) - const migrationProgress = reactive({ - status: 'completed', - progress: 0 - }); - - // === 计算属性 === - const isConnected = computed(() => connectionState.value === ConnectionState.CONNECTED); - const isConnecting = computed(() => - connectionState.value === ConnectionState.CONNECTING || - connectionState.value === ConnectionState.RECONNECTING - ); - const canReconnect = computed(() => reconnectAttempts.value < maxReconnectAttempts); - - // === 内部状态 === - let ws: WebSocket | null = null; - let reconnectTimer: number | null = null; - let heartbeatTimer: number | null = null; - let isManualDisconnect = false; - - // 事件处理器 - const eventHandlers: EventHandlers = {}; - const connectionEventHandlers: Map = new Map(); - - // === 工具函数 === - const log = (level: 'info' | 'warn' | 'error', message: string, ...args: any[]) => { - if (debug) { - console[level](`[WebSocket] ${message}`, ...args); - } - }; - - const clearTimers = () => { - if (reconnectTimer) { - clearTimeout(reconnectTimer); - reconnectTimer = null; - } - if (heartbeatTimer) { - clearInterval(heartbeatTimer); - heartbeatTimer = null; - } - }; - - const updateConnectionState = (newState: ConnectionState, error?: string) => { - connectionState.value = newState; - connectionError.value = error || null; - log('info', `Connection state changed to: ${newState}`, error); - }; - - // === 事件系统 === - const on = (messageType: string, handler: MessageHandler) => { - if (!eventHandlers[messageType]) { - eventHandlers[messageType] = []; - } - eventHandlers[messageType].push(handler as MessageHandler); - - // 返回取消订阅函数 - return () => off(messageType, handler); - }; - - const off = (messageType: string, handler: MessageHandler) => { - if (eventHandlers[messageType]) { - const index = eventHandlers[messageType].indexOf(handler as MessageHandler); - if (index > -1) { - eventHandlers[messageType].splice(index, 1); - } - } - }; - - const onConnection = (eventType: ConnectionEventType, handler: ConnectionEventHandler) => { - if (!connectionEventHandlers.has(eventType)) { - connectionEventHandlers.set(eventType, []); - } - connectionEventHandlers.get(eventType)!.push(handler); - - return () => offConnection(eventType, handler); - }; - - const offConnection = (eventType: ConnectionEventType, handler: ConnectionEventHandler) => { - const handlers = connectionEventHandlers.get(eventType); - if (handlers) { - const index = handlers.indexOf(handler); - if (index > -1) { - handlers.splice(index, 1); - } - } - }; - - const emit = (eventType: ConnectionEventType, event?: Event | CloseEvent | ErrorEvent) => { - const handlers = connectionEventHandlers.get(eventType); - if (handlers) { - handlers.forEach(handler => { - try { - handler(event); - } catch (error) { - log('error', `Error in ${eventType} event handler:`, error); - } - }); - } - }; - - // === 消息处理 === - const handleMessage = (event: MessageEvent) => { - try { - const message: WebSocketMessage = JSON.parse(event.data); - log('info', 'Received message:', message); - - // 更新消息历史 - lastMessage.value = message; - messageHistory.value.push(message); - - // 限制历史记录长度 - if (messageHistory.value.length > 100) { - messageHistory.value.shift(); - } - - // 特殊处理迁移进度消息(保持向后兼容) - if (message.type === 'migration_progress') { - Object.assign(migrationProgress, message.data); - } - - // 触发注册的处理器 - const handlers = eventHandlers[message.type]; - if (handlers) { - handlers.forEach(handler => { - try { - handler(message.data); - } catch (error) { - log('error', `Error in message handler for ${message.type}:`, error); - } - }); - } - } catch (error) { - log('error', 'Failed to parse message:', error, event.data); - } - }; - - // === 心跳机制 === - const startHeartbeat = () => { - if (!heartbeat.enabled || heartbeatTimer) return; - - heartbeatTimer = window.setInterval(() => { - if (isConnected.value) { - send(heartbeat.message); - } - }, heartbeat.interval); - }; - - const stopHeartbeat = () => { - if (heartbeatTimer) { - clearInterval(heartbeatTimer); - heartbeatTimer = null; - } - }; - - // === 连接管理 === - const connect = async (): Promise => { - if (isConnecting.value || isConnected.value) { - log('warn', 'Already connecting or connected'); - return; - } - - updateConnectionState(ConnectionState.CONNECTING); - isManualDisconnect = false; - - try { - log('info', 'Connecting to:', url); - - ws = new WebSocket(url, protocols); - - // 连接超时处理 - const connectTimeout = setTimeout(() => { - if (ws && ws.readyState === WebSocket.CONNECTING) { - ws.close(); - updateConnectionState(ConnectionState.ERROR, 'Connection timeout'); - } - }, 10000); - - ws.onopen = () => { - clearTimeout(connectTimeout); - log('info', 'Connected successfully'); - updateConnectionState(ConnectionState.CONNECTED); - reconnectAttempts.value = 0; - clearTimers(); - startHeartbeat(); - emit('connect'); - }; - - ws.onmessage = handleMessage; - - ws.onclose = (event) => { - clearTimeout(connectTimeout); - stopHeartbeat(); - log('info', 'Connection closed:', event.code, event.reason); - - const wasConnected = connectionState.value === ConnectionState.CONNECTED; - updateConnectionState(ConnectionState.DISCONNECTED); - ws = null; - - emit('disconnect', event); - - // 自动重连逻辑 - if (!isManualDisconnect && event.code !== 1000 && canReconnect.value) { - scheduleReconnect(); - } else if (reconnectAttempts.value >= maxReconnectAttempts) { - updateConnectionState(ConnectionState.ERROR, 'Max reconnection attempts reached'); - } - }; - - ws.onerror = (event) => { - clearTimeout(connectTimeout); - log('error', 'Connection error:', event); - updateConnectionState(ConnectionState.ERROR, 'WebSocket connection error'); - emit('error', event); - }; - - } catch (error) { - log('error', 'Failed to create WebSocket:', error); - updateConnectionState(ConnectionState.ERROR, 'Failed to create WebSocket connection'); - } - }; - - const disconnect = (code: number = 1000, reason: string = 'Manual disconnect') => { - isManualDisconnect = true; - clearTimers(); - stopHeartbeat(); - - if (ws) { - log('info', 'Disconnecting manually'); - ws.close(code, reason); - } - - updateConnectionState(ConnectionState.DISCONNECTED); - }; - - const scheduleReconnect = () => { - if (!canReconnect.value || isManualDisconnect) return; - - clearTimers(); - reconnectAttempts.value++; - updateConnectionState(ConnectionState.RECONNECTING, - `Reconnecting... (${reconnectAttempts.value}/${maxReconnectAttempts})`); - - log('info', `Scheduling reconnect attempt ${reconnectAttempts.value}/${maxReconnectAttempts} in ${reconnectInterval}ms`); - - reconnectTimer = window.setTimeout(() => { - connect(); - }, reconnectInterval); - - emit('reconnect'); - }; - - const reconnect = () => { - disconnect(); - reconnectAttempts.value = 0; - nextTick(() => { - connect(); - }); - }; - - // === 消息发送 === - const send = (message: any): boolean => { - if (!isConnected.value || !ws) { - log('warn', 'Cannot send message: not connected'); - return false; - } - - try { - const data = typeof message === 'string' ? message : JSON.stringify(message); - ws.send(data); - log('info', 'Sent message:', data); - return true; - } catch (error) { - log('error', 'Failed to send message:', error); - return false; - } - }; - - const sendMessage = (type: string, data?: T): boolean => { - return send({ type, data }); - }; - - // === 状态查询 === - const getConnectionInfo = () => ({ - state: connectionState.value, - error: connectionError.value, - reconnectAttempts: reconnectAttempts.value, - maxReconnectAttempts, - canReconnect: canReconnect.value, - url, - readyState: ws?.readyState, - protocol: ws?.protocol, - extensions: ws?.extensions - }); - - // === 初始化 === - if (autoConnect) { - nextTick(() => { - connect(); - }); - } - - // === 清理 === - onUnmounted(() => { - disconnect(); - }); - - // 监听连接状态变化,用于调试 - if (debug) { - watch(connectionState, (newState, oldState) => { - log('info', `State transition: ${oldState} -> ${newState}`); - }); - } - - return { - // === 状态(只读) === - connectionState: computed(() => connectionState.value), - isConnected, - isConnecting, - connectionError: computed(() => connectionError.value), - reconnectAttempts: computed(() => reconnectAttempts.value), - canReconnect, - lastMessage: computed(() => lastMessage.value), - messageHistory: computed(() => messageHistory.value), - - // === 向后兼容的状态 === - migrationProgress, - - // === 连接控制 === - connect, - disconnect, - reconnect, - - // === 消息发送 === - send, - sendMessage, - - // === 事件系统 === - on, - off, - onConnection, - offConnection, - - // === 工具方法 === - getConnectionInfo, - clearHistory: () => { - messageHistory.value = []; - lastMessage.value = null; - } - }; -} - -// 导出类型 -export type { WebSocketMessage, MigrationProgress, WebSocketOptions, MessageHandler, ConnectionEventType }; -export { ConnectionState }; \ No newline at end of file diff --git a/frontend/src/views/settings/pages/GeneralPage.vue b/frontend/src/views/settings/pages/GeneralPage.vue index 23837a4..33ca938 100644 --- a/frontend/src/views/settings/pages/GeneralPage.vue +++ b/frontend/src/views/settings/pages/GeneralPage.vue @@ -6,19 +6,51 @@ import SettingSection from '../components/SettingSection.vue'; import SettingItem from '../components/SettingItem.vue'; import ToggleSwitch from '../components/ToggleSwitch.vue'; import {useErrorHandler} from '@/utils/errorHandler'; -import {DialogService, MigrationService} from '@/../bindings/voidraft/internal/services'; -import {useWebSocket} from '@/composables/useWebSocket'; +import {DialogService, MigrationService, MigrationProgress, MigrationStatus} from '@/../bindings/voidraft/internal/services'; import * as runtime from '@wailsio/runtime'; const {t} = useI18n(); const configStore = useConfigStore(); const {safeCall} = useErrorHandler(); -// WebSocket连接 -const {migrationProgress, isConnected, connectionState, connect, disconnect, on} = useWebSocket({ - debug: true, - autoConnect: false // 手动控制连接 -}); +// 迁移进度状态 +const migrationProgress = ref(new MigrationProgress({ + status: MigrationStatus.MigrationStatusCompleted, + progress: 0 +})); + +// 轮询相关 +let pollingTimer: number | null = null; +const isPolling = ref(false); + +// 开始轮询迁移进度 +const startPolling = () => { + if (isPolling.value) return; + + isPolling.value = true; + pollingTimer = window.setInterval(async () => { + try { + const progress = await MigrationService.GetProgress(); + migrationProgress.value = progress; + + // 如果迁移完成或失败,停止轮询 + if (progress.status === MigrationStatus.MigrationStatusCompleted || progress.status === MigrationStatus.MigrationStatusFailed) { + stopPolling(); + } + } catch (error) { + stopPolling(); + } + }, 500); // 每500ms轮询一次 +}; + +// 停止轮询 +const stopPolling = () => { + if (pollingTimer) { + clearInterval(pollingTimer); + pollingTimer = null; + } + isPolling.value = false; +}; // 迁移消息链 interface MigrationMessage { @@ -52,26 +84,15 @@ const clearMigrationMessages = () => { showMessages.value = false; }; -// 监听连接状态变化 -const connectionWatcher = computed(() => isConnected.value); -watch(connectionWatcher, (connected) => { - if (connected && isMigrating.value) { - // 如果连接成功且正在迁移,添加连接消息 - if (!migrationMessages.value.some(msg => msg.content === '实时连接中')) { - addMigrationMessage('实时连接中', 'progress'); - } - } -}); - // 监听迁移进度变化 -on('migration_progress', (data) => { +watch(() => migrationProgress.value, (progress, oldProgress) => { // 清除之前的隐藏定时器 if (hideMessagesTimer) { clearTimeout(hideMessagesTimer); hideMessagesTimer = null; } - if (data.status === 'migrating') { + if (progress.status === MigrationStatus.MigrationStatusMigrating) { // 如果是第一次收到迁移状态,添加开始消息 if (migrationMessages.value.length === 0) { addMigrationMessage(t('migration.started'), 'start'); @@ -80,34 +101,24 @@ on('migration_progress', (data) => { if (!migrationMessages.value.some(msg => msg.type === 'progress' && msg.content === t('migration.migrating'))) { addMigrationMessage(t('migration.migrating'), 'progress'); } - } else if (data.status === 'completed') { + } else if (progress.status === MigrationStatus.MigrationStatusCompleted && oldProgress?.status === MigrationStatus.MigrationStatusMigrating) { addMigrationMessage(t('migration.completed'), 'success'); - // 3秒后断开连接 - setTimeout(() => { - disconnect(); - }, 3000); - // 5秒后开始逐个隐藏消息 hideMessagesTimer = setTimeout(() => { hideMessagesSequentially(); }, 5000); - } else if (data.status === 'failed') { - const errorMsg = data.error || t('migration.failed'); + } else if (progress.status === MigrationStatus.MigrationStatusFailed && oldProgress?.status === MigrationStatus.MigrationStatusMigrating) { + const errorMsg = progress.error || t('migration.failed'); addMigrationMessage(errorMsg, 'error'); - // 3秒后断开连接 - setTimeout(() => { - disconnect(); - }, 3000); - // 8秒后开始逐个隐藏消息 hideMessagesTimer = setTimeout(() => { hideMessagesSequentially(); }, 8000); } -}); +}, { deep: true }); // 逐个隐藏消息 const hideMessagesSequentially = () => { @@ -129,18 +140,18 @@ const hideMessagesSequentially = () => { }; // 迁移状态 -const isMigrating = computed(() => migrationProgress.status === 'migrating'); -const migrationComplete = computed(() => migrationProgress.status === 'completed'); -const migrationFailed = computed(() => migrationProgress.status === 'failed'); +const isMigrating = computed(() => migrationProgress.value.status === MigrationStatus.MigrationStatusMigrating); +const migrationComplete = computed(() => migrationProgress.value.status === MigrationStatus.MigrationStatusCompleted); +const migrationFailed = computed(() => migrationProgress.value.status === MigrationStatus.MigrationStatusFailed); // 进度条样式和宽度 const progressBarClass = computed(() => { - switch (migrationProgress.status) { - case 'migrating': + switch (migrationProgress.value.status) { + case MigrationStatus.MigrationStatusMigrating: return 'migrating'; - case 'completed': + case MigrationStatus.MigrationStatusCompleted: return 'success'; - case 'failed': + case MigrationStatus.MigrationStatusFailed: return 'error'; default: return ''; @@ -150,7 +161,7 @@ const progressBarClass = computed(() => { const progressBarWidth = computed(() => { // 只有在显示消息且正在迁移时才显示进度条 if (showMessages.value && isMigrating.value) { - return migrationProgress.progress + '%'; + return migrationProgress.value.progress + '%'; } else if (showMessages.value && (migrationComplete.value || migrationFailed.value)) { // 迁移完成或失败时,短暂显示100%,然后随着消息隐藏而隐藏 return '100%'; @@ -264,17 +275,17 @@ const currentDataPath = computed(() => configStore.config.general.dataPath); const selectDataDirectory = async () => { if (isMigrating.value) return; - const selectedPath = await DialogService.SelectDirectory(); + const selectedPath = await DialogService.SelectDirectory(); + + if (selectedPath && selectedPath.trim() && selectedPath !== currentDataPath.value) { + // 清除之前的消息 + clearMigrationMessages(); - if (selectedPath && selectedPath.trim() && selectedPath !== currentDataPath.value) { - // 清除之前的消息 - clearMigrationMessages(); - - // 连接WebSocket以接收迁移进度 - await connect(); - - // 开始迁移 - try { + // 开始轮询迁移进度 + startPolling(); + + // 开始迁移 + try { await safeCall(async () => { const oldPath = currentDataPath.value; const newPath = selectedPath.trim(); @@ -282,16 +293,17 @@ const selectDataDirectory = async () => { await MigrationService.MigrateDirectory(oldPath, newPath); await configStore.setDataPath(newPath); }, ''); - } catch (error) { - // 发生错误时清除消息 - clearMigrationMessages(); + } catch (error) { + // 发生错误时清除消息并停止轮询 + clearMigrationMessages(); + stopPolling(); } } }; -// 清理定时器和WebSocket连接 +// 清理定时器 onUnmounted(() => { - disconnect(); + stopPolling(); if (resetConfirmTimer) { clearTimeout(resetConfirmTimer); } diff --git a/go.mod b/go.mod index 4387239..31681aa 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,6 @@ toolchain go1.24.2 require ( github.com/fsnotify/fsnotify v1.9.0 - github.com/gin-gonic/gin v1.10.1 - github.com/lxzan/gws v1.8.9 github.com/spf13/viper v1.20.1 github.com/wailsapp/wails/v3 v3.0.0-alpha.9 ) @@ -18,44 +16,27 @@ require ( github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/adrg/xdg v0.5.3 // indirect github.com/bep/debounce v1.2.1 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudflare/circl v1.6.1 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect - github.com/dolthub/maphash v0.1.0 // indirect github.com/ebitengine/purego v0.8.4 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-git/go-git/v5 v5.16.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect - github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/uuid v1.6.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leaanthony/go-ansi-parser v1.6.1 // indirect github.com/leaanthony/u v1.1.1 // indirect - github.com/leodido/go-urn v1.4.0 // indirect github.com/lmittmann/tint v1.1.1 // indirect github.com/matryer/is v1.4.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect @@ -69,19 +50,15 @@ require ( github.com/spf13/cast v1.9.2 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect github.com/wailsapp/go-webview2 v1.0.21 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.38.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/net v0.40.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 2b7539b..33d3d18 100644 --- a/go.sum +++ b/go.sum @@ -13,23 +13,13 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= -github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ= -github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= @@ -40,12 +30,6 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= -github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -58,41 +42,22 @@ github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9 github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ= github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -104,12 +69,8 @@ github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lmittmann/tint v1.1.1 h1:xmmGuinUsCSxWdwH1OqMUQ4tzQsq3BdjJLAAmVKJ9Dw= github.com/lmittmann/tint v1.1.1/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/lxzan/gws v1.8.9 h1:VU3SGUeWlQrEwfUSfokcZep8mdg/BrUF+y73YYshdBM= -github.com/lxzan/gws v1.8.9/go.mod h1:d9yHaR1eDTBHagQC6KY7ycUOaz5KWeqQtP3xu7aMK8Y= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= @@ -117,11 +78,6 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= @@ -159,23 +115,12 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= github.com/wailsapp/go-webview2 v1.0.21/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= @@ -186,9 +131,6 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= @@ -206,7 +148,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= @@ -217,8 +158,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -229,8 +168,5 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/services/http_service.go b/internal/services/http_service.go deleted file mode 100644 index 6d8074a..0000000 --- a/internal/services/http_service.go +++ /dev/null @@ -1,156 +0,0 @@ -package services - -import ( - "context" - "errors" - "net/http" - "time" - - "github.com/gin-gonic/gin" - "github.com/wailsapp/wails/v3/pkg/services/log" -) - -// HTTPService HTTP 服务 -type HTTPService struct { - logger *log.LoggerService - server *http.Server - wsService *WebSocketService -} - -// NewHTTPService 创建 HTTP 服务 -func NewHTTPService(logger *log.LoggerService, wsService *WebSocketService) *HTTPService { - if logger == nil { - logger = log.New() - } - - return &HTTPService{ - logger: logger, - wsService: wsService, - } -} - -// StartServer 启动 HTTP 服务器 -func (hs *HTTPService) StartServer(port string) error { - // 设置 Gin 为发布模式 - gin.SetMode(gin.ReleaseMode) - - // 创建 Gin 路由器 - router := gin.New() - - // 添加中间件 - router.Use(gin.Recovery()) - router.Use(hs.corsMiddleware()) - - // 设置路由 - hs.setupRoutes(router) - - // 创建 HTTP 服务器 - hs.server = &http.Server{ - Addr: ":" + port, - Handler: router, - ReadTimeout: 30 * time.Second, - WriteTimeout: 30 * time.Second, - IdleTimeout: 60 * time.Second, - MaxHeaderBytes: 1 << 20, // 1MB - } - - hs.logger.Info("HTTP: Starting server", "port", port) - - // 启动服务器 - go func() { - if err := hs.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { - hs.logger.Error("HTTP: Server failed to start", "error", err) - } - }() - - return nil -} - -// setupRoutes 设置路由 -func (hs *HTTPService) setupRoutes(router *gin.Engine) { - // WebSocket 端点 - router.GET("/ws/migration", hs.handleWebSocket) - - // API 端点组 - api := router.Group("/api") - { - api.GET("/health", hs.handleHealth) - api.GET("/ws/clients", hs.handleWSClients) - } -} - -// handleWebSocket 处理 WebSocket 连接 -func (hs *HTTPService) handleWebSocket(c *gin.Context) { - if hs.wsService == nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "WebSocket service not available"}) - return - } - - hs.wsService.HandleUpgrade(c.Writer, c.Request) -} - -// handleHealth 健康检查端点 -func (hs *HTTPService) handleHealth(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "status": "ok", - "timestamp": time.Now().Unix(), - "service": "voidraft-http", - }) -} - -// handleWSClients 获取 WebSocket 客户端数量 -func (hs *HTTPService) handleWSClients(c *gin.Context) { - if hs.wsService == nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "WebSocket service not available"}) - return - } - - count := hs.wsService.GetConnectedClientsCount() - c.JSON(http.StatusOK, gin.H{ - "connected_clients": count, - }) -} - -// corsMiddleware CORS 中间件 -func (hs *HTTPService) corsMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - c.Header("Access-Control-Allow-Origin", "*") - c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") - c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization") - c.Header("Access-Control-Allow-Credentials", "true") - - if c.Request.Method == "OPTIONS" { - c.AbortWithStatus(204) - return - } - - c.Next() - } -} - -// StopServer 停止 HTTP 服务器 -func (hs *HTTPService) StopServer() error { - if hs.server == nil { - return nil - } - - hs.logger.Info("HTTP: Stopping server...") - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - return hs.server.Shutdown(ctx) -} - -// ServiceShutdown 服务关闭 -func (hs *HTTPService) ServiceShutdown() error { - hs.logger.Info("HTTP: Service is shutting down...") - - if err := hs.StopServer(); err != nil { - hs.logger.Error("HTTP: Failed to stop server", "error", err) - return err - } - - hs.logger.Info("HTTP: Service shutdown completed") - return nil -} diff --git a/internal/services/migration_service.go b/internal/services/migration_service.go index 2e1af67..97f435b 100644 --- a/internal/services/migration_service.go +++ b/internal/services/migration_service.go @@ -32,12 +32,11 @@ type MigrationProgress struct { // MigrationService 迁移服务 type MigrationService struct { - logger *log.LoggerService - mu sync.RWMutex - currentProgress MigrationProgress - cancelFunc context.CancelFunc - ctx context.Context - progressBroadcaster func(MigrationProgress) // WebSocket广播函数 + logger *log.LoggerService + mu sync.RWMutex + currentProgress MigrationProgress + cancelFunc context.CancelFunc + ctx context.Context } // NewMigrationService 创建迁移服务 @@ -67,11 +66,6 @@ func (ms *MigrationService) updateProgress(progress MigrationProgress) { ms.mu.Lock() ms.currentProgress = progress ms.mu.Unlock() - - // 通过WebSocket广播进度 - if ms.progressBroadcaster != nil { - ms.progressBroadcaster(progress) - } } // MigrateDirectory 迁移目录 @@ -407,13 +401,6 @@ func (ms *MigrationService) CancelMigration() error { return fmt.Errorf("No active migration to cancel") } -// SetProgressBroadcaster 设置进度广播函数 -func (ms *MigrationService) SetProgressBroadcaster(broadcaster func(MigrationProgress)) { - ms.mu.Lock() - defer ms.mu.Unlock() - ms.progressBroadcaster = broadcaster -} - // ServiceShutdown 服务关闭 func (ms *MigrationService) ServiceShutdown() error { ms.logger.Info("Migration: Service is shutting down...") diff --git a/internal/services/service_manager.go b/internal/services/service_manager.go index 35999ea..efeccaa 100644 --- a/internal/services/service_manager.go +++ b/internal/services/service_manager.go @@ -15,8 +15,6 @@ type ServiceManager struct { systemService *SystemService hotkeyService *HotkeyService dialogService *DialogService - websocketService *WebSocketService - httpService *HTTPService trayService *TrayService logger *log.LoggerService } @@ -44,29 +42,11 @@ func NewServiceManager() *ServiceManager { // 初始化对话服务 dialogService := NewDialogService(logger) - // 初始化 WebSocket 服务 - websocketService := NewWebSocketService(logger) - - // 初始化 HTTP 服务 - httpService := NewHTTPService(logger, websocketService) - // 初始化托盘服务 trayService := NewTrayService(logger, configService) - // 设置迁移服务的WebSocket广播 - migrationService.SetProgressBroadcaster(func(progress MigrationProgress) { - websocketService.BroadcastMigrationProgress(progress) - }) - - // 启动 HTTP 服务器 - err := httpService.StartServer("8899") - if err != nil { - logger.Error("Failed to start HTTP server", "error", err) - panic(err) - } - // 使用新的配置通知系统设置热键配置变更监听 - err = configService.SetHotkeyChangeCallback(func(enable bool, hotkey *models.HotkeyCombo) error { + err := configService.SetHotkeyChangeCallback(func(enable bool, hotkey *models.HotkeyCombo) error { return hotkeyService.UpdateHotkey(enable, hotkey) }) if err != nil { @@ -97,8 +77,6 @@ func NewServiceManager() *ServiceManager { systemService: systemService, hotkeyService: hotkeyService, dialogService: dialogService, - websocketService: websocketService, - httpService: httpService, trayService: trayService, logger: logger, } diff --git a/internal/services/websocket_service.go b/internal/services/websocket_service.go deleted file mode 100644 index 8f9e131..0000000 --- a/internal/services/websocket_service.go +++ /dev/null @@ -1,159 +0,0 @@ -package services - -import ( - "encoding/json" - "net/http" - "sync" - - "github.com/lxzan/gws" - "github.com/wailsapp/wails/v3/pkg/services/log" -) - -// WebSocketService WebSocket 服务 -type WebSocketService struct { - logger *log.LoggerService - upgrader *gws.Upgrader - clients map[*gws.Conn]bool - clientsMu sync.RWMutex -} - -// NewWebSocketService 创建 WebSocket 服务 -func NewWebSocketService(logger *log.LoggerService) *WebSocketService { - if logger == nil { - logger = log.New() - } - - ws := &WebSocketService{ - logger: logger, - clients: make(map[*gws.Conn]bool), - } - - // 创建 WebSocket 升级器 - ws.upgrader = gws.NewUpgrader(&WebSocketHandler{service: ws}, &gws.ServerOption{ - ParallelEnabled: true, - Recovery: gws.Recovery, - PermessageDeflate: gws.PermessageDeflate{Enabled: true}, - }) - - return ws -} - -// WebSocketHandler WebSocket 事件处理器 -type WebSocketHandler struct { - service *WebSocketService -} - -// OnOpen 连接建立时调用 -func (h *WebSocketHandler) OnOpen(socket *gws.Conn) { - h.service.logger.Info("WebSocket: Client connected") - - h.service.clientsMu.Lock() - h.service.clients[socket] = true - h.service.clientsMu.Unlock() -} - -// OnClose 连接关闭时调用 -func (h *WebSocketHandler) OnClose(socket *gws.Conn, err error) { - h.service.logger.Info("WebSocket: Client disconnected", "error", err) - - h.service.clientsMu.Lock() - delete(h.service.clients, socket) - h.service.clientsMu.Unlock() -} - -// OnPing 收到 Ping 时调用 -func (h *WebSocketHandler) OnPing(socket *gws.Conn, payload []byte) { - _ = socket.WritePong(nil) -} - -// OnPong 收到 Pong 时调用 -func (h *WebSocketHandler) OnPong(socket *gws.Conn, payload []byte) { - // Do nothing -} - -// OnMessage 收到消息时调用 -func (h *WebSocketHandler) OnMessage(socket *gws.Conn, message *gws.Message) { - defer message.Close() - - h.service.logger.Debug("WebSocket: Received message", "message", string(message.Bytes())) - -} - -// HandleUpgrade 处理 WebSocket 升级请求 -func (ws *WebSocketService) HandleUpgrade(w http.ResponseWriter, r *http.Request) { - socket, err := ws.upgrader.Upgrade(w, r) - if err != nil { - ws.logger.Error("WebSocket: Failed to upgrade connection", "error", err) - return - } - - // 启动读取循环(必须在 goroutine 中运行以防止阻塞) - go func() { - socket.ReadLoop() - }() -} - -// BroadcastMessage 广播消息给所有连接的客户端 -func (ws *WebSocketService) BroadcastMessage(messageType string, data interface{}) { - message := map[string]interface{}{ - "type": messageType, - "data": data, - } - - jsonData, err := json.Marshal(message) - if err != nil { - ws.logger.Error("WebSocket: Failed to marshal message", "error", err) - return - } - - ws.clientsMu.RLock() - clients := make([]*gws.Conn, 0, len(ws.clients)) - for client := range ws.clients { - clients = append(clients, client) - } - ws.clientsMu.RUnlock() - - // 使用广播器进行高效广播 - broadcaster := gws.NewBroadcaster(gws.OpcodeText, jsonData) - defer broadcaster.Close() - - for _, client := range clients { - if err := broadcaster.Broadcast(client); err != nil { - ws.logger.Error("WebSocket: Failed to broadcast to client", "error", err) - // 清理失效的连接 - ws.clientsMu.Lock() - delete(ws.clients, client) - ws.clientsMu.Unlock() - } - } - - ws.logger.Debug("WebSocket: Broadcasted message", "type", messageType, "clients", len(clients)) -} - -// BroadcastMigrationProgress 广播迁移进度 -func (ws *WebSocketService) BroadcastMigrationProgress(progress MigrationProgress) { - ws.BroadcastMessage("migration_progress", progress) -} - -// GetConnectedClientsCount 获取连接的客户端数量 -func (ws *WebSocketService) GetConnectedClientsCount() int { - ws.clientsMu.RLock() - defer ws.clientsMu.RUnlock() - return len(ws.clients) -} - -// ServiceShutdown 服务关闭 -func (ws *WebSocketService) ServiceShutdown() error { - ws.logger.Info("WebSocket: Service is shutting down...") - - // 关闭所有客户端连接 - ws.clientsMu.Lock() - for client := range ws.clients { - _ = client.WriteClose(1000, []byte("Server shutting down")) - } - ws.clients = make(map[*gws.Conn]bool) - ws.clientsMu.Unlock() - - ws.logger.Info("WebSocket: Service shutdown completed") - return nil -}