import { useLoaderData, useNavigate } from "react-router-dom"; import { ShieldCheckIcon } from "@heroicons/react/24/outline"; import { useCallback, useEffect, useState } from "react"; import api from "@/api"; import { SettingsPageHeader } from "@components/SettingsPageheader"; import { GridCard } from "@/components/Card"; import { Button, LinkButton } from "@/components/Button"; import { InputFieldWithLabel } from "@/components/InputField"; import { SelectMenuBasic } from "@/components/SelectMenuBasic"; import { SettingsSectionHeader } from "@/components/SettingsSectionHeader"; import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; import notifications from "@/notifications"; import { DEVICE_API } from "@/ui.config"; import { useJsonRpc } from "@/hooks/useJsonRpc"; import { isOnDevice } from "@/main"; import { TextAreaWithLabel } from "@components/TextArea"; import { LocalDevice } from "./devices.$id"; import { SettingsItem } from "./devices.$id.settings"; import { CloudState } from "./adopt"; import { useVpnStore } from "@/hooks/stores"; import Checkbox from "../components/Checkbox"; import { LogDialog } from "../components/LogDialog"; import {useReactAt} from 'i18n-auto-extractor/react' import AutoHeight from "@/components/AutoHeight"; import { Tabs } from "@/components/Tabs"; export interface TailScaleResponse { state: string; loginUrl: string; ip: string; xEdge: boolean; } export interface ZeroTierResponse { state: string; networkID: string; ip: string; } export interface FrpcResponse { running: boolean; } export interface EasyTierRunningResponse { running: boolean; } export interface EasyTierResponse { name: string; secret: string; node: string; } export interface VntRunningResponse { running: boolean; } export interface VntResponse { config_mode: string; token: string; device_id: string; name: string; server_addr: string; config_file: string; model?: string; password?: string; } export interface CloudflaredRunningResponse { running: boolean; } export interface TLSState { mode: "self-signed" | "custom" | "disabled"; certificate?: string; privateKey?: string; } const loader = async () => { if (isOnDevice) { const status = await api .GET(`${DEVICE_API}/device`) .then(res => res.json() as Promise); return status; } return null; }; export default function SettingsAccessIndexRoute() { const { $at }= useReactAt(); const loaderData = useLoaderData() as LocalDevice | null; const { navigateTo } = useDeviceUiNavigation(); const [send] = useJsonRpc(); const [deviceId, setDeviceId] = useState(null); // Use a simple string identifier for the selected provider const [tlsMode, setTlsMode] = useState("unknown"); const [tlsCert, setTlsCert] = useState(""); const [tlsKey, setTlsKey] = useState(""); const tailScaleConnectionState = useVpnStore(state => state.tailScaleConnectionState); const tailScaleLoginUrl = useVpnStore(state => state.tailScaleLoginUrl); const tailScaleXEdge = useVpnStore(state => state.tailScaleXEdge) const tailScaleIP = useVpnStore(state => state.tailScaleIP); const setTailScaleConnectionState = useVpnStore(state => state.setTailScaleConnectionState); const setTailScaleLoginUrl = useVpnStore(state => state.setTailScaleLoginUrl); const setTailScaleXEdge = useVpnStore(state => state.setTailScaleXEdge); const setTailScaleIP = useVpnStore(state => state.setTailScaleIP); const zeroTierConnectionState = useVpnStore(state => state.zeroTierConnectionState); const zeroTierNetworkID = useVpnStore(state => state.zeroTierNetworkID); const zeroTierIP = useVpnStore(state => state.zeroTierIP); const setZeroTierConnectionState = useVpnStore(state => state.setZeroTierConnectionState); const setZeroTierNetworkID = useVpnStore(state => state.setZeroTierNetworkID); const setZeroTierIP = useVpnStore(state => state.setZeroTierIP); const [tempNetworkID, setTempNetworkID] = useState(""); const [isDisconnecting, setIsDisconnecting] = useState(false); const [frpcToml, setFrpcToml] = useState(""); const [frpcLog, setFrpcLog] = useState(""); const [showFrpcLogModal, setShowFrpcLogModal] = useState(false); const [frpcRunningStatus, setFrpcRunningStatus] = useState({ running: false }); const [tempEasyTierNetworkName, setTempEasyTierNetworkName] = useState(""); const [tempEasyTierNetworkSecret, setTempEasyTierNetworkSecret] = useState(""); const [tempEasyTierNetworkNodeMode, setTempEasyTierNetworkNodeMode] = useState("default"); const [tempEasyTierNetworkNode, setTempEasyTierNetworkNode] = useState("tcp://public.easytier.cn:11010"); const [easyTierRunningStatus, setEasyTierRunningStatus] = useState({ running: false }); const [showEasyTierLogModal, setShowEasyTierLogModal] = useState(false); const [showEasyTierNodeInfoModal, setShowEasyTierNodeInfoModal] = useState(false); const [easyTierLog, setEasyTierLog] = useState(""); const [easyTierNodeInfo, setEasyTierNodeInfo] = useState(""); const [easyTierConfig, setEasyTierConfig] = useState({ name: "", secret: "", node: "", }); const [vntConfigMode, setVntConfigMode] = useState("params"); // "params" or "file" const [tempVntToken, setTempVntToken] = useState(""); const [tempVntDeviceId, setTempVntDeviceId] = useState(""); const [tempVntName, setTempVntName] = useState(""); const [tempVntServerAddr, setTempVntServerAddr] = useState(""); const [vntConfigFileContent, setVntConfigFileContent] = useState(""); const [vntRunningStatus, setVntRunningStatus] = useState({ running: false }); const [showVntLogModal, setShowVntLogModal] = useState(false); const [showVntInfoModal, setShowVntInfoModal] = useState(false); const [vntLog, setVntLog] = useState(""); const [vntInfo, setVntInfo] = useState(""); const [vntConfig, setVntConfig] = useState({ config_mode: "params", token: "", device_id: "", name: "", server_addr: "", config_file: "", model: "", password: "", }); const [tempVntModel, setTempVntModel] = useState("aes_gcm"); const [tempVntPassword, setTempVntPassword] = useState(""); // Cloudflare Tunnel const [cloudflaredRunningStatus, setCloudflaredRunningStatus] = useState({ running: false }); const [cloudflaredToken, setCloudflaredToken] = useState(""); const [cloudflaredLog, setCloudflaredLog] = useState(""); const [showCloudflaredLogModal, setShowCloudflaredLogModal] = useState(false); const getTLSState = useCallback(() => { send("getTLSState", {}, resp => { if ("error" in resp) return console.error(resp.error); const tlsState = resp.result as TLSState; setTlsMode(tlsState.mode); if (tlsState.certificate) setTlsCert(tlsState.certificate); if (tlsState.privateKey) setTlsKey(tlsState.privateKey); }); }, [send]); // Function to update TLS state - accepts a mode parameter const updateTlsState = useCallback( (mode: string, cert?: string, key?: string) => { const state = { mode } as TLSState; if (cert && key) { state.certificate = cert; state.privateKey = key; } send("setTLSState", { state }, resp => { if ("error" in resp) { notifications.error( `Failed to update TLS settings: ${resp.error.data || "Unknown error"}`, ); return; } notifications.success("TLS settings updated successfully"); }); }, [send], ); const getCloudflaredStatus = useCallback(() => { send("getCloudflaredStatus", {}, resp => { if ("error" in resp) { notifications.error(`Failed to get Cloudflare status: ${resp.error.data || "Unknown error"}`); return; } setCloudflaredRunningStatus(resp.result as CloudflaredRunningResponse); }); }, [send]); const handleStartCloudflared = useCallback(() => { if (!cloudflaredToken) { notifications.error("Please enter Cloudflare Tunnel Token"); return; } send("startCloudflared", { token: cloudflaredToken }, resp => { if ("error" in resp) { notifications.error(`Failed to start Cloudflare: ${resp.error.data || "Unknown error"}`); setCloudflaredRunningStatus({ running: false }); return; } notifications.success("Cloudflare started"); setCloudflaredRunningStatus({ running: true }); }); }, [send, cloudflaredToken]); const handleStopCloudflared = useCallback(() => { send("stopCloudflared", {}, resp => { if ("error" in resp) { notifications.error(`Failed to stop Cloudflare: ${resp.error.data || "Unknown error"}`); return; } notifications.success("Cloudflare stopped"); setCloudflaredRunningStatus({ running: false }); }); }, [send]); const handleGetCloudflaredLog = useCallback(() => { send("getCloudflaredLog", {}, resp => { if ("error" in resp) { notifications.error(`Failed to get Cloudflare log: ${resp.error.data || "Unknown error"}`); setCloudflaredLog(""); return; } setCloudflaredLog(resp.result as string); setShowCloudflaredLogModal(true); }); }, [send]); useEffect(() => { getCloudflaredStatus(); }, [getCloudflaredStatus]); // Handle TLS mode change const handleTlsModeChange = (value: string) => { setTlsMode(value); // For "disabled" and "self-signed" modes, immediately apply the settings if (value !== "custom") { updateTlsState(value); } }; const handleTlsCertChange = (value: string) => { setTlsCert(value); }; const handleTlsKeyChange = (value: string) => { setTlsKey(value); }; // Update the custom TLS settings button click handler const handleCustomTlsUpdate = () => { updateTlsState(tlsMode, tlsCert, tlsKey); }; // Fetch device ID and cloud state on component mount useEffect(() => { getTLSState(); send("getDeviceID", {}, async resp => { if ("error" in resp) return console.error(resp.error); setDeviceId(resp.result as string); }); }, [send, getTLSState]); const handleTailScaleLogin = useCallback(() => { setTailScaleConnectionState("connecting"); send("loginTailScale", { xEdge: tailScaleXEdge }, resp => { if ("error" in resp) { notifications.error( `Failed to login TailScale: ${resp.error.data || "Unknown error"}`, ); setTailScaleConnectionState("closed"); setTailScaleLoginUrl(""); setTailScaleIP(""); return; } const result = resp.result as TailScaleResponse; const validState = ["closed", "connecting", "connected", "disconnected" , "logined"].includes(result.state) ? result.state as "closed" | "connecting" | "connected" | "disconnected" | "logined" : "closed"; setTailScaleConnectionState(validState); setTailScaleLoginUrl(result.loginUrl); setTailScaleIP(result.ip); }); }, [send, tailScaleXEdge]); const handleTailScaleXEdgeChange = (enabled: boolean) => { setTailScaleXEdge(enabled); }; const handleTailScaleLogout = useCallback(() => { setIsDisconnecting(true); send("logoutTailScale", {}, resp => { if ("error" in resp) { notifications.error( `Failed to logout TailScale: ${resp.error.data || "Unknown error"}`, ); setIsDisconnecting(false); return; } setTailScaleConnectionState("disconnected"); setTailScaleLoginUrl(""); setTailScaleIP(""); setIsDisconnecting(false); }); },[send]); const handleTailScaleCancel = useCallback(() => { setIsDisconnecting(true); send("cancelTailScale", {}, resp => { if ("error" in resp) { notifications.error( `Failed to cancel TailScale: ${resp.error.data || "Unknown error"}`, ); setIsDisconnecting(false); return; } setTailScaleConnectionState("disconnected"); setTailScaleLoginUrl(""); setTailScaleIP(""); setIsDisconnecting(false); }); },[send]); const handleZeroTierLogin = useCallback(() => { setZeroTierConnectionState("connecting"); const currentNetworkID = tempNetworkID; if (!/^[0-9a-f]{16}$/.test(currentNetworkID)) { notifications.error("Please enter a valid Network ID"); setZeroTierConnectionState("disconnected"); return; } setZeroTierNetworkID(currentNetworkID); send("loginZeroTier", { networkID: currentNetworkID }, resp => { if ("error" in resp) { notifications.error( `Failed to login ZeroTier: ${resp.error.data || "Unknown error"}`, ); setZeroTierConnectionState("closed"); setZeroTierNetworkID(""); setZeroTierIP(""); return; } const result = resp.result as ZeroTierResponse; const validState = ["closed", "connecting", "connected", "disconnected" , "logined" ].includes(result.state) ? result.state as "closed" | "connecting" | "connected" | "disconnected" | "logined" : "closed"; setZeroTierConnectionState(validState); setZeroTierIP(result.ip); }); }, [send, tempNetworkID]); const handleZeroTierNetworkIdChange = useCallback((e: React.ChangeEvent) => { const value = e.target.value.trim(); setTempNetworkID(value); }, []); const handleZeroTierLogout = useCallback(() => { send("logoutZeroTier", { networkID: zeroTierNetworkID }, resp => { if ("error" in resp) { notifications.error( `Failed to logout ZeroTier: ${resp.error.data || "Unknown error"}`, ); return; } setZeroTierConnectionState("disconnected"); setZeroTierNetworkID(""); setZeroTierIP(""); }); },[send, zeroTierNetworkID]); const handleStartFrpc = useCallback(() => { send("startFrpc", { frpcToml }, resp => { if ("error" in resp) { notifications.error( `Failed to start frpc: ${resp.error.data || "Unknown error"}`, ); setFrpcRunningStatus({ running: false }); return; } notifications.success("frpc started"); setFrpcRunningStatus({ running: true }); }); }, [send, frpcToml]); const handleStopFrpc = useCallback(() => { send("stopFrpc", {}, resp => { if ("error" in resp) { notifications.error( `Failed to stop frpc: ${resp.error.data || "Unknown error"}`, ); return; } notifications.success("frpc stopped"); setFrpcRunningStatus({ running: false }); }); }, [send]); const handleGetFrpcLog = useCallback(() => { send("getFrpcLog", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get frpc log: ${resp.error.data || "Unknown error"}`, ); setFrpcLog(""); return; } setFrpcLog(resp.result as string); setShowFrpcLogModal(true); }); }, [send]); const getFrpcToml = useCallback(() => { send("getFrpcToml", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get frpc toml: ${resp.error.data || "Unknown error"}`, ); setFrpcToml(""); return; } setFrpcToml(resp.result as string); }); }, [send]); const getFrpcStatus = useCallback(() => { send("getFrpcStatus", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get frpc status: ${resp.error.data || "Unknown error"}`, ); return; } setFrpcRunningStatus(resp.result as FrpcResponse); }); }, [send]); useEffect(() => { getFrpcStatus(); getFrpcToml(); }, [getFrpcStatus, getFrpcToml]); const handleStartEasyTier = useCallback(() => { if (!tempEasyTierNetworkName || !tempEasyTierNetworkSecret || !tempEasyTierNetworkNode) { notifications.error("Please enter EasyTier network name, secret and node"); return; } setEasyTierConfig({ name: tempEasyTierNetworkName, secret: tempEasyTierNetworkSecret, node: tempEasyTierNetworkNode, }); send("startEasyTier", { name: tempEasyTierNetworkName, secret: tempEasyTierNetworkSecret, node: tempEasyTierNetworkNode }, resp => { if ("error" in resp) { notifications.error( `Failed to start EasyTier: ${resp.error.data || "Unknown error"}`, ); setEasyTierRunningStatus({ running: false }); return; } notifications.success("EasyTier started"); setEasyTierRunningStatus({ running: true }); }); }, [send, tempEasyTierNetworkName, tempEasyTierNetworkSecret, tempEasyTierNetworkNode]); const handleStopEasyTier = useCallback(() => { send("stopEasyTier", {}, resp => { if ("error" in resp) { notifications.error( `Failed to stop EasyTier: ${resp.error.data || "Unknown error"}`, ); return; } notifications.success("EasyTier stopped"); setEasyTierRunningStatus({ running: false }); }); }, [send]); const handleGetEasyTierLog = useCallback(() => { send("getEasyTierLog", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get EasyTier log: ${resp.error.data || "Unknown error"}`, ); setEasyTierLog(""); return; } setEasyTierLog(resp.result as string); setShowEasyTierLogModal(true); }); }, [send]); const handleGetEasyTierNodeInfo = useCallback(() => { send("getEasyTierNodeInfo", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get EasyTier Node Info: ${resp.error.data || "Unknown error"}`, ); setEasyTierNodeInfo(""); return; } setEasyTierNodeInfo(resp.result as string); setShowEasyTierNodeInfoModal(true); }); }, [send]); const getEasyTierConfig = useCallback(() => { send("getEasyTierConfig", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get EasyTier config: ${resp.error.data || "Unknown error"}`, ); return; } const result = resp.result as EasyTierResponse; setEasyTierConfig({ name: result.name, secret: result.secret, node: result.node, }); }); }, [send]); const getEasyTierStatus = useCallback(() => { console.log("getEasyTierStatus") send("getEasyTierStatus", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get EasyTier status: ${resp.error.data || "Unknown error"}`, ); return; } setEasyTierRunningStatus(resp.result as EasyTierRunningResponse); }); }, [send]); useEffect(() => { getEasyTierConfig(); getEasyTierStatus(); }, [getEasyTierStatus, getEasyTierConfig]); useEffect(() => { if (tempEasyTierNetworkNodeMode === 'default') { setTempEasyTierNetworkNode('tcp://public.easytier.cn:11010'); } else { setTempEasyTierNetworkNode(''); } }, [tempEasyTierNetworkNodeMode]); const handleStartVnt = useCallback(() => { if (vntConfigMode === "file") { if (!vntConfigFileContent) { notifications.error("Please enter Vnt config file content"); return; } setVntConfig({ config_mode: "file", token: "", device_id: "", name: "", server_addr: "", config_file: vntConfigFileContent, }); send("startVnt", { config_mode: "file", token: "", device_id: "", name: "", server_addr: "", config_file: vntConfigFileContent, model: tempVntModel, password: tempVntPassword, }, resp => { if ("error" in resp) { notifications.error( `Failed to start Vnt: ${resp.error.data || "Unknown error"}`, ); setVntRunningStatus({ running: false }); return; } notifications.success("Vnt started"); setVntRunningStatus({ running: true }); }); } else { if (!tempVntToken) { notifications.error("Please enter Vnt token"); return; } setVntConfig({ config_mode: "params", token: tempVntToken, device_id: tempVntDeviceId, name: tempVntName, server_addr: tempVntServerAddr, config_file: "", model: tempVntModel, password: tempVntPassword, }); send("startVnt", { config_mode: "params", token: tempVntToken, device_id: tempVntDeviceId, name: tempVntName, server_addr: tempVntServerAddr, config_file: "", model: tempVntModel, password: tempVntPassword, }, resp => { if ("error" in resp) { notifications.error( `Failed to start Vnt: ${resp.error.data || "Unknown error"}`, ); setVntRunningStatus({ running: false }); return; } notifications.success("Vnt started"); setVntRunningStatus({ running: true }); }); } }, [send, vntConfigMode, tempVntToken, tempVntDeviceId, tempVntName, tempVntServerAddr, vntConfigFileContent, tempVntModel, tempVntPassword]); const handleStopVnt = useCallback(() => { send("stopVnt", {}, resp => { if ("error" in resp) { notifications.error( `Failed to stop Vnt: ${resp.error.data || "Unknown error"}`, ); return; } notifications.success("Vnt stopped"); setVntRunningStatus({ running: false }); }); }, [send]); const handleGetVntLog = useCallback(() => { send("getVntLog", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get Vnt log: ${resp.error.data || "Unknown error"}`, ); setVntLog(""); return; } setVntLog(resp.result as string); setShowVntLogModal(true); }); }, [send]); const handleGetVntInfo = useCallback(() => { send("getVntInfo", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get Vnt Info: ${resp.error.data || "Unknown error"}`, ); setVntInfo(""); return; } setVntInfo(resp.result as string); setShowVntInfoModal(true); }); }, [send]); const getVntConfig = useCallback(() => { send("getVntConfig", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get Vnt config: ${resp.error.data || "Unknown error"}`, ); return; } const result = resp.result as VntResponse; setVntConfig({ config_mode: result.config_mode || "params", token: result.token, device_id: result.device_id, name: result.name, server_addr: result.server_addr, config_file: result.config_file, model: result.model || "", password: result.password || "", }); setVntConfigMode(result.config_mode || "params"); if (result.config_file) { setVntConfigFileContent(result.config_file); } if (result.model) setTempVntModel(result.model); if (result.password) setTempVntPassword(result.password); }); }, [send]); const getVntConfigFile = useCallback(() => { send("getVntConfigFile", {}, resp => { if ("error" in resp) { return; } const result = resp.result as string; if (result) { setVntConfigFileContent(result); } }); }, [send]); const getVntStatus = useCallback(() => { send("getVntStatus", {}, resp => { if ("error" in resp) { notifications.error( `Failed to get Vnt status: ${resp.error.data || "Unknown error"}`, ); return; } setVntRunningStatus(resp.result as VntRunningResponse); }); }, [send]); useEffect(() => { getVntConfig(); getVntStatus(); getVntConfigFile(); }, [getVntStatus, getVntConfig, getVntConfigFile]); return (
{loaderData?.authMode && ( <>
<> handleTlsModeChange(e.target.value)} disabled={tlsMode === "unknown"} options={[ { value: "disabled", label: $at("Disabled") }, { value: "self-signed", label: $at("Self-signed") }, { value: "custom", label: $at("Custom") }, ]} /> {tlsMode === "custom" && (
handleTlsCertChange(e.target.value)} />
handleTlsKeyChange(e.target.value)} />
)} {loaderData.authMode === "password" ? (
)}
{/* Experimental Badge */}
Experimental
{/* TailScale use xEdge server - checkbox on the right */}
{$at("TailScale use xEdge server")} { if (tailScaleConnectionState !== "disconnected") { notifications.error("TailScale is running and this setting cannot be modified"); return; } handleTailScaleXEdgeChange(e.target.checked); }} />
{tailScaleConnectionState === "connecting" && (

Connecting...

)} {tailScaleConnectionState === "connected" && (
{tailScaleLoginUrl && (

{$at("Login URL:")} LoginUrl

)} {!tailScaleLoginUrl && (

{$at("Wait to obtain the Login URL")}

)}
)} {tailScaleConnectionState === "logined" && (
{/* IP and Quit button on the same line */}
IP: {tailScaleIP}
)} {tailScaleConnectionState === "closed" && (

Connect fail, please retry

)} {((tailScaleConnectionState === "disconnected") || (tailScaleConnectionState === "closed")) && (
), }, { id: "zerotier", label: "ZeroTier", content: (
{/* Experimental Badge */}
Experimental
{zeroTierConnectionState === "connecting" && (

{$at("Connecting...")}

)} {zeroTierConnectionState === "connected" && (
{$at("Network ID")} {zeroTierNetworkID}
)} {zeroTierConnectionState === "logined" && (
{$at("Network ID")} {zeroTierNetworkID}
{$at("Network IP")} {zeroTierIP}
)} {zeroTierConnectionState === "closed" && (

{$at("Connect fail, please retry")}

)} {(zeroTierConnectionState === "disconnected") && (
)}
), }, { id: "easytier", label: "EasyTier", content: (
{ easyTierRunningStatus.running ? (
{$at("Network Node")} {easyTierConfig.node || tempEasyTierNetworkNode}
{$at("Network Name")} {easyTierConfig.name || tempEasyTierNetworkName}
{$at("Network Secret")} {easyTierConfig.secret || tempEasyTierNetworkSecret}
) : (
setTempEasyTierNetworkNodeMode(e.target.value)} options={[ { value: "default", label: $at("Default") }, { value: "custom", label: $at("Custom") }, ]} />
{tempEasyTierNetworkNodeMode === "custom" && (
setTempEasyTierNetworkNode(e.target.value)} placeholder={$at("Enter EasyTier Network Node")} />
)}
setTempEasyTierNetworkName(e.target.value)} placeholder={$at("Enter EasyTier Network Name")} />
setTempEasyTierNetworkSecret(e.target.value)} placeholder={$at("Enter EasyTier Network Secret")} />
)}
), }, { id: "vnt", label: "Vnt", content: (
{ vntRunningStatus.running ? (
{$at("Config Mode")} {vntConfig.config_mode === "file" ? $at("Config File") : $at("Parameters")}
{vntConfig.config_mode === "file" ? (
{$at("Config")} {$at("Using config file")}
) : ( <>
{$at("Token")} {vntConfig.token || tempVntToken}
{(vntConfig.device_id || tempVntDeviceId) && (
{$at("Device ID")} {vntConfig.device_id || tempVntDeviceId}
)} {(vntConfig.name || tempVntName) && (
{$at("Name")} {vntConfig.name || tempVntName}
)} {(vntConfig.server_addr || tempVntServerAddr) && (
{$at("Server Address")} {vntConfig.server_addr || tempVntServerAddr}
)} )}
) : (
{/* Config Mode Selector */}
setVntConfigMode(e.target.value)} options={[ { value: "params", label: $at("Parameters") }, { value: "file", label: $at("Config File") }, ]} />
{vntConfigMode === "file" ? ( // Config File Mode
setVntConfigFileContent(e.target.value)} />
) : ( // Parameters Mode
setTempVntToken(e.target.value)} placeholder={$at("Enter Vnt Token")} />
setTempVntDeviceId(e.target.value)} placeholder={$at("Enter Device ID")} />
setTempVntName(e.target.value)} placeholder={$at("Enter Device Name")} />
setTempVntServerAddr(e.target.value)} placeholder={$at("Enter Server Address")} />
setTempVntModel(e.target.value)} options={[ { value: "aes_gcm", label: "aes_gcm" }, { value: "chacha20_poly1305", label: "chacha20_poly1305" }, { value: "chacha20", label: "chacha20" }, { value: "aes_cbc", label: "aes_cbc" }, { value: "aes_ecb", label: "aes_ecb" }, { value: "sm4_cbc", label: "sm4_cbc" }, { value: "xor", label: "xor" }, ]} />
setTempVntPassword(e.target.value)} placeholder={$at("Enter Vnt Password")} />
)}
)}
), }, { id: "cloudflare", label: "Cloudflare", content: (
{cloudflaredRunningStatus.running ? (
) : ( <>
setCloudflaredToken(e.target.value)} placeholder={$at("Enter Cloudflare Tunnel Token")} />
)}
), }, { id: "frp", label: "Frp", content: (
setFrpcToml(e.target.value)} />
{frpcRunningStatus.running ? (
) : (
), }, ]} />
{ setShowCloudflaredLogModal(false); }} title="Cloudflare Log" description={cloudflaredLog} /> { setShowEasyTierLogModal(false); }} title="EasyTier Log" description={easyTierLog} /> { setShowEasyTierNodeInfoModal(false); }} title="EasyTier Node Info" description={easyTierNodeInfo} /> { setShowFrpcLogModal(false); }} title="Frpc Log" description={frpcLog} /> { setShowVntLogModal(false); }} title="Vnt Log" description={vntLog} /> { setShowVntInfoModal(false); }} title="Vnt Info" description={vntInfo} />
); } SettingsAccessIndexRoute.loader = loader;