diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts
index 256c2122..38bee98d 100644
--- a/src/backend/database/database.ts
+++ b/src/backend/database/database.ts
@@ -68,7 +68,7 @@ app.use(
return callback(null, true);
}
- callback(null, true);
+ callback(new Error("Not allowed by CORS"));
},
credentials: true,
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
@@ -77,6 +77,8 @@ app.use(
"Authorization",
"User-Agent",
"X-Electron-App",
+ "Accept",
+ "Origin",
],
}),
);
diff --git a/src/backend/utils/user-agent-parser.ts b/src/backend/utils/user-agent-parser.ts
index 74e0dca7..0d67c487 100644
--- a/src/backend/utils/user-agent-parser.ts
+++ b/src/backend/utils/user-agent-parser.ts
@@ -44,10 +44,10 @@ function parseElectronUserAgent(userAgent: string): DeviceInfo {
let os = "Unknown OS";
let version = "Unknown";
- const termixMatch = userAgent.match(/Termix-Desktop\/([\d.]+) \(([^;]+);/);
+ const termixMatch = userAgent.match(/Termix-Desktop\/([\d.]+)\s*\(([^;)]+)/);
if (termixMatch) {
version = termixMatch[1];
- os = termixMatch[2];
+ os = termixMatch[2].trim();
} else {
if (userAgent.includes("Windows")) {
os = parseWindowsVersion(userAgent);
diff --git a/src/main.tsx b/src/main.tsx
index aceeca24..31612fdf 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -5,6 +5,7 @@ import "./index.css";
import DesktopApp from "@/ui/desktop/DesktopApp.tsx";
import { MobileApp } from "@/ui/mobile/MobileApp.tsx";
import { ThemeProvider } from "@/components/theme-provider";
+import { ElectronVersionCheck } from "@/ui/desktop/user/ElectronVersionCheck.tsx";
import "./i18n/i18n";
import { isElectron } from "./ui/main-axios.ts";
@@ -55,20 +56,57 @@ function useWindowWidth() {
function RootApp() {
const width = useWindowWidth();
const isMobile = width < 768;
+ const [showVersionCheck, setShowVersionCheck] = useState(true);
const userAgent =
navigator.userAgent || navigator.vendor || (window as any).opera || "";
const isTermixMobile = /Termix-Mobile/.test(userAgent);
- if (isElectron()) {
- return ;
- }
+ const renderApp = () => {
+ if (isElectron()) {
+ return ;
+ }
- if (isTermixMobile) {
- return ;
- }
+ if (isTermixMobile) {
+ return ;
+ }
- return isMobile ? : ;
+ return isMobile ? : ;
+ };
+
+ return (
+ <>
+ {isElectron() && (
+
+ )}
+
+ {isElectron() && showVersionCheck ? (
+ setShowVersionCheck(false)}
+ isAuthenticated={false}
+ />
+ ) : (
+ renderApp()
+ )}
+
+ >
+ );
}
createRoot(document.getElementById("root")!).render(
diff --git a/src/ui/desktop/DesktopApp.tsx b/src/ui/desktop/DesktopApp.tsx
index f8a3de80..4d8c2b16 100644
--- a/src/ui/desktop/DesktopApp.tsx
+++ b/src/ui/desktop/DesktopApp.tsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { LeftSidebar } from "@/ui/desktop/navigation/LeftSidebar.tsx";
import { Dashboard } from "@/ui/desktop/apps/dashboard/Dashboard.tsx";
import { AppView } from "@/ui/desktop/navigation/AppView.tsx";
-import { HostManager } from "@/ui/desktop/apps/host manager/HostManager.tsx";
+import { HostManager } from "@/ui/desktop/apps/host-manager/HostManager.tsx";
import {
TabProvider,
useTabs,
@@ -11,7 +11,6 @@ import { TopNavbar } from "@/ui/desktop/navigation/TopNavbar.tsx";
import { AdminSettings } from "@/ui/desktop/admin/AdminSettings.tsx";
import { UserProfile } from "@/ui/desktop/user/UserProfile.tsx";
import { Toaster } from "@/components/ui/sonner.tsx";
-import { VersionCheckModal } from "@/components/ui/version-check-modal.tsx";
import { getUserInfo } from "@/ui/main-axios.ts";
function AppContent() {
@@ -19,7 +18,6 @@ function AppContent() {
const [username, setUsername] = useState(null);
const [isAdmin, setIsAdmin] = useState(false);
const [authLoading, setAuthLoading] = useState(true);
- const [showVersionCheck, setShowVersionCheck] = useState(true);
const [isTopbarOpen, setIsTopbarOpen] = useState(() => {
const saved = localStorage.getItem("topNavbarOpen");
return saved !== null ? JSON.parse(saved) : true;
@@ -31,9 +29,16 @@ function AppContent() {
setAuthLoading(true);
getUserInfo()
.then((meRes) => {
- setIsAuthenticated(true);
- setIsAdmin(!!meRes.is_admin);
- setUsername(meRes.username || null);
+ // Check if response is actually HTML (Vite dev server page)
+ if (typeof meRes === "string" || !meRes.username) {
+ setIsAuthenticated(false);
+ setIsAdmin(false);
+ setUsername(null);
+ } else {
+ setIsAuthenticated(true);
+ setIsAdmin(!!meRes.is_admin);
+ setUsername(meRes.username || null);
+ }
})
.catch((err) => {
setIsAuthenticated(false);
@@ -45,7 +50,9 @@ function AppContent() {
console.warn("Session expired - please log in again");
}
})
- .finally(() => setAuthLoading(false));
+ .finally(() => {
+ setAuthLoading(false);
+ });
};
checkAuth();
@@ -84,35 +91,7 @@ function AppContent() {
return (
- {showVersionCheck && (
-
setShowVersionCheck(false)}
- onContinue={() => setShowVersionCheck(false)}
- isAuthenticated={isAuthenticated}
- />
- )}
-
- {!isAuthenticated && !authLoading && !showVersionCheck && (
-
- )}
-
- {!isAuthenticated && !authLoading && !showVersionCheck && (
+ {!isAuthenticated && !authLoading && (
{
- if (isInElectronWebView()) {
- return;
- }
-
getRegistrationAllowed().then((res) => {
setRegistrationAllowed(res.allowed);
});
}, []);
useEffect(() => {
- if (isInElectronWebView()) {
- return;
- }
-
getPasswordLoginAllowed()
.then((res) => {
setPasswordLoginAllowed(res.allowed);
@@ -130,10 +122,6 @@ export function Auth({
}, []);
useEffect(() => {
- if (isInElectronWebView()) {
- return;
- }
-
getOIDCConfig()
.then((response) => {
if (response) {
diff --git a/src/components/ui/version-check-modal.tsx b/src/ui/desktop/user/ElectronVersionCheck.tsx
similarity index 70%
rename from src/components/ui/version-check-modal.tsx
rename to src/ui/desktop/user/ElectronVersionCheck.tsx
index 52b10ddd..9496afc2 100644
--- a/src/components/ui/version-check-modal.tsx
+++ b/src/ui/desktop/user/ElectronVersionCheck.tsx
@@ -9,7 +9,7 @@ interface VersionCheckModalProps {
isAuthenticated?: boolean;
}
-export function VersionCheckModal({
+export function ElectronVersionCheck({
onContinue,
isAuthenticated = false,
}: VersionCheckModalProps) {
@@ -35,7 +35,26 @@ export function VersionCheckModal({
const updateInfo = await checkElectronUpdate();
setVersionInfo(updateInfo);
+ // Get current app version
+ const currentVersion = await (window as any).electronAPI?.getAppVersion();
+ const dismissedVersion = localStorage.getItem(
+ "electron-version-check-dismissed",
+ );
+
+ // If this version was already dismissed, skip the modal
+ if (dismissedVersion === currentVersion) {
+ onContinue();
+ return;
+ }
+
if (updateInfo?.status === "up_to_date") {
+ // Store this version as checked (but don't show modal since up to date)
+ if (currentVersion) {
+ localStorage.setItem(
+ "electron-version-check-dismissed",
+ currentVersion,
+ );
+ }
onContinue();
return;
}
@@ -53,7 +72,12 @@ export function VersionCheckModal({
}
};
- const handleContinue = () => {
+ const handleContinue = async () => {
+ // Store the current version as dismissed
+ const currentVersion = await (window as any).electronAPI?.getAppVersion();
+ if (currentVersion) {
+ localStorage.setItem("electron-version-check-dismissed", currentVersion);
+ }
onContinue();
};
@@ -64,23 +88,6 @@ export function VersionCheckModal({
if (versionChecking && !versionInfo) {
return (
- {!isAuthenticated && (
-
- )}
@@ -96,23 +103,6 @@ export function VersionCheckModal({
if (!versionInfo || versionDismissed) {
return (
- {!isAuthenticated && (
-
- )}
@@ -141,23 +131,6 @@ export function VersionCheckModal({
return (
- {!isAuthenticated && (
-
- )}
diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts
index 4e2b05bb..d367b886 100644
--- a/src/ui/main-axios.ts
+++ b/src/ui/main-axios.ts
@@ -484,12 +484,7 @@ function getApiUrl(path: string, defaultPort: number): string {
const devMode = isDev();
const electronMode = isElectron();
- if (devMode) {
- const protocol = window.location.protocol === "https:" ? "https" : "http";
- const sslPort = protocol === "https" ? 8443 : defaultPort;
- const url = `${protocol}://${apiHost}:${sslPort}${path}`;
- return url;
- } else if (electronMode) {
+ if (electronMode) {
if (configuredServerUrl) {
const baseUrl = configuredServerUrl.replace(/\/$/, "");
const url = `${baseUrl}${path}`;
@@ -497,6 +492,11 @@ function getApiUrl(path: string, defaultPort: number): string {
}
console.warn("Electron mode but no server configured!");
return "http://no-server-configured";
+ } else if (devMode) {
+ const protocol = window.location.protocol === "https:" ? "https" : "http";
+ const sslPort = protocol === "https" ? 8443 : defaultPort;
+ const url = `${protocol}://${apiHost}:${sslPort}${path}`;
+ return url;
} else {
return path;
}