Compare commits

...

16 Commits

Author SHA1 Message Date
Kirill Sobakin
5fc3c95928 Merge pull request #262 from itdoginfo/feat/hy2
Feat/hy2
2025-12-01 17:44:40 +03:00
divocat
dd3e70153a fix: correct small points 2025-12-01 16:38:26 +02:00
divocat
622e092317 feat: add hy2 validator 2025-11-30 18:35:06 +02:00
Kirill Sobakin
c045f8f224 Add grpc mode example from #259 2025-11-28 00:44:21 +03:00
Kirill Sobakin
b45088dad7 Merge pull request #259 from kokoc26/main
Feat: service_name for gRPC
2025-11-28 00:43:34 +03:00
Andrey Petelin
82345047cb feat: Add Hysteria2 outbound support 2025-11-26 21:04:46 +05:00
Andrey Petelin
0a4ed367bc refactor: add url_get_scheme and simplify url_get_host/url_get_port using parameter expansion 2025-11-26 21:01:33 +05:00
Andrey Petelin
c3f322ae61 Merge branch 'main' into feat/hy2 2025-11-26 17:06:27 +05:00
Kokoc
eb9239696e feat: add support for optional gRPC service name in outbound transport configuration 2025-11-26 14:52:13 +03:00
Kirill Sobakin
5b3421498e Merge pull request #258 from itdoginfo/refactor/dnsmasq
Refactor/dnsmasq
2025-11-26 14:14:02 +03:00
Andrey Petelin
6a48a060e1 refactor: remove sing-box start exit check 2025-11-26 16:01:41 +05:00
Andrey Petelin
14f704fcb8 fix: use echolog for sing-box start failure 2025-11-26 15:47:12 +05:00
Andrey Petelin
ff43f477e9 chore: restore shutdown_correctly logic 2025-11-26 14:14:27 +05:00
Andrey Petelin
576e58fd17 chore: restore start_main and stop_main; have reload call them instead of full start/stop 2025-11-26 13:56:10 +05:00
Andrey Petelin
d72c98a254 chore: clarify and standardize argument type annotations and optional flags 2025-11-26 10:14:06 +05:00
Kirill Sobakin
352d10a047 Fix: HY2 links 2025-11-25 11:32:50 +03:00
15 changed files with 892 additions and 231 deletions

View File

@@ -34,6 +34,7 @@ vless://4d21ce62-8723-4c4d-93e3-d586b107aa40@127.0.0.1:51394?type=ws&encryption=
# gRPC # gRPC
vless://974b39e3-f7bf-42b9-933c-16699c635e77@127.0.0.1:15633?type=grpc&encryption=none&serviceName=TunService&authority=&security=none#vless-gRPC-none vless://974b39e3-f7bf-42b9-933c-16699c635e77@127.0.0.1:15633?type=grpc&encryption=none&serviceName=TunService&authority=&security=none#vless-gRPC-none
vless://651e7eca-5152-46f1-baf2-d502e0af7b27@127.0.0.1:28535?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=reality&pbk=nhZ7NiKfcqESa5ZeBFfsq9o18W-OWOAHLln9UmuVXSk&fp=chrome&sni=google.com&sid=11cbaeaa&spx=%2F#vless-gRPC-reality vless://651e7eca-5152-46f1-baf2-d502e0af7b27@127.0.0.1:28535?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=reality&pbk=nhZ7NiKfcqESa5ZeBFfsq9o18W-OWOAHLln9UmuVXSk&fp=chrome&sni=google.com&sid=11cbaeaa&spx=%2F#vless-gRPC-reality
vless://221ff905-b783-41a0-a6a6-8089eaf3b34b@abc.def.xyz:443?security=reality&type=grpc&headerType=&authority=abc.def.xyz&serviceName=name&mode=gun&sni=abc.def.xyz&fp=chrome&pbk=C3nhDJw02ZU_rjx4GbC54Sp79-ysF5lWIQVWdY4FOnE&sid=#vless-gRPC-reality-mode
vless://af1f8b5f-26c9-4fe8-8ce7-6d6366c5c9ce@127.0.0.1:47904?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=google.com#vless-gRPC-tls vless://af1f8b5f-26c9-4fe8-8ce7-6d6366c5c9ce@127.0.0.1:47904?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=google.com#vless-gRPC-tls
vless://95f2c4bb-abcb-47ba-bfad-e181c03e4659@127.0.0.1:34530?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&allowInsecure=1&sni=google.com#vless-gRPC-tls-insecure vless://95f2c4bb-abcb-47ba-bfad-e181c03e4659@127.0.0.1:34530?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&allowInsecure=1&sni=google.com#vless-gRPC-tls-insecure
vless://bd39490f-9a4f-49b2-96b6-824190cf89e9@127.0.0.1:27779?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=google.com&ech=AF3%2BDQBZAAAgACBc%2FiNdo4QkTt9eQCQgkOiJVSfA9G6UWAyipaBFtBD%2FVQAkAAEAAQABAAIAAQADAAIAAQACAAIAAgADAAMAAQADAAIAAwADAApnb29nbGUuY29tAAA%3D#vless-gRPC-tls-ech vless://bd39490f-9a4f-49b2-96b6-824190cf89e9@127.0.0.1:27779?type=grpc&encryption=none&serviceName=TunService&authority=authority&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=google.com&ech=AF3%2BDQBZAAAgACBc%2FiNdo4QkTt9eQCQgkOiJVSfA9G6UWAyipaBFtBD%2FVQAkAAEAAQABAAIAAQADAAIAAQACAAIAAgADAAMAAQADAAIAAwADAApnb29nbGUuY29tAAA%3D#vless-gRPC-tls-ech
@@ -87,13 +88,9 @@ trojan://VEetltxLtw@127.0.0.1:59072?type=xhttp&path=%2Fxhttppath&host=google.com
hysteria2:// hysteria2://
``` ```
# Basic (no authentication)
hysteria2://127.0.0.1:443/#hysteria2-basic
hysteria2://127.0.0.1:443/?insecure=1#hysteria2-basic-insecure
# With password # With password
hysteria2://password@example.com:443/#hysteria2-password hysteria2://password@example.com:443/#hysteria2-password
hysteria2://password@example.com:443/?insecure=0#hysteria2-password-insecure hysteria2://password@example.com:443/?insecure=1#hysteria2-password-insecure
# With SNI # With SNI
hysteria2://password@example.com:443/?sni=example.com#hysteria2-password-sni hysteria2://password@example.com:443/?sni=example.com#hysteria2-password-sni
@@ -102,18 +99,14 @@ hysteria2://password@example.com:443/?sni=example.com#hysteria2-password-sni
hysteria2://password@example.com:443/?obfs=salamander&obfs-password=obfspassword#hysteria2-obfs hysteria2://password@example.com:443/?obfs=salamander&obfs-password=obfspassword#hysteria2-obfs
# All parameters combined # All parameters combined
hysteria2://mypassword@example.com:8443/?sni=example.com&obfs=salamander&obfs-password=obfspass&insecure=0#hysteria2-all-params hysteria2://mypassword@example.com:8443/?sni=example.com&obfs=salamander&obfs-password=obfspass&insecure=1#hysteria2-all-params
``` ```
hy2:// hy2://
``` ```
# Basic (no authentication)
hy2://127.0.0.1:443/#hysteria2-basic
hy2://127.0.0.1:443/?insecure=1#hysteria2-basic-insecure
# With password # With password
hy2://password@example.com:443/#hysteria2-password hy2://password@example.com:443/#hysteria2-password
hy2://password@example.com:443/?insecure=0#hysteria2-password-insecure hy2://password@example.com:443/?insecure=1#hysteria2-password-insecure
# With SNI # With SNI
hy2://password@example.com:443/?sni=example.com#hysteria2-password-sni hy2://password@example.com:443/?sni=example.com#hysteria2-password-sni
@@ -122,5 +115,5 @@ hy2://password@example.com:443/?sni=example.com#hysteria2-password-sni
hy2://password@example.com:443/?obfs=salamander&obfs-password=obfspassword#hysteria2-obfs hy2://password@example.com:443/?obfs=salamander&obfs-password=obfspassword#hysteria2-obfs
# All parameters combined # All parameters combined
hy2://mypassword@example.com:8443/?sni=example.com&obfs=salamander&obfs-password=obfspass&insecure=0#hysteria2-all-params hy2://mypassword@example.com:8443/?sni=example.com&obfs=salamander&obfs-password=obfspass&insecure=1#hysteria2-all-params
``` ```

View File

