diff --git a/backend/api/preferences_api.go b/backend/api/preferences_api.go index eab4200..c7d6497 100644 --- a/backend/api/preferences_api.go +++ b/backend/api/preferences_api.go @@ -47,10 +47,6 @@ func registerPreferencesRoutes(rg *gin.RouterGroup) { c.JSON(http.StatusOK, services.Preferences().GetBuildInDecoder()) }) - g.GET("/version", func(c *gin.Context) { - c.JSON(http.StatusOK, services.Preferences().GetAppVersion()) - }) - g.GET("/check-update", func(c *gin.Context) { c.JSON(http.StatusOK, services.Preferences().CheckForUpdate()) }) diff --git a/backend/api/router.go b/backend/api/router.go index ca8e27b..3b29fa2 100644 --- a/backend/api/router.go +++ b/backend/api/router.go @@ -3,8 +3,6 @@ package api import ( - "embed" - "io/fs" "log" "net/http" "strings" @@ -17,7 +15,7 @@ import ( const maxRequestBodySize = 10 << 20 // 10MB // SetupRouter creates the Gin router with all API routes and static file serving -func SetupRouter(assets embed.FS) *gin.Engine { +func SetupRouter() *gin.Engine { gin.SetMode(gin.ReleaseMode) r := gin.Default() @@ -57,9 +55,8 @@ func SetupRouter(assets embed.FS) *gin.Engine { // Public routes (no auth required) registerAuthRoutes(r) - r.GET("/api/version", func(c *gin.Context) { - resp := services.Preferences().GetAppVersion() - c.JSON(200, resp) + r.GET("/api/preferences/version", func(c *gin.Context) { + c.JSON(http.StatusOK, services.Preferences().GetAppVersion()) }) // WebSocket endpoint (auth checked via cookie + origin) @@ -76,22 +73,6 @@ func SetupRouter(assets embed.FS) *gin.Engine { registerPreferencesRoutes(api) registerSystemRoutes(api) - // Serve frontend static files from embedded assets - distFS, err := fs.Sub(assets, "frontend/dist") - if err == nil { - fileServer := http.FileServer(http.FS(distFS)) - r.NoRoute(func(c *gin.Context) { - path := c.Request.URL.Path - f, ferr := http.FS(distFS).Open(path) - if ferr == nil { - f.Close() - fileServer.ServeHTTP(c.Writer, c.Request) - return - } - c.FileFromFS("/", http.FS(distFS)) - }) - } - return r } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 9ae9b58..0ec9711 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -23,17 +23,15 @@ import ImportKeyDialog from '@/components/dialogs/ImportKeyDialog.vue' import { Info } from 'wailsjs/go/services/systemService.js' import DecoderDialog from '@/components/dialogs/DecoderDialog.vue' import { loadModule, trackEvent } from '@/utils/analytics.js' +import { isWeb } from '@/utils/platform.js' const prefStore = usePreferencesStore() const connectionStore = useConnectionStore() const i18n = useI18n() const initializing = ref(true) -// Detect if running in web mode (VITE_WEB=true at build time) -const isWebMode = import.meta.env.VITE_WEB === 'true' - // Web-only: lazy load LoginPage to avoid importing websocket.js in desktop mode -const LoginPage = isWebMode ? defineAsyncComponent(() => import('@/components/LoginPage.vue')) : null +const LoginPage = isWeb() ? defineAsyncComponent(() => import('@/components/LoginPage.vue')) : null // Viewport management for mobile const setViewport = (mode) => { @@ -57,7 +55,7 @@ const setViewport = (mode) => { } // Auth state (web mode only) -const authChecking = ref(isWebMode) // desktop: false (skip), web: true (checking) +const authChecking = ref(isWeb()) // desktop: false (skip), web: true (checking) const authenticated = ref(false) const authEnabled = ref(false) @@ -82,33 +80,37 @@ const onLogin = async () => { // Reconnect WebSocket with auth cookie (dynamic import to avoid desktop issues) try { const runtime = await import('wailsjs/runtime/runtime.js') - if (runtime.ReconnectWebSocket) runtime.ReconnectWebSocket() + if (runtime.ReconnectWebSocket) { + runtime.ReconnectWebSocket() + } } catch {} // Capture login page choices before loadPreferences overwrites them const loginTheme = localStorage.getItem('rdm_login_theme') const loginLang = localStorage.getItem('rdm_login_lang') await initApp() // Sync login page choices to preferences - let needSave = false + let prefUpdated = false if (loginTheme && ['auto', 'light', 'dark'].includes(loginTheme)) { prefStore.general.theme = loginTheme - needSave = true + prefUpdated = true } if (loginLang) { const validLangs = ['auto', 'zh', 'tw', 'en', 'ja', 'ko', 'es', 'fr', 'ru', 'pt', 'tr'] if (validLangs.includes(loginLang)) { prefStore.general.language = loginLang i18n.locale.value = prefStore.currentLanguage - needSave = true + prefUpdated = true } } - if (needSave) prefStore.savePreferences() + if (prefUpdated) { + prefStore.savePreferences() + } } const initApp = async () => { try { initializing.value = true - if (isWebMode) { + if (isWeb()) { const prefResult = await prefStore.loadPreferences() // If loadPreferences failed (e.g. 401 from expired session), // rdm:unauthorized event already fired → silently abort init @@ -118,7 +120,7 @@ const initApp = async () => { await prefStore.loadFontList() await prefStore.loadBuildInDecoder() await connectionStore.initConnections() - if (prefStore.autoCheckUpdate) { + if (!isWeb() && prefStore.autoCheckUpdate) { prefStore.checkForUpdate() } const env = await Environment() @@ -196,7 +198,7 @@ const onOrientationChange = () => { } onMounted(async () => { - if (isWebMode) { + if (isWeb()) { // Apply saved login theme before auth check to prevent flash const savedTheme = localStorage.getItem('rdm_login_theme') if (savedTheme && ['auto', 'light', 'dark'].includes(savedTheme)) { @@ -225,7 +227,7 @@ onMounted(async () => { }) onUnmounted(() => { - if (isWebMode) { + if (isWeb()) { window.removeEventListener('rdm:unauthorized', onUnauthorized) window.removeEventListener('orientationchange', onOrientationChange) window.removeEventListener('resize', onOrientationChange) @@ -253,10 +255,10 @@ watch( :theme-overrides="prefStore.isDark ? darkThemeOverrides : themeOverrides" class="fill-height"> -