Compare commits

...

26 Commits

Author SHA1 Message Date
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
7a497f1e31 fix: reload PODKOP_CONFIG after uci commit to refresh config on shutdown 2025-11-25 17:05:25 +05:00
Andrey Petelin
d52f6e26ae refactor: add configurable DNS/curl timeouts and retries, detect service proxy, and improve connection checks 2025-11-25 17:04:31 +05:00
Andrey Petelin
68c61aed50 refactor: use uci wrappers 2025-11-25 14:10:18 +05:00
Andrey Petelin
626ac981eb refactor: configuring dnsmasq after starting sing-box 2025-11-25 13:53:24 +05:00
Kirill Sobakin
031c419ffb Merge pull request #252 from itdoginfo/fix/argument-list-too-long
jq: Argument list too long
2025-11-24 18:13:43 +03:00
Kirill Sobakin
c13fdf5785 HY2 examples 2025-11-24 18:05:40 +03:00
Andrey Petelin
1b7ab606ba refactor: unify source ruleset preparation and list handlers; make ruleset creation idempotent and atomic updates 2025-11-21 20:37:19 +05:00
Andrey Petelin
2bf208ecac fix: import remote plain domain and subnet lists using chunked processing 2025-11-16 13:21:51 +05:00
Andrey Petelin
e256e4bee5 chore: shorten Text List option label by removing the detailed format hint 2025-11-16 09:56:12 +05:00
Andrey Petelin
32c385b309 fix: load large plain domain/subnet lists in chunks; move ruleset logic to rulesets.sh and nft chunker to nft.sh 2025-11-16 09:55:44 +05:00
Kirill Sobakin
56829c74c8 Merge pull request #246 from itdoginfo/fix/listening_address 2025-11-10 12:58:10 +03:00
Andrey Petelin
9d78cd2ce4 style: add missing semicolons to o.depends calls in luci-app-podkop settings.js 2025-11-06 21:20:05 +05:00
Andrey Petelin
d9ce3b361e chore: correct typo "spedifying" to "specifying" in REST API secret comment 2025-11-06 21:18:15 +05:00
divocat
c67aadf267 feat: add yacd_secret_key support for ws 2025-11-06 16:52:08 +02:00
divocat
ac4d7570f3 feat: add translations for new keys 2025-11-06 16:20:35 +02:00
Andrey Petelin
86897fd0af fix: bind mixed proxy and Clash API to service IP (no 0.0.0.0); add YACD WAN toggle and secret key 2025-11-06 16:33:03 +05:00
Andrey Petelin
230ffbce46 feat: Add optional secret for RESTful API to experimental.clash_api config 2025-11-06 16:30:42 +05:00
Kirill Sobakin
dd5ddd1a14 Merge pull request #240 from itdoginfo/fix/long-nft-command
Import large subnet lists in chunks into nft sets
2025-10-30 16:01:14 +03:00
Andrey Petelin
cc947f9734 fix: import large subnet lists in chunks into nft sets 2025-10-30 14:07:12 +05:00
Kirill Sobakin
f8510cd828 Merge pull request #239 from itdoginfo/fix/crlf-clean
BUG: Clearing CRLF from SRS files
2025-10-29 21:15:47 +03:00
Andrey Petelin
23cbe7be4a fix: include filename in log and remove temp file on CRLF-to-LF conversion 2025-10-29 22:11:29 +05:00
Andrey Petelin
f168fb7e31 refactor: fetch remote JSON to temp files and parse ip_cidr into subnets; remove download_to_stream 2025-10-29 21:52:44 +05:00
Andrey Petelin
fe84b3154f fix: convert Windows CRLF line endings to LF for downloaded files 2025-10-29 21:36:46 +05:00
19 changed files with 804 additions and 432 deletions

View File

@@ -81,4 +81,46 @@ trojan://ou8pLSyx9N@127.0.0.1:17737?type=httpupgrade&path=%2Fhttpupgradepath&hos
# XHTTP # XHTTP
trojan://VEetltxLtw@127.0.0.1:59072?type=xhttp&path=%2Fxhttppath&host=google.com&mode=auto&security=none#trojan-xhttp trojan://VEetltxLtw@127.0.0.1:59072?type=xhttp&path=%2Fxhttppath&host=google.com&mode=auto&security=none#trojan-xhttp
```
## 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
hysteria2://password@example.com:443/#hysteria2-password
hysteria2://password@example.com:443/?insecure=0#hysteria2-password-insecure
# With SNI
hysteria2://password@example.com:443/?sni=example.com#hysteria2-password-sni
# With obfuscation
hysteria2://password@example.com:443/?obfs=salamander&obfs-password=obfspassword#hysteria2-obfs
# All parameters combined
hysteria2://mypassword@example.com:8443/?sni=example.com&obfs=salamander&obfs-password=obfspass&insecure=0#hysteria2-all-params
```
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
hy2://password@example.com:443/#hysteria2-password
hy2://password@example.com:443/?insecure=0#hysteria2-password-insecure
# With SNI
hy2://password@example.com:443/?sni=example.com#hysteria2-password-sni
# With obfuscation
hy2://password@example.com:443/?obfs=salamander&obfs-password=obfspassword#hysteria2-obfs
# All parameters combined
hy2://mypassword@example.com:8443/?sni=example.com&obfs=salamander&obfs-password=obfspass&insecure=0#hysteria2-all-params
``` ```

View File