@@ -3,35 +3,35 @@
"call": "✔ Enabled", "call": "✔ Enabled",
"key": "✔ Enabled", "key": "✔ Enabled",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:342" "src/podkop/tabs/dashboard/initController.ts:345"
] ]
}, },
{ {
"call": "✔ Running", "call": "✔ Running",
"key": "✔ Running", "key": "✔ Running",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:353" "src/podkop/tabs/dashboard/initController.ts:356"
] ]
}, },
{ {
"call": "✘ Disabled", "call": "✘ Disabled",
"key": "✘ Disabled", "key": "✘ Disabled",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:343" "src/podkop/tabs/dashboard/initController.ts:346"
] ]
}, },
{ {
"call": "✘ Stopped", "call": "✘ Stopped",
"key": "✘ Stopped", "key": "✘ Stopped",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:354" "src/podkop/tabs/dashboard/initController.ts:357"
] ]
}, },
{ {
"call": "Active Connections", "call": "Active Connections",
"key": "Active Connections", "key": "Active Connections",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:304" "src/podkop/tabs/dashboard/initController.ts:307"
] ]
}, },
{ {
@@ -379,8 +379,8 @@
"call": "Downlink", "call": "Downlink",
"key": "Downlink", "key": "Downlink",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:238", "src/podkop/tabs/dashboard/initController.ts:241",
"src/podkop/tabs/dashboard/initController.ts:272" "src/podkop/tabs/dashboard/initController.ts:275"
] ]
}, },
{ {
@@ -637,6 +637,97 @@
"src/validators/validateSubnet.ts:11" "src/validators/validateSubnet.ts:11"
] ]
}, },
{
"call": "Invalid HY2 URL: insecure must be 0 or 1",
"key": "Invalid HY2 URL: insecure must be 0 or 1",
"places": [
"src/validators/validateHysteriaUrl.ts:76"
]
},
{
"call": "Invalid HY2 URL: invalid port number",
"key": "Invalid HY2 URL: invalid port number",
"places": [
"src/validators/validateHysteriaUrl.ts:62"
]
},
{
"call": "Invalid HY2 URL: missing credentials/server",
"key": "Invalid HY2 URL: missing credentials/server",
"places": [
"src/validators/validateHysteriaUrl.ts:32"
]
},
{
"call": "Invalid HY2 URL: missing host",
"key": "Invalid HY2 URL: missing host",
"places": [
"src/validators/validateHysteriaUrl.ts:49"
]
},
{
"call": "Invalid HY2 URL: missing host & port",
"key": "Invalid HY2 URL: missing host & port",
"places": [
"src/validators/validateHysteriaUrl.ts:43"
]
},
{
"call": "Invalid HY2 URL: missing password",
"key": "Invalid HY2 URL: missing password",
"places": [
"src/validators/validateHysteriaUrl.ts:38"
]
},
{
"call": "Invalid HY2 URL: missing port",
"key": "Invalid HY2 URL: missing port",
"places": [
"src/validators/validateHysteriaUrl.ts:53"
]
},
{
"call": "Invalid HY2 URL: must not contain spaces",
"key": "Invalid HY2 URL: must not contain spaces",
"places": [
"src/validators/validateHysteriaUrl.ts:19"
]
},
{
"call": "Invalid HY2 URL: must start with hysteria2:// or hy2://",
"key": "Invalid HY2 URL: must start with hysteria2:// or hy2://",
"places": [
"src/validators/validateHysteriaUrl.ts:12"
]
},
{
"call": "Invalid HY2 URL: obfs-password required when obfs is set",
"key": "Invalid HY2 URL: obfs-password required when obfs is set",
"places": [
"src/validators/validateHysteriaUrl.ts:99"
]
},
{
"call": "Invalid HY2 URL: parsing failed",
"key": "Invalid HY2 URL: parsing failed",
"places": [
"src/validators/validateHysteriaUrl.ts:113"
]
},
{
"call": "Invalid HY2 URL: sni cannot be empty",
"key": "Invalid HY2 URL: sni cannot be empty",
"places": [
"src/validators/validateHysteriaUrl.ts:106"
]
},
{
"call": "Invalid HY2 URL: unsupported obfs type",
"key": "Invalid HY2 URL: unsupported obfs type",
"places": [
"src/validators/validateHysteriaUrl.ts:88"
]
},
{ {
"call": "Invalid IP address", "call": "Invalid IP address",
"key": "Invalid IP address", "key": "Invalid IP address",
@@ -880,7 +971,7 @@
"call": "Memory Usage", "call": "Memory Usage",
"key": "Memory Usage", "key": "Memory Usage",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:308" "src/podkop/tabs/dashboard/initController.ts:311"
] ]
}, },
{ {
@@ -1023,7 +1114,7 @@
"call": "Podkop", "call": "Podkop",
"key": "Podkop", "key": "Podkop",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:340" "src/podkop/tabs/dashboard/initController.ts:343"
] ]
}, },
{ {
@@ -1290,7 +1381,7 @@
"call": "Services info", "call": "Services info",
"key": "Services info", "key": "Services info",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:337" "src/podkop/tabs/dashboard/initController.ts:340"
] ]
}, },
{ {
@@ -1312,7 +1403,7 @@
"call": "Sing-box", "call": "Sing-box",
"key": "Sing-box", "key": "Sing-box",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:351" "src/podkop/tabs/dashboard/initController.ts:354"
] ]
}, },
{ {
@@ -1425,7 +1516,7 @@
"call": "System info", "call": "System info",
"key": "System info", "key": "System info",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:301" "src/podkop/tabs/dashboard/initController.ts:304"
] ]
}, },
{ {
@@ -1453,13 +1544,7 @@
"call": "Text List", "call": "Text List",
"key": "Text List", "key": "Text List",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:368" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:368",
]
},
{
"call": "Text List (comma/space/newline separated)",
"key": "Text List (comma/space/newline separated)",
"places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:448" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:448"
] ]
}, },
@@ -1502,14 +1587,14 @@
"call": "Traffic", "call": "Traffic",
"key": "Traffic", "key": "Traffic",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:235" "src/podkop/tabs/dashboard/initController.ts:238"
] ]
}, },
{ {
"call": "Traffic Total", "call": "Traffic Total",
"key": "Traffic Total", "key": "Traffic Total",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:265" "src/podkop/tabs/dashboard/initController.ts:268"
] ]
}, },
{ {
@@ -1572,15 +1657,15 @@
"call": "Uplink", "call": "Uplink",
"key": "Uplink", "key": "Uplink",
"places": [ "places": [
"src/podkop/tabs/dashboard/initController.ts:237", "src/podkop/tabs/dashboard/initController.ts:240",
"src/podkop/tabs/dashboard/initController.ts:268" "src/podkop/tabs/dashboard/initController.ts:271"
] ]
}, },
{ {
"call": "URL must start with vless://, ss://, trojan://, or socks4/5://", "call": "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://",
"key": "URL must start with vless://, ss://, trojan://, or socks4/5://", "key": "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://",
"places": [ "places": [
"src/validators/validateProxyUrl.ts:29" "src/validators/validateProxyUrl.ts:37"
] ]
}, },
{ {
@@ -1675,6 +1760,7 @@
"src/validators/validateDns.ts:18", "src/validators/validateDns.ts:18",
"src/validators/validateDomain.ts:13", "src/validators/validateDomain.ts:13",
"src/validators/validateDomain.ts:30", "src/validators/validateDomain.ts:30",
"src/validators/validateHysteriaUrl.ts:111",
"src/validators/validateIp.ts:8", "src/validators/validateIp.ts:8",
"src/validators/validateOutboundJson.ts:7", "src/validators/validateOutboundJson.ts:7",
"src/validators/validatePath.ts:16", "src/validators/validatePath.ts:16",

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PODKOP\n" "Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-06 14:19+0200\n" "POT-Creation-Date: 2025-12-01 14:30+0200\n"
"PO-Revision-Date: 2025-11-06 14:19+0200\n" "PO-Revision-Date: 2025-12-01 14:30+0200\n"
"Last-Translator: divocat <divocatt@gmail.com>\n" "Last-Translator: divocat <divocatt@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
@@ -16,23 +16,23 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: src/podkop/tabs/dashboard/initController.ts:342 #: src/podkop/tabs/dashboard/initController.ts:345
msgid "✔ Enabled" msgid "✔ Enabled"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:353 #: src/podkop/tabs/dashboard/initController.ts:356
msgid "✔ Running" msgid "✔ Running"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:343 #: src/podkop/tabs/dashboard/initController.ts:346
msgid "✘ Disabled" msgid "✘ Disabled"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:354 #: src/podkop/tabs/dashboard/initController.ts:357
msgid "✘ Stopped" msgid "✘ Stopped"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:304 #: src/podkop/tabs/dashboard/initController.ts:307
msgid "Active Connections" msgid "Active Connections"
msgstr "" msgstr ""
@@ -236,8 +236,8 @@ msgstr ""
msgid "Dont Touch My DHCP!" msgid "Dont Touch My DHCP!"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:238 #: src/podkop/tabs/dashboard/initController.ts:241
#: src/podkop/tabs/dashboard/initController.ts:272 #: src/podkop/tabs/dashboard/initController.ts:275
msgid "Downlink" msgid "Downlink"
msgstr "" msgstr ""
@@ -390,6 +390,58 @@ msgstr ""
msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y" msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
msgstr "" msgstr ""
#: src/validators/validateHysteriaUrl.ts:76
msgid "Invalid HY2 URL: insecure must be 0 or 1"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:62
msgid "Invalid HY2 URL: invalid port number"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:32
msgid "Invalid HY2 URL: missing credentials/server"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:49
msgid "Invalid HY2 URL: missing host"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:43
msgid "Invalid HY2 URL: missing host & port"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:38
msgid "Invalid HY2 URL: missing password"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:53
msgid "Invalid HY2 URL: missing port"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:19
msgid "Invalid HY2 URL: must not contain spaces"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:12
msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:99
msgid "Invalid HY2 URL: obfs-password required when obfs is set"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:113
msgid "Invalid HY2 URL: parsing failed"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:106
msgid "Invalid HY2 URL: sni cannot be empty"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:88
msgid "Invalid HY2 URL: unsupported obfs type"
msgstr ""
#: src/validators/validateIp.ts:11 #: src/validators/validateIp.ts:11
msgid "Invalid IP address" msgid "Invalid IP address"
msgstr "" msgstr ""
@@ -527,7 +579,7 @@ msgstr ""
msgid "Main DNS" msgid "Main DNS"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:308 #: src/podkop/tabs/dashboard/initController.ts:311
msgid "Memory Usage" msgid "Memory Usage"
msgstr "" msgstr ""
@@ -613,7 +665,7 @@ msgstr ""
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:340 #: src/podkop/tabs/dashboard/initController.ts:343
msgid "Podkop" msgid "Podkop"
msgstr "" msgstr ""
@@ -766,7 +818,7 @@ msgstr ""
msgid "Select the WAN interfaces to be monitored" msgid "Select the WAN interfaces to be monitored"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:337 #: src/podkop/tabs/dashboard/initController.ts:340
msgid "Services info" msgid "Services info"
msgstr "" msgstr ""
@@ -779,7 +831,7 @@ msgstr ""
msgid "Show sing-box config" msgid "Show sing-box config"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:351 #: src/podkop/tabs/dashboard/initController.ts:354
msgid "Sing-box" msgid "Sing-box"
msgstr "" msgstr ""
@@ -844,7 +896,7 @@ msgstr ""
msgid "Successfully copied!" msgid "Successfully copied!"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:301 #: src/podkop/tabs/dashboard/initController.ts:304
msgid "System info" msgid "System info"
msgstr "" msgstr ""
@@ -861,11 +913,8 @@ msgid "Test latency"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:368 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:368
msgid "Text List"
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:448 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:448
msgid "Text List (comma/space/newline separated)" msgid "Text List"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:46 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:46
@@ -888,11 +937,11 @@ msgstr ""
msgid "Time in seconds for DNS record caching (default: 60)" msgid "Time in seconds for DNS record caching (default: 60)"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:235 #: src/podkop/tabs/dashboard/initController.ts:238
msgid "Traffic" msgid "Traffic"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:265 #: src/podkop/tabs/dashboard/initController.ts:268
msgid "Traffic Total" msgid "Traffic Total"
msgstr "" msgstr ""
@@ -931,13 +980,13 @@ msgstr ""
msgid "Unknown error" msgid "Unknown error"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:237 #: src/podkop/tabs/dashboard/initController.ts:240
#: src/podkop/tabs/dashboard/initController.ts:268 #: src/podkop/tabs/dashboard/initController.ts:271
msgid "Uplink" msgid "Uplink"
msgstr "" msgstr ""
#: src/validators/validateProxyUrl.ts:29 #: src/validators/validateProxyUrl.ts:37
msgid "URL must start with vless://, ss://, trojan://, or socks4/5://" msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
msgstr "" msgstr ""
#: src/validators/validateUrl.ts:17 #: src/validators/validateUrl.ts:17
@@ -992,6 +1041,7 @@ msgstr ""
#: src/validators/validateDns.ts:18 #: src/validators/validateDns.ts:18
#: src/validators/validateDomain.ts:13 #: src/validators/validateDomain.ts:13
#: src/validators/validateDomain.ts:30 #: src/validators/validateDomain.ts:30
#: src/validators/validateHysteriaUrl.ts:111
#: src/validators/validateIp.ts:8 #: src/validators/validateIp.ts:8
#: src/validators/validateOutboundJson.ts:7 #: src/validators/validateOutboundJson.ts:7
#: src/validators/validatePath.ts:16 #: src/validators/validatePath.ts:16

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PODKOP\n" "Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-06 16:19+0200\n" "POT-Creation-Date: 2025-12-01 16:30+0200\n"
"PO-Revision-Date: 2025-11-06 16:19+0200\n" "PO-Revision-Date: 2025-12-01 16:30+0200\n"
"Last-Translator: divocat\n" "Last-Translator: divocat\n"
"Language-Team: none\n" "Language-Team: none\n"
"Language: ru\n" "Language: ru\n"
@@ -281,6 +281,45 @@ msgstr "Неверный домен"
msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y" msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
msgstr "Неверный формат. Используйте X.X.X.X или X.X.X.X/Y" msgstr "Неверный формат. Используйте X.X.X.X или X.X.X.X/Y"
msgid "Invalid HY2 URL: insecure must be 0 or 1"
msgstr "Неверный URL Hysteria2: параметр insecure должен быть 0 или 1"
msgid "Invalid HY2 URL: invalid port number"
msgstr "Неверный URL Hysteria2: неверный номер порта"
msgid "Invalid HY2 URL: missing credentials/server"
msgstr "Неверный URL Hysteria2: отсутствуют учетные данные/сервер"
msgid "Invalid HY2 URL: missing host"
msgstr "Неверный URL Hysteria2: отсутствует хост"
msgid "Invalid HY2 URL: missing host & port"
msgstr "Неверный URL Hysteria2: отсутствуют хост и порт"
msgid "Invalid HY2 URL: missing password"
msgstr "Неверный URL Hysteria2: отсутствует пароль"
msgid "Invalid HY2 URL: missing port"
msgstr "Неверный URL Hysteria2: отсутствует порт"
msgid "Invalid HY2 URL: must not contain spaces"
msgstr "Неверный URL Hysteria2: не должен содержать пробелов"
msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
msgstr "Неверный URL Hysteria2: должен начинаться с hysteria2:// или hy2://"
msgid "Invalid HY2 URL: obfs-password required when obfs is set"
msgstr "Неверный URL Hysteria2: требуется obfs-password, когда установлен obfs"
msgid "Invalid HY2 URL: parsing failed"
msgstr "Неверный URL Hysteria2: ошибка разбора"
msgid "Invalid HY2 URL: sni cannot be empty"
msgstr "Неверный URL Hysteria2: sni не может быть пустым"
msgid "Invalid HY2 URL: unsupported obfs type"
msgstr "Неверный URL Hysteria2: неподдерживаемый тип obfs"
msgid "Invalid IP address" msgid "Invalid IP address"
msgstr "Неверный IP-адрес" msgstr "Неверный IP-адрес"
@@ -626,9 +665,6 @@ msgstr "Тестирование задержки"
msgid "Text List" msgid "Text List"
msgstr "Текстовый список" msgstr "Текстовый список"
msgid "Text List (comma/space/newline separated)"
msgstr "Текстовый список (через запятую, пробел или новую строку)"
msgid "The DNS server used to look up the IP address of an upstream DNS server" msgid "The DNS server used to look up the IP address of an upstream DNS server"
msgstr "DNS-сервер, используемый для поиска IP-адреса вышестоящего DNS-сервера" msgstr "DNS-сервер, используемый для поиска IP-адреса вышестоящего DNS-сервера"
@@ -674,8 +710,8 @@ msgstr "Неизвестная ошибка"
msgid "Uplink" msgid "Uplink"
msgstr "Исходящий" msgstr "Исходящий"
msgid "URL must start with vless://, ss://, trojan://, or socks4/5://" msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
msgstr "URL должен начинаться с vless://, ss://, trojan:// или socks4/5://" msgstr "URL должен начинаться с vless://, ss://, trojan://, socks4/5:// или hysteria2:// hy2://"
msgid "URL must use one of the following protocols:" msgid "URL must use one of the following protocols:"
msgstr "URL должен использовать один из следующих протоколов:" msgstr "URL должен использовать один из следующих протоколов:"

View File

@@ -0,0 +1,74 @@
import { describe, it, expect } from 'vitest';
import { validateHysteria2Url } from '../validateHysteriaUrl.js';
const validUrls = [
// Basic password-only
['password basic', 'hysteria2://pass@example.com:443/#hy2-basic'],
// insecure=1
[
'insecure allowed',
'hysteria2://pass@example.com:443/?insecure=1#hy2-insecure',
],
// SNI
['SNI param', 'hysteria2://pass@example.com:443/?sni=google.com#hy2-sni'],
// Obfuscation
[
'Obfs + password',
'hysteria2://mypassword@1.1.1.1:8443/?obfs=salamander&obfs-password=abc123#hy2-obfs',
],
// All params
[
'All options combined',
'hysteria2://pw@8.8.8.8:8443/?sni=example.com&obfs=salamander&obfs-password=hello&insecure=1#hy2-full',
],
// Explicit obfs=none (valid)
['obfs none = ok', 'hysteria2://pw@example.com:443/?obfs=none#hy2-none'],
];
const invalidUrls = [
['No prefix', 'pw@example.com:443'],
['Missing password', 'hysteria2://@example.com:443/'],
['Missing host', 'hysteria2://pw@:443/'],
['Missing port', 'hysteria2://pw@example.com/'],
['Non-numeric port', 'hysteria2://pw@example.com:port/'],
['Port out of range', 'hysteria2://pw@example.com:99999/'],
// Obfuscation errors
['Unknown obfs type', 'hysteria2://pw@example.com:443/?obfs=weird'],
[
'obfs without obfs-password',
'hysteria2://pw@example.com:443/?obfs=salamander',
],
// insecure only accepts 0/1
['invalid insecure', 'hysteria2://pw@example.com:443/?insecure=5'],
// SNI empty
['empty sni', 'hysteria2://pw@example.com:443/?sni='],
];
describe('validateHysteria2Url', () => {
describe.each(validUrls)('Valid HY2 URL: %s', (_desc, url) => {
it(`returns valid=true for "${url}"`, () => {
const res = validateHysteria2Url(url);
expect(res.valid).toBe(true);
});
});
describe.each(invalidUrls)('Invalid HY2 URL: %s', (_desc, url) => {
it(`returns valid=false for "${url}"`, () => {
const res = validateHysteria2Url(url);
expect(res.valid).toBe(false);
});
});
it('detects invalid port range', () => {
const res = validateHysteria2Url('hysteria2://pw@example.com:70000/');
expect(res.valid).toBe(false);
});
});

View File

@@ -0,0 +1,117 @@
import { ValidationResult } from './types';
import { parseQueryString } from '../helpers/parseQueryString';
export function validateHysteria2Url(url: string): ValidationResult {
try {
const isHY2 = url.startsWith('hysteria2://');
const isHY2Short = url.startsWith('hy2://');
if (!isHY2 && !isHY2Short) {
return {
valid: false,
message: _('Invalid HY2 URL: must start with hysteria2:// or hy2://'),
};
}
if (/\s/.test(url)) {
return {
valid: false,
message: _('Invalid HY2 URL: must not contain spaces'),
};
}
const prefix = isHY2 ? 'hysteria2://' : 'hy2://';
const body = url.slice(prefix.length);
const [mainPart] = body.split('#');
const [authHostPort, queryString] = mainPart.split('?');
if (!authHostPort)
return {
valid: false,
message: _('Invalid HY2 URL: missing credentials/server'),
};
const [passwordPart, hostPortPart] = authHostPort.split('@');
if (!passwordPart)
return { valid: false, message: _('Invalid HY2 URL: missing password') };
if (!hostPortPart)
return {
valid: false,
message: _('Invalid HY2 URL: missing host & port'),
};
const [host, port] = hostPortPart.split(':');
if (!host) {
return { valid: false, message: _('Invalid HY2 URL: missing host') };
}
if (!port) {
return { valid: false, message: _('Invalid HY2 URL: missing port') };
}
const cleanedPort = port.replace('/', '');
const portNum = Number(cleanedPort);
if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {
return {
valid: false,
message: _('Invalid HY2 URL: invalid port number'),
};
}
if (queryString) {
const params = parseQueryString(queryString);
const paramsKeys = Object.keys(params);
if (
paramsKeys.includes('insecure') &&
!['0', '1'].includes(params.insecure)
) {
return {
valid: false,
message: _('Invalid HY2 URL: insecure must be 0 or 1'),
};
}
const validObfsTypes = ['none', 'salamander'];
if (
paramsKeys.includes('obfs') &&
!validObfsTypes.includes(params.obfs)
) {
return {
valid: false,
message: _('Invalid HY2 URL: unsupported obfs type'),
};
}
if (
paramsKeys.includes('obfs') &&
params.obfs !== 'none' &&
!params['obfs-password']
) {
return {
valid: false,
message: _(
'Invalid HY2 URL: obfs-password required when obfs is set',
),
};
}
if (paramsKeys.includes('sni') && !params.sni) {
return {
valid: false,
message: _('Invalid HY2 URL: sni cannot be empty'),
};
}
}
return { valid: true, message: _('Valid') };
} catch (_e) {
return { valid: false, message: _('Invalid HY2 URL: parsing failed') };
}
}

View File

@@ -3,6 +3,7 @@ import { validateShadowsocksUrl } from './validateShadowsocksUrl';
import { validateVlessUrl } from './validateVlessUrl'; import { validateVlessUrl } from './validateVlessUrl';
import { validateTrojanUrl } from './validateTrojanUrl'; import { validateTrojanUrl } from './validateTrojanUrl';
import { validateSocksUrl } from './validateSocksUrl'; import { validateSocksUrl } from './validateSocksUrl';
import { validateHysteria2Url } from './validateHysteriaUrl';
// TODO refactor current validation and add tests // TODO refactor current validation and add tests
export function validateProxyUrl(url: string): ValidationResult { export function validateProxyUrl(url: string): ValidationResult {
@@ -24,10 +25,17 @@ export function validateProxyUrl(url: string): ValidationResult {
return validateSocksUrl(trimmedUrl); return validateSocksUrl(trimmedUrl);
} }
if (
trimmedUrl.startsWith('hysteria2://') ||
trimmedUrl.startsWith('hy2://')
) {
return validateHysteria2Url(trimmedUrl);
}
return { return {
valid: false, valid: false,
message: _( message: _(
'URL must start with vless://, ss://, trojan://, or socks4/5://', 'URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://',
), ),
}; };
} }

View File

@@ -448,6 +448,92 @@ function validateSocksUrl(url) {
return { valid: true, message: _("Valid") }; return { valid: true, message: _("Valid") };
} }
// src/validators/validateHysteriaUrl.ts
function validateHysteria2Url(url) {
try {
const isHY2 = url.startsWith("hysteria2://");
const isHY2Short = url.startsWith("hy2://");
if (!isHY2 && !isHY2Short) {
return {
valid: false,
message: _("Invalid HY2 URL: must start with hysteria2:// or hy2://")
};
}
if (/\s/.test(url)) {
return {
valid: false,
message: _("Invalid HY2 URL: must not contain spaces")
};
}
const prefix = isHY2 ? "hysteria2://" : "hy2://";
const body = url.slice(prefix.length);
const [mainPart] = body.split("#");
const [authHostPort, queryString] = mainPart.split("?");
if (!authHostPort)
return {
valid: false,
message: _("Invalid HY2 URL: missing credentials/server")
};
const [passwordPart, hostPortPart] = authHostPort.split("@");
if (!passwordPart)
return { valid: false, message: _("Invalid HY2 URL: missing password") };
if (!hostPortPart)
return {
valid: false,
message: _("Invalid HY2 URL: missing host & port")
};
const [host, port] = hostPortPart.split(":");
if (!host) {
return { valid: false, message: _("Invalid HY2 URL: missing host") };
}
if (!port) {
return { valid: false, message: _("Invalid HY2 URL: missing port") };
}
const cleanedPort = port.replace("/", "");
const portNum = Number(cleanedPort);
if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {
return {
valid: false,
message: _("Invalid HY2 URL: invalid port number")
};
}
if (queryString) {
const params = parseQueryString(queryString);
const paramsKeys = Object.keys(params);
if (paramsKeys.includes("insecure") && !["0", "1"].includes(params.insecure)) {
return {
valid: false,
message: _("Invalid HY2 URL: insecure must be 0 or 1")
};
}
const validObfsTypes = ["none", "salamander"];
if (paramsKeys.includes("obfs") && !validObfsTypes.includes(params.obfs)) {
return {
valid: false,
message: _("Invalid HY2 URL: unsupported obfs type")
};
}
if (paramsKeys.includes("obfs") && params.obfs !== "none" && !params["obfs-password"]) {
return {
valid: false,
message: _(
"Invalid HY2 URL: obfs-password required when obfs is set"
)
};
}
if (paramsKeys.includes("sni") && !params.sni) {
return {
valid: false,
message: _("Invalid HY2 URL: sni cannot be empty")
};
}
}
return { valid: true, message: _("Valid") };
} catch (_e) {
return { valid: false, message: _("Invalid HY2 URL: parsing failed") };
}
}
// src/validators/validateProxyUrl.ts // src/validators/validateProxyUrl.ts
function validateProxyUrl(url) { function validateProxyUrl(url) {
const trimmedUrl = url.trim(); const trimmedUrl = url.trim();
@@ -463,10 +549,13 @@ function validateProxyUrl(url) {
if (/^socks(4|4a|5):\/\//.test(trimmedUrl)) { if (/^socks(4|4a|5):\/\//.test(trimmedUrl)) {
return validateSocksUrl(trimmedUrl); return validateSocksUrl(trimmedUrl);
} }
if (trimmedUrl.startsWith("hysteria2://") || trimmedUrl.startsWith("hy2://")) {
return validateHysteria2Url(trimmedUrl);
}
return { return {
valid: false, valid: false,
message: _( message: _(
"URL must start with vless://, ss://, trojan://, or socks4/5://" "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
) )
}; };
} }

