v1.10.0 #471
@@ -23,7 +23,7 @@ const activeSessions = new Map<string, SSHSession>();
|
|||||||
|
|
||||||
const wss = new WebSocketServer({
|
const wss = new WebSocketServer({
|
||||||
port: 30008,
|
port: 30008,
|
||||||
verifyClient: async (info, callback) => {
|
verifyClient: async (info) => {
|
||||||
try {
|
try {
|
||||||
const url = parseUrl(info.req.url || "", true);
|
const url = parseUrl(info.req.url || "", true);
|
||||||
const token = url.query.token as string;
|
const token = url.query.token as string;
|
||||||
@@ -32,7 +32,7 @@ const wss = new WebSocketServer({
|
|||||||
dockerConsoleLogger.warn("WebSocket connection rejected: No token", {
|
dockerConsoleLogger.warn("WebSocket connection rejected: No token", {
|
||||||
operation: "ws_verify",
|
operation: "ws_verify",
|
||||||
});
|
});
|
||||||
return callback(false, 401, "Authentication required");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const authManager = AuthManager.getInstance();
|
const authManager = AuthManager.getInstance();
|
||||||
@@ -45,17 +45,15 @@ const wss = new WebSocketServer({
|
|||||||
operation: "ws_verify",
|
operation: "ws_verify",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return callback(false, 401, "Invalid token");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
(info.req as any).userId = decoded.userId;
|
return true;
|
||||||
|
|
||||||
callback(true);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dockerConsoleLogger.error("WebSocket verification error", error, {
|
dockerConsoleLogger.error("WebSocket verification error", error, {
|
||||||
operation: "ws_verify",
|
operation: "ws_verify",
|
||||||
});
|
});
|
||||||
callback(false, 500, "Authentication failed");
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
type ServerConfig,
|
type ServerConfig,
|
||||||
} from "@/ui/main-axios.ts";
|
} from "@/ui/main-axios.ts";
|
||||||
import { Server } from "lucide-react";
|
import { Server } from "lucide-react";
|
||||||
|
import { useTheme } from "@/components/theme-provider";
|
||||||
|
|
||||||
interface ServerConfigProps {
|
interface ServerConfigProps {
|
||||||
onServerConfigured: (serverUrl: string) => void;
|
onServerConfigured: (serverUrl: string) => void;
|
||||||
@@ -23,10 +24,17 @@ export function ElectronServerConfig({
|
|||||||
isFirstTime = false,
|
isFirstTime = false,
|
||||||
}: ServerConfigProps) {
|
}: ServerConfigProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { theme } = useTheme();
|
||||||
const [serverUrl, setServerUrl] = useState("");
|
const [serverUrl, setServerUrl] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const isDarkMode =
|
||||||
|
theme === "dark" ||
|
||||||
|
(theme === "system" &&
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||||
|
const lineColor = isDarkMode ? "#151517" : "#f9f9f9";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadServerConfig();
|
loadServerConfig();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -88,68 +96,84 @@ export function ElectronServerConfig({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div
|
||||||
<div className="text-center">
|
className="fixed inset-0 flex items-center justify-center"
|
||||||
<div className="mx-auto mb-4 w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center">
|
style={{
|
||||||
<Server className="w-6 h-6 text-primary" />
|
background: "var(--bg-elevated)",
|
||||||
</div>
|
backgroundImage: `repeating-linear-gradient(
|
||||||
<h2 className="text-xl font-semibold">{t("serverConfig.title")}</h2>
|
45deg,
|
||||||
<p className="text-sm text-muted-foreground mt-2">
|
transparent,
|
||||||
{t("serverConfig.description")}
|
transparent 35px,
|
||||||
</p>
|
${lineColor} 35px,
|
||||||
</div>
|
${lineColor} 37px
|
||||||
<div className="space-y-4">
|
)`,
|
||||||
<div className="space-y-2">
|
}}
|
||||||
<Label htmlFor="server-url">{t("serverConfig.serverUrl")}</Label>
|
>
|
||||||
<Input
|
<div className="w-[420px] max-w-full p-8 flex flex-col backdrop-blur-sm bg-card/50 rounded-2xl shadow-xl border-2 border-edge overflow-y-auto thin-scrollbar my-2 animate-in fade-in zoom-in-95 duration-300">
|
||||||
id="server-url"
|
<div className="space-y-6">
|
||||||
type="text"
|
<div className="text-center">
|
||||||
placeholder="http://localhost:30001 or https://your-server.com"
|
<div className="mx-auto mb-4 w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center">
|
||||||
value={serverUrl}
|
<Server className="w-6 h-6 text-primary" />
|
||||||
onChange={(e) => handleUrlChange(e.target.value)}
|
</div>
|
||||||
className="w-full h-10"
|
<h2 className="text-xl font-semibold">{t("serverConfig.title")}</h2>
|
||||||
disabled={loading}
|
<p className="text-sm text-muted-foreground mt-2">
|
||||||
/>
|
{t("serverConfig.description")}
|
||||||
</div>
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="server-url">{t("serverConfig.serverUrl")}</Label>
|
||||||
|
<Input
|
||||||
|
id="server-url"
|
||||||
|
type="text"
|
||||||
|
placeholder="http://localhost:30001 or https://your-server.com"
|
||||||
|
value={serverUrl}
|
||||||
|
onChange={(e) => handleUrlChange(e.target.value)}
|
||||||
|
className="w-full h-10"
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<Alert variant="destructive">
|
<Alert variant="destructive">
|
||||||
<AlertTitle>{t("common.error")}</AlertTitle>
|
<AlertTitle>{t("common.error")}</AlertTitle>
|
||||||
<AlertDescription>{error}</AlertDescription>
|
<AlertDescription>{error}</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex space-x-2">
|
|
||||||
{onCancel && !isFirstTime && (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
className="flex-1"
|
|
||||||
onClick={onCancel}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
className={onCancel && !isFirstTime ? "flex-1" : "w-full"}
|
|
||||||
onClick={handleSaveConfig}
|
|
||||||
disabled={loading || !serverUrl.trim()}
|
|
||||||
>
|
|
||||||
{loading ? (
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
||||||
<span>{t("serverConfig.saving")}</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
t("serverConfig.saveConfig")
|
|
||||||
)}
|
)}
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-xs text-muted-foreground text-center">
|
<div className="flex space-x-2">
|
||||||
{t("serverConfig.helpText")}
|
{onCancel && !isFirstTime && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
className="flex-1"
|
||||||
|
onClick={onCancel}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
className={onCancel && !isFirstTime ? "flex-1" : "w-full"}
|
||||||
|
onClick={handleSaveConfig}
|
||||||
|
disabled={loading || !serverUrl.trim()}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||||
|
<span>{t("serverConfig.saving")}</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
t("serverConfig.saveConfig")
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-xs text-muted-foreground text-center">
|
||||||
|
{t("serverConfig.helpText")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Button } from "@/components/ui/button.tsx";
|
|||||||
import { VersionAlert } from "@/components/ui/version-alert.tsx";
|
import { VersionAlert } from "@/components/ui/version-alert.tsx";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { checkElectronUpdate, isElectron } from "@/ui/main-axios.ts";
|
import { checkElectronUpdate, isElectron } from "@/ui/main-axios.ts";
|
||||||
|
import { useTheme } from "@/components/theme-provider";
|
||||||
|
|
||||||
interface VersionCheckModalProps {
|
interface VersionCheckModalProps {
|
||||||
onContinue: () => void;
|
onContinue: () => void;
|
||||||
@@ -14,6 +15,7 @@ export function ElectronVersionCheck({
|
|||||||
isAuthenticated = false,
|
isAuthenticated = false,
|
||||||
}: VersionCheckModalProps) {
|
}: VersionCheckModalProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { theme } = useTheme();
|
||||||
const [versionInfo, setVersionInfo] = useState<Record<
|
const [versionInfo, setVersionInfo] = useState<Record<
|
||||||
string,
|
string,
|
||||||
unknown
|
unknown
|
||||||
@@ -21,6 +23,12 @@ export function ElectronVersionCheck({
|
|||||||
const [versionChecking, setVersionChecking] = useState(false);
|
const [versionChecking, setVersionChecking] = useState(false);
|
||||||
const [versionDismissed] = useState(false);
|
const [versionDismissed] = useState(false);
|
||||||
|
|
||||||
|
const isDarkMode =
|
||||||
|
theme === "dark" ||
|
||||||
|
(theme === "system" &&
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||||
|
const lineColor = isDarkMode ? "#151517" : "#f9f9f9";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
checkForUpdates();
|
checkForUpdates();
|
||||||
@@ -83,8 +91,20 @@ export function ElectronVersionCheck({
|
|||||||
|
|
||||||
if (versionChecking && !versionInfo) {
|
if (versionChecking && !versionInfo) {
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center z-50">
|
<div
|
||||||
<div className="bg-canvas border-2 border-edge rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
className="fixed inset-0 flex items-center justify-center z-50"
|
||||||
|
style={{
|
||||||
|
background: "var(--bg-elevated)",
|
||||||
|
backgroundImage: `repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
transparent,
|
||||||
|
transparent 35px,
|
||||||
|
${lineColor} 35px,
|
||||||
|
${lineColor} 37px
|
||||||
|
)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="w-[420px] max-w-full p-8 flex flex-col backdrop-blur-sm bg-card/50 rounded-2xl shadow-xl border-2 border-edge overflow-y-auto thin-scrollbar my-2 animate-in fade-in zoom-in-95 duration-300">
|
||||||
<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" />
|
||||||
</div>
|
</div>
|
||||||
@@ -98,8 +118,20 @@ export function ElectronVersionCheck({
|
|||||||
|
|
||||||
if (!versionInfo || versionDismissed) {
|
if (!versionInfo || versionDismissed) {
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center z-50">
|
<div
|
||||||
<div className="bg-canvas border-2 border-edge rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
className="fixed inset-0 flex items-center justify-center z-50"
|
||||||
|
style={{
|
||||||
|
background: "var(--bg-elevated)",
|
||||||
|
backgroundImage: `repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
transparent,
|
||||||
|
transparent 35px,
|
||||||
|
${lineColor} 35px,
|
||||||
|
${lineColor} 37px
|
||||||
|
)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="w-[420px] max-w-full p-8 flex flex-col backdrop-blur-sm bg-card/50 rounded-2xl shadow-xl border-2 border-edge overflow-y-auto thin-scrollbar my-2 animate-in fade-in zoom-in-95 duration-300">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold">
|
<h2 className="text-lg font-semibold">
|
||||||
{t("versionCheck.checkUpdates")}
|
{t("versionCheck.checkUpdates")}
|
||||||
@@ -126,8 +158,20 @@ export function ElectronVersionCheck({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center z-50">
|
<div
|
||||||
<div className="bg-canvas border-2 border-edge rounded-lg p-6 max-w-md w-full mx-4 relative z-10">
|
className="fixed inset-0 flex items-center justify-center z-50"
|
||||||
|
style={{
|
||||||
|
background: "var(--bg-elevated)",
|
||||||
|
backgroundImage: `repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
transparent,
|
||||||
|
transparent 35px,
|
||||||
|
${lineColor} 35px,
|
||||||
|
${lineColor} 37px
|
||||||
|
)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="w-[420px] max-w-full p-8 flex flex-col backdrop-blur-sm bg-card/50 rounded-2xl shadow-xl border-2 border-edge overflow-y-auto thin-scrollbar my-2 animate-in fade-in zoom-in-95 duration-300">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h2 className="text-lg font-semibold">
|
<h2 className="text-lg font-semibold">
|
||||||
{t("versionCheck.updateRequired")}
|
{t("versionCheck.updateRequired")}
|
||||||
|
|||||||
Reference in New Issue
Block a user