feat: shadowsocks

This commit is contained in:
zhom
2026-04-19 19:34:14 +04:00
parent 8284b62e34
commit 3152e0de59
11 changed files with 73 additions and 24 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ use utoipa::ToSchema;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProxySettings {
pub proxy_type: String, // "http", "https", "socks4", or "socks5"
pub proxy_type: String, // "http", "https", "socks4", "socks5", or "ss" (Shadowsocks)
pub host: String,
pub port: u16,
pub username: Option<String>,
+4
View File
@@ -1368,6 +1368,10 @@ impl ProxyManager {
("socks5", rest)
} else if let Some(rest) = line.strip_prefix("socks://") {
("socks5", rest) // Default socks to socks5
} else if let Some(rest) = line.strip_prefix("ss://") {
("ss", rest)
} else if let Some(rest) = line.strip_prefix("shadowsocks://") {
("ss", rest)
} else {
return None;
};
+32 -8
View File
@@ -94,6 +94,19 @@ export function ProxyFormDialog({
return;
}
if (
form.proxy_type === "ss" &&
(!form.username.trim() || !form.password.trim())
) {
toast.error(
t(
"proxies.form.ssCipherRequired",
"Cipher and password are required for Shadowsocks",
),
);
return;
}
setIsSubmitting(true);
try {
const payload = {
@@ -136,7 +149,12 @@ export function ProxyFormDialog({
}, [isSubmitting, onClose]);
const isFormValid =
form.name.trim() && form.host.trim() && form.port > 0 && form.port <= 65535;
form.name.trim() &&
form.host.trim() &&
form.port > 0 &&
form.port <= 65535 &&
(form.proxy_type !== "ss" ||
(form.username.trim() && form.password.trim()));
return (
<Dialog open={isOpen} onOpenChange={handleClose}>
@@ -174,9 +192,9 @@ export function ProxyFormDialog({
<SelectValue placeholder="Select proxy type" />
</SelectTrigger>
<SelectContent>
{["http", "https", "socks4", "socks5"].map((type) => (
{["http", "https", "socks4", "socks5", "ss"].map((type) => (
<SelectItem key={type} value={type}>
{type.toUpperCase()}
{type === "ss" ? "Shadowsocks" : type.toUpperCase()}
</SelectItem>
))}
</SelectContent>
@@ -220,8 +238,9 @@ export function ProxyFormDialog({
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="proxy-username">
{t("proxies.form.username")} (
{t("proxies.form.usernamePlaceholder")})
{form.proxy_type === "ss"
? t("proxies.form.cipher")
: `${t("proxies.form.username")} (${t("proxies.form.usernamePlaceholder")})`}
</Label>
<Input
id="proxy-username"
@@ -229,15 +248,20 @@ export function ProxyFormDialog({
onChange={(e) => {
setForm({ ...form, username: e.target.value });
}}
placeholder={t("proxies.form.usernamePlaceholder")}
placeholder={
form.proxy_type === "ss"
? t("proxies.form.cipherPlaceholder")
: t("proxies.form.usernamePlaceholder")
}
disabled={isSubmitting}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="proxy-password">
{t("proxies.form.password")} (
{t("proxies.form.passwordPlaceholder")})
{form.proxy_type === "ss"
? t("proxies.form.password")
: `${t("proxies.form.password")} (${t("proxies.form.passwordPlaceholder")})`}
</Label>
<Input
id="proxy-password"
+5 -2
View File
@@ -278,13 +278,16 @@
"username": "Username",
"usernamePlaceholder": "Optional",
"password": "Password",
"passwordPlaceholder": "Optional"
"passwordPlaceholder": "Optional",
"cipher": "Cipher",
"cipherPlaceholder": "aes-256-gcm"
},
"types": {
"http": "HTTP",
"https": "HTTPS",
"socks4": "SOCKS4",
"socks5": "SOCKS5"
"socks5": "SOCKS5",
"ss": "Shadowsocks"
},
"tabs": {
"regular": "Regular",
+5 -2
View File
@@ -278,13 +278,16 @@
"username": "Usuario",
"usernamePlaceholder": "Opcional",
"password": "Contraseña",
"passwordPlaceholder": "Opcional"
"passwordPlaceholder": "Opcional",
"cipher": "Cifrado",
"cipherPlaceholder": "aes-256-gcm"
},
"types": {
"http": "HTTP",
"https": "HTTPS",
"socks4": "SOCKS4",
"socks5": "SOCKS5"
"socks5": "SOCKS5",
"ss": "Shadowsocks"
},
"tabs": {
"regular": "Regular",
+5 -2
View File
@@ -278,13 +278,16 @@
"username": "Nom d'utilisateur",
"usernamePlaceholder": "Optionnel",
"password": "Mot de passe",
"passwordPlaceholder": "Optionnel"
"passwordPlaceholder": "Optionnel",
"cipher": "Chiffrement",
"cipherPlaceholder": "aes-256-gcm"
},
"types": {
"http": "HTTP",
"https": "HTTPS",
"socks4": "SOCKS4",
"socks5": "SOCKS5"
"socks5": "SOCKS5",
"ss": "Shadowsocks"
},
"tabs": {
"regular": "Standard",
+5 -2
View File
@@ -278,13 +278,16 @@
"username": "ユーザー名",
"usernamePlaceholder": "任意",
"password": "パスワード",
"passwordPlaceholder": "任意"
"passwordPlaceholder": "任意",
"cipher": "暗号方式",
"cipherPlaceholder": "aes-256-gcm"
},
"types": {
"http": "HTTP",
"https": "HTTPS",
"socks4": "SOCKS4",
"socks5": "SOCKS5"
"socks5": "SOCKS5",
"ss": "Shadowsocks"
},
"tabs": {
"regular": "通常",
+5 -2
View File
@@ -278,13 +278,16 @@
"username": "Usuário",
"usernamePlaceholder": "Opcional",
"password": "Senha",
"passwordPlaceholder": "Opcional"
"passwordPlaceholder": "Opcional",
"cipher": "Cifra",
"cipherPlaceholder": "aes-256-gcm"
},
"types": {
"http": "HTTP",
"https": "HTTPS",
"socks4": "SOCKS4",
"socks5": "SOCKS5"
"socks5": "SOCKS5",
"ss": "Shadowsocks"
},
"tabs": {
"regular": "Regular",
+5 -2
View File
@@ -278,13 +278,16 @@
"username": "Имя пользователя",
"usernamePlaceholder": "Необязательно",
"password": "Пароль",
"passwordPlaceholder": "Необязательно"
"passwordPlaceholder": "Необязательно",
"cipher": "Шифр",
"cipherPlaceholder": "aes-256-gcm"
},
"types": {
"http": "HTTP",
"https": "HTTPS",
"socks4": "SOCKS4",
"socks5": "SOCKS5"
"socks5": "SOCKS5",
"ss": "Shadowsocks"
},
"tabs": {
"regular": "Обычный",
+5 -2
View File
@@ -278,13 +278,16 @@
"username": "用户名",
"usernamePlaceholder": "可选",
"password": "密码",
"passwordPlaceholder": "可选"
"passwordPlaceholder": "可选",
"cipher": "加密方式",
"cipherPlaceholder": "aes-256-gcm"
},
"types": {
"http": "HTTP",
"https": "HTTPS",
"socks4": "SOCKS4",
"socks5": "SOCKS5"
"socks5": "SOCKS5",
"ss": "Shadowsocks"
},
"tabs": {
"regular": "常规",
+1 -1
View File
@@ -1,5 +1,5 @@
export interface ProxySettings {
proxy_type: string; // "http", "https", "socks4", or "socks5"
proxy_type: string; // "http", "https", "socks4", "socks5", or "ss" (Shadowsocks)
host: string;
port: number;
username?: string;