View File

@@ -87,7 +87,7 @@ function createSectionContent(section) {
_("URLTest Proxy Links"), _("URLTest Proxy Links"),
); );
o.depends("proxy_config_type", "urltest"); o.depends("proxy_config_type", "urltest");
o.placeholder = "vless://, ss://, trojan://, socks4/5:// links"; o.placeholder = "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links";
o.rmempty = false; o.rmempty = false;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PODKOP\n" "Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-06 16:19+0200\n" "POT-Creation-Date: 2025-12-01 16:30+0200\n"
"PO-Revision-Date: 2025-11-06 16:19+0200\n" "PO-Revision-Date: 2025-12-01 16:30+0200\n"
"Last-Translator: divocat\n" "Last-Translator: divocat\n"
"Language-Team: none\n" "Language-Team: none\n"
"Language: ru\n" "Language: ru\n"
@@ -281,6 +281,45 @@ msgstr "Неверный домен"
msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y" msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
msgstr "Неверный формат. Используйте X.X.X.X или X.X.X.X/Y" msgstr "Неверный формат. Используйте X.X.X.X или X.X.X.X/Y"
msgid "Invalid HY2 URL: insecure must be 0 or 1"
msgstr "Неверный URL Hysteria2: параметр insecure должен быть 0 или 1"
msgid "Invalid HY2 URL: invalid port number"
msgstr "Неверный URL Hysteria2: неверный номер порта"
msgid "Invalid HY2 URL: missing credentials/server"
msgstr "Неверный URL Hysteria2: отсутствуют учетные данные/сервер"
msgid "Invalid HY2 URL: missing host"
msgstr "Неверный URL Hysteria2: отсутствует хост"
msgid "Invalid HY2 URL: missing host & port"
msgstr "Неверный URL Hysteria2: отсутствуют хост и порт"
msgid "Invalid HY2 URL: missing password"
msgstr "Неверный URL Hysteria2: отсутствует пароль"
msgid "Invalid HY2 URL: missing port"
msgstr "Неверный URL Hysteria2: отсутствует порт"
msgid "Invalid HY2 URL: must not contain spaces"
msgstr "Неверный URL Hysteria2: не должен содержать пробелов"
msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
msgstr "Неверный URL Hysteria2: должен начинаться с hysteria2:// или hy2://"
msgid "Invalid HY2 URL: obfs-password required when obfs is set"
msgstr "Неверный URL Hysteria2: требуется obfs-password, когда установлен obfs"
msgid "Invalid HY2 URL: parsing failed"
msgstr "Неверный URL Hysteria2: ошибка разбора"
msgid "Invalid HY2 URL: sni cannot be empty"
msgstr "Неверный URL Hysteria2: sni не может быть пустым"
msgid "Invalid HY2 URL: unsupported obfs type"
msgstr "Неверный URL Hysteria2: неподдерживаемый тип obfs"
msgid "Invalid IP address" msgid "Invalid IP address"
msgstr "Неверный IP-адрес" msgstr "Неверный IP-адрес"
@@ -626,9 +665,6 @@ msgstr "Тестирование задержки"
msgid "Text List" msgid "Text List"
msgstr "Текстовый список" msgstr "Текстовый список"
msgid "Text List (comma/space/newline separated)"
msgstr "Текстовый список (через запятую, пробел или новую строку)"
msgid "The DNS server used to look up the IP address of an upstream DNS server" msgid "The DNS server used to look up the IP address of an upstream DNS server"
msgstr "DNS-сервер, используемый для поиска IP-адреса вышестоящего DNS-сервера" msgstr "DNS-сервер, используемый для поиска IP-адреса вышестоящего DNS-сервера"
@@ -674,8 +710,8 @@ msgstr "Неизвестная ошибка"
msgid "Uplink" msgid "Uplink"
msgstr "Исходящий" msgstr "Исходящий"
msgid "URL must start with vless://, ss://, trojan://, or socks4/5://" msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
msgstr "URL должен начинаться с vless://, ss://, trojan:// или socks4/5://" msgstr "URL должен начинаться с vless://, ss://, trojan://, socks4/5:// или hysteria2:// hy2://"
msgid "URL must use one of the following protocols:" msgid "URL must use one of the following protocols:"
msgstr "URL должен использовать один из следующих протоколов:" msgstr "URL должен использовать один из следующих протоколов:"

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PODKOP\n" "Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-06 14:19+0200\n" "POT-Creation-Date: 2025-12-01 14:30+0200\n"
"PO-Revision-Date: 2025-11-06 14:19+0200\n" "PO-Revision-Date: 2025-12-01 14:30+0200\n"
"Last-Translator: divocat <divocatt@gmail.com>\n" "Last-Translator: divocat <divocatt@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
@@ -16,23 +16,23 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: src/podkop/tabs/dashboard/initController.ts:342 #: src/podkop/tabs/dashboard/initController.ts:345
msgid "✔ Enabled" msgid "✔ Enabled"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:353 #: src/podkop/tabs/dashboard/initController.ts:356
msgid "✔ Running" msgid "✔ Running"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:343 #: src/podkop/tabs/dashboard/initController.ts:346
msgid "✘ Disabled" msgid "✘ Disabled"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:354 #: src/podkop/tabs/dashboard/initController.ts:357
msgid "✘ Stopped" msgid "✘ Stopped"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:304 #: src/podkop/tabs/dashboard/initController.ts:307
msgid "Active Connections" msgid "Active Connections"
msgstr "" msgstr ""
@@ -236,8 +236,8 @@ msgstr ""
msgid "Dont Touch My DHCP!" msgid "Dont Touch My DHCP!"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:238 #: src/podkop/tabs/dashboard/initController.ts:241
#: src/podkop/tabs/dashboard/initController.ts:272 #: src/podkop/tabs/dashboard/initController.ts:275
msgid "Downlink" msgid "Downlink"
msgstr "" msgstr ""
@@ -390,6 +390,58 @@ msgstr ""
msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y" msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
msgstr "" msgstr ""
#: src/validators/validateHysteriaUrl.ts:76
msgid "Invalid HY2 URL: insecure must be 0 or 1"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:62
msgid "Invalid HY2 URL: invalid port number"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:32
msgid "Invalid HY2 URL: missing credentials/server"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:49
msgid "Invalid HY2 URL: missing host"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:43
msgid "Invalid HY2 URL: missing host & port"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:38
msgid "Invalid HY2 URL: missing password"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:53
msgid "Invalid HY2 URL: missing port"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:19
msgid "Invalid HY2 URL: must not contain spaces"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:12
msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:99
msgid "Invalid HY2 URL: obfs-password required when obfs is set"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:113
msgid "Invalid HY2 URL: parsing failed"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:106
msgid "Invalid HY2 URL: sni cannot be empty"
msgstr ""
#: src/validators/validateHysteriaUrl.ts:88
msgid "Invalid HY2 URL: unsupported obfs type"
msgstr ""
#: src/validators/validateIp.ts:11 #: src/validators/validateIp.ts:11
msgid "Invalid IP address" msgid "Invalid IP address"
msgstr "" msgstr ""
@@ -527,7 +579,7 @@ msgstr ""
msgid "Main DNS" msgid "Main DNS"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:308 #: src/podkop/tabs/dashboard/initController.ts:311
msgid "Memory Usage" msgid "Memory Usage"
msgstr "" msgstr ""
@@ -613,7 +665,7 @@ msgstr ""
msgid "Pending" msgid "Pending"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:340 #: src/podkop/tabs/dashboard/initController.ts:343
msgid "Podkop" msgid "Podkop"
msgstr "" msgstr ""
@@ -766,7 +818,7 @@ msgstr ""
msgid "Select the WAN interfaces to be monitored" msgid "Select the WAN interfaces to be monitored"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:337 #: src/podkop/tabs/dashboard/initController.ts:340
msgid "Services info" msgid "Services info"
msgstr "" msgstr ""
@@ -779,7 +831,7 @@ msgstr ""
msgid "Show sing-box config" msgid "Show sing-box config"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:351 #: src/podkop/tabs/dashboard/initController.ts:354
msgid "Sing-box" msgid "Sing-box"
msgstr "" msgstr ""
@@ -844,7 +896,7 @@ msgstr ""
msgid "Successfully copied!" msgid "Successfully copied!"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:301 #: src/podkop/tabs/dashboard/initController.ts:304
msgid "System info" msgid "System info"
msgstr "" msgstr ""
@@ -861,11 +913,8 @@ msgid "Test latency"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:368 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:368
msgid "Text List"
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:448 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:448
msgid "Text List (comma/space/newline separated)" msgid "Text List"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:46 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:46
@@ -888,11 +937,11 @@ msgstr ""
msgid "Time in seconds for DNS record caching (default: 60)" msgid "Time in seconds for DNS record caching (default: 60)"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:235 #: src/podkop/tabs/dashboard/initController.ts:238
msgid "Traffic" msgid "Traffic"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:265 #: src/podkop/tabs/dashboard/initController.ts:268
msgid "Traffic Total" msgid "Traffic Total"
msgstr "" msgstr ""
@@ -931,13 +980,13 @@ msgstr ""
msgid "Unknown error" msgid "Unknown error"
msgstr "" msgstr ""
#: src/podkop/tabs/dashboard/initController.ts:237 #: src/podkop/tabs/dashboard/initController.ts:240
#: src/podkop/tabs/dashboard/initController.ts:268 #: src/podkop/tabs/dashboard/initController.ts:271
msgid "Uplink" msgid "Uplink"
msgstr "" msgstr ""
#: src/validators/validateProxyUrl.ts:29 #: src/validators/validateProxyUrl.ts:37
msgid "URL must start with vless://, ss://, trojan://, or socks4/5://" msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
msgstr "" msgstr ""
#: src/validators/validateUrl.ts:17 #: src/validators/validateUrl.ts:17
@@ -992,6 +1041,7 @@ msgstr ""
#: src/validators/validateDns.ts:18 #: src/validators/validateDns.ts:18
#: src/validators/validateDomain.ts:13 #: src/validators/validateDomain.ts:13
#: src/validators/validateDomain.ts:30 #: src/validators/validateDomain.ts:30
#: src/validators/validateHysteriaUrl.ts:111
#: src/validators/validateIp.ts:8 #: src/validators/validateIp.ts:8
#: src/validators/validateOutboundJson.ts:7 #: src/validators/validateOutboundJson.ts:7
#: src/validators/validatePath.ts:16 #: src/validators/validatePath.ts:16