@@ -41,6 +41,13 @@
"src/podkop/tabs/diagnostic/checks/runNftCheck.ts:106" "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:106"
] ]
}, },
{
"call": "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall.",
"key": "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall.",
"places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247"
]
},
{ {
"call": "Applicable for SOCKS and Shadowsocks proxy", "call": "Applicable for SOCKS and Shadowsocks proxy",
"key": "Applicable for SOCKS and Shadowsocks proxy", "key": "Applicable for SOCKS and Shadowsocks proxy",
@@ -101,14 +108,14 @@
"call": "Cache File Path", "call": "Cache File Path",
"key": "Cache File Path", "key": "Cache File Path",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:329" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:348"
] ]
}, },
{ {
"call": "Cache file path cannot be empty", "call": "Cache file path cannot be empty",
"key": "Cache file path cannot be empty", "key": "Cache file path cannot be empty",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:343" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:362"
] ]
}, },
{ {
@@ -178,7 +185,7 @@
"call": "Config File Path", "call": "Config File Path",
"key": "Config File Path", "key": "Config File Path",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:316" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:335"
] ]
}, },
{ {
@@ -276,14 +283,14 @@
"call": "Disable QUIC", "call": "Disable QUIC",
"key": "Disable QUIC", "key": "Disable QUIC",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:265"
] ]
}, },
{ {
"call": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming", "call": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming",
"key": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming", "key": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:266"
] ]
}, },
{ {
@@ -365,7 +372,7 @@
"call": "Dont Touch My DHCP!", "call": "Dont Touch My DHCP!",
"key": "Dont Touch My DHCP!", "key": "Dont Touch My DHCP!",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:307" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:326"
] ]
}, },
{ {
@@ -387,22 +394,22 @@
"call": "Download Lists via Proxy/VPN", "call": "Download Lists via Proxy/VPN",
"key": "Download Lists via Proxy/VPN", "key": "Download Lists via Proxy/VPN",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:269" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:288"
] ]
}, },
{ {
"call": "Download Lists via specific proxy section", "call": "Download Lists via specific proxy section",
"key": "Download Lists via specific proxy section", "key": "Download Lists via specific proxy section",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:278" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:297"
] ]
}, },
{ {
"call": "Downloading all lists via specific Proxy/VPN", "call": "Downloading all lists via specific Proxy/VPN",
"key": "Downloading all lists via specific Proxy/VPN", "key": "Downloading all lists via specific Proxy/VPN",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:270", "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:289",
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:279" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:298"
] ]
}, },
{ {
@@ -455,6 +462,13 @@
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:237" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:237"
] ]
}, },
{
"call": "Enable YACD WAN Access",
"key": "Enable YACD WAN Access",
"places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246"
]
},
{ {
"call": "Enter complete outbound configuration in JSON format", "call": "Enter complete outbound configuration in JSON format",
"key": "Enter complete outbound configuration in JSON format", "key": "Enter complete outbound configuration in JSON format",
@@ -515,14 +529,14 @@
"call": "Exclude NTP", "call": "Exclude NTP",
"key": "Exclude NTP", "key": "Exclude NTP",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:365" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:384"
] ]
}, },
{ {
"call": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN", "call": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN",
"key": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN", "key": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:385"
] ]
}, },
{ {
@@ -838,7 +852,7 @@
"call": "List Update Frequency", "call": "List Update Frequency",
"key": "List Update Frequency", "key": "List Update Frequency",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:276"
] ]
}, },
{ {
@@ -977,21 +991,21 @@
"call": "Path must be absolute (start with /)", "call": "Path must be absolute (start with /)",
"key": "Path must be absolute (start with /)", "key": "Path must be absolute (start with /)",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:347" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366"
] ]
}, },
{ {
"call": "Path must contain at least one directory (like /tmp/cache.db)", "call": "Path must contain at least one directory (like /tmp/cache.db)",
"key": "Path must contain at least one directory (like /tmp/cache.db)", "key": "Path must contain at least one directory (like /tmp/cache.db)",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:356" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:375"
] ]
}, },
{ {
"call": "Path must end with cache.db", "call": "Path must end with cache.db",
"key": "Path must end with cache.db", "key": "Path must end with cache.db",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:351" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:370"
] ]
}, },
{ {
@@ -1023,7 +1037,7 @@
"call": "Podkop will not modify your DHCP configuration", "call": "Podkop will not modify your DHCP configuration",
"key": "Podkop will not modify your DHCP configuration", "key": "Podkop will not modify your DHCP configuration",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:308" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:327"
] ]
}, },
{ {
@@ -1093,7 +1107,7 @@
"call": "Routing Excluded IPs", "call": "Routing Excluded IPs",
"key": "Routing Excluded IPs", "key": "Routing Excluded IPs",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:376" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:395"
] ]
}, },
{ {
@@ -1152,6 +1166,13 @@
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:328" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:328"
] ]
}, },
{
"call": "Secret key for authenticating remote access to YACD when WAN access is enabled.",
"key": "Secret key for authenticating remote access to YACD when WAN access is enabled.",
"places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257"
]
},
{ {
"call": "Sections", "call": "Sections",
"key": "Sections", "key": "Sections",
@@ -1184,7 +1205,7 @@
"call": "Select how often the domain or subnet lists are updated automatically", "call": "Select how often the domain or subnet lists are updated automatically",
"key": "Select how often the domain or subnet lists are updated automatically", "key": "Select how often the domain or subnet lists are updated automatically",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:258" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:277"
] ]
}, },
{ {
@@ -1213,14 +1234,14 @@
"call": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing", "call": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing",
"key": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing", "key": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:330" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:349"
] ]
}, },
{ {
"call": "Select path for sing-box config file. Change this ONLY if you know what you are doing", "call": "Select path for sing-box config file. Change this ONLY if you know what you are doing",
"key": "Select path for sing-box config file. Change this ONLY if you know what you are doing", "key": "Select path for sing-box config file. Change this ONLY if you know what you are doing",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:317" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:336"
] ]
}, },
{ {
@@ -1347,7 +1368,7 @@
"call": "Specify a local IP address to be excluded from routing", "call": "Specify a local IP address to be excluded from routing",
"key": "Specify a local IP address to be excluded from routing", "key": "Specify a local IP address to be excluded from routing",
"places": [ "places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:377" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:396"
] ]
}, },
{ {
@@ -1702,6 +1723,13 @@
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:330" "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:330"
] ]
}, },
{
"call": "YACD Secret Key",
"key": "YACD Secret Key",
"places": [
"../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:256"
]
},
{ {
"call": "You can select Output Network Interface, by default autodetect", "call": "You can select Output Network Interface, by default autodetect",
"key": "You can select Output Network Interface, by default autodetect", "key": "You can select Output Network Interface, by default autodetect",

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-10-27 11:15+0200\n" "POT-Creation-Date: 2025-11-06 14:19+0200\n"
"PO-Revision-Date: 2025-10-27 11:15+0200\n" "PO-Revision-Date: 2025-11-06 14:19+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"
@@ -40,6 +40,10 @@ msgstr ""
msgid "Additional marking rules found" msgid "Additional marking rules found"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:175 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:175
msgid "Applicable for SOCKS and Shadowsocks proxy" msgid "Applicable for SOCKS and Shadowsocks proxy"
msgstr "" msgstr ""
@@ -72,11 +76,11 @@ msgstr ""
msgid "Browser is using FakeIP correctly" msgid "Browser is using FakeIP correctly"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:329 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:348
msgid "Cache File Path" msgid "Cache File Path"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:343 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:362
msgid "Cache file path cannot be empty" msgid "Cache file path cannot be empty"
msgstr "" msgstr ""
@@ -119,7 +123,7 @@ msgstr ""
msgid "Community Lists" msgid "Community Lists"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:316 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:335
msgid "Config File Path" msgid "Config File Path"
msgstr "" msgstr ""
@@ -175,11 +179,11 @@ msgstr ""
msgid "Disable autostart" msgid "Disable autostart"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:265
msgid "Disable QUIC" msgid "Disable QUIC"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:266
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming" msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr "" msgstr ""
@@ -228,7 +232,7 @@ msgstr ""
msgid "Domain Resolver" msgid "Domain Resolver"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:307 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:326
msgid "Dont Touch My DHCP!" msgid "Dont Touch My DHCP!"
msgstr "" msgstr ""
@@ -241,16 +245,16 @@ msgstr ""
msgid "Download" msgid "Download"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:269 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:288
msgid "Download Lists via Proxy/VPN" msgid "Download Lists via Proxy/VPN"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:278 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:297
msgid "Download Lists via specific proxy section" msgid "Download Lists via specific proxy section"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:270 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:289
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:279 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:298
msgid "Downloading all lists via specific Proxy/VPN" msgid "Downloading all lists via specific Proxy/VPN"
msgstr "" msgstr ""
@@ -283,6 +287,10 @@ msgstr ""
msgid "Enable YACD" msgid "Enable YACD"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246
msgid "Enable YACD WAN Access"
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:65 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:65
msgid "Enter complete outbound configuration in JSON format" msgid "Enter complete outbound configuration in JSON format"
msgstr "" msgstr ""
@@ -315,11 +323,11 @@ msgstr ""
msgid "Every 5 minutes" msgid "Every 5 minutes"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:365 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:384
msgid "Exclude NTP" msgid "Exclude NTP"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:385
msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN" msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
msgstr "" msgstr ""
@@ -503,7 +511,7 @@ msgstr ""
msgid "Latest" msgid "Latest"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:276
msgid "List Update Frequency" msgid "List Update Frequency"
msgstr "" msgstr ""
@@ -585,15 +593,15 @@ msgstr ""
msgid "Path cannot be empty" msgid "Path cannot be empty"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:347 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366
msgid "Path must be absolute (start with /)" msgid "Path must be absolute (start with /)"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:356 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:375
msgid "Path must contain at least one directory (like /tmp/cache.db)" msgid "Path must contain at least one directory (like /tmp/cache.db)"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:351 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:370
msgid "Path must end with cache.db" msgid "Path must end with cache.db"
msgstr "" msgstr ""
@@ -613,7 +621,7 @@ msgstr ""
msgid "Podkop Settings" msgid "Podkop Settings"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:308 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:327
msgid "Podkop will not modify your DHCP configuration" msgid "Podkop will not modify your DHCP configuration"
msgstr "" msgstr ""
@@ -653,7 +661,7 @@ msgstr ""
msgid "Router DNS is routed through sing-box" msgid "Router DNS is routed through sing-box"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:376 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:395
msgid "Routing Excluded IPs" msgid "Routing Excluded IPs"
msgstr "" msgstr ""
@@ -689,6 +697,10 @@ msgstr ""
msgid "Russia inside restrictions" msgid "Russia inside restrictions"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:36 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:36
msgid "Sections" msgid "Sections"
msgstr "" msgstr ""
@@ -705,7 +717,7 @@ msgstr ""
msgid "Select DNS protocol to use" msgid "Select DNS protocol to use"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:258 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:277
msgid "Select how often the domain or subnet lists are updated automatically" msgid "Select how often the domain or subnet lists are updated automatically"
msgstr "" msgstr ""
@@ -722,11 +734,11 @@ msgstr ""
msgid "Select or enter DNS server address" msgid "Select or enter DNS server address"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:330 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:349
msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing" msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:317 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:336
msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing" msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
msgstr "" msgstr ""
@@ -799,7 +811,7 @@ msgstr ""
msgid "Source Network Interface" msgid "Source Network Interface"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:377 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:396
msgid "Specify a local IP address to be excluded from routing" msgid "Specify a local IP address to be excluded from routing"
msgstr "" msgstr ""
@@ -1014,6 +1026,10 @@ msgstr ""
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection." msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:256
msgid "YACD Secret Key"
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:127 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:127
msgid "You can select Output Network Interface, by default autodetect" msgid "You can select Output Network Interface, by default autodetect"
msgstr "" msgstr ""

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-10-27 13:15+0200\n" "POT-Creation-Date: 2025-11-06 16:19+0200\n"
"PO-Revision-Date: 2025-10-27 13:15+0200\n" "PO-Revision-Date: 2025-11-06 16:19+0200\n"
"Last-Translator: divocat\n" "Last-Translator: divocat\n"
"Language-Team: none\n" "Language-Team: none\n"
"Language: ru\n" "Language: ru\n"
@@ -35,6 +35,9 @@ msgstr "Активные соединения"
msgid "Additional marking rules found" msgid "Additional marking rules found"
msgstr "Найдены дополнительные правила маркировки" msgstr "Найдены дополнительные правила маркировки"
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr "Обеспечивает доступ к YACD из WAN. Убедитесь, что в брандмауэре открыт соответствующий порт."
msgid "Applicable for SOCKS and Shadowsocks proxy" msgid "Applicable for SOCKS and Shadowsocks proxy"
msgstr "Применимо для SOCKS и Shadowsocks прокси" msgstr "Применимо для SOCKS и Shadowsocks прокси"
@@ -206,6 +209,9 @@ msgstr "Включить смешанный прокси-сервер, разр
msgid "Enable YACD" msgid "Enable YACD"
msgstr "Включить YACD" msgstr "Включить YACD"
msgid "Enable YACD WAN Access"
msgstr "Включить доступ YACD WAN"
msgid "Enter complete outbound configuration in JSON format" msgid "Enter complete outbound configuration in JSON format"
msgstr "Введите полную конфигурацию исходящего соединения в формате JSON" msgstr "Введите полную конфигурацию исходящего соединения в формате JSON"
@@ -497,6 +503,9 @@ msgstr "Запустить диагностику"
msgid "Russia inside restrictions" msgid "Russia inside restrictions"
msgstr "Ограничения Russia inside" msgstr "Ограничения Russia inside"
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr "Секретный ключ для аутентификации удаленного доступа к YACD при включенном доступе через WAN."
msgid "Sections" msgid "Sections"
msgstr "Секции" msgstr "Секции"
@@ -722,5 +731,8 @@ msgstr "Предупреждение: %s нельзя использовать
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection." msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr "Предупреждение: Russia inside может быть использован только с %s. %s уже есть в Russia inside и будет удален из выбранных." msgstr "Предупреждение: Russia inside может быть использован только с %s. %s уже есть в Russia inside и будет удален из выбранных."
msgid "YACD Secret Key"
msgstr "Секретный ключ YACD"
msgid "You can select Output Network Interface, by default autodetect" msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически." msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически."

View File

@@ -0,0 +1,9 @@
import { getConfigSections } from './getConfigSections';
export async function getClashApiSecret() {
const sections = await getConfigSections();
const settings = sections.find((section) => section['.type'] === 'settings');
return settings?.yacd_secret_key || '';
}

View File

@@ -1,7 +1,9 @@
import { getConfigSections } from './getConfigSections'; import { getConfigSections } from './getConfigSections';
import { getDashboardSections } from './getDashboardSections'; import { getDashboardSections } from './getDashboardSections';
import { getClashApiSecret } from './getClashApiSecret';
export const CustomPodkopMethods = { export const CustomPodkopMethods = {
getConfigSections, getConfigSections,
getDashboardSections, getDashboardSections,
getClashApiSecret,
}; };

View File

@@ -8,6 +8,7 @@ import { CustomPodkopMethods, PodkopShellMethods } from '../../methods';
import { logger, socket, store, StoreType } from '../../services'; import { logger, socket, store, StoreType } from '../../services';
import { renderSections, renderWidget } from './partials'; import { renderSections, renderWidget } from './partials';
import { fetchServicesInfo } from '../../fetchers'; import { fetchServicesInfo } from '../../fetchers';
import { getClashApiSecret } from '../../methods/custom/getClashApiSecret';
// Fetchers // Fetchers
@@ -38,8 +39,10 @@ async function fetchDashboardSections() {
} }
async function connectToClashSockets() { async function connectToClashSockets() {
const clashApiSecret = await getClashApiSecret();
socket.subscribe( socket.subscribe(
`${getClashWsUrl()}/traffic?token=`, `${getClashWsUrl()}/traffic?token=${clashApiSecret}`,
(msg) => { (msg) => {
const parsedMsg = JSON.parse(msg); const parsedMsg = JSON.parse(msg);
@@ -68,7 +71,7 @@ async function connectToClashSockets() {
); );
socket.subscribe( socket.subscribe(
`${getClashWsUrl()}/connections?token=`, `${getClashWsUrl()}/connections?token=${clashApiSecret}`,
(msg) => { (msg) => {
const parsedMsg = JSON.parse(msg); const parsedMsg = JSON.parse(msg);

View File

@@ -126,6 +126,7 @@ export namespace Podkop {
export type ConfigSection = ConfigBaseSection & { export type ConfigSection = ConfigBaseSection & {
'.name': string; '.name': string;
'.type': 'settings' | 'section'; '.type': 'settings' | 'section';
yacd_secret_key?: string;
}; };
export interface MethodSuccessResponse<T> { export interface MethodSuccessResponse<T> {

View File

@@ -731,10 +731,18 @@ async function getDashboardSections() {
}; };
} }
// src/podkop/methods/custom/getClashApiSecret.ts
async function getClashApiSecret() {
const sections = await getConfigSections();
const settings = sections.find((section) => section[".type"] === "settings");
return settings?.yacd_secret_key || "";
}
// src/podkop/methods/custom/index.ts // src/podkop/methods/custom/index.ts
var CustomPodkopMethods = { var CustomPodkopMethods = {
getConfigSections, getConfigSections,
getDashboardSections getDashboardSections,
getClashApiSecret
}; };
// src/constants.ts // src/constants.ts
@@ -1876,8 +1884,9 @@ async function fetchDashboardSections() {
}); });
} }
async function connectToClashSockets() { async function connectToClashSockets() {
const clashApiSecret = await getClashApiSecret();
socket.subscribe( socket.subscribe(
`${getClashWsUrl()}/traffic?token=`, `${getClashWsUrl()}/traffic?token=${clashApiSecret}`,
(msg) => { (msg) => {
const parsedMsg = JSON.parse(msg); const parsedMsg = JSON.parse(msg);
store.set({ store.set({
@@ -1904,7 +1913,7 @@ async function connectToClashSockets() {
} }
); );
socket.subscribe( socket.subscribe(
`${getClashWsUrl()}/connections?token=`, `${getClashWsUrl()}/connections?token=${clashApiSecret}`,
(msg) => { (msg) => {
const parsedMsg = JSON.parse(msg); const parsedMsg = JSON.parse(msg);
store.set({ store.set({

View File

@@ -445,7 +445,7 @@ function createSectionContent(section) {
); );
o.value("disabled", _("Disabled")); o.value("disabled", _("Disabled"));
o.value("dynamic", _("Dynamic List")); o.value("dynamic", _("Dynamic List"));
o.value("text", _("Text List (comma/space/newline separated)")); o.value("text", _("Text List"));
o.default = "disabled"; o.default = "disabled";
o.rmempty = false; o.rmempty = false;

View File

@@ -240,6 +240,25 @@ function createSettingsContent(section) {
o.default = "0"; o.default = "0";
o.rmempty = false; o.rmempty = false;
o = section.option(
form.Flag,
"enable_yacd_wan_access",
_("Enable YACD WAN Access"),
_("Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."),
);
o.depends("enable_yacd", "1");
o.default = "0";
o.rmempty = false;
o = section.option(
form.Value,
"yacd_secret_key",
_("YACD Secret Key"),
_("Secret key for authenticating remote access to YACD when WAN access is enabled."),
);
o.depends("enable_yacd_wan_access", "1");
o.rmempty = false;
o = section.option( o = section.option(
form.Flag, form.Flag,
"disable_quic", "disable_quic",

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-10-27 13:15+0200\n" "POT-Creation-Date: 2025-11-06 16:19+0200\n"
"PO-Revision-Date: 2025-10-27 13:15+0200\n" "PO-Revision-Date: 2025-11-06 16:19+0200\n"
"Last-Translator: divocat\n" "Last-Translator: divocat\n"
"Language-Team: none\n" "Language-Team: none\n"
"Language: ru\n" "Language: ru\n"
@@ -35,6 +35,9 @@ msgstr "Активные соединения"
msgid "Additional marking rules found" msgid "Additional marking rules found"
msgstr "Найдены дополнительные правила маркировки" msgstr "Найдены дополнительные правила маркировки"
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr "Обеспечивает доступ к YACD из WAN. Убедитесь, что в брандмауэре открыт соответствующий порт."
msgid "Applicable for SOCKS and Shadowsocks proxy" msgid "Applicable for SOCKS and Shadowsocks proxy"
msgstr "Применимо для SOCKS и Shadowsocks прокси" msgstr "Применимо для SOCKS и Shadowsocks прокси"
@@ -206,6 +209,9 @@ msgstr "Включить смешанный прокси-сервер, разр
msgid "Enable YACD" msgid "Enable YACD"
msgstr "Включить YACD" msgstr "Включить YACD"
msgid "Enable YACD WAN Access"
msgstr "Включить доступ YACD WAN"
msgid "Enter complete outbound configuration in JSON format" msgid "Enter complete outbound configuration in JSON format"
msgstr "Введите полную конфигурацию исходящего соединения в формате JSON" msgstr "Введите полную конфигурацию исходящего соединения в формате JSON"
@@ -497,6 +503,9 @@ msgstr "Запустить диагностику"
msgid "Russia inside restrictions" msgid "Russia inside restrictions"
msgstr "Ограничения Russia inside" msgstr "Ограничения Russia inside"
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr "Секретный ключ для аутентификации удаленного доступа к YACD при включенном доступе через WAN."
msgid "Sections" msgid "Sections"
msgstr "Секции" msgstr "Секции"
@@ -681,10 +690,10 @@ msgid "URLTest Proxy Links"
msgstr "Ссылки прокси для URLTest" msgstr "Ссылки прокси для URLTest"
msgid "URLTest Testing URL" msgid "URLTest Testing URL"
msgstr "URL для тестирования URLTest" msgstr "URLTest ссылка для проверки"
msgid "URLTest Tolerance" msgid "URLTest Tolerance"
msgstr "Порог переключения URLTest" msgstr "URLTest допустимое отклонение"
msgid "User Domain List Type" msgid "User Domain List Type"
msgstr "Тип пользовательского списка доменов" msgstr "Тип пользовательского списка доменов"
@@ -722,5 +731,8 @@ msgstr "Предупреждение: %s нельзя использовать
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection." msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr "Предупреждение: Russia inside может быть использован только с %s. %s уже есть в Russia inside и будет удален из выбранных." msgstr "Предупреждение: Russia inside может быть использован только с %s. %s уже есть в Russia inside и будет удален из выбранных."
msgid "YACD Secret Key"
msgstr "Секретный ключ YACD"
msgid "You can select Output Network Interface, by default autodetect" msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически." msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически."

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-10-27 11:15+0200\n" "POT-Creation-Date: 2025-11-06 14:19+0200\n"
"PO-Revision-Date: 2025-10-27 11:15+0200\n" "PO-Revision-Date: 2025-11-06 14:19+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"
@@ -40,6 +40,10 @@ msgstr ""
msgid "Additional marking rules found" msgid "Additional marking rules found"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:175 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:175
msgid "Applicable for SOCKS and Shadowsocks proxy" msgid "Applicable for SOCKS and Shadowsocks proxy"
msgstr "" msgstr ""
@@ -72,11 +76,11 @@ msgstr ""
msgid "Browser is using FakeIP correctly" msgid "Browser is using FakeIP correctly"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:329 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:348
msgid "Cache File Path" msgid "Cache File Path"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:343 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:362
msgid "Cache file path cannot be empty" msgid "Cache file path cannot be empty"
msgstr "" msgstr ""
@@ -119,7 +123,7 @@ msgstr ""
msgid "Community Lists" msgid "Community Lists"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:316 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:335
msgid "Config File Path" msgid "Config File Path"
msgstr "" msgstr ""
@@ -175,11 +179,11 @@ msgstr ""
msgid "Disable autostart" msgid "Disable autostart"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:265
msgid "Disable QUIC" msgid "Disable QUIC"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:266
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming" msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr "" msgstr ""
@@ -228,7 +232,7 @@ msgstr ""
msgid "Domain Resolver" msgid "Domain Resolver"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:307 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:326
msgid "Dont Touch My DHCP!" msgid "Dont Touch My DHCP!"
msgstr "" msgstr ""
@@ -241,16 +245,16 @@ msgstr ""
msgid "Download" msgid "Download"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:269 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:288
msgid "Download Lists via Proxy/VPN" msgid "Download Lists via Proxy/VPN"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:278 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:297
msgid "Download Lists via specific proxy section" msgid "Download Lists via specific proxy section"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:270 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:289
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:279 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:298
msgid "Downloading all lists via specific Proxy/VPN" msgid "Downloading all lists via specific Proxy/VPN"
msgstr "" msgstr ""
@@ -283,6 +287,10 @@ msgstr ""
msgid "Enable YACD" msgid "Enable YACD"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246
msgid "Enable YACD WAN Access"
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:65 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:65
msgid "Enter complete outbound configuration in JSON format" msgid "Enter complete outbound configuration in JSON format"
msgstr "" msgstr ""
@@ -315,11 +323,11 @@ msgstr ""
msgid "Every 5 minutes" msgid "Every 5 minutes"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:365 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:384
msgid "Exclude NTP" msgid "Exclude NTP"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:385
msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN" msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
msgstr "" msgstr ""
@@ -503,7 +511,7 @@ msgstr ""
msgid "Latest" msgid "Latest"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:276
msgid "List Update Frequency" msgid "List Update Frequency"
msgstr "" msgstr ""
@@ -585,15 +593,15 @@ msgstr ""
msgid "Path cannot be empty" msgid "Path cannot be empty"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:347 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366
msgid "Path must be absolute (start with /)" msgid "Path must be absolute (start with /)"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:356 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:375
msgid "Path must contain at least one directory (like /tmp/cache.db)" msgid "Path must contain at least one directory (like /tmp/cache.db)"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:351 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:370
msgid "Path must end with cache.db" msgid "Path must end with cache.db"
msgstr "" msgstr ""
@@ -613,7 +621,7 @@ msgstr ""
msgid "Podkop Settings" msgid "Podkop Settings"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:308 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:327
msgid "Podkop will not modify your DHCP configuration" msgid "Podkop will not modify your DHCP configuration"
msgstr "" msgstr ""
@@ -653,7 +661,7 @@ msgstr ""
msgid "Router DNS is routed through sing-box" msgid "Router DNS is routed through sing-box"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:376 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:395
msgid "Routing Excluded IPs" msgid "Routing Excluded IPs"
msgstr "" msgstr ""
@@ -689,6 +697,10 @@ msgstr ""
msgid "Russia inside restrictions" msgid "Russia inside restrictions"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:36 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:36
msgid "Sections" msgid "Sections"
msgstr "" msgstr ""
@@ -705,7 +717,7 @@ msgstr ""
msgid "Select DNS protocol to use" msgid "Select DNS protocol to use"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:258 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:277
msgid "Select how often the domain or subnet lists are updated automatically" msgid "Select how often the domain or subnet lists are updated automatically"
msgstr "" msgstr ""
@@ -722,11 +734,11 @@ msgstr ""
msgid "Select or enter DNS server address" msgid "Select or enter DNS server address"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:330 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:349
msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing" msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:317 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:336
msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing" msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
msgstr "" msgstr ""
@@ -799,7 +811,7 @@ msgstr ""
msgid "Source Network Interface" msgid "Source Network Interface"
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:377 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:396
msgid "Specify a local IP address to be excluded from routing" msgid "Specify a local IP address to be excluded from routing"
msgstr "" msgstr ""
@@ -1014,6 +1026,10 @@ msgstr ""
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection." msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr "" msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:256
msgid "YACD Secret Key"
msgstr ""
#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:127 #: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:127
msgid "You can select Output Network Interface, by default autodetect" msgid "You can select Output Network Interface, by default autodetect"
msgstr "" msgstr ""

View File

@@ -18,6 +18,7 @@ check_required_file "$PODKOP_LIB/helpers.sh"
check_required_file "$PODKOP_LIB/sing_box_config_manager.sh" check_required_file "$PODKOP_LIB/sing_box_config_manager.sh"
check_required_file "$PODKOP_LIB/sing_box_config_facade.sh" check_required_file "$PODKOP_LIB/sing_box_config_facade.sh"
check_required_file "$PODKOP_LIB/logging.sh" check_required_file "$PODKOP_LIB/logging.sh"
check_required_file "$PODKOP_LIB/rulesets.sh"
. /lib/config/uci.sh . /lib/config/uci.sh
. /lib/functions.sh . /lib/functions.sh
. "$PODKOP_LIB/constants.sh" . "$PODKOP_LIB/constants.sh"
@@ -26,6 +27,7 @@ check_required_file "$PODKOP_LIB/logging.sh"
. "$PODKOP_LIB/sing_box_config_manager.sh" . "$PODKOP_LIB/sing_box_config_manager.sh"
. "$PODKOP_LIB/sing_box_config_facade.sh" . "$PODKOP_LIB/sing_box_config_facade.sh"
. "$PODKOP_LIB/logging.sh" . "$PODKOP_LIB/logging.sh"
. "$PODKOP_LIB/rulesets.sh"
config_load "$PODKOP_CONFIG" config_load "$PODKOP_CONFIG"
@@ -123,19 +125,16 @@ start_main() {
# base # base
route_table_rule_mark route_table_rule_mark
create_nft_table create_nft_rules
sing_box_uci sing_box_configure_service
# sing-box # sing-box
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
local exclude_ntp echolog "Failed to start sing-box service"
config_get_bool exclude_ntp "settings" "exclude_ntp" "0" exit 1
if [ "$exclude_ntp" -eq 1 ]; then
log "NTP traffic exclude for proxy"
nft insert rule inet "$NFT_TABLE_NAME" mangle udp dport 123 return
fi fi
log "Nice" log "Nice"
@@ -143,16 +142,6 @@ start_main() {
echo $! > /var/run/podkop_list_update.pid echo $! > /var/run/podkop_list_update.pid
} }
start() {
start_main
config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
if [ "$dont_touch_dhcp" -eq 0 ]; then
dnsmasq_add_resolver
fi
uci_set "podkop" "settings" "shutdown_correctly" 0
uci commit "podkop" && config_load "$PODKOP_CONFIG"
}
stop_main() { stop_main() {
log "Stopping the podkop" log "Stopping the podkop"
@@ -188,13 +177,27 @@ stop_main() {
/etc/init.d/sing-box stop /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() { 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
stop_main stop_main
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"
} }
@@ -279,7 +282,7 @@ nft_init_interfaces_set() {
done done
} }
create_nft_table() { create_nft_rules() {
log "Create nft table" log "Create nft table"
nft_create_table "$NFT_TABLE_NAME" nft_create_table "$NFT_TABLE_NAME"
@@ -327,6 +330,13 @@ create_nft_table() {
nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_COMMON_SET_NAME" meta l4proto udp meta mark set 0x105 counter nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_COMMON_SET_NAME" meta l4proto udp meta mark set 0x105 counter
nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "$SB_FAKEIP_INET4_RANGE" meta l4proto tcp meta mark set 0x105 counter nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "$SB_FAKEIP_INET4_RANGE" meta l4proto tcp meta mark set 0x105 counter
nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "$SB_FAKEIP_INET4_RANGE" meta l4proto tcp meta mark set 0x105 counter nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "$SB_FAKEIP_INET4_RANGE" meta l4proto tcp meta mark set 0x105 counter
local exclude_ntp
config_get_bool exclude_ntp "settings" "exclude_ntp" "0"
if [ "$exclude_ntp" -eq 1 ]; then
log "NTP traffic exclude for proxy"
nft insert rule inet "$NFT_TABLE_NAME" mangle udp dport 123 return
fi
} }
backup_dnsmasq_config_option() { backup_dnsmasq_config_option() {
@@ -340,7 +350,7 @@ backup_dnsmasq_config_option() {
fi fi
} }
dnsmasq_add_resolver() { dnsmasq_configure() {
local shutdown_correctly local shutdown_correctly
config_get shutdown_correctly "settings" "shutdown_correctly" config_get shutdown_correctly "settings" "shutdown_correctly"
if [ "$shutdown_correctly" -eq 0 ]; then if [ "$shutdown_correctly" -eq 0 ]; then
@@ -472,42 +482,55 @@ remove_cron_job() {
list_update() { list_update() {
echolog "🔄 Starting lists update..." echolog "🔄 Starting lists update..."
local nslookup_timeout=3
local nslookup_attempts=10
local curl_timeout=5
local curl_attempts=10
local curl_max_timeout=10
local delay=3
local i local i
for i in $(seq 1 60); do # DNS Check
if nslookup -timeout=1 openwrt.org > /dev/null 2>&1; then for i in $(seq 1 $nslookup_timeout); do
if nslookup -timeout=$nslookup_timeout openwrt.org > /dev/null 2>&1; then
echolog "✅ DNS check passed" echolog "✅ DNS check passed"
break break
fi fi
log "DNS is unavailable [$i/60]" echolog "DNS is unavailable [$i/$nslookup_attempts]"
sleep 3 sleep $delay
done done
if [ "$i" -eq 60 ]; then if [ "$i" -eq $nslookup_attempts ]; then
echolog "❌ DNS check failed after 60 attempts" echolog "❌ DNS check failed after $nslookup_attempts attempts"
return 1 return 1
fi fi
for i in $(seq 1 60); do # Github Check
config_get_bool download_lists_via_proxy "settings" "download_lists_via_proxy" "0" for i in $(seq 1 $curl_attempts); do
if [ "$download_lists_via_proxy" -eq 1 ]; then local service_proxy_address
if http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" curl -s -m 3 https://github.com > /dev/null; then service_proxy_address="$(get_service_proxy_address)"
if [ -n "$http_proxy_address" ]; then
if curl -s -x "http://$service_proxy_address" -m $curl_timeout https://github.com > /dev/null; then
echolog "✅ GitHub connection check passed (via proxy)" echolog "✅ GitHub connection check passed (via proxy)"
break break
fi fi
else else
if curl -s -m 3 https://github.com > /dev/null; then if curl -s -m $curl_timeout https://github.com > /dev/null; then
echolog "✅ GitHub connection check passed" echolog "✅ GitHub connection check passed"
break break
fi fi
fi fi
echolog "GitHub is unavailable [$i/60]" echolog "GitHub is unavailable [$i/$curl_attempts] (max-timeout=$curl_timeout)"
sleep 3 if [ "$curl_timeout" -lt $curl_max_timeout ]; then
curl_timeout=$((curl_timeout + 1))
fi
sleep $delay
done done
if [ "$i" -eq 60 ]; then if [ "$i" -eq $curl_attempts ]; then
echolog "❌ GitHub connection check failed after 60 attempts" echolog "❌ GitHub connection check failed after $curl_attempts attempts"
return 1 return 1
fi fi
@@ -525,30 +548,30 @@ list_update() {
} }
# sing-box funcs # sing-box funcs
sing_box_configure_service() {
sing_box_uci() {
local sing_box_enabled sing_box_user sing_box_config_path sing_box_conffile local sing_box_enabled sing_box_user sing_box_config_path sing_box_conffile
sing_box_enabled=$(uci get "sing-box.main.enabled") sing_box_enabled="$(uci_get "sing-box" "main" "enabled")"
sing_box_user=$(uci get "sing-box.main.user") sing_box_user="$(uci_get "sing-box" "main" "user")"
if [ "$sing_box_enabled" -ne 1 ]; then if [ "$sing_box_enabled" -ne 1 ]; then
uci set "sing-box.main.enabled=1" uci_set "sing-box" "main" "enabled" 1
uci commit "sing-box" uci_commit "sing-box"
log "sing-box service has been enabled" log "sing-box service has been enabled"
fi fi
if [ "$sing_box_user" != "root" ]; then if [ "$sing_box_user" != "root" ]; then
uci set "sing-box.main.user=root" uci_set "sing-box" "main" "user" "root"
uci commit "sing-box" uci_commit "sing-box"
log "sing-box service user has been changed to root" log "sing-box service user has been changed to root"
fi fi
config_get sing_box_config_path "settings" "config_path" config_get sing_box_config_path "settings" "config_path"
sing_box_conffile=$(uci get "sing-box.main.conffile") sing_box_conffile="$(uci_get "sing-box" "main" "conffile")"
log "sing-box config path: $sing_box_config_path" "debug" log "sing-box config path: $sing_box_config_path" "debug"
log "sing-box service conffile: $sing_box_conffile" "debug" log "sing-box service conffile: $sing_box_conffile" "debug"
if [ "$sing_box_conffile" != "$sing_box_config_path" ]; then if [ "$sing_box_conffile" != "$sing_box_config_path" ]; then
uci set "sing-box.main.conffile=$sing_box_config_path" uci_set "sing-box" "main" "conffile" "$sing_box_config_path"
uci commit "sing-box" uci_commit "sing-box"
log "Configuration file path has been set to $sing_box_config_path" log "Configuration file path has been set to $sing_box_config_path"
fi fi
@@ -865,66 +888,37 @@ configure_routing_for_section_lists() {
if [ "$user_domain_list_type" != "disabled" ]; then if [ "$user_domain_list_type" != "disabled" ]; then
log "Processing user domains routing rules for '$section' section" log "Processing user domains routing rules for '$section' section"
prepare_common_ruleset "$section" "domains" "$route_rule_tag" configure_user_domain_list "$section" "$route_rule_tag"
configure_user_domain_or_subnets_list "$section" "domains" "$route_rule_tag"
fi fi
if [ "$user_subnet_list_type" != "disabled" ]; then if [ "$user_subnet_list_type" != "disabled" ]; then
log "Processing user subnets routing rules for '$section' section" log "Processing user subnets routing rules for '$section' section"
prepare_common_ruleset "$section" "subnets" "$route_rule_tag" configure_user_subnet_list "$section" "$route_rule_tag"
configure_user_domain_or_subnets_list "$section" "subnets" "$route_rule_tag"
fi fi
if [ -n "$local_domain_lists" ]; then if [ -n "$local_domain_lists" ]; then
log "Processing local domains routing rules for '$section' section" log "Processing local domains routing rules for '$section' section"
configure_local_domain_or_subnet_lists "$section" "domains" "$route_rule_tag" configure_local_domain_lists "$section" "$route_rule_tag"
fi fi
if [ -n "$local_subnet_lists" ]; then if [ -n "$local_subnet_lists" ]; then
log "Processing local subnets routing rules for '$section' section" log "Processing local subnets routing rules for '$section' section"
configure_local_domain_or_subnet_lists "$section" "subnets" "$route_rule_tag" configure_local_subnet_lists "$section" "$route_rule_tag"
fi fi
if [ -n "$remote_domain_lists" ]; then if [ -n "$remote_domain_lists" ]; then
log "Processing remote domains routing rules for '$section' section" log "Processing remote domains routing rules for '$section' section"
prepare_common_ruleset "$section" "domains" "$route_rule_tag"
config_list_foreach "$section" "remote_domain_lists" configure_remote_domain_or_subnet_list_handler \ config_list_foreach "$section" "remote_domain_lists" configure_remote_domain_or_subnet_list_handler \
"domains" "$section" "$route_rule_tag" "domains" "$section" "$route_rule_tag"
fi fi
if [ -n "$remote_subnet_lists" ]; then if [ -n "$remote_subnet_lists" ]; then
log "Processing remote subnets routing rules for '$section' section" log "Processing remote subnets routing rules for '$section' section"
prepare_common_ruleset "$section" "subnets" "$route_rule_tag"
config_list_foreach "$section" "remote_subnet_lists" configure_remote_domain_or_subnet_list_handler \ config_list_foreach "$section" "remote_subnet_lists" configure_remote_domain_or_subnet_list_handler \
"subnets" "$section" "$route_rule_tag" "subnets" "$section" "$route_rule_tag"
fi fi
} }
prepare_common_ruleset() {
local section="$1"
local type="$2"
local route_rule_tag="$3"
log "Preparing a common $type ruleset for '$section' section" "debug"
ruleset_tag=$(get_ruleset_tag "$section" "common" "$type")
ruleset_filename="$ruleset_tag.json"
ruleset_filepath="$TMP_RULESET_FOLDER/$ruleset_filename"
if file_exists "$ruleset_filepath"; then
log "Ruleset $ruleset_filepath already exists. Skipping." "debug"
else
sing_box_cm_create_local_source_ruleset "$ruleset_filepath"
config=$(sing_box_cm_add_local_ruleset "$config" "$ruleset_tag" "source" "$ruleset_filepath")
config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag")
case "$type" in
domains)
config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_FAKEIP_DNS_RULE_TAG" "rule_set" "$ruleset_tag")
;;
subnets) ;;
*) log "Unsupported remote rule set type: $type" "error" ;;
esac
fi
}
configure_community_list_handler() { configure_community_list_handler() {
local tag="$1" local tag="$1"
local section="$2" local section="$2"
@@ -942,99 +936,113 @@ configure_community_list_handler() {
config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_FAKEIP_DNS_RULE_TAG" "rule_set" "$ruleset_tag") config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_FAKEIP_DNS_RULE_TAG" "rule_set" "$ruleset_tag")
} }
configure_user_domain_or_subnets_list() { prepare_source_ruleset() {
local section="$1" local section="$1"
local type="$2" local name="$2"
local type="$3"
local route_rule_tag="$4"
local items ruleset_tag ruleset_filename ruleset_filepath json_array log "Preparing a $name $type rule set for '$section' section" "debug"
case "$type" in ruleset_tag=$(get_ruleset_tag "$section" "$name" "$type")
domains) ruleset_filepath="$TMP_RULESET_FOLDER/$ruleset_tag.json"
local user_domain_list_type create_source_rule_set "$ruleset_filepath"
config_get user_domain_list_type "$section" "user_domain_list_type" case $? in
case "$user_domain_list_type" in 0)
dynamic) config_get items "$section" "user_domains" ;; config=$(sing_box_cm_add_local_ruleset "$config" "$ruleset_tag" "source" "$ruleset_filepath")
text) config_get items "$section" "user_domains_text" ;; config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag")
esac case "$type" in
;; domains)
subnets) config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_FAKEIP_DNS_RULE_TAG" "rule_set" "$ruleset_tag")
local user_subnet_list_type ;;
config_get user_subnet_list_type "$section" "user_subnet_list_type" subnets) ;;
case "$user_subnet_list_type" in *)
dynamic) config_get items "$section" "user_subnets" ;; log "Unsupported remote rule set type: $type" "error"
text) config_get items "$section" "user_subnets_text" ;; return 1
;;
esac esac
;; ;;
3) log "Source rule set $ruleset_filepath already exists, skipping." "debug" ;;
esac
}
configure_user_domain_list() {
local section="$1"
local route_rule_tag="$2"
prepare_source_ruleset "$section" "user" "domains" "$route_rule_tag"
local user_domain_list_type items json_array
config_get user_domain_list_type "$section" "user_domain_list_type"
case "$user_domain_list_type" in
dynamic) config_get items "$section" "user_domains" ;;
text) config_get items "$section" "user_domains_text" ;;
esac esac
ruleset_tag=$(get_ruleset_tag "$section" "common" "$type") items="$(parse_domain_or_subnet_string_to_commas_string "$items" "domains")"
ruleset_filename="$ruleset_tag.json"
ruleset_filepath="$TMP_RULESET_FOLDER/$ruleset_filename"
items="$(parse_domain_or_subnet_string_to_commas_string "$items" "$type")"
json_array="$(comma_string_to_json_array "$items")" json_array="$(comma_string_to_json_array "$items")"
case "$type" in patch_source_ruleset_rules "$ruleset_filepath" "domain_suffix" "$json_array"
domains) sing_box_cm_patch_local_source_ruleset_rules "$ruleset_filepath" "domain_suffix" "$json_array" ;;
subnets)
sing_box_cm_patch_local_source_ruleset_rules "$ruleset_filepath" "ip_cidr" "$json_array"
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME" "$items"
;;
esac
} }
configure_local_domain_or_subnet_lists() { configure_user_subnet_list() {
local section="$1" local section="$1"
local type="$2" local route_rule_tag="$2"
local route_rule_tag="$3"
local ruleset_tag ruleset_filename ruleset_filepath prepare_source_ruleset "$section" "user" "subnets" "$route_rule_tag"
ruleset_tag="$(get_ruleset_tag "$section" "local" "$type")"
ruleset_filename="$ruleset_tag.json"
ruleset_filepath="$TMP_RULESET_FOLDER/$ruleset_filename"
sing_box_cm_create_local_source_ruleset "$ruleset_filepath" local user_subnet_list_type items json_array
config=$(sing_box_cm_add_local_ruleset "$config" "$ruleset_tag" "source" "$ruleset_filepath") config_get user_subnet_list_type "$section" "user_subnet_list_type"
config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag") case "$user_subnet_list_type" in
dynamic) config_get items "$section" "user_subnets" ;;
case "$type" in text) config_get items "$section" "user_subnets_text" ;;
domains)
config_list_foreach "$section" "local_domain_lists" import_local_domain_or_subnet_list "$type" \
"$section" "$ruleset_filepath"
config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_FAKEIP_DNS_RULE_TAG" "rule_set" "$ruleset_tag")
;;
subnets)
config_list_foreach "$section" "local_subnet_lists" import_local_domain_or_subnet_list "$type" \
"$section" "$ruleset_filepath"
;;
*) log "Unsupported local rule set type: $type" "error" ;;
esac esac
items="$(parse_domain_or_subnet_string_to_commas_string "$items" "subnets")"
json_array="$(comma_string_to_json_array "$items")"
patch_source_ruleset_rules "$ruleset_filepath" "ip_cidr" "$json_array"
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME" "$items"
} }
import_local_domain_or_subnet_list() { configure_local_domain_lists() {
local filepath="$1" local section="$1"
local type="$2" local route_rule_tag="$2"
local section="$3"
local ruleset_filepath="$4"
if ! file_exists "$filepath"; then prepare_source_ruleset "$section" "local" "domains" "$route_rule_tag"
log "File $filepath not found" "error"
config_list_foreach "$section" "local_domain_lists" import_local_domain_list_handler "$ruleset_filepath"
}
import_local_domain_list_handler() {
local local_domain_list_filepath="$1"
local ruleset_filepath="$2"
if ! file_exists "$local_domain_list_filepath"; then
log "Local domain list file $local_domain_list_filepath not found" "error"
return 1 return 1
fi fi
local items json_array import_plain_domain_list_to_local_source_ruleset_chunked "$local_domain_list_filepath" "$ruleset_filepath"
items="$(parse_domain_or_subnet_file_to_comma_string "$filepath" "$type")" }
if [ -z "$items" ]; then configure_local_subnet_lists() {
log "No valid $type found in $filepath" "warn" local section="$1"
return 0 local route_rule_tag="$2"
prepare_source_ruleset "$section" "local" "subnets" "$route_rule_tag"
config_list_foreach "$section" "local_subnet_lists" import_local_subnets_list_handler "$ruleset_filepath"
}
import_local_subnets_list_handler() {
local local_subnet_list_filepath="$1"
local ruleset_filepath="$2"
if ! file_exists "$local_subnet_list_filepath"; then
log "Local subnet list file $local_subnet_list_filepath not found" "error"
return 1
fi fi
json_array="$(comma_string_to_json_array "$items")" import_plain_subnet_list_to_local_source_ruleset_chunked "$local_subnet_list_filepath" "$ruleset_filepath"
case "$type" in nft_add_set_elements_from_file_chunked "$local_subnet_list_filepath" "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME"
domains) sing_box_cm_patch_local_source_ruleset_rules "$ruleset_filepath" "domain_suffix" "$json_array" ;;
subnets)
sing_box_cm_patch_local_source_ruleset_rules "$ruleset_filepath" "ip_cidr" "$json_array"
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME" "$items"
;;
esac
} }
configure_remote_domain_or_subnet_list_handler() { configure_remote_domain_or_subnet_list_handler() {
@@ -1045,9 +1053,10 @@ configure_remote_domain_or_subnet_list_handler() {
local file_extension local file_extension
file_extension=$(url_get_file_extension "$url") file_extension=$(url_get_file_extension "$url")
log "Detected file extension: '$file_extension'" "debug"
case "$file_extension" in case "$file_extension" in
json | srs) json | srs)
log "Detected file extension: '$file_extension' → proceeding with processing" "debug" log "Creating a remote $type ruleset from the source URL" "info"
local basename ruleset_tag format detour update_interval local basename ruleset_tag format detour update_interval
basename=$(url_get_basename "$url") basename=$(url_get_basename "$url")
ruleset_tag=$(get_ruleset_tag "$section" "$basename" "remote-$type") ruleset_tag=$(get_ruleset_tag "$section" "$basename" "remote-$type")
@@ -1066,7 +1075,7 @@ configure_remote_domain_or_subnet_list_handler() {
esac esac
;; ;;
*) *)
log "Detected file extension: '$file_extension' → no processing needed, managed on list_update" "debug" prepare_source_ruleset "$section" "remote" "$type" "$route_rule_tag"
;; ;;
esac esac
} }
@@ -1079,16 +1088,39 @@ sing_box_configure_experimental() {
config_get cache_file "settings" "cache_path" "/tmp/sing-box/cache.db" config_get cache_file "settings" "cache_path" "/tmp/sing-box/cache.db"
config=$(sing_box_cm_configure_cache_file "$config" true "$cache_file" true) config=$(sing_box_cm_configure_cache_file "$config" true "$cache_file" true)
local enable_yacd external_controller_ui
config_get_bool enable_yacd "settings" "enable_yacd" 0
log "Configuring Clash API" log "Configuring Clash API"
local enable_yacd enable_yacd_wan_access clash_api_controller_address
config_get_bool enable_yacd "settings" "enable_yacd" 0
config_get_bool enable_yacd_wan_access "settings" "enable_yacd_wan_access" 0
if [ "$enable_yacd" -eq 1 ] && [ "$enable_yacd_wan_access" -eq 1 ]; then
clash_api_controller_address="0.0.0.0"
else
clash_api_controller_address="$(get_service_listen_address)"
if [ -z "$clash_api_controller_address" ]; then
log "Could not determine the listening IP address for the Clash API controller. It will run only on localhost." "warn"
clash_api_controller_address="127.0.0.1"
fi
fi
if [ "$enable_yacd" -eq 1 ]; then if [ "$enable_yacd" -eq 1 ]; then
log "YACD is enabled, enabling Clash API with downloadable YACD" "debug" log "YACD is enabled, enabling Clash API with downloadable YACD" "debug"
local external_controller_ui="ui" local yacd_secret_key external_controller_ui
config=$(sing_box_cm_configure_clash_api "$config" "$SB_CLASH_API_CONTROLLER" "$external_controller_ui") config_get yacd_secret_key "settings" "yacd_secret_key"
external_controller_ui="ui"
config=$(
sing_box_cm_configure_clash_api \
"$config" \
"$clash_api_controller_address:$SB_CLASH_API_CONTROLLER_PORT" \
"$external_controller_ui" \
"$yacd_secret_key"
)
else else
log "YACD is disabled, enabling Clash API in online mode" "debug" log "YACD is disabled, enabling Clash API in online mode" "debug"
config=$(sing_box_cm_configure_clash_api "$config" "$SB_CLASH_API_CONTROLLER") config=$(
sing_box_cm_configure_clash_api "$config" "$clash_api_controller_address:$SB_CLASH_API_CONTROLLER_PORT"
)
fi fi
} }
@@ -1117,8 +1149,13 @@ sing_box_additional_inbounds() {
configure_section_mixed_proxy() { configure_section_mixed_proxy() {
local section="$1" local section="$1"
local mixed_inbound_enabled mixed_proxy_port mixed_inbound_tag mixed_outbound_tag local mixed_inbound_enabled mixed_proxy_port mixed_inbound_tag mixed_outbound_tag mixed_proxy_address
config_get_bool mixed_inbound_enabled "$section" "mixed_proxy_enabled" 0 config_get_bool mixed_inbound_enabled "$section" "mixed_proxy_enabled" 0
mixed_proxy_address="$(get_service_listen_address)"
if [ -z "$mixed_proxy_address" ]; then
log "Could not determine the listening IP address for the Mixed Proxy. The proxy will not be created." "warn"
return 1
fi
config_get mixed_proxy_port "$section" "mixed_proxy_port" config_get mixed_proxy_port "$section" "mixed_proxy_port"
if [ "$mixed_inbound_enabled" -eq 1 ]; then if [ "$mixed_inbound_enabled" -eq 1 ]; then
mixed_inbound_tag="$(get_inbound_tag_by_section "$section-mixed")" mixed_inbound_tag="$(get_inbound_tag_by_section "$section-mixed")"
@@ -1127,7 +1164,7 @@ configure_section_mixed_proxy() {
sing_box_cf_add_mixed_inbound_and_route_rule \ sing_box_cf_add_mixed_inbound_and_route_rule \
"$config" \ "$config" \
"$mixed_inbound_tag" \ "$mixed_inbound_tag" \
"$SB_MIXED_INBOUND_ADDRESS" \ "$mixed_proxy_address" \
"$mixed_proxy_port" \ "$mixed_proxy_port" \
"$mixed_outbound_tag" "$mixed_outbound_tag"
) )
@@ -1213,7 +1250,7 @@ import_community_service_subnet_list_handler() {
*) return 0 ;; *) return 0 ;;
esac esac
local tmpfile http_proxy_address subnets local tmpfile http_proxy_address
tmpfile=$(mktemp) tmpfile=$(mktemp)
http_proxy_address="$(get_service_proxy_address)" http_proxy_address="$(get_service_proxy_address)"
@@ -1224,14 +1261,13 @@ import_community_service_subnet_list_handler() {
return 1 return 1
fi fi
subnets="$(parse_domain_or_subnet_file_to_comma_string "$tmpfile" "subnets")"
rm -f "$tmpfile"
if [ "$service" = "discord" ]; then if [ "$service" = "discord" ]; then
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_DISCORD_SET_NAME" "$subnets" nft_add_set_elements_from_file_chunked "$tmpfile" "$NFT_TABLE_NAME" "$NFT_DISCORD_SET_NAME"
else else
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME" "$subnets" nft_add_set_elements_from_file_chunked "$tmpfile" "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME"
fi fi
rm -f "$tmpfile"
} }
import_domains_from_remote_domain_lists() { import_domains_from_remote_domain_lists() {
@@ -1252,17 +1288,41 @@ import_domains_from_remote_domain_list_handler() {
local file_extension local file_extension
file_extension=$(url_get_file_extension "$url") file_extension=$(url_get_file_extension "$url")
log "Detected file extension: '$file_extension'" "debug"
case "$file_extension" in case "$file_extension" in
json | srs) json | srs)
log "Detected file extension: '$file_extension' → no update needed, sing-box manages updates" "debug" log "No update needed - sing-box manages updates automatically."
;; ;;
*) *)
log "Detected file extension: '$file_extension' → proceeding with processing" "debug" log "Import domains from a remote plain-text list"
import_domains_or_subnets_from_remote_file "$url" "$section" "domains" import_domains_from_remote_plain_file "$url" "$section"
;; ;;
esac esac
} }
import_domains_from_remote_plain_file() {
local url="$1"
local section="$2"
local tmpfile http_proxy_address items json_array
tmpfile=$(mktemp)
http_proxy_address="$(get_service_proxy_address)"
download_to_file "$url" "$tmpfile" "$http_proxy_address"
if [ $? -ne 0 ] || [ ! -s "$tmpfile" ]; then
log "Download $url list failed" "error"
return 1
fi
convert_crlf_to_lf "$tmpfile"
ruleset_tag=$(get_ruleset_tag "$section" "remote" "domains")
ruleset_filepath="$TMP_RULESET_FOLDER/$ruleset_tag.json"
import_plain_domain_list_to_local_source_ruleset_chunked "$tmpfile" "$ruleset_filepath"
rm -f "$tmpfile"
}
import_subnets_from_remote_subnet_lists() { import_subnets_from_remote_subnet_lists() {
local section="$1" local section="$1"
local remote_subnet_lists local remote_subnet_lists
@@ -1281,81 +1341,46 @@ import_subnets_from_remote_subnet_list_handler() {
local file_extension local file_extension
file_extension="$(url_get_file_extension "$url")" file_extension="$(url_get_file_extension "$url")"
log "Detected file extension: '$file_extension'" "debug"
case "$file_extension" in case "$file_extension" in
json) json)
log "Detected file extension: '$file_extension' → proceeding with processing" "debug" log "Import subnets from a remote JSON list" "info"
import_subnets_from_remote_json_file "$url" import_subnets_from_remote_json_file "$url"
;; ;;
srs) srs)
log "Detected file extension: '$file_extension' → proceeding with processing" "debug" log "Import subnets from a remote SRS list" "info"
import_subnets_from_remote_srs_file "$url" import_subnets_from_remote_srs_file "$url"
;; ;;
*) *)
log "Detected file extension: '$file_extension' → proceeding with processing" "debug" log "Import subnets from a remote plain-text list" "info"
import_domains_or_subnets_from_remote_file "$url" "$section" "subnets" import_subnets_from_remote_plain_file "$url" "$section"
;;
esac
}
import_domains_or_subnets_from_remote_file() {
local url="$1"
local section="$2"
local type="$3"
local tmpfile http_proxy_address items json_array
tmpfile=$(mktemp)
http_proxy_address="$(get_service_proxy_address)"
download_to_file "$url" "$tmpfile" "$http_proxy_address"
if [ $? -ne 0 ] || [ ! -s "$tmpfile" ]; then
log "Download $url list failed" "error"
return 1
fi
items="$(parse_domain_or_subnet_file_to_comma_string "$tmpfile" "$type")"
rm -f "$tmpfile"
if [ -z "$items" ]; then
log "No valid $type found in $url" "warn"
return 0
fi
ruleset_tag=$(get_ruleset_tag "$section" "common" "$type")
ruleset_filename="$ruleset_tag.json"
ruleset_filepath="$TMP_RULESET_FOLDER/$ruleset_filename"
json_array="$(comma_string_to_json_array "$items")"
case "$type" in
domains) sing_box_cm_patch_local_source_ruleset_rules "$ruleset_filepath" "domain_suffix" "$json_array" ;;
subnets)
sing_box_cm_patch_local_source_ruleset_rules "$ruleset_filepath" "ip_cidr" "$json_array"
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME" "$items"
;; ;;
esac esac
} }
import_subnets_from_remote_json_file() { import_subnets_from_remote_json_file() {
local url="$1" local url="$1"
local tmpfile subnets http_proxy_address local json_tmpfile subnets_tmpfile http_proxy_address
tmpfile="$(mktemp)" json_tmpfile="$(mktemp)"
subnets_tmpfile="$(mktemp)"
http_proxy_address="$(get_service_proxy_address)" http_proxy_address="$(get_service_proxy_address)"
download_to_stream "$url" "$http_proxy_address" | jq -r '.rules[].ip_cidr[]?' > "$tmpfile" download_to_file "$url" "$json_tmpfile" "$http_proxy_address"
if [ $? -ne 0 ] || [ ! -s "$tmpfile" ]; then if [ $? -ne 0 ] || [ ! -s "$json_tmpfile" ]; then
log "Download $url list failed" "error" log "Download $url list failed" "error"
return 1 return 1
fi fi
subnets="$(parse_domain_or_subnet_file_to_comma_string "$tmpfile" "subnets")" extract_ip_cidr_from_json_ruleset_to_file "$json_tmpfile" "$subnets_tmpfile"
rm -f "$tmpfile" nft_add_set_elements_from_file_chunked "$subnets_tmpfile" "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME"
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME" "$subnets" rm -f "$json_tmpfile" "$subnets_tmpfile"
} }
import_subnets_from_remote_srs_file() { import_subnets_from_remote_srs_file() {
local url="$1" local url="$1"
local binary_tmpfile json_tmpfile subnets_tmpfile subnets http_proxy_address local binary_tmpfile json_tmpfile subnets_tmpfile http_proxy_address
binary_tmpfile="$(mktemp)" binary_tmpfile="$(mktemp)"
json_tmpfile="$(mktemp)" json_tmpfile="$(mktemp)"
subnets_tmpfile="$(mktemp)" subnets_tmpfile="$(mktemp)"
@@ -1368,15 +1393,39 @@ import_subnets_from_remote_srs_file() {
return 1 return 1
fi fi
if ! decompile_srs_file "$binary_tmpfile" "$json_tmpfile"; then if ! decompile_binary_ruleset "$binary_tmpfile" "$json_tmpfile"; then
log "Failed to decompile SRS file" "error" log "Failed to decompile binary rule set file" "error"
return 1 return 1
fi fi
jq -r '.rules[].ip_cidr[]' "$json_tmpfile" > "$subnets_tmpfile" extract_ip_cidr_from_json_ruleset_to_file "$json_tmpfile" "$subnets_tmpfile"
subnets="$(parse_domain_or_subnet_file_to_comma_string "$subnets_tmpfile" "subnets")" nft_add_set_elements_from_file_chunked "$subnets_tmpfile" "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME"
rm -f "$binary_tmpfile" "$json_tmpfile" "$subnets_tmpfile" rm -f "$binary_tmpfile" "$json_tmpfile" "$subnets_tmpfile"
nft_add_set_elements "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME" "$subnets" }
import_subnets_from_remote_plain_file() {
local url="$1"
local section="$2"
local tmpfile http_proxy_address items json_array
tmpfile=$(mktemp)
http_proxy_address="$(get_service_proxy_address)"
download_to_file "$url" "$tmpfile" "$http_proxy_address"
if [ $? -ne 0 ] || [ ! -s "$tmpfile" ]; then
log "Download $url list failed" "error"
return 1
fi
convert_crlf_to_lf "$tmpfile"
ruleset_tag=$(get_ruleset_tag "$section" "remote" "subnets")
ruleset_filepath="$TMP_RULESET_FOLDER/$ruleset_tag.json"
import_plain_subnet_list_to_local_source_ruleset_chunked "$tmpfile" "$ruleset_filepath"
nft_add_set_elements_from_file_chunked "$tmpfile" "$NFT_TABLE_NAME" "$NFT_COMMON_SET_NAME"
rm -f "$tmpfile"
} }
## Support functions ## Support functions
@@ -1459,6 +1508,23 @@ section_has_enabled_lists() {
fi fi
} }
get_service_listen_address() {
local service_listen_address
service_listen_address="$(uci_get "network" "lan" "ipaddr")"
if [ -z "$service_listen_address" ]; then
config_get service_listen_address "settings" "service_listen_address" # TODO(ampetelin): Remove after testing
fi
if [ -z "$service_listen_address" ]; then
log "Failed to determine the listening IP address. Please open an issue to report this problem: https://github.com/itdoginfo/podkop/issues" "error"
return 1
fi
echo "$service_listen_address"
}
## nftables ## nftables
nft_list_all_traffic_from_ip() { nft_list_all_traffic_from_ip() {
local ip="$1" local ip="$1"
@@ -1630,7 +1696,7 @@ check_logs() {
nolog "Logs not found" nolog "Logs not found"
return 1 return 1
fi fi
ы
# Find the last occurrence of "Starting podkop" # Find the last occurrence of "Starting podkop"
local start_line local start_line
start_line=$(echo "$logs" | grep -n "podkop.*Starting podkop" | tail -n 1 | cut -d: -f1) start_line=$(echo "$logs" | grep -n "podkop.*Starting podkop" | tail -n 1 | cut -d: -f1)
@@ -1692,6 +1758,7 @@ show_config() {
-e 's/\(list urltest_proxy_links\).*/\1 '\''MASKED'\''/g' \ -e 's/\(list urltest_proxy_links\).*/\1 '\''MASKED'\''/g' \
-e "s@\\(option dns_server '[^/]*\\)/[^']*'@\\1/MASKED'@g" \ -e "s@\\(option dns_server '[^/]*\\)/[^']*'@\\1/MASKED'@g" \
-e "s@\\(option domain_resolver_dns_server '[^/]*\\)/[^']*'@\\1/MASKED'@g" \ -e "s@\\(option domain_resolver_dns_server '[^/]*\\)/[^']*'@\\1/MASKED'@g" \
-e 's/\(option yacd_secret_key\).*/\1 '\''MASKED'\''/g' \
"$PODKOP_CONFIG" > "$tmp_config" "$PODKOP_CONFIG" > "$tmp_config"
cat "$tmp_config" cat "$tmp_config"
@@ -2071,13 +2138,28 @@ check_fakeip() {
####################################### #######################################
clash_api() { clash_api() {
local CLASH_URL="127.0.0.1:9090"
local TEST_URL="https://www.gstatic.com/generate_204"
local action="$1" local action="$1"
local clash_api_controller_address CLASH_URL TEST_URL
clash_api_controller_address="$(get_service_listen_address)"
if [ -z "$clash_api_controller_address" ]; then
clash_api_controller_address="127.0.0.1"
fi
CLASH_URL="$clash_api_controller_address:$SB_CLASH_API_CONTROLLER_PORT"
TEST_URL="https://www.gstatic.com/generate_204"
local enable_yacd_wan_access yacd_secret_key auth_header
config_get_bool enable_yacd_wan_access "settings" "enable_yacd_wan_access" 0
config_get yacd_secret_key "settings" "yacd_secret_key"
if [ "$enable_yacd_wan_access" -eq 1 ]; then
auth_header="Authorization: Bearer $yacd_secret_key"
else
auth_header=""
fi
case "$action" in case "$action" in
get_proxies) get_proxies)
curl -s "$CLASH_URL/proxies" | jq . curl -s --header "$auth_header" "$CLASH_URL/proxies" | jq .
;; ;;
get_proxy_latency) get_proxy_latency)
@@ -2090,6 +2172,7 @@ clash_api() {
fi fi
curl -G -s "$CLASH_URL/proxies/$proxy_tag/delay" \ curl -G -s "$CLASH_URL/proxies/$proxy_tag/delay" \
--header "$auth_header" \
--data-urlencode "url=$TEST_URL" \ --data-urlencode "url=$TEST_URL" \
--data-urlencode "timeout=$timeout" | jq . --data-urlencode "timeout=$timeout" | jq .
;; ;;
@@ -2104,6 +2187,7 @@ clash_api() {
fi fi
curl -G -s "$CLASH_URL/group/$group_tag/delay" \ curl -G -s "$CLASH_URL/group/$group_tag/delay" \
--header "$auth_header" \
--data-urlencode "url=$TEST_URL" \ --data-urlencode "url=$TEST_URL" \
--data-urlencode "timeout=$timeout" | jq . --data-urlencode "timeout=$timeout" | jq .
;; ;;
@@ -2118,8 +2202,11 @@ clash_api() {
fi fi
local response local response
response=$(curl -X PUT -s -w "\n%{http_code}" "$CLASH_URL/proxies/$group_tag" \ response=$(
--data-raw "{\"name\":\"$proxy_tag\"}") curl -X PUT -s -w "\n%{http_code}" "$CLASH_URL/proxies/$group_tag" \
--header "$auth_header" \
--data-raw "{\"name\":\"$proxy_tag\"}"
)
local http_code local http_code
local body local body

View File

@@ -38,7 +38,6 @@ SB_TPROXY_INBOUND_PORT=1602
SB_DNS_INBOUND_TAG="dns-in" SB_DNS_INBOUND_TAG="dns-in"
SB_DNS_INBOUND_ADDRESS="127.0.0.42" SB_DNS_INBOUND_ADDRESS="127.0.0.42"
SB_DNS_INBOUND_PORT=53 SB_DNS_INBOUND_PORT=53
SB_MIXED_INBOUND_ADDRESS="0.0.0.0" # TODO(ampetelin): maybe to determine address?
SB_SERVICE_MIXED_INBOUND_TAG="service-mixed-in" SB_SERVICE_MIXED_INBOUND_TAG="service-mixed-in"
SB_SERVICE_MIXED_INBOUND_ADDRESS="127.0.0.1" SB_SERVICE_MIXED_INBOUND_ADDRESS="127.0.0.1"
SB_SERVICE_MIXED_INBOUND_PORT=4534 SB_SERVICE_MIXED_INBOUND_PORT=4534
@@ -47,7 +46,7 @@ SB_DIRECT_OUTBOUND_TAG="direct-out"
# Route # Route
SB_REJECT_RULE_TAG="reject-rule-tag" SB_REJECT_RULE_TAG="reject-rule-tag"
# Experimental # Experimental
SB_CLASH_API_CONTROLLER="0.0.0.0:9090" SB_CLASH_API_CONTROLLER_PORT=9090
## Lists ## Lists
GITHUB_RAW_URL="https://raw.githubusercontent.com/itdoginfo/allow-domains/main" GITHUB_RAW_URL="https://raw.githubusercontent.com/itdoginfo/allow-domains/main"

View File

@@ -105,37 +105,6 @@ get_domain_resolver_tag() {
echo "$section-$postfix" echo "$section-$postfix"
} }
# Constructs and returns a ruleset tag using section, name, optional type, and a fixed postfix
get_ruleset_tag() {
local section="$1"
local name="$2"
local type="$3"
local postfix="ruleset"
if [ -n "$type" ]; then
echo "$section-$name-$type-$postfix"
else
echo "$section-$name-$postfix"
fi
}
# Determines the ruleset format based on the file extension (json → source, srs → binary)
get_ruleset_format_by_file_extension() {
local file_extension="$1"
local format
case "$file_extension" in
json) format="source" ;;
srs) format="binary" ;;
*)
log "Unsupported file extension: .$file_extension" "error"
return 1
;;
esac
echo "$format"
}
# Converts a comma-separated string into a JSON array string # Converts a comma-separated string into a JSON array string
comma_string_to_json_array() { comma_string_to_json_array() {
local input="$1" local input="$1"
@@ -268,25 +237,6 @@ migration_rename_config_key() {
fi fi
} }
# Download URL content directly
download_to_stream() {
local url="$1"
local http_proxy_address="$2"
local retries="${3:-3}"
local wait="${4:-2}"
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" wget -qO- "$url" | sed 's/\r$//' && break
else
wget -qO- "$url" | sed 's/\r$//' && break
fi
log "Attempt $attempt/$retries to download $url failed" "warn"
sleep "$wait"
done
}
# Download URL to file # Download URL to file
download_to_file() { download_to_file() {
local url="$1" local url="$1"
@@ -305,29 +255,17 @@ download_to_file() {
log "Attempt $attempt/$retries to download $url failed" "warn" log "Attempt $attempt/$retries to download $url failed" "warn"
sleep "$wait" sleep "$wait"
done done
if grep -q $'\r' "$filepath"; then
log "Downloaded file has Windows line endings (CRLF). Converting to Unix (LF)"
sed -i 's/\r$//' "$filepath"
fi
} }
# Decompiles a sing-box SRS binary file into a JSON ruleset file # Converts Windows-style line endings (CRLF) to Unix-style (LF)
decompile_srs_file() { convert_crlf_to_lf() {
local binary_filepath="$1" local filepath="$1"
local output_filepath="$2"
log "Decompiling $binary_filepath to $output_filepath" "debug" if grep -q $'\r' "$filepath"; then
log "File '$filepath' contains CRLF line endings. Converting to LF..." "debug"
if ! file_exists "$binary_filepath"; then local tmpfile
log "File $binary_filepath not found" "error" tmpfile=$(mktemp)
return 1 tr -d '\r' < "$filepath" > "$tmpfile" && mv "$tmpfile" "$filepath" || rm -f "$tmpfile"
fi
sing-box rule-set decompile "$binary_filepath" -o "$output_filepath"
if [[ $? -ne 0 ]]; then
log "Decompilation command failed for $binary_filepath" "error"
return 1
fi fi
} }
@@ -399,4 +337,4 @@ parse_domain_or_subnet_file_to_comma_string() {
done < "$filepath" done < "$filepath"
echo "$result" echo "$result"
} }

View File

@@ -27,4 +27,44 @@ nft_add_set_elements() {
local elements="$3" local elements="$3"
nft add element inet "$table" "$set" "{ $elements }" nft add element inet "$table" "$set" "{ $elements }"
}
nft_add_set_elements_from_file_chunked() {
local filepath="$1"
local nft_table_name="$2"
local nft_set_name="$3"
local chunk_size="${4:-5000}"
local array count
count=0
while IFS= read -r line; do
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -z "$line" ] && continue
if ! is_ipv4 "$line" && ! is_ipv4_cidr "$line"; then
log "'$line' is not IPv4 or IPv4 CIDR" "debug"
continue
fi
if [ -z "$array" ]; then
array="$line"
else
array="$array,$line"
fi
count=$((count + 1))
if [ "$count" = "$chunk_size" ]; then
log "Adding $count elements to nft set $nft_set_name" "debug"
nft_add_set_elements "$nft_table_name" "$nft_set_name" "$array"
array=""
count=0
fi
done < "$filepath"
if [ -n "$array" ]; then
log "Adding $count elements to nft set $nft_set_name" "debug"
nft_add_set_elements "$nft_table_name" "$nft_set_name" "$array"
fi
} }

View File

@@ -0,0 +1,180 @@
# Constructs and returns a ruleset tag using section, name, optional type, and a fixed postfix
get_ruleset_tag() {
local section="$1"
local name="$2"
local type="$3"
local postfix="ruleset"
if [ -n "$type" ]; then
echo "$section-$name-$type-$postfix"
else
echo "$section-$name-$postfix"
fi
}
# Creates a new ruleset JSON file if it doesn't already exist
create_source_rule_set() {
local ruleset_filepath="$1"
if file_exists "$ruleset_filepath"; then
return 3
fi
jq -n '{version: 3, rules: []}' > "$ruleset_filepath"
}
#######################################
# Patch a source ruleset JSON file for sing-box by appending a new ruleset object containing the provided key
# and value.
# Arguments:
# filepath: path to the JSON file to patch
# key: the ruleset key to insert (e.g., "ip_cidr")
# value: a JSON array of values to assign to the key
# Example:
# patch_source_ruleset_rules "/tmp/sing-box/ruleset.json" "ip_cidr" '["1.1.1.1","2.2.2.2"]'
#######################################
patch_source_ruleset_rules() {
local filepath="$1"
local key="$2"
local value="$3"
local tmpfile=$(mktemp)
jq --arg key "$key" --argjson value "$value" \
'( .rules | map(has($key)) | index(true) ) as $idx |
if $idx != null then
.rules[$idx][$key] = (.rules[$idx][$key] + $value | unique)
else
.rules += [{ ($key): $value }]
end' "$filepath" > "$tmpfile"
if [ $? -ne 0 ]; then
rm -f "$tmpfile"
return 1
fi
mv "$tmpfile" "$filepath"
}
# Imports a plain domain list into a ruleset in chunks, validating domains and appending them as domain_suffix rules
import_plain_domain_list_to_local_source_ruleset_chunked() {
local plain_list_filepath="$1"
local ruleset_filepath="$2"
local chunk_size="${3:-5000}"
local array count json_array
count=0
while IFS= read -r line; do
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -z "$line" ] && continue
if ! is_domain_suffix "$line"; then
log "'$line' is not a valid domain" "debug"
continue
fi
if [ -z "$array" ]; then
array="$line"
else
array="$array,$line"
fi
count=$((count + 1))
if [ "$count" = "$chunk_size" ]; then
log "Adding $count elements to rule set at $ruleset_filepath" "debug"
json_array="$(comma_string_to_json_array "$array")"
patch_source_ruleset_rules "$ruleset_filepath" "domain_suffix" "$json_array"
array=""
count=0
fi
done < "$plain_list_filepath"
if [ -n "$array" ]; then
log "Adding $count elements to rule set at $ruleset_filepath" "debug"
json_array="$(comma_string_to_json_array "$array")"
patch_source_ruleset_rules "$ruleset_filepath" "domain_suffix" "$json_array"
fi
}
# Imports a plain IPv4/CIDR list into a ruleset in chunks, validating entries and appending them as ip_cidr rules
import_plain_subnet_list_to_local_source_ruleset_chunked() {
local plain_list_filepath="$1"
local ruleset_filepath="$2"
local chunk_size="${3:-5000}"
local array count json_array
count=0
while IFS= read -r line; do
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -z "$line" ] && continue
if ! is_ipv4 "$line" && ! is_ipv4_cidr "$line"; then
log "'$line' is not IPv4 or IPv4 CIDR" "debug"
continue
fi
if [ -z "$array" ]; then
array="$line"
else
array="$array,$line"
fi
count=$((count + 1))
if [ "$count" = "$chunk_size" ]; then
log "Adding $count elements to ruleset at $ruleset_filepath" "debug"
json_array="$(comma_string_to_json_array "$array")"
patch_source_ruleset_rules "$ruleset_filepath" "ip_cidr" "$json_array"
array=""
count=0
fi
done < "$plain_list_filepath"
if [ -n "$array" ]; then
log "Adding $count elements to ruleset at $ruleset_filepath" "debug"
json_array="$(comma_string_to_json_array "$array")"
patch_source_ruleset_rules "$ruleset_filepath" "ip_cidr" "$json_array"
fi
}
# Determines the ruleset format based on the file extension (json → source, srs → binary)
get_ruleset_format_by_file_extension() {
local file_extension="$1"
local format
case "$file_extension" in
json) format="source" ;;
srs) format="binary" ;;
*)
log "Unsupported file extension: .$file_extension" "error"
return 1
;;
esac
echo "$format"
}
# Decompiles a sing-box SRS binary file into a JSON ruleset file
decompile_binary_ruleset() {
local binary_filepath="$1"
local output_filepath="$2"
log "Decompiling $binary_filepath to $output_filepath" "debug"
sing-box rule-set decompile "$binary_filepath" -o "$output_filepath"
if [[ $? -ne 0 ]]; then
log "Decompilation command failed for $binary_filepath" "error"
return 1
fi
}
# Extracts all ip_cidr entries from a JSON ruleset file and writes them to an output file.
extract_ip_cidr_from_json_ruleset_to_file() {
local json_file="$1"
local output_file="$2"
log "Extracting ip_cidr entries from $json_file to $output_file" "debug"
jq -r '.rules[].ip_cidr[]' "$json_file" > "$output_file"
}

