fix: cleanup files
This commit is contained in:
@@ -652,6 +652,7 @@
|
|||||||
"failedToLoadHosts": "Failed to load hosts",
|
"failedToLoadHosts": "Failed to load hosts",
|
||||||
"retry": "Retry",
|
"retry": "Retry",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
|
"optional": "Optional",
|
||||||
"hostsCount": "{{count}} hosts",
|
"hostsCount": "{{count}} hosts",
|
||||||
"importJson": "Import JSON",
|
"importJson": "Import JSON",
|
||||||
"importing": "Importing...",
|
"importing": "Importing...",
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ import {
|
|||||||
import { TerminalPreview } from "@/ui/desktop/apps/terminal/TerminalPreview.tsx";
|
import { TerminalPreview } from "@/ui/desktop/apps/terminal/TerminalPreview.tsx";
|
||||||
import type { TerminalConfig, SSHHost, Credential } from "@/types";
|
import type { TerminalConfig, SSHHost, Credential } from "@/types";
|
||||||
import { Plus, X, Check, ChevronsUpDown, Save } from "lucide-react";
|
import { Plus, X, Check, ChevronsUpDown, Save } from "lucide-react";
|
||||||
import { Socks5ProxyConfig } from "./Socks5ProxyConfig.tsx";
|
|
||||||
|
|
||||||
interface JumpHostItemProps {
|
interface JumpHostItemProps {
|
||||||
jumpHost: { hostId: number };
|
jumpHost: { hostId: number };
|
||||||
@@ -303,9 +302,7 @@ export function HostManagerEditor({
|
|||||||
const [snippets, setSnippets] = useState<
|
const [snippets, setSnippets] = useState<
|
||||||
Array<{ id: number; name: string; content: string }>
|
Array<{ id: number; name: string; content: string }>
|
||||||
>([]);
|
>([]);
|
||||||
const [proxyMode, setProxyMode] = useState<"single" | "chain">(
|
const [proxyMode, setProxyMode] = useState<"single" | "chain">("single");
|
||||||
"single",
|
|
||||||
);
|
|
||||||
|
|
||||||
const [authTab, setAuthTab] = useState<
|
const [authTab, setAuthTab] = useState<
|
||||||
"password" | "key" | "credential" | "none"
|
"password" | "key" | "credential" | "none"
|
||||||
@@ -777,8 +774,7 @@ export function HostManagerEditor({
|
|||||||
| "ssh-rsa-sha2-256"
|
| "ssh-rsa-sha2-256"
|
||||||
| "ssh-rsa-sha2-512") || "auto";
|
| "ssh-rsa-sha2-512") || "auto";
|
||||||
} else if (defaultAuthType === "credential") {
|
} else if (defaultAuthType === "credential") {
|
||||||
formData.credentialId =
|
formData.credentialId = cleanedHost.credentialId;
|
||||||
cleanedHost.credentialId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form.reset(formData as FormData);
|
form.reset(formData as FormData);
|
||||||
@@ -942,13 +938,13 @@ export function HostManagerEditor({
|
|||||||
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
|
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
|
||||||
|
|
||||||
if (savedHost?.id) {
|
if (savedHost?.id) {
|
||||||
const { notifyHostCreatedOrUpdated } = await import(
|
const { notifyHostCreatedOrUpdated } =
|
||||||
"@/ui/main-axios.ts"
|
await import("@/ui/main-axios.ts");
|
||||||
);
|
|
||||||
notifyHostCreatedOrUpdated(savedHost.id);
|
notifyHostCreatedOrUpdated(savedHost.id);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : String(error);
|
||||||
toast.error(t("hosts.failedToSaveHost") + ": " + errorMessage);
|
toast.error(t("hosts.failedToSaveHost") + ": " + errorMessage);
|
||||||
console.error("Failed to save host:", error);
|
console.error("Failed to save host:", error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1481,7 +1477,8 @@ export function HostManagerEditor({
|
|||||||
<span
|
<span
|
||||||
className="truncate"
|
className="truncate"
|
||||||
title={
|
title={
|
||||||
(field.value as File)?.name || t("hosts.upload")
|
(field.value as File)?.name ||
|
||||||
|
t("hosts.upload")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{field.value === "existing_key"
|
{field.value === "existing_key"
|
||||||
@@ -1792,11 +1789,9 @@ export function HostManagerEditor({
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="useSocks5"
|
name="useSocks5"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<FormLabel className="text-base">
|
<FormLabel>{t("hosts.enableSocks5")}</FormLabel>
|
||||||
{t("hosts.enableSocks5")}
|
|
||||||
</FormLabel>
|
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{t("hosts.enableSocks5Description")}
|
{t("hosts.enableSocks5Description")}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
@@ -1812,13 +1807,354 @@ export function HostManagerEditor({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{form.watch("useSocks5") && (
|
{form.watch("useSocks5") && (
|
||||||
<Socks5ProxyConfig
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5ProxyMode")}
|
||||||
|
</FormLabel>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={
|
||||||
|
proxyMode === "single"
|
||||||
|
? "default"
|
||||||
|
: "outline"
|
||||||
|
}
|
||||||
|
onClick={() => setProxyMode("single")}
|
||||||
|
className="flex-1"
|
||||||
|
>
|
||||||
|
{t("hosts.socks5UseSingleProxy")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={
|
||||||
|
proxyMode === "chain"
|
||||||
|
? "default"
|
||||||
|
: "outline"
|
||||||
|
}
|
||||||
|
onClick={() => setProxyMode("chain")}
|
||||||
|
className="flex-1"
|
||||||
|
>
|
||||||
|
{t("hosts.socks5UseProxyChain")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{proxyMode === "single" && (
|
||||||
|
<div className="space-y-4 p-4 border rounded-lg">
|
||||||
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
watch={form.watch}
|
name="socks5Host"
|
||||||
setValue={form.setValue}
|
render={({ field }) => (
|
||||||
proxyMode={proxyMode}
|
<FormItem>
|
||||||
onProxyModeChange={setProxyMode}
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Host")}
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder="proxy.example.com"
|
||||||
|
{...field}
|
||||||
/>
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
{t("hosts.socks5HostDescription")}
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="socks5Port"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Port")}
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="1080"
|
||||||
|
{...field}
|
||||||
|
onChange={(e) =>
|
||||||
|
field.onChange(
|
||||||
|
parseInt(e.target.value) || 1080,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
{t("hosts.socks5PortDescription")}
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="socks5Username"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Username")} (
|
||||||
|
{t("hosts.optional")})
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("hosts.username")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="socks5Password"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Password")} (
|
||||||
|
{t("hosts.optional")})
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<PasswordInput
|
||||||
|
placeholder={t("hosts.password")}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{proxyMode === "chain" && (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5ProxyChain")}
|
||||||
|
</FormLabel>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
const currentChain =
|
||||||
|
form.watch("socks5ProxyChain") || [];
|
||||||
|
form.setValue("socks5ProxyChain", [
|
||||||
|
...currentChain,
|
||||||
|
{
|
||||||
|
host: "",
|
||||||
|
port: 1080,
|
||||||
|
type: 5 as 4 | 5,
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
{t("hosts.addProxyNode")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(form.watch("socks5ProxyChain") || [])
|
||||||
|
.length === 0 && (
|
||||||
|
<div className="text-sm text-muted-foreground text-center p-4 border rounded-lg border-dashed">
|
||||||
|
{t("hosts.noProxyNodes")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(form.watch("socks5ProxyChain") || []).map(
|
||||||
|
(node: any, index: number) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="p-4 border rounded-lg space-y-3 relative"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{t("hosts.proxyNode")} {index + 1}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => {
|
||||||
|
const currentChain =
|
||||||
|
form.watch("socks5ProxyChain") ||
|
||||||
|
[];
|
||||||
|
form.setValue(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
currentChain.filter(
|
||||||
|
(_: any, i: number) =>
|
||||||
|
i !== index,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Host")}
|
||||||
|
</FormLabel>
|
||||||
|
<Input
|
||||||
|
placeholder="proxy.example.com"
|
||||||
|
value={node.host}
|
||||||
|
onChange={(e) => {
|
||||||
|
const currentChain =
|
||||||
|
form.watch(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
) || [];
|
||||||
|
const newChain = [
|
||||||
|
...currentChain,
|
||||||
|
];
|
||||||
|
newChain[index] = {
|
||||||
|
...newChain[index],
|
||||||
|
host: e.target.value,
|
||||||
|
};
|
||||||
|
form.setValue(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
newChain,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Port")}
|
||||||
|
</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="1080"
|
||||||
|
value={node.port}
|
||||||
|
onChange={(e) => {
|
||||||
|
const currentChain =
|
||||||
|
form.watch(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
) || [];
|
||||||
|
const newChain = [
|
||||||
|
...currentChain,
|
||||||
|
];
|
||||||
|
newChain[index] = {
|
||||||
|
...newChain[index],
|
||||||
|
port:
|
||||||
|
parseInt(e.target.value) ||
|
||||||
|
1080,
|
||||||
|
};
|
||||||
|
form.setValue(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
newChain,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.proxyType")}
|
||||||
|
</FormLabel>
|
||||||
|
<Select
|
||||||
|
value={String(node.type)}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
const currentChain =
|
||||||
|
form.watch("socks5ProxyChain") ||
|
||||||
|
[];
|
||||||
|
const newChain = [...currentChain];
|
||||||
|
newChain[index] = {
|
||||||
|
...newChain[index],
|
||||||
|
type: parseInt(value) as 4 | 5,
|
||||||
|
};
|
||||||
|
form.setValue(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
newChain,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="4">
|
||||||
|
SOCKS4
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="5">
|
||||||
|
SOCKS5
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Username")} (
|
||||||
|
{t("hosts.optional")})
|
||||||
|
</FormLabel>
|
||||||
|
<Input
|
||||||
|
placeholder={t("hosts.username")}
|
||||||
|
value={node.username || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
const currentChain =
|
||||||
|
form.watch(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
) || [];
|
||||||
|
const newChain = [
|
||||||
|
...currentChain,
|
||||||
|
];
|
||||||
|
newChain[index] = {
|
||||||
|
...newChain[index],
|
||||||
|
username: e.target.value,
|
||||||
|
};
|
||||||
|
form.setValue(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
newChain,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.socks5Password")} (
|
||||||
|
{t("hosts.optional")})
|
||||||
|
</FormLabel>
|
||||||
|
<PasswordInput
|
||||||
|
placeholder={t("hosts.password")}
|
||||||
|
value={node.password || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
const currentChain =
|
||||||
|
form.watch(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
) || [];
|
||||||
|
const newChain = [
|
||||||
|
...currentChain,
|
||||||
|
];
|
||||||
|
newChain[index] = {
|
||||||
|
...newChain[index],
|
||||||
|
password: e.target.value,
|
||||||
|
};
|
||||||
|
form.setValue(
|
||||||
|
"socks5ProxyChain",
|
||||||
|
newChain,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
@@ -1832,7 +2168,9 @@ export function HostManagerEditor({
|
|||||||
defaultValue={["appearance", "behavior", "advanced"]}
|
defaultValue={["appearance", "behavior", "advanced"]}
|
||||||
>
|
>
|
||||||
<AccordionItem value="appearance">
|
<AccordionItem value="appearance">
|
||||||
<AccordionTrigger>{t("hosts.appearance")}</AccordionTrigger>
|
<AccordionTrigger>
|
||||||
|
{t("hosts.appearance")}
|
||||||
|
</AccordionTrigger>
|
||||||
<AccordionContent className="space-y-4 pt-4">
|
<AccordionContent className="space-y-4 pt-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|||||||
@@ -1,244 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { FormField, FormItem, FormLabel, FormControl, FormDescription } from "@/components/ui/form.tsx";
|
|
||||||
import { Input } from "@/components/ui/input.tsx";
|
|
||||||
import { PasswordInput } from "@/components/ui/password-input.tsx";
|
|
||||||
import { Button } from "@/components/ui/button.tsx";
|
|
||||||
import { Plus, X } from "lucide-react";
|
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx";
|
|
||||||
import type { Control, UseFormWatch, UseFormSetValue } from "react-hook-form";
|
|
||||||
import type { ProxyNode } from "@/types";
|
|
||||||
|
|
||||||
interface Socks5ProxyConfigProps {
|
|
||||||
control: Control<any>;
|
|
||||||
watch: UseFormWatch<any>;
|
|
||||||
setValue: UseFormSetValue<any>;
|
|
||||||
proxyMode: "single" | "chain";
|
|
||||||
onProxyModeChange: (mode: "single" | "chain") => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Socks5ProxyConfig({
|
|
||||||
control,
|
|
||||||
watch,
|
|
||||||
setValue,
|
|
||||||
proxyMode,
|
|
||||||
onProxyModeChange,
|
|
||||||
}: Socks5ProxyConfigProps) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const proxyChain = watch("socks5ProxyChain") || [];
|
|
||||||
|
|
||||||
const addProxyNode = () => {
|
|
||||||
const currentChain = watch("socks5ProxyChain") || [];
|
|
||||||
const newNode: ProxyNode = {
|
|
||||||
host: "",
|
|
||||||
port: 1080,
|
|
||||||
type: 5,
|
|
||||||
username: "",
|
|
||||||
password: "",
|
|
||||||
};
|
|
||||||
setValue("socks5ProxyChain", [...currentChain, newNode]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeProxyNode = (index: number) => {
|
|
||||||
const currentChain = watch("socks5ProxyChain") || [];
|
|
||||||
const newChain = currentChain.filter((_: any, i: number) => i !== index);
|
|
||||||
setValue("socks5ProxyChain", newChain);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateProxyNode = (index: number, field: keyof ProxyNode, value: any) => {
|
|
||||||
const currentChain = watch("socks5ProxyChain") || [];
|
|
||||||
const newChain = [...currentChain];
|
|
||||||
newChain[index] = { ...newChain[index], [field]: value };
|
|
||||||
setValue("socks5ProxyChain", newChain);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<FormLabel>{t("hosts.socks5ProxyMode")}</FormLabel>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant={proxyMode === "single" ? "default" : "outline"}
|
|
||||||
onClick={() => onProxyModeChange("single")}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
{t("hosts.socks5UseSingleProxy")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant={proxyMode === "chain" ? "default" : "outline"}
|
|
||||||
onClick={() => onProxyModeChange("chain")}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
{t("hosts.socks5UseProxyChain")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{proxyMode === "single" && (
|
|
||||||
<div className="space-y-4 p-4 border rounded-lg">
|
|
||||||
<FormField
|
|
||||||
control={control}
|
|
||||||
name="socks5Host"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t("hosts.socks5Host")}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="proxy.example.com" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription>
|
|
||||||
{t("hosts.socks5HostDescription")}
|
|
||||||
</FormDescription>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={control}
|
|
||||||
name="socks5Port"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t("hosts.socks5Port")}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="1080"
|
|
||||||
{...field}
|
|
||||||
onChange={(e) => field.onChange(parseInt(e.target.value) || 1080)}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription>
|
|
||||||
{t("hosts.socks5PortDescription")}
|
|
||||||
</FormDescription>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={control}
|
|
||||||
name="socks5Username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t("hosts.socks5Username")} ({t("hosts.optional")})</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder={t("hosts.username")} {...field} />
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={control}
|
|
||||||
name="socks5Password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t("hosts.socks5Password")} ({t("hosts.optional")})</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<PasswordInput placeholder={t("hosts.password")} {...field} />
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{proxyMode === "chain" && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<FormLabel>{t("hosts.socks5ProxyChain")}</FormLabel>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={addProxyNode}
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
|
||||||
{t("hosts.addProxyNode")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{proxyChain.length === 0 && (
|
|
||||||
<div className="text-sm text-muted-foreground text-center p-4 border rounded-lg border-dashed">
|
|
||||||
{t("hosts.noProxyNodes")}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{proxyChain.map((node: ProxyNode, index: number) => (
|
|
||||||
<div key={index} className="p-4 border rounded-lg space-y-3 relative">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{t("hosts.proxyNode")} {index + 1}
|
|
||||||
</span>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => removeProxyNode(index)}
|
|
||||||
>
|
|
||||||
<X className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<FormLabel>{t("hosts.socks5Host")}</FormLabel>
|
|
||||||
<Input
|
|
||||||
placeholder="proxy.example.com"
|
|
||||||
value={node.host}
|
|
||||||
onChange={(e) => updateProxyNode(index, "host", e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<FormLabel>{t("hosts.socks5Port")}</FormLabel>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="1080"
|
|
||||||
value={node.port}
|
|
||||||
onChange={(e) => updateProxyNode(index, "port", parseInt(e.target.value) || 1080)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<FormLabel>{t("hosts.proxyType")}</FormLabel>
|
|
||||||
<Select
|
|
||||||
value={String(node.type)}
|
|
||||||
onValueChange={(value) => updateProxyNode(index, "type", parseInt(value) as 4 | 5)}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="4">SOCKS4</SelectItem>
|
|
||||||
<SelectItem value="5">SOCKS5</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<FormLabel>{t("hosts.socks5Username")} ({t("hosts.optional")})</FormLabel>
|
|
||||||
<Input
|
|
||||||
placeholder={t("hosts.username")}
|
|
||||||
value={node.username || ""}
|
|
||||||
onChange={(e) => updateProxyNode(index, "username", e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<FormLabel>{t("hosts.socks5Password")} ({t("hosts.optional")})</FormLabel>
|
|
||||||
<PasswordInput
|
|
||||||
placeholder={t("hosts.password")}
|
|
||||||
value={node.password || ""}
|
|
||||||
onChange={(e) => updateProxyNode(index, "password", e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user