From ea04c4da23253ad9c99b4c9754bfcb7fc86b235e Mon Sep 17 00:00:00 2001 From: yangtaowillv Date: Thu, 7 May 2026 11:06:48 +0800 Subject: [PATCH] feat: add ALLOW-LAN; command: clashctl lan status/on/off --- .editorconfig | 18 ++++----- .env | 4 +- .gitattributes | 14 +++---- .gitignore | 36 ++++++++--------- LICENSE | 38 +++++++++--------- README.md | 15 +++++++ config/template.yaml | 2 +- resources/bin/README.md | 72 ++++++++++++++++----------------- scripts/core/clashctl.sh | 81 ++++++++++++++++++++++++++++++++++++++ scripts/core/common.sh | 13 ++++++ scripts/core/completion.sh | 14 ++++++- scripts/core/config.sh | 33 +++++++++++++++- 12 files changed, 245 insertions(+), 95 deletions(-) diff --git a/.editorconfig b/.editorconfig index acf3bcf..809f082 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true - -[*.{sh,yaml,yml,env,md}] -charset = utf-8 +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true + +[*.{sh,yaml,yml,env,md}] +charset = utf-8 diff --git a/.env b/.env index 8d7466f..753c786 100644 --- a/.env +++ b/.env @@ -2,10 +2,10 @@ export KERNEL_TYPE="mihomo" # 可选订阅(首次安装可留空) -export CLASH_SUBSCRIPTION_URL="" +export CLASH_SUBSCRIPTION_URL="https://sakuracat1203.xn--3iq226gfdb94q.com/api/v1/client/subscribe?token=df9fd8af2086471190a42435d354a3ec" # 控制器密钥(默认留空) -export CLASH_CONTROLLER_SECRET="" +export CLASH_CONTROLLER_SECRET="f4962effeac0815d" # 端口 export MIXED_PORT="7890" diff --git a/.gitattributes b/.gitattributes index 9ed3e6c..cdd257d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,7 @@ -*.sh text eol=lf working-tree-encoding=UTF-8 -*.yaml text eol=lf -*.yml text eol=lf -*.env text eol=lf -*.gz binary -*.zip binary -*.mmdb binary +*.sh text eol=lf working-tree-encoding=UTF-8 +*.yaml text eol=lf +*.yml text eol=lf +*.env text eol=lf +*.gz binary +*.zip binary +*.mmdb binary diff --git a/.gitignore b/.gitignore index c43da6a..6b3df72 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,18 @@ -# IDE -.idea/ -.vscode/ - -# Local env -.env - -# Runtime -runtime/ - -# Local dashboard unpacked files -resources/dashboard/dist/ - -# Keep packaged dashboard asset -!resources/dashboard/dist.zip - -# Local test scripts -test.sh +# IDE +.idea/ +.vscode/ + +# Local env +.env + +# Runtime +runtime/ + +# Local dashboard unpacked files +resources/dashboard/dist/ + +# Keep packaged dashboard asset +!resources/dashboard/dist.zip + +# Local test scripts +test.sh diff --git a/LICENSE b/LICENSE index e1cba32..4dd3496 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,19 @@ -Copyright (c) 2026 wnlen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Copyright (c) 2026 wnlen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8ef65bf..db74bd8 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ bash install.sh clashctl secret 🔑 查看密钥 clashctl secret 123 🔐 设置密钥 📌 高级 + clashctl lan 🏠 局域网代理管理 clashctl tun 🧪 Tun 模式管理 clashctl boot 🚦 开机代理接管管理 clashctl mixin 🧩 Mixin 配置管理 @@ -117,6 +118,20 @@ $ clashsecret ------ +## 🏠 局域网代理 + +项目默认开启局域网代理:运行配置会写入 `allow-lan: true`,并把 `external-controller` 绑定到 `0.0.0.0`,避免订阅文件里的 `allow-lan: false` 覆盖项目默认值。 + +```bash +clashctl lan status +clashctl lan on +clashctl lan off +``` + +开启后,同一局域网设备可把 HTTP / SOCKS 代理地址设置为 `http://<本机局域网IP>:`,端口默认是 `7890`;如访问不了,请检查系统防火墙是否放行该端口。 + +------ + OpenWrt 下 root/system 安装会把 `clashctl`、`clashon`、`clashoff` 等命令入口写入 `/usr/bin`,运行状态、日志和内核二进制仍保存在项目目录的 `runtime/` 下。仅脚本模式不会注册开机自启,设备重启后需要重新执行 `clashon`。 ## 🧰 常用管理命令 diff --git a/config/template.yaml b/config/template.yaml index 5d5da05..94b941e 100644 --- a/config/template.yaml +++ b/config/template.yaml @@ -2,7 +2,7 @@ mixed-port: 7890 allow-lan: true mode: rule log-level: info -external-controller: 127.0.0.1:9090 +external-controller: 0.0.0.0:9090 tun: enable: false stack: system diff --git a/resources/bin/README.md b/resources/bin/README.md index d23e76e..1b05ea3 100644 --- a/resources/bin/README.md +++ b/resources/bin/README.md @@ -1,36 +1,36 @@ -# 内置运行依赖 - -如果安装时 GitHub 下载太慢,可以把 Mihomo、yq、subconverter 提前放到本目录。安装和 `clashctl upgrade` 会优先读取这里的文件;没有匹配文件时,仍会回退到原来的下载逻辑。 - -Clash 是兼容内核,固定走远程下载,不使用本目录中的本地资源。 - -默认目录结构: - -```text -resources/bin/ - mihomo/ - mihomo-linux-amd64-compatible-v1.19.23.gz - mihomo-linux-arm64-v1.19.23.gz - mihomo-linux-armv7-v1.19.23.gz - yq/ - yq_linux_amd64.tar.gz - yq_linux_arm64.tar.gz - yq_linux_arm.tar.gz - subconverter/ - subconverter_linux64.tar.gz - subconverter_aarch64.tar.gz - subconverter_armv7.tar.gz -``` - -当前正式支持 `amd64`、`arm64`、`armv7`。版本号要和 `.env` 里的 `MIHOMO_VERSION`、`YQ_VERSION`、`SUBCONVERTER_VERSION` 对应。脚本只按当前目标文件名精确命中,不会扫描目录,也不会自动选择最高版本;如果本地没有对应文件,会继续联网下载。 - -Mihomo、yq、subconverter 兼容旧路径 `resources/bin///`,但默认推荐直接使用 `resources/bin//`。 - -可选开关: - -```bash -export CLASH_BUNDLED_ASSET_ENABLED=false -export CLASH_BUNDLED_ASSET_DIR=/path/to/assets -``` - -`Country.mmdb` 仍放在 `resources/geo/Country.mmdb` 或 `resources/geo/country.mmdb`。 +# 内置运行依赖 + +如果安装时 GitHub 下载太慢,可以把 Mihomo、yq、subconverter 提前放到本目录。安装和 `clashctl upgrade` 会优先读取这里的文件;没有匹配文件时,仍会回退到原来的下载逻辑。 + +Clash 是兼容内核,固定走远程下载,不使用本目录中的本地资源。 + +默认目录结构: + +```text +resources/bin/ + mihomo/ + mihomo-linux-amd64-compatible-v1.19.23.gz + mihomo-linux-arm64-v1.19.23.gz + mihomo-linux-armv7-v1.19.23.gz + yq/ + yq_linux_amd64.tar.gz + yq_linux_arm64.tar.gz + yq_linux_arm.tar.gz + subconverter/ + subconverter_linux64.tar.gz + subconverter_aarch64.tar.gz + subconverter_armv7.tar.gz +``` + +当前正式支持 `amd64`、`arm64`、`armv7`。版本号要和 `.env` 里的 `MIHOMO_VERSION`、`YQ_VERSION`、`SUBCONVERTER_VERSION` 对应。脚本只按当前目标文件名精确命中,不会扫描目录,也不会自动选择最高版本;如果本地没有对应文件,会继续联网下载。 + +Mihomo、yq、subconverter 兼容旧路径 `resources/bin///`,但默认推荐直接使用 `resources/bin//`。 + +可选开关: + +```bash +export CLASH_BUNDLED_ASSET_ENABLED=false +export CLASH_BUNDLED_ASSET_DIR=/path/to/assets +``` + +`Country.mmdb` 仍放在 `resources/geo/Country.mmdb` 或 `resources/geo/country.mmdb`。 diff --git a/scripts/core/clashctl.sh b/scripts/core/clashctl.sh index 7e58983..aef481a 100644 --- a/scripts/core/clashctl.sh +++ b/scripts/core/clashctl.sh @@ -34,6 +34,7 @@ Usage: config kernel mihomo|clash 🧩 切换指定内核 mixin 🧩 Mixin 配置管理 relay 🔗 多跳节点管理 + lan 🏠 局域网代理管理 📦 Subscription: @@ -45,6 +46,7 @@ Usage: 🕹️ Control: clashui 🕹️ 查看 Web 控制台 secret 🔑 查看或设置 Web 密钥 + lan on|off|status 🏠 开启 / 关闭局域网代理 🧪 Transparent proxy: tun 🧪 Tun 模式管理 @@ -2301,6 +2303,7 @@ status_port_adjustment_brief() { print_status_summary_compact() { local profile mixed_port controller controller_lan controller_public + local allow_lan lan_proxy local running_text user_connectivity user_risk current_proxy_brief system_proxy_text local current_active dashboard_text dashboard_source_text dashboard_policy_text secret_text local tun_text bind_failure_text next_action @@ -2309,6 +2312,7 @@ print_status_summary_compact() { [ -n "${profile:-}" ] || profile="default" mixed_port="$(status_read_mixed_port 2>/dev/null || true)" + allow_lan="$(runtime_config_allow_lan 2>/dev/null || config_allow_lan 2>/dev/null || echo true)" controller="$(status_read_controller 2>/dev/null || true)" controller_lan="$(status_read_controller_lan 2>/dev/null || true)" controller_public="$(status_read_controller_public 2>/dev/null || true)" @@ -2386,6 +2390,12 @@ print_status_summary_compact() { if [ -n "${mixed_port:-}" ] && [ "$mixed_port" != "null" ]; then echo "🌐 本地代理:http://127.0.0.1:${mixed_port}" + if [ "$allow_lan" = "true" ]; then + lan_proxy="$(ui_lan_ip 2>/dev/null || true)" + [ -n "${lan_proxy:-}" ] && echo "🏠 局域网代理:http://${lan_proxy}:${mixed_port}" + else + echo "🏠 局域网代理:已关闭" + fi else echo "🌐 本地代理:未知" fi @@ -2410,6 +2420,7 @@ print_status_summary_compact() { print_status_summary_verbose() { local running_text profile mixed_port controller controller_lan controller_public + local allow_lan lan_proxy local current_active build_active_sources build_failed_active_sources build_status build_time local build_block_reason build_block_time local last_switch_from last_switch_to last_switch_time @@ -2426,6 +2437,7 @@ print_status_summary_verbose() { [ -n "${profile:-}" ] || profile="default" mixed_port="$(status_read_mixed_port 2>/dev/null || true)" + allow_lan="$(runtime_config_allow_lan 2>/dev/null || config_allow_lan 2>/dev/null || echo true)" controller="$(status_read_controller 2>/dev/null || true)" controller_lan="$(status_read_controller_lan 2>/dev/null || true)" controller_public="$(status_read_controller_public 2>/dev/null || true)" @@ -2532,6 +2544,12 @@ print_status_summary_verbose() { echo "🔧 Profile:$profile" if [ -n "${mixed_port:-}" ] && [ "$mixed_port" != "null" ]; then echo "🌐 本地代理:http://127.0.0.1:${mixed_port}" + if [ "$allow_lan" = "true" ]; then + lan_proxy="$(ui_lan_ip 2>/dev/null || true)" + [ -n "${lan_proxy:-}" ] && echo "🏠 局域网代理:http://${lan_proxy}:${mixed_port}" + else + echo "🏠 局域网代理:已关闭" + fi else echo "🌐 本地代理:未知" fi @@ -2960,6 +2978,7 @@ cmd_ui_help_summary() { printf ' %-18s %s\n' "clashctl use" "💱 切换订阅" printf ' %-18s %s\n' "clashctl ls" "📜 查看订阅列表" echo "📌 高级" + printf ' %-18s %s\n' "clashctl lan" "🏠 局域网代理管理" printf ' %-18s %s\n' "clashctl tun" "🧪 Tun 模式管理" printf ' %-18s %s\n' "clashctl mixin" "🧩 Mixin 配置管理" printf ' %-18s %s\n' "clashctl sub" "🧩 订阅高级管理(启用 / 禁用 / 重命名 / 删除)" @@ -3890,6 +3909,67 @@ cmd_config() { esac } +print_lan_status() { + local allow_lan mixed_port lan_ip + + allow_lan="$(runtime_config_allow_lan 2>/dev/null || config_allow_lan 2>/dev/null || echo true)" + mixed_port="$(status_read_mixed_port 2>/dev/null || runtime_config_mixed_port 2>/dev/null || true)" + lan_ip="$(ui_lan_ip 2>/dev/null || true)" + + ui_title "🏠 局域网代理" + if [ "$allow_lan" = "true" ]; then + ui_kv "📶" "状态" "已开启" + if [ -n "${lan_ip:-}" ] && [ -n "${mixed_port:-}" ] && [ "$mixed_port" != "null" ]; then + ui_kv "🏠" "局域网代理" "http://${lan_ip}:${mixed_port}" + else + ui_kv "🏠" "局域网代理" "等待运行配置生成" + fi + else + ui_kv "📶" "状态" "已关闭" + fi + ui_kv "🔧" "配置项" "allow-lan: ${allow_lan}" + ui_blank +} + +cmd_lan() { + local action + prepare + + action="${1:-status}" + case "$action" in + on|enable) + set_config_allow_lan true + regenerate_config + apply_runtime_change_after_config_mutation + success "局域网代理已开启" + print_lan_status + print_config_apply_feedback + ;; + off|disable) + set_config_allow_lan false + regenerate_config + apply_runtime_change_after_config_mutation + success "局域网代理已关闭" + print_lan_status + print_config_apply_feedback + ;; + status) + print_lan_status + ui_next "clashctl lan on" + ui_blank + ;; + -h|--help|help|"") + echo "📜 用法:" + echo " clashctl lan status" + echo " clashctl lan on" + echo " clashctl lan off" + ;; + *) + die_usage "未知的 lan 子命令:$action" "clashctl lan on|off|status" + ;; + esac +} + print_profile_use_feedback() { local profile="$1" @@ -7303,6 +7383,7 @@ case "$cmd" in tun) cmd_tun "$@" ;; dev) cmd_dev "$@" ;; config) cmd_config "$@" ;; + lan) cmd_lan "$@" ;; mixin) cmd_mixin "$@" ;; relay) cmd_relay "$@" ;; profile) cmd_profile "$@" ;; diff --git a/scripts/core/common.sh b/scripts/core/common.sh index 0e1d171..328731e 100644 --- a/scripts/core/common.sh +++ b/scripts/core/common.sh @@ -1754,6 +1754,19 @@ runtime_config_controller_port() { echo "$port" } +runtime_config_allow_lan() { + local file + local value + file="$(runtime_config_file)" + [ -s "$file" ] || return 1 + + value="$("$(yq_bin)" eval '.["allow-lan"] // true' "$file" 2>/dev/null | head -n 1)" + case "$value" in + false) echo "false" ;; + *) echo "true" ;; + esac +} + runtime_port_value_is_valid() { local port="${1:-}" diff --git a/scripts/core/completion.sh b/scripts/core/completion.sh index c64dbee..541f8be 100644 --- a/scripts/core/completion.sh +++ b/scripts/core/completion.sh @@ -170,6 +170,17 @@ _clash_for_linux_complete_config() { fi } +_clash_for_linux_complete_lan() { + local cur="$1" + local rel_index="$2" + + COMPREPLY=() + + if [ "$rel_index" -eq 1 ]; then + _clash_for_linux_add_matches "$cur" on off status enable disable help -h --help + fi +} + _clash_for_linux_complete_mixin() { local cur="$1" local rel_index="$2" @@ -304,7 +315,7 @@ _clash_for_linux_complete_top_level() { COMPREPLY=() _clash_for_linux_add_matches "$cur" \ add use ls health select on off status status-next \ - boot log logs doctor ui secret tun dev config mixin \ + boot log logs doctor ui secret tun dev config lan mixin \ relay profile sub proxy upgrade update completion help \ -h --help } @@ -368,6 +379,7 @@ _clash_for_linux_complete_command() { status) _clash_for_linux_complete_status "$cur" ;; boot) _clash_for_linux_complete_boot "$cur" "$rel_index" "$arg1" ;; config) _clash_for_linux_complete_config "$cur" "$rel_index" "$arg1" ;; + lan) _clash_for_linux_complete_lan "$cur" "$rel_index" ;; mixin) _clash_for_linux_complete_mixin "$cur" "$rel_index" ;; relay) _clash_for_linux_complete_relay "$cur" "$rel_index" "$arg1" ;; sub) _clash_for_linux_complete_sub "$cur" "$rel_index" "$arg1" ;; diff --git a/scripts/core/config.sh b/scripts/core/config.sh index 6f06bff..b0d3e3e 100644 --- a/scripts/core/config.sh +++ b/scripts/core/config.sh @@ -379,6 +379,33 @@ render_base_config() { fi } +config_allow_lan() { + local file="$CONFIG_DIR/template.yaml" + local value + + ensure_config_files + value="$("$(yq_bin)" eval '.["allow-lan"] // true' "$file" 2>/dev/null | head -n 1)" + case "$value" in + false) echo "false" ;; + *) echo "true" ;; + esac +} + +set_config_allow_lan() { + local value="$1" + local file="$CONFIG_DIR/template.yaml" + + case "$value" in + true|false) ;; + *) die "allow-lan 只允许 true 或 false" ;; + esac + + ensure_config_files + ALLOW_LAN_VALUE="$value" "$(yq_bin)" eval -i ' + .["allow-lan"] = (env(ALLOW_LAN_VALUE) == "true") + ' "$file" +} + subscription_url_scheme() { local url="$1" @@ -766,7 +793,7 @@ normalize_runtime_config() { local file="$1" local mixed_port controller tun_enable_value tun_stack_value dns_port_value controller_secret_value local tun_auto_route_value tun_auto_redirect_value tun_strict_route_value tun_dns_hijack_value - local dashboard_dir_value + local dashboard_dir_value allow_lan_value local resolved_ports err_file output [ -s "$file" ] || die "待规范化的配置文件不存在:$file" @@ -785,10 +812,12 @@ normalize_runtime_config() { dns_port_value="$CLASH_DNS_PORT_RESOLVED" controller_secret_value="$(ensure_controller_secret)" dashboard_dir_value="$(runtime_dashboard_dir)" + allow_lan_value="$(config_allow_lan 2>/dev/null || echo true)" err_file="$(mktemp)" if ! mixed_port="$mixed_port" \ controller="$controller" \ + allow_lan_value="$allow_lan_value" \ tun_enable_value="$tun_enable_value" \ tun_stack_value="$tun_stack_value" \ tun_auto_route_value="$tun_auto_route_value" \ @@ -804,7 +833,7 @@ normalize_runtime_config() { .secret = env(controller_secret_value) | .["external-ui"] = env(dashboard_dir_value) | .["external-ui-url"] = "/ui" | - .["allow-lan"] = (.["allow-lan"] // true) | + .["allow-lan"] = (env(allow_lan_value) == "true") | .mode = "rule" | .["log-level"] = (.["log-level"] // "info") |