fix: Desktop app login issues and rename version check and host manager folder
This commit is contained in:
@@ -68,7 +68,7 @@ app.use(
|
|||||||
return callback(null, true);
|
return callback(null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, true);
|
callback(new Error("Not allowed by CORS"));
|
||||||
},
|
},
|
||||||
credentials: true,
|
credentials: true,
|
||||||
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
||||||
@@ -77,6 +77,8 @@ app.use(
|
|||||||
"Authorization",
|
"Authorization",
|
||||||
"User-Agent",
|
"User-Agent",
|
||||||
"X-Electron-App",
|
"X-Electron-App",
|
||||||
|
"Accept",
|
||||||
|
"Origin",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ function parseElectronUserAgent(userAgent: string): DeviceInfo {
|
|||||||
let os = "Unknown OS";
|
let os = "Unknown OS";
|
||||||
let version = "Unknown";
|
let version = "Unknown";
|
||||||
|
|
||||||
const termixMatch = userAgent.match(/Termix-Desktop\/([\d.]+) \(([^;]+);/);
|
const termixMatch = userAgent.match(/Termix-Desktop\/([\d.]+)\s*\(([^;)]+)/);
|
||||||
if (termixMatch) {
|
if (termixMatch) {
|
||||||
version = termixMatch[1];
|
version = termixMatch[1];
|
||||||
os = termixMatch[2];
|
os = termixMatch[2].trim();
|
||||||
} else {
|
} else {
|
||||||
if (userAgent.includes("Windows")) {
|
if (userAgent.includes("Windows")) {
|
||||||
os = parseWindowsVersion(userAgent);
|
os = parseWindowsVersion(userAgent);
|
||||||
|
|||||||
38
src/main.tsx
38
src/main.tsx
@@ -5,6 +5,7 @@ import "./index.css";
|
|||||||
import DesktopApp from "@/ui/desktop/DesktopApp.tsx";
|
import DesktopApp from "@/ui/desktop/DesktopApp.tsx";
|
||||||
import { MobileApp } from "@/ui/mobile/MobileApp.tsx";
|
import { MobileApp } from "@/ui/mobile/MobileApp.tsx";
|
||||||
import { ThemeProvider } from "@/components/theme-provider";
|
import { ThemeProvider } from "@/components/theme-provider";
|
||||||
|
import { ElectronVersionCheck } from "@/ui/desktop/user/ElectronVersionCheck.tsx";
|
||||||
import "./i18n/i18n";
|
import "./i18n/i18n";
|
||||||
import { isElectron } from "./ui/main-axios.ts";
|
import { isElectron } from "./ui/main-axios.ts";
|
||||||
|
|
||||||
@@ -55,11 +56,13 @@ function useWindowWidth() {
|
|||||||
function RootApp() {
|
function RootApp() {
|
||||||
const width = useWindowWidth();
|
const width = useWindowWidth();
|
||||||
const isMobile = width < 768;
|
const isMobile = width < 768;
|
||||||
|
const [showVersionCheck, setShowVersionCheck] = useState(true);
|
||||||
|
|
||||||
const userAgent =
|
const userAgent =
|
||||||
navigator.userAgent || navigator.vendor || (window as any).opera || "";
|
navigator.userAgent || navigator.vendor || (window as any).opera || "";
|
||||||
const isTermixMobile = /Termix-Mobile/.test(userAgent);
|
const isTermixMobile = /Termix-Mobile/.test(userAgent);
|
||||||
|
|
||||||
|
const renderApp = () => {
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
return <DesktopApp />;
|
return <DesktopApp />;
|
||||||
}
|
}
|
||||||
@@ -69,6 +72,41 @@ function RootApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return isMobile ? <MobileApp key="mobile" /> : <DesktopApp key="desktop" />;
|
return isMobile ? <MobileApp key="mobile" /> : <DesktopApp key="desktop" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isElectron() && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 pointer-events-none"
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#09090b",
|
||||||
|
backgroundImage: `linear-gradient(
|
||||||
|
135deg,
|
||||||
|
transparent 0%,
|
||||||
|
transparent 49%,
|
||||||
|
rgba(255, 255, 255, 0.03) 49%,
|
||||||
|
rgba(255, 255, 255, 0.03) 51%,
|
||||||
|
transparent 51%,
|
||||||
|
transparent 100%
|
||||||
|
)`,
|
||||||
|
backgroundSize: "80px 80px",
|
||||||
|
zIndex: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="relative min-h-screen" style={{ zIndex: 1 }}>
|
||||||
|
{isElectron() && showVersionCheck ? (
|
||||||
|
<ElectronVersionCheck
|
||||||
|
onContinue={() => setShowVersionCheck(false)}
|
||||||
|
isAuthenticated={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
renderApp()
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { LeftSidebar } from "@/ui/desktop/navigation/LeftSidebar.tsx";
|
import { LeftSidebar } from "@/ui/desktop/navigation/LeftSidebar.tsx";
|
||||||
import { Dashboard } from "@/ui/desktop/apps/dashboard/Dashboard.tsx";
|
import { Dashboard } from "@/ui/desktop/apps/dashboard/Dashboard.tsx";
|
||||||
import { AppView } from "@/ui/desktop/navigation/AppView.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 {
|
import {
|
||||||
TabProvider,
|
TabProvider,
|
||||||
useTabs,
|
useTabs,
|
||||||
@@ -11,7 +11,6 @@ import { TopNavbar } from "@/ui/desktop/navigation/TopNavbar.tsx";
|
|||||||
import { AdminSettings } from "@/ui/desktop/admin/AdminSettings.tsx";
|
import { AdminSettings } from "@/ui/desktop/admin/AdminSettings.tsx";
|
||||||
import { UserProfile } from "@/ui/desktop/user/UserProfile.tsx";
|
import { UserProfile } from "@/ui/desktop/user/UserProfile.tsx";
|
||||||
import { Toaster } from "@/components/ui/sonner.tsx";
|
import { Toaster } from "@/components/ui/sonner.tsx";
|
||||||
import { VersionCheckModal } from "@/components/ui/version-check-modal.tsx";
|
|
||||||
import { getUserInfo } from "@/ui/main-axios.ts";
|
import { getUserInfo } from "@/ui/main-axios.ts";
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
@@ -19,7 +18,6 @@ function AppContent() {
|
|||||||
const [username, setUsername] = useState<string | null>(null);
|
const [username, setUsername] = useState<string | null>(null);
|
||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
const [authLoading, setAuthLoading] = useState(true);
|
const [authLoading, setAuthLoading] = useState(true);
|
||||||
const [showVersionCheck, setShowVersionCheck] = useState(true);
|
|
||||||
const [isTopbarOpen, setIsTopbarOpen] = useState<boolean>(() => {
|
const [isTopbarOpen, setIsTopbarOpen] = useState<boolean>(() => {
|
||||||
const saved = localStorage.getItem("topNavbarOpen");
|
const saved = localStorage.getItem("topNavbarOpen");
|
||||||
return saved !== null ? JSON.parse(saved) : true;
|
return saved !== null ? JSON.parse(saved) : true;
|
||||||
@@ -31,9 +29,16 @@ function AppContent() {
|
|||||||
setAuthLoading(true);
|
setAuthLoading(true);
|
||||||
getUserInfo()
|
getUserInfo()
|
||||||
.then((meRes) => {
|
.then((meRes) => {
|
||||||
|
// 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);
|
setIsAuthenticated(true);
|
||||||
setIsAdmin(!!meRes.is_admin);
|
setIsAdmin(!!meRes.is_admin);
|
||||||
setUsername(meRes.username || null);
|
setUsername(meRes.username || null);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setIsAuthenticated(false);
|
setIsAuthenticated(false);
|
||||||
@@ -45,7 +50,9 @@ function AppContent() {
|
|||||||
console.warn("Session expired - please log in again");
|
console.warn("Session expired - please log in again");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => setAuthLoading(false));
|
.finally(() => {
|
||||||
|
setAuthLoading(false);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
checkAuth();
|
checkAuth();
|
||||||
@@ -84,35 +91,7 @@ function AppContent() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{showVersionCheck && (
|
{!isAuthenticated && !authLoading && (
|
||||||
<VersionCheckModal
|
|
||||||
onDismiss={() => setShowVersionCheck(false)}
|
|
||||||
onContinue={() => setShowVersionCheck(false)}
|
|
||||||
isAuthenticated={isAuthenticated}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isAuthenticated && !authLoading && !showVersionCheck && (
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
className="absolute inset-0"
|
|
||||||
style={{
|
|
||||||
backgroundImage: `linear-gradient(
|
|
||||||
135deg,
|
|
||||||
transparent 0%,
|
|
||||||
transparent 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 51%,
|
|
||||||
transparent 51%,
|
|
||||||
transparent 100%
|
|
||||||
)`,
|
|
||||||
backgroundSize: "80px 80px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isAuthenticated && !authLoading && !showVersionCheck && (
|
|
||||||
<div className="fixed inset-0 flex items-center justify-center z-[10000]">
|
<div className="fixed inset-0 flex items-center justify-center z-[10000]">
|
||||||
<Dashboard
|
<Dashboard
|
||||||
onSelectView={handleSelectView}
|
onSelectView={handleSelectView}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import { HostManagerViewer } from "@/ui/desktop/apps/host manager/HostManagerViewer.tsx";
|
import { HostManagerViewer } from "@/ui/desktop/apps/host-manager/HostManagerViewer.tsx";
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@/components/ui/tabs.tsx";
|
} from "@/components/ui/tabs.tsx";
|
||||||
import { Separator } from "@/components/ui/separator.tsx";
|
import { Separator } from "@/components/ui/separator.tsx";
|
||||||
import { HostManagerEditor } from "@/ui/desktop/apps/host manager/HostManagerEditor.tsx";
|
import { HostManagerEditor } from "@/ui/desktop/apps/host-manager/HostManagerEditor.tsx";
|
||||||
import { CredentialsManager } from "@/ui/desktop/apps/credentials/CredentialsManager.tsx";
|
import { CredentialsManager } from "@/ui/desktop/apps/credentials/CredentialsManager.tsx";
|
||||||
import { CredentialEditor } from "@/ui/desktop/apps/credentials/CredentialEditor.tsx";
|
import { CredentialEditor } from "@/ui/desktop/apps/credentials/CredentialEditor.tsx";
|
||||||
import { useSidebar } from "@/components/ui/sidebar.tsx";
|
import { useSidebar } from "@/components/ui/sidebar.tsx";
|
||||||
@@ -104,20 +104,12 @@ export function Auth({
|
|||||||
}, [loggedIn]);
|
}, [loggedIn]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInElectronWebView()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRegistrationAllowed().then((res) => {
|
getRegistrationAllowed().then((res) => {
|
||||||
setRegistrationAllowed(res.allowed);
|
setRegistrationAllowed(res.allowed);
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInElectronWebView()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getPasswordLoginAllowed()
|
getPasswordLoginAllowed()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setPasswordLoginAllowed(res.allowed);
|
setPasswordLoginAllowed(res.allowed);
|
||||||
@@ -130,10 +122,6 @@ export function Auth({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInElectronWebView()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getOIDCConfig()
|
getOIDCConfig()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ interface VersionCheckModalProps {
|
|||||||
isAuthenticated?: boolean;
|
isAuthenticated?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VersionCheckModal({
|
export function ElectronVersionCheck({
|
||||||
onContinue,
|
onContinue,
|
||||||
isAuthenticated = false,
|
isAuthenticated = false,
|
||||||
}: VersionCheckModalProps) {
|
}: VersionCheckModalProps) {
|
||||||
@@ -35,7 +35,26 @@ export function VersionCheckModal({
|
|||||||
const updateInfo = await checkElectronUpdate();
|
const updateInfo = await checkElectronUpdate();
|
||||||
setVersionInfo(updateInfo);
|
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") {
|
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();
|
onContinue();
|
||||||
return;
|
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();
|
onContinue();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,23 +88,6 @@ export function VersionCheckModal({
|
|||||||
if (versionChecking && !versionInfo) {
|
if (versionChecking && !versionInfo) {
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center z-50">
|
<div className="fixed inset-0 flex items-center justify-center z-50">
|
||||||
{!isAuthenticated && (
|
|
||||||
<div
|
|
||||||
className="absolute inset-0"
|
|
||||||
style={{
|
|
||||||
backgroundImage: `linear-gradient(
|
|
||||||
135deg,
|
|
||||||
transparent 0%,
|
|
||||||
transparent 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 51%,
|
|
||||||
transparent 51%,
|
|
||||||
transparent 100%
|
|
||||||
)`,
|
|
||||||
backgroundSize: "80px 80px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="bg-dark-bg border-2 border-dark-border rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
<div className="bg-dark-bg border-2 border-dark-border rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
||||||
<div className="flex items-center justify-center mb-4">
|
<div className="flex items-center justify-center mb-4">
|
||||||
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||||
@@ -96,23 +103,6 @@ export function VersionCheckModal({
|
|||||||
if (!versionInfo || versionDismissed) {
|
if (!versionInfo || versionDismissed) {
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center z-50">
|
<div className="fixed inset-0 flex items-center justify-center z-50">
|
||||||
{!isAuthenticated && (
|
|
||||||
<div
|
|
||||||
className="absolute inset-0"
|
|
||||||
style={{
|
|
||||||
backgroundImage: `linear-gradient(
|
|
||||||
135deg,
|
|
||||||
transparent 0%,
|
|
||||||
transparent 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 51%,
|
|
||||||
transparent 51%,
|
|
||||||
transparent 100%
|
|
||||||
)`,
|
|
||||||
backgroundSize: "80px 80px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="bg-dark-bg border-2 border-dark-border rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
<div className="bg-dark-bg border-2 border-dark-border rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold">
|
<h2 className="text-lg font-semibold">
|
||||||
@@ -141,23 +131,6 @@ export function VersionCheckModal({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center z-50">
|
<div className="fixed inset-0 flex items-center justify-center z-50">
|
||||||
{!isAuthenticated && (
|
|
||||||
<div
|
|
||||||
className="absolute inset-0"
|
|
||||||
style={{
|
|
||||||
backgroundImage: `linear-gradient(
|
|
||||||
135deg,
|
|
||||||
transparent 0%,
|
|
||||||
transparent 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 49%,
|
|
||||||
rgba(255, 255, 255, 0.03) 51%,
|
|
||||||
transparent 51%,
|
|
||||||
transparent 100%
|
|
||||||
)`,
|
|
||||||
backgroundSize: "80px 80px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="bg-dark-bg border-2 border-dark-border rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
<div className="bg-dark-bg border-2 border-dark-border rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold">
|
<h2 className="text-lg font-semibold">
|
||||||
@@ -484,12 +484,7 @@ function getApiUrl(path: string, defaultPort: number): string {
|
|||||||
const devMode = isDev();
|
const devMode = isDev();
|
||||||
const electronMode = isElectron();
|
const electronMode = isElectron();
|
||||||
|
|
||||||
if (devMode) {
|
if (electronMode) {
|
||||||
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 (configuredServerUrl) {
|
if (configuredServerUrl) {
|
||||||
const baseUrl = configuredServerUrl.replace(/\/$/, "");
|
const baseUrl = configuredServerUrl.replace(/\/$/, "");
|
||||||
const url = `${baseUrl}${path}`;
|
const url = `${baseUrl}${path}`;
|
||||||
@@ -497,6 +492,11 @@ function getApiUrl(path: string, defaultPort: number): string {
|
|||||||
}
|
}
|
||||||
console.warn("Electron mode but no server configured!");
|
console.warn("Electron mode but no server configured!");
|
||||||
return "http://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 {
|
} else {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user