View File

@@ -104,7 +104,7 @@ has_outbound_section() {
return $section_exists return $section_exists
} }
start() { start_main() {
log "Starting podkop" log "Starting podkop"
check_requirements check_requirements
@@ -132,24 +132,13 @@ start() {
sing_box_init_config sing_box_init_config
config_foreach add_cron_job "section" config_foreach add_cron_job "section"
/etc/init.d/sing-box start /etc/init.d/sing-box start
if [ $? -ne 0 ]; then
echo "Failed to start sing-box service"
exit 1
fi
config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
if [ "$dont_touch_dhcp" -eq 0 ]; then
dnsmasq_configure
fi
uci_set "podkop" "settings" "shutdown_correctly" 0
uci commit "podkop" && config_load "$PODKOP_CONFIG"
log "Nice" log "Nice"
list_update & list_update &
echo $! > /var/run/podkop_list_update.pid echo $! > /var/run/podkop_list_update.pid
} }
stop() { stop_main() {
log "Stopping the podkop" log "Stopping the podkop"
if [ -f /var/run/podkop_list_update.pid ]; then if [ -f /var/run/podkop_list_update.pid ]; then
@@ -180,14 +169,30 @@ stop() {
ip route flush table podkop ip route flush table podkop
fi fi
log "Stop sing-box"
/etc/init.d/sing-box stop
}
start() {
start_main
config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
if [ "$dont_touch_dhcp" -eq 0 ]; then
dnsmasq_configure
fi
uci_set "podkop" "settings" "shutdown_correctly" 0
uci commit "podkop" && config_load "$PODKOP_CONFIG"
}
stop() {
local dont_touch_dhcp local dont_touch_dhcp
config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0 config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
if [ "$dont_touch_dhcp" -eq 0 ]; then if [ "$dont_touch_dhcp" -eq 0 ]; then
dnsmasq_restore dnsmasq_restore
fi fi
log "Stop sing-box" stop_main
/etc/init.d/sing-box stop
uci_set "podkop" "settings" "shutdown_correctly" 1 uci_set "podkop" "settings" "shutdown_correctly" 1
uci commit "podkop" && config_load "$PODKOP_CONFIG" uci commit "podkop" && config_load "$PODKOP_CONFIG"
@@ -195,8 +200,8 @@ stop() {
reload() { reload() {
log "Podkop reload" log "Podkop reload"
stop stop_main
start start_main
} }
restart() { restart() {

View File

@@ -125,6 +125,12 @@ url_decode() {
printf '%b' "$(echo "$encoded" | sed 's/+/ /g; s/%/\\x/g')" printf '%b' "$(echo "$encoded" | sed 's/+/ /g; s/%/\\x/g')"
} }
# Returns the scheme (protocol) part of a URL
url_get_scheme() {
local url="$1"
echo "${url%%://*}"
}
# Extracts the userinfo (username[:password]) part from a URL # Extracts the userinfo (username[:password]) part from a URL
url_get_userinfo() { url_get_userinfo() {
local url="$1" local url="$1"
@@ -134,13 +140,23 @@ url_get_userinfo() {
# Extracts the host part from a URL # Extracts the host part from a URL
url_get_host() { url_get_host() {
local url="$1" local url="$1"
echo "$url" | sed -n -e 's#^[^:/?]*://##' -e 's#^[^/]*@##' -e 's#\([:/].*\|$\)##p'
url="${url#*://}"
url="${url#*@}"
url="${url%%[/?#]*}"
echo "${url%%:*}"
} }
# Extracts the port number from a URL # Extracts the port number from a URL
url_get_port() { url_get_port() {
local url="$1" local url="$1"
echo "$url" | sed -n -e 's#^[^:/?]*://##' -e 's#^[^/]*@##' -e 's#^[^/]*:\([0-9][0-9]*\).*#\1#p'
url="${url#*://}"
url="${url#*@}"
url="${url%%[/?#]*}"
[[ "$url" == *:* ]] && echo "${url#*:}" || echo ""
} }
# Extracts the path from a URL (without query or fragment; returns "/" if empty) # Extracts the path from a URL (without query or fragment; returns "/" if empty)

View File

@@ -64,7 +64,8 @@ sing_box_cf_add_proxy_outbound() {
url=$(url_decode "$url") url=$(url_decode "$url")
url=$(url_strip_fragment "$url") url=$(url_strip_fragment "$url")
local scheme="${url%%://*}" local scheme
scheme="$(url_get_scheme "$url")"
case "$scheme" in case "$scheme" in
socks4 | socks4a | socks5) socks4 | socks4a | socks5)
local tag host port version userinfo username password udp_over_tcp local tag host port version userinfo username password udp_over_tcp
@@ -146,6 +147,21 @@ sing_box_cf_add_proxy_outbound() {
config=$(_add_outbound_security "$config" "$tag" "$url") config=$(_add_outbound_security "$config" "$tag" "$url")
config=$(_add_outbound_transport "$config" "$tag" "$url") config=$(_add_outbound_transport "$config" "$tag" "$url")
;; ;;
hysteria2 | hy2)
local tag host port password obfuscator_type obfuscator_password upload_mbps download_mbps
tag=$(get_outbound_tag_by_section "$section")
host=$(url_get_host "$url")
port="$(url_get_port "$url")"
password=$(url_get_userinfo "$url")
obfuscator_type=$(url_get_query_param "$url" "obfs")
obfuscator_password=$(url_get_query_param "$url" "obfs-password")
upload_mbps=$(url_get_query_param "$url" "upmbps")
download_mbps=$(url_get_query_param "$url" "downmbps")
config=$(sing_box_cm_add_hysteria2_outbound "$config" "$tag" "$host" "$port" "$password" "$obfuscator_type" \
"$obfuscator_password" "$upload_mbps" "$download_mbps")
config=$(_add_outbound_security "$config" "$tag" "$url")
;;
*) *)
log "Unsupported proxy $scheme type. Aborted." "fatal" log "Unsupported proxy $scheme type. Aborted." "fatal"
exit 1 exit 1
@@ -160,13 +176,20 @@ _add_outbound_security() {
local outbound_tag="$2" local outbound_tag="$2"
local url="$3" local url="$3"
local security local security scheme
security=$(url_get_query_param "$url" "security") security=$(url_get_query_param "$url" "security")
if [ -z "$security" ]; then
scheme="$(url_get_scheme "$url")"
if [ "$scheme" = "hysteria2" ] || [ "$scheme" = "hy2" ]; then
security="tls"
fi
fi
case "$security" in case "$security" in
tls | reality) tls | reality)
local sni insecure alpn fingerprint public_key short_id local sni insecure alpn fingerprint public_key short_id
sni=$(url_get_query_param "$url" "sni") sni=$(url_get_query_param "$url" "sni")
insecure=$(url_get_query_param "$url" "allowInsecure") insecure=$(_get_insecure_query_param_from_url "$url")
alpn=$(comma_string_to_json_array "$(url_get_query_param "$url" "alpn")") alpn=$(comma_string_to_json_array "$(url_get_query_param "$url" "alpn")")
fingerprint=$(url_get_query_param "$url" "fp") fingerprint=$(url_get_query_param "$url" "fp")
public_key=$(url_get_query_param "$url" "pbk") public_key=$(url_get_query_param "$url" "pbk")
@@ -193,6 +216,18 @@ _add_outbound_security() {
echo "$config" echo "$config"
} }
_get_insecure_query_param_from_url() {
local url="$1"
local insecure
insecure=$(url_get_query_param "$url" "allowInsecure")
if [ -z "$insecure" ]; then
insecure=$(url_get_query_param "$url" "insecure")
fi
echo "$insecure"
}
_add_outbound_transport() { _add_outbound_transport() {
local config="$1" local config="$1"
local outbound_tag="$2" local outbound_tag="$2"
@@ -214,7 +249,12 @@ _add_outbound_transport() {
;; ;;
grpc) grpc)
# TODO(ampetelin): Add handling of optional gRPC parameters; example links are needed. # TODO(ampetelin): Add handling of optional gRPC parameters; example links are needed.
config=$(sing_box_cm_set_grpc_transport_for_outbound "$config" "$outbound_tag") local grpc_service_name
grpc_service_name=$(url_get_query_param "$url" "serviceName")
config=$(
sing_box_cm_set_grpc_transport_for_outbound "$config" "$outbound_tag" "$grpc_service_name"
)
;; ;;
*) *)
log "Unknown transport '$transport' detected." "error" log "Unknown transport '$transport' detected." "error"

View File

@@ -21,9 +21,9 @@ SERVICE_TAG="__service_tag"
####################################### #######################################
# Configure the logging section of a sing-box JSON configuration. # Configure the logging section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string, JSON configuration
# disabled: boolean, true to disable logging # disabled: boolean, true to disable logging
# level: string, e.g., "info", "debug", "warn" # level: string, log level. One of: trace debug info warn error fatal panic.
# timestamp: boolean, true to include timestamps # timestamp: boolean, true to include timestamps
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
@@ -50,7 +50,7 @@ sing_box_cm_configure_log() {
####################################### #######################################
# Configure the DNS section of a sing-box JSON configuration. # Configure the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# final: string, default dns server tag # final: string, default dns server tag
# strategy: string, default domain strategy for resolving the domain names # strategy: string, default domain strategy for resolving the domain names
# independent_cache: boolean, whether to use an independent DNS cache # independent_cache: boolean, whether to use an independent DNS cache
@@ -82,12 +82,12 @@ sing_box_cm_configure_dns() {
####################################### #######################################
# Add a UDP DNS server to the DNS section of a sing-box JSON configuration. # Add a UDP DNS server to the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the DNS server # tag: string, identifier for the DNS server
# server_address: string, IP address or hostname of the DNS server # server_address: string, IP address or hostname of the DNS server
# server_port: string or number, port of the DNS server # server_port: string or integer, port of the DNS server
# domain_resolver: string, domain resolver to use for resolving domain names # domain_resolver: string, domain resolver to use for resolving domain names (optional)
# detour: string, tag of the upstream outbound # detour: string, tag of the upstream outbound (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -122,12 +122,12 @@ sing_box_cm_add_udp_dns_server() {
####################################### #######################################
# Add a TLS DNS server to the DNS section of a sing-box JSON configuration. # Add a TLS DNS server to the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the DNS server # tag: string, identifier for the DNS server
# server_address: string, IP address or hostname of the DNS server # server_address: string, IP address or hostname of the DNS server
# server_port: string or number, port of the DNS server # server_port: string or integer, port of the DNS server
# domain_resolver: string, domain resolver to use for resolving domain names # domain_resolver: string, domain resolver to use for resolving domain names (optional)
# detour: string, tag of the upstream outbound # detour: string, tag of the upstream outbound (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -162,14 +162,14 @@ sing_box_cm_add_tls_dns_server() {
####################################### #######################################
# Add an HTTPS DNS server to the DNS section of a sing-box JSON configuration. # Add an HTTPS DNS server to the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the DNS server # tag: string, identifier for the DNS server
# server_address: string, IP address or hostname of the DNS server # server_address: string, IP address or hostname of the DNS server
# server_port: string or number, port of the DNS server # server_port: string or integer, port of the DNS server
# path: string, optional URL path for HTTPS DNS requests # path: string, URL path for HTTPS DNS requests (optional)
# headers: string, optional additional headers for HTTPS DNS requests # headers: string, additional headers for HTTPS DNS requests (optional)
# domain_resolver: string, domain resolver to use for resolving domain names # domain_resolver: string, domain resolver to use for resolving domain names (optional)
# detour: string, tag of the upstream outbound # detour: string, tag of the upstream outbound (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -210,7 +210,7 @@ sing_box_cm_add_https_dns_server() {
####################################### #######################################
# Add a FakeIP DNS server to the DNS section of a sing-box JSON configuration. # Add a FakeIP DNS server to the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the DNS server # tag: string, identifier for the DNS server
# inet4_range: string, IPv4 range used for fake IP mapping # inet4_range: string, IPv4 range used for fake IP mapping
# Outputs: # Outputs:
@@ -236,7 +236,7 @@ sing_box_cm_add_fakeip_dns_server() {
####################################### #######################################
# Add a DNS routing rule to the DNS section of a sing-box JSON configuration. # Add a DNS routing rule to the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# server: string, target DNS server for the rule # server: string, target DNS server for the rule
# tag: string, identifier for the route rule # tag: string, identifier for the route rule
# Outputs: # Outputs:
@@ -263,10 +263,10 @@ sing_box_cm_add_dns_route_rule() {
####################################### #######################################
# Patch a DNS routing rule in the DNS section of a sing-box JSON configuration. # Patch a DNS routing rule in the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier of the rule to patch # tag: string, identifier of the rule to patch
# key: string, the key in the rule to update or add # key: string, the key in the rule to update or add
# value: JSON value to assign to the key # value: string, JSON value to assign to the key
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -304,9 +304,9 @@ sing_box_cm_patch_dns_route_rule() {
####################################### #######################################
# Add a DNS reject rule to the DNS section of a sing-box JSON configuration. # Add a DNS reject rule to the DNS section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# key: string, the key to set for the reject rule # key: string, the key to set for the reject rule
# value: JSON value to assign to the key # value: string, JSON value to assign to the key
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -331,10 +331,10 @@ sing_box_cm_add_dns_reject_rule() {
####################################### #######################################
# Add a TProxy inbound to the inbounds section of a sing-box JSON configuration. # Add a TProxy inbound to the inbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the inbound # tag: string, identifier for the inbound
# listen_address: string, IP address to listen on # listen_address: string, IP address to listen on
# listen_port: number, port to listen on # listen_port: integer, port to listen on
# tcp_fast_open: boolean, enable or disable TCP Fast Open # tcp_fast_open: boolean, enable or disable TCP Fast Open
# udp_fragment: boolean, enable or disable UDP fragmentation # udp_fragment: boolean, enable or disable UDP fragmentation
# Outputs: # Outputs:
@@ -369,10 +369,10 @@ sing_box_cm_add_tproxy_inbound() {
####################################### #######################################
# Add a Direct inbound to the inbounds section of a sing-box JSON configuration. # Add a Direct inbound to the inbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the inbound # tag: string, identifier for the inbound
# listen_address: string, IP address to listen on # listen_address: string, IP address to listen on
# listen_port: number, port to listen on # listen_port: integer, port to listen on
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -399,10 +399,10 @@ sing_box_cm_add_direct_inbound() {
####################################### #######################################
# Add a Mixed inbound to the inbounds section of a sing-box JSON configuration. # Add a Mixed inbound to the inbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the inbound # tag: string, identifier for the inbound
# listen_address: string, IP address to listen on # listen_address: string, IP address to listen on
# listen_port: number, port to listen on # listen_port: integer, port to listen on
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -429,7 +429,7 @@ sing_box_cm_add_mixed_inbound() {
####################################### #######################################
# Add a Direct outbound to the outbounds section of a sing-box JSON configuration. # Add a Direct outbound to the outbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound # tag: string, identifier for the outbound
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
@@ -451,15 +451,15 @@ sing_box_cm_add_direct_outbound() {
####################################### #######################################
# Add a SOCKS outbound to the outbounds section of a sing-box JSON configuration. # Add a SOCKS outbound to the outbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound # tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the SOCKS server # server_address: string, IP address or hostname of the SOCKS server
# server_port: number, port of the SOCKS server # server_port: integer, port of the SOCKS server
# version: string, optional SOCKS version # version: string, SOCKS version (optional)
# username: string, optional username for authentication # username: string, username for authentication (optional)
# password: string, optional password for authentication # password: string, password for authentication (optional)
# network: string, optional network type (e.g., "tcp") # network: string, network type (e.g., "tcp") (optional)
# udp_over_tcp: number, optional version for UDP over TCP # udp_over_tcp: integer, version for UDP over TCP (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -509,16 +509,16 @@ sing_box_cm_add_socks_outbound() {
####################################### #######################################
# Add a Shadowsocks outbound to the outbounds section of a sing-box JSON configuration. # Add a Shadowsocks outbound to the outbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound # tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the Shadowsocks server # server_address: string, IP address or hostname of the Shadowsocks server
# server_port: number, port of the Shadowsocks server # server_port: integer, port of the Shadowsocks server
# method: string, encryption method # method: string, encryption method
# password: string, password for encryption # password: string, password for encryption
# network: string, optional network type (e.g., "tcp") # network: string, network type (e.g., "tcp") (optional)
# udp_over_tcp: number, optional version for UDP over TCP # udp_over_tcp: integer, version for UDP over TCP (optional)
# plugin: string, optional plugin name # plugin: string, plugin name (optional)
# plugin_opts: string, optional plugin options # plugin_opts: string, plugin options (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -573,14 +573,14 @@ sing_box_cm_add_shadowsocks_outbound() {
####################################### #######################################
# Add a VLESS outbound to the outbounds section of a sing-box JSON configuration. # Add a VLESS outbound to the outbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound # tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the VLESS server # server_address: string, IP address or hostname of the VLESS server
# server_port: number, port of the VLESS server # server_port: integer, port of the VLESS server
# uuid: string, user UUID # uuid: string, user UUID
# flow: string, optional flow setting # flow: string, flow setting (optional)
# network: string, optional network type (e.g., "tcp") # network: string, network type (e.g., "tcp") (optional)
# packet_encoding: string, optional packet encoding method # packet_encoding: string, packet encoding method (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -624,12 +624,12 @@ sing_box_cm_add_vless_outbound() {
####################################### #######################################
# Add a Trojan outbound to the outbounds section of a sing-box JSON configuration. # Add a Trojan outbound to the outbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: string, JSON configuration # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound # tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the Trojan server # server_address: string, IP address or hostname of the Trojan server
# server_port: number, port of the Trojan server # server_port: integer, port of the Trojan server
# password: string, password for authentication # password: string, password for authentication
# network: string, optional network type (e.g., "tcp") # network: string, network type (e.g., "tcp") (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -661,15 +661,76 @@ sing_box_cm_add_trojan_outbound() {
)]' )]'
} }
#######################################
# Add a Hysteria2 outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the Hysteria2 server
# server_port: integer, port of the Hysteria2 server
# password: string, password for authentication
# obfuscator_type: string, obfuscation type (optional)
# obfuscator_password: string, obfuscation password (optional)
# upload_mbps: integer, upload bandwidth limit in Mbps (optional)
# download_mbps: integer, download bandwidth limit in Mbps (optional)
# network: string, network type (e.g., "udp") (optional)
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_hysteria2_outbound "$CONFIG" "hysteria2-out" "example.com" 443 "supersecret" \
# "salamander" "obfs-pass" "50" "200" "udp")
#######################################
sing_box_cm_add_hysteria2_outbound() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local password="$5"
local obfuscator_type="$6"
local obfuscator_password="$7"
local upload_mbps="$8"
local download_mbps="$9"
local network="${10}"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg password "$password" \
--arg obfuscator_type "$obfuscator_type" \
--arg obfuscator_password "$obfuscator_password" \
--arg upload_mbps "$upload_mbps" \
--arg download_mbps "$download_mbps" \
--arg network "$network" \
'.outbounds += [(
{
type: "hysteria2",
tag: $tag,
server: $server_address,
server_port: ($server_port | tonumber),
password: $password
}
+ (if $obfuscator_type != "" and $obfuscator_password != "" then {
obfs: {
type: $obfuscator_type,
password: $obfuscator_password
}
} else {} end)
+ (if $upload_mbps != "" then {up_mbps: ($upload_mbps | tonumber)} else {} end)
+ (if $download_mbps != "" then {down_mbps: ($download_mbps | tonumber)} else {} end)
+ (if $network != "" then {network: $network} else {} end)
)]'
}
####################################### #######################################
# Set gRPC transport settings for an outbound in a sing-box JSON configuration. # Set gRPC transport settings for an outbound in a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier of the outbound to modify # tag: string, identifier of the outbound to modify
# service_name: string, optional gRPC service name # service_name: string, gRPC service name (optional)
# idle_timeout: string or number, optional idle timeout # idle_timeout: string or integer, idle timeout (optional)
# ping_timeout: string or number, optional ping timeout # ping_timeout: string or integer, ping timeout (optional)
# permit_without_stream: boolean, optional flag for permitting without stream # permit_without_stream: boolean, flag for permitting without stream (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -709,12 +770,12 @@ sing_box_cm_set_grpc_transport_for_outbound() {
####################################### #######################################
# Set WebSocket transport settings for an outbound in a sing-box JSON configuration. # Set WebSocket transport settings for an outbound in a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier of the outbound to modify # tag: string, identifier of the outbound to modify
# path: string, WebSocket path # path: string, WebSocket path
# host: string, optional Host header for WebSocket # host: string, Host header for WebSocket (optional)
# max_early_data: number, optional maximum early data # max_early_data: integer, maximum early data (optional)
# early_data_header_name: string, optional header name for early data # early_data_header_name: string, header name for early data (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -759,14 +820,14 @@ sing_box_cm_set_ws_transport_for_outbound() {
####################################### #######################################
# Set TLS settings for an outbound in a sing-box JSON configuration. # Set TLS settings for an outbound in a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier of the outbound to modify # tag: string, identifier of the outbound to modify
# server_name: string, optional, used to verify the hostname on the returned certificates # server_name: string, used to verify the hostname on the returned certificates (optional)
# insecure: boolean, accept any server certificate # insecure: boolean, accept any server certificate (optional)
# alpn: JSON value or null, optional supported application level protocols # alpn: string, JSON value, supported application level protocols (optional)
# utls_fingerprint: string, optional uTLS fingerprint # utls_fingerprint: string, uTLS fingerprint (optional)
# reality_public_key: string, optional Reality public key # reality_public_key: string, Reality public key (optional)
# reality_short_id: string, optional Reality short ID # reality_short_id: string, Reality short ID (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -825,7 +886,7 @@ sing_box_cm_set_tls_for_outbound() {
####################################### #######################################
# Add a Direct outbound with a specific network interface to a sing-box JSON configuration. # Add a Direct outbound with a specific network interface to a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound # tag: string, identifier for the outbound
# interface: string, network interface to bind the outbound # interface: string, network interface to bind the outbound
# domain_resolver: string, tag of the domain resolver to be used for this outbound (optional) # domain_resolver: string, tag of the domain resolver to be used for this outbound (optional)
@@ -857,9 +918,9 @@ sing_box_cm_add_interface_outbound() {
####################################### #######################################
# Add a raw outbound JSON object to the outbounds section of a sing-box configuration. # Add a raw outbound JSON object to the outbounds section of a sing-box configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the outbound # tag: string, identifier for the outbound
# raw_outbound: JSON object, the raw outbound configuration to add # raw_outbound: string, JSON object, the raw outbound configuration to add
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -881,14 +942,14 @@ sing_box_cm_add_raw_outbound() {
####################################### #######################################
# Add a URLTest outbound to the outbounds section of a sing-box JSON configuration. # Add a URLTest outbound to the outbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the URLTest outbound # tag: string, identifier for the URLTest outbound
# outbounds: JSON array of outbound tags to test # outbounds: string, JSON array of outbound tags to test
# url: URL to probe (optional) # url: string, URL to probe (optional)
# interval: test interval (e.g., "10s") (optional) # interval: string, test interval (e.g., "10s") (optional)
# tolerance: max latency difference tolerated (optional) # tolerance: string or integer, max latency difference tolerated (optional)
# idle_timeout: idle timeout duration (optional) # idle_timeout: string or integer, idle timeout duration (optional)
# interrupt_exist_connections: flag to interrupt existing connections ("true"/"false") (optional) # interrupt_exist_connections: boolean, flag to interrupt existing connections ("true"/"false") (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -929,11 +990,11 @@ sing_box_cm_add_urltest_outbound() {
####################################### #######################################
# Add a Selector outbound to the outbounds section of a sing-box JSON configuration. # Add a Selector outbound to the outbounds section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the Selector outbound # tag: string, identifier for the Selector outbound
# outbounds: JSON array of outbound tags to choose from # outbounds: string (JSON), array of outbound tags to choose from
# default: default outbound tag if none selected (optional) # default: string, default outbound tag if none selected
# interrupt_exist_connections: flag to interrupt existing connections ("true"/"false") (optional) # interrupt_exist_connections: boolean, flag to interrupt existing connections ("true"/"false") (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -965,11 +1026,11 @@ sing_box_cm_add_selector_outbound() {
####################################### #######################################
# Configure the route section of a sing-box JSON configuration. # Configure the route section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# final: string, final outbound tag for unmatched traffic # final: string, final outbound tag for unmatched traffic
# auto_detect_interface: boolean, enable or disable automatic interface detection # auto_detect_interface: boolean, enable or disable automatic interface detection
# default_domain_resolver: string, default DNS resolver for domain-based routing # default_domain_resolver: string, default DNS resolver for domain-based routing
# default_interface: string, default network interface to use when auto detection is disabled # default_interface: string, default network interface to use when auto detection is disabled (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1001,7 +1062,7 @@ sing_box_cm_configure_route() {
####################################### #######################################
# Add a routing rule to the route section of a sing-box JSON configuration. # Add a routing rule to the route section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the route rule # tag: string, identifier for the route rule
# inbound: string, inbound tag to match # inbound: string, inbound tag to match
# outbound: string, outbound tag to route matched traffic to # outbound: string, outbound tag to route matched traffic to
@@ -1032,10 +1093,10 @@ sing_box_cm_add_route_rule() {
####################################### #######################################
# Patch a routing rule in the route section of a sing-box JSON configuration. # Patch a routing rule in the route section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier of the route rule to patch # tag: string, identifier of the route rule to patch
# key: string, the key in the rule to update or add # key: string, the key in the rule to update or add
# value: JSON value to assign to the key # value: string (JSON), value to assign to the key
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1071,9 +1132,9 @@ sing_box_cm_patch_route_rule() {
####################################### #######################################
# Add a reject rule to the route section of a sing-box JSON configuration. # Add a reject rule to the route section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# key: string, the key to set for the reject rule # key: string, the key to set for the reject rule
# value: JSON value to assign to the key # value: string (JSON), value to assign to the key
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1098,9 +1159,9 @@ sing_box_cm_add_reject_route_rule() {
####################################### #######################################
# Add a hijack-dns rule to the route section of a sing-box JSON configuration. # Add a hijack-dns rule to the route section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# key: string, the key to set for the hijack-dns rule # key: string, the key to set for the hijack-dns rule
# value: JSON value to assign to the key # value: string (JSON), value to assign to the key
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1125,7 +1186,7 @@ sing_box_cm_add_hijack_dns_route_rule() {
####################################### #######################################
# Add a route-options rule to the route section of a sing-box JSON configuration. # Add a route-options rule to the route section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the route-options rule # tag: string, identifier for the route-options rule
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
@@ -1148,9 +1209,9 @@ sing_box_cm_add_options_route_rule() {
####################################### #######################################
# Add a sniff rule to the route section of a sing-box JSON configuration. # Add a sniff rule to the route section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# key: string, the key to set for the sniff rule # key: string, the key to set for the sniff rule
# value: JSON value to assign to the key # value: string (JSON), value to assign to the key
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1176,7 +1237,7 @@ sing_box_cm_sniff_route_rule() {
####################################### #######################################
# Add an inline ruleset to the route.rule_set section of a sing-box JSON configuration. # Add an inline ruleset to the route.rule_set section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the inline ruleset # tag: string, identifier for the inline ruleset
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
@@ -1198,10 +1259,10 @@ sing_box_cm_add_inline_ruleset() {
####################################### #######################################
# Add or update a rule in an inline ruleset within the route.rule_set section of a sing-box JSON configuration. # Add or update a rule in an inline ruleset within the route.rule_set section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier of the inline ruleset # tag: string, identifier of the inline ruleset
# key: string, the key in the ruleset to update or add # key: string, the key in the ruleset to update or add
# value: JSON value to assign to the key # value: string (JSON), value to assign to the key
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1238,7 +1299,7 @@ sing_box_cm_add_inline_ruleset_rule() {
####################################### #######################################
# Add a local ruleset to the route.rule_set section of a sing-box JSON configuration. # Add a local ruleset to the route.rule_set section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the local ruleset # tag: string, identifier for the local ruleset
# format: string, format of the local ruleset ("source" or "binary") # format: string, format of the local ruleset ("source" or "binary")
# path: string, file path to the local ruleset # path: string, file path to the local ruleset
@@ -1269,12 +1330,12 @@ sing_box_cm_add_local_ruleset() {
####################################### #######################################
# Add a remote ruleset to the route.rule_set section of a sing-box JSON configuration. # Add a remote ruleset to the route.rule_set section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# tag: string, identifier for the remote ruleset # tag: string, identifier for the remote ruleset
# format: string, format of the remote ruleset ("source" or "binary") # format: string, format of the remote ruleset ("source" or "binary")
# url: string, URL to download the ruleset from # url: string, URL to download the ruleset from
# download_detour: string, optional detour tag for downloading # download_detour: string, detour tag for downloading (optional)
# update_interval: string, optional update interval for the ruleset # update_interval: string, update interval for the ruleset (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1310,7 +1371,7 @@ sing_box_cm_add_remote_ruleset() {
####################################### #######################################
# Configure the experimental cache_file section of a sing-box JSON configuration. # Configure the experimental cache_file section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# enabled: boolean, enable or disable file caching # enabled: boolean, enable or disable file caching
# path: string, file path for cache storage # path: string, file path for cache storage
# store_fakeip: boolean, whether to store fake IPs in the cache # store_fakeip: boolean, whether to store fake IPs in the cache
@@ -1339,10 +1400,10 @@ sing_box_cm_configure_cache_file() {
####################################### #######################################
# Configure the experimental clash_api section of a sing-box JSON configuration. # Configure the experimental clash_api section of a sing-box JSON configuration.
# Arguments: # Arguments:
# config: string, JSON configuration # config: string (JSON), sing-box configuration to modify
# external_controller: string, API listening address; Clash API will be disabled if empty # external_controller: string, API listening address; Clash API will be disabled if empty
# external_ui: string, Optional path to static web resources to serve at http://{{external-controller}}/ui # external_ui: string, path to static web resources to serve at http://{{external-controller}}/ui (optional)
# secret: string, Optional secret for the RESTful API Authenticate by specifying HTTP header # secret: string, secret for the RESTful API Authenticate by specifying HTTP header (optional)
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1368,7 +1429,7 @@ sing_box_cm_configure_clash_api() {
####################################### #######################################
# Save a sing-box JSON configuration to a file, removing service-specific tags. # Save a sing-box JSON configuration to a file, removing service-specific tags.
# Arguments: # Arguments:
# config: JSON configuration (string) # config: string (JSON), sing-box configuration to modify
# file_path: string, path to save the configuration file # file_path: string, path to save the configuration file
# Outputs: # Outputs:
# Writes the cleaned JSON configuration to the specified file # Writes the cleaned JSON configuration to the specified file