View File

@@ -1339,9 +1339,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: JSON configuration (string) # config: string, JSON configuration
# external_controller: 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: Optional path to static web resources to serve at http://{{external-controller}}/ui # external_ui: string, Optional path to static web resources to serve at http://{{external-controller}}/ui
# secret: string, Optional secret for the RESTful API Authenticate by specifying HTTP header
# Outputs: # Outputs:
# Writes updated JSON configuration to stdout # Writes updated JSON configuration to stdout
# Example: # Example:
@@ -1351,59 +1352,17 @@ sing_box_cm_configure_clash_api() {
local config="$1" local config="$1"
local external_controller="$2" local external_controller="$2"
local external_ui="$3" local external_ui="$3"
local secret="$4"
echo "$config" | jq \ echo "$config" | jq \
--arg external_controller "$external_controller" \ --arg external_controller "$external_controller" \
--arg external_ui "$external_ui" \ --arg external_ui "$external_ui" \
--arg secret "$secret" \
'.experimental.clash_api = { '.experimental.clash_api = {
external_controller: $external_controller, external_controller: $external_controller,
} }
+ (if $external_ui != "" then { external_ui: $external_ui } else {} end)' + (if $external_ui != "" then { external_ui: $external_ui } else {} end)
} + (if $secret != "" then { secret: $secret } else {} end)'
#######################################
# Create a local source ruleset JSON file for sing-box.
# Arguments:
# filepath: path to the JSON file to create
# Example:
# sing_box_cm_create_local_source_ruleset "/tmp/sing-box/ruleset.json"
#######################################
sing_box_cm_create_local_source_ruleset() {
local filepath="$1"
jq -n '{version: 3, rules: []}' > "$filepath"
}
#######################################
# Patch a local source ruleset JSON file for sing-box by adding unique! values to a given key.
# Arguments:
# filepath: path to the JSON file to patch
# key: the ruleset key to update (e.g., "ip_cidr")
# value: a JSON array of values to add to the key
# Example:
# sing_box_cm_patch_local_source_ruleset_rules "/tmp/sing-box/ruleset.json" "ip_cidr" '["1.1.1.1","2.2.2.2"]'
#######################################
sing_box_cm_patch_local_source_ruleset_rules() {
local filepath="$1"
local key="$2"
local value="$3"
value=$(_normalize_arg "$value")
local content
content="$(cat "$filepath")"
echo "$content" | jq \
--arg key "$key" \
--argjson value "$value" '
([.rules[]?[$key][]] | unique) as $existing
| ($value - $existing) as $value
| if ($value | length) > 0 then
.rules += [{($key): $value}]
else
.
end
' > "$filepath"
} }
####################################### #######################################