v1.10.0 #471

Merged
LukeGus merged 106 commits from dev-1.10.0 into main 2026-01-01 04:20:12 +00:00
3 changed files with 138 additions and 72 deletions
Showing only changes of commit 2eac94be22 - Show all commits

View File

@@ -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;
} }
}, },
}); });

View File

@@ -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>

View File

@@ -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")}