Skip to content

Commit aa44a52

Browse files
committed
feat(webui): 统一三套管理面板入口
将 NetProxy WebUI、zashboard 与 sing-box Dashboard 统一规划到 webroot。 新增安全区适配的根入口页,内置两套 Dashboard,隔离 WebUI 构建目录,并同步资源更新、面板配置与 CLI 地址输出。
1 parent c45dc3c commit aa44a52

87 files changed

Lines changed: 651 additions & 17 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/build-module/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ runs:
2626
cache: npm
2727
cache-dependency-path: src/webui/package-lock.json
2828

29-
- name: 构建 WebUI(产物 → src/module/webroot)
29+
- name: 构建 WebUI(产物 → src/module/webroot/netproxy
3030
shell: bash
3131
working-directory: src/webui
3232
run: |

.github/resources.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,18 @@
2323
],
2424
"githubRelease": [
2525
{
26-
"path": "src/module/bin/zashboard",
26+
"path": "src/module/webroot/zashboard",
2727
"repo": "Zephyruso/zashboard",
2828
"assetPattern": "dist-no-fonts.zip",
2929
"currentVersion": "v3.16.1"
3030
}
31+
],
32+
"githubArchive": [
33+
{
34+
"path": "src/module/webroot/sing-box-dashboard",
35+
"repo": "SagerNet/sing-box-dashboard",
36+
"ref": "gh-pages",
37+
"currentCommit": "4254b9c42e70b622953dca6c79bf3451bc50a489"
38+
}
3139
]
3240
}

.github/workflows/update-resources.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,60 @@ jobs:
115115
echo "ZASHBOARD_EOF"
116116
} >> "$GITHUB_OUTPUT"
117117
118+
- name: 更新 sing-box Dashboard
119+
id: singbox_dashboard
120+
env:
121+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
122+
run: |
123+
repo="$(jq -r '.githubArchive[0].repo' .github/resources.json)"
124+
ref="$(jq -r '.githubArchive[0].ref' .github/resources.json)"
125+
dest="$(jq -r '.githubArchive[0].path' .github/resources.json)"
126+
current="$(jq -r '.githubArchive[0].currentCommit' .github/resources.json)"
127+
latest="$(gh api "repos/${repo}/commits/${ref}" --jq '.sha')"
128+
129+
echo "sing-box Dashboard 当前: '${current:-无}',最新: '$latest'"
130+
131+
summary=""
132+
if [ "$latest" != "$current" ]; then
133+
echo "发现上游更新,开始同步..."
134+
rm -rf tmp-sing-box-dashboard
135+
mkdir -p tmp-sing-box-dashboard/extract
136+
137+
if curl -fsSL --retry 3 --connect-timeout 15 \
138+
"https://github.com/${repo}/archive/${latest}.zip" \
139+
-o tmp-sing-box-dashboard/dashboard.zip; then
140+
unzip -q tmp-sing-box-dashboard/dashboard.zip -d tmp-sing-box-dashboard/extract
141+
srcdir="$(find tmp-sing-box-dashboard/extract -mindepth 1 -maxdepth 1 -type d | head -n 1)"
142+
143+
if [ -n "$srcdir" ] && [ -f "$srcdir/index.html" ]; then
144+
rm -rf "$dest"
145+
mkdir -p "$dest"
146+
cp -r "$srcdir"/. "$dest"/
147+
148+
tmpjson="$(mktemp)"
149+
jq --arg commit "$latest" \
150+
'.githubArchive[0].currentCommit = $commit' \
151+
.github/resources.json > "$tmpjson"
152+
mv "$tmpjson" .github/resources.json
153+
summary="- ${dest}:sing-box Dashboard ${current:0:7} → ${latest:0:7}"
154+
else
155+
echo "::warning::归档中没有找到 Dashboard 入口文件,跳过更新"
156+
fi
157+
else
158+
echo "::warning::下载 sing-box Dashboard 失败,跳过更新"
159+
fi
160+
161+
rm -rf tmp-sing-box-dashboard
162+
else
163+
echo "已是最新,跳过"
164+
fi
165+
166+
{
167+
echo "summary<<SING_BOX_DASHBOARD_EOF"
168+
printf '%s\n' "$summary"
169+
echo "SING_BOX_DASHBOARD_EOF"
170+
} >> "$GITHUB_OUTPUT"
171+
118172
- name: 更新 npm 依赖
119173
id: npm
120174
run: |
@@ -230,6 +284,7 @@ jobs:
230284
### 内置资源
231285
${{ steps.raw.outputs.summary }}
232286
${{ steps.zashboard.outputs.summary }}
287+
${{ steps.singbox_dashboard.outputs.summary }}
233288
234289
### npm 依赖
235290
${{ steps.npm.outputs.summary }}

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
# Claude Code 本地项目指引 (不纳入仓库)
88
CLAUDE.md
99

10-
# WebUI 构建产物 (由 src/webui 构建生成,不纳入仓库)
11-
src/module/webroot/
10+
# NetProxy WebUI 构建产物由 src/webui 构建生成,不纳入仓库
11+
src/module/webroot/netproxy/

src/module/config/singbox/confdir/02_experimental.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"default_mode": "Rule",
1010
"external_controller": "0.0.0.0:9999",
1111
"secret": "singbox",
12-
"external_ui": "/data/adb/modules/netproxy/bin/zashboard",
12+
"external_ui": "/data/adb/modules/netproxy/webroot/zashboard",
1313
"external_ui_download_url": "https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip"
1414
}
1515
}

src/module/config/singbox/confdir/08_services.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"secret": "singbox",
88
"dashboard": {
99
"enabled": true,
10-
"path": "dashboard",
10+
"path": "/data/adb/modules/netproxy/webroot/sing-box-dashboard",
1111
"download_url": "https://github.com/SagerNet/sing-box-dashboard/archive/refs/heads/gh-pages.zip",
1212
"http_client": "rule-set-download",
1313
"update_interval": "1d"

src/module/module.prop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ versionCode=2
55
author=Fanju
66
description=基于 sing-box eBPF 入站的 Android 透明代理工具
77
updateJson=https://github.com/Fanju6/NetProxy-Magisk/releases/latest/download/update.json
8-
webuiIcon=webroot/favicon.svg
8+
webuiIcon=webroot/netproxy/favicon.svg

src/module/scripts/cli

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -758,16 +758,18 @@ cmd_api_close_all() {
758758
info "全部连接已关闭"
759759
}
760760

761-
# 显示控制器与面板访问地址及密钥
761+
# 显示控制接口与面板访问地址及密钥
762762
cmd_api_ui() {
763763
local ip
764764

765765
ip="$(detect_primary_ipv4)"
766766
[ -n "$ip" ] || ip="<设备IP>"
767767

768-
printf "控制地址: http://%s:9999\n" "$ip"
769-
printf "面板地址: http://%s:9999/ui\n" "$ip"
770-
printf "密钥: %s\n" "$(api_secret)"
768+
printf "Clash API: http://%s:9999\n" "$ip"
769+
printf "zashboard: http://%s:9999/ui/\n" "$ip"
770+
printf "Service API: http://%s:9090\n" "$ip"
771+
printf "sing-box Dashboard: http://%s:9090/dashboard/\n" "$ip"
772+
printf "控制密钥: %s\n" "$(api_secret)"
771773
}
772774

773775
# api 命令组分发

src/module/webroot/index.html

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
<!doctype html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta name="theme-color" content="#f4f7f6" />
7+
<link rel="icon" type="image/svg+xml" href="./netproxy/favicon.svg" />
8+
<link rel="stylesheet" href="https://mui.kernelsu.org/internal/insets.css" />
9+
<title>NetProxy</title>
10+
<style>
11+
:root {
12+
color-scheme: light dark;
13+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
14+
background: #f4f7f6;
15+
color: #17201d;
16+
}
17+
18+
* {
19+
box-sizing: border-box;
20+
letter-spacing: 0;
21+
}
22+
23+
body {
24+
min-width: 280px;
25+
min-height: 100vh;
26+
margin: 0;
27+
background: #f4f7f6;
28+
}
29+
30+
main {
31+
width: min(100%, 720px);
32+
margin: 0 auto;
33+
padding:
34+
max(28px, var(--safe-area-inset-top, env(safe-area-inset-top)))
35+
max(20px, var(--safe-area-inset-right, env(safe-area-inset-right)))
36+
max(28px, var(--safe-area-inset-bottom, env(safe-area-inset-bottom)))
37+
max(20px, var(--safe-area-inset-left, env(safe-area-inset-left)));
38+
}
39+
40+
header {
41+
display: flex;
42+
align-items: center;
43+
gap: 14px;
44+
margin-bottom: 30px;
45+
}
46+
47+
.brand-icon {
48+
width: 54px;
49+
height: 54px;
50+
flex: 0 0 54px;
51+
}
52+
53+
h1 {
54+
margin: 0;
55+
font-size: 28px;
56+
line-height: 1.2;
57+
font-weight: 720;
58+
}
59+
60+
.subtitle {
61+
margin: 5px 0 0;
62+
color: #52615c;
63+
font-size: 14px;
64+
line-height: 1.45;
65+
}
66+
67+
.entries {
68+
display: grid;
69+
gap: 12px;
70+
}
71+
72+
.entry {
73+
display: grid;
74+
grid-template-columns: 48px minmax(0, 1fr) auto;
75+
align-items: center;
76+
gap: 14px;
77+
min-height: 88px;
78+
padding: 16px;
79+
color: inherit;
80+
text-decoration: none;
81+
background: #ffffff;
82+
border: 1px solid #d8e0dd;
83+
border-radius: 8px;
84+
transition: border-color 150ms ease, background-color 150ms ease, transform 150ms ease;
85+
}
86+
87+
.entry:hover,
88+
.entry:focus-visible {
89+
background: #f9fffc;
90+
border-color: #168a66;
91+
outline: none;
92+
transform: translateY(-1px);
93+
}
94+
95+
.entry:active {
96+
transform: translateY(0);
97+
}
98+
99+
.entry-icon {
100+
width: 48px;
101+
height: 48px;
102+
object-fit: contain;
103+
border-radius: 8px;
104+
}
105+
106+
.entry-title {
107+
display: block;
108+
margin-bottom: 4px;
109+
font-size: 17px;
110+
line-height: 1.3;
111+
font-weight: 680;
112+
}
113+
114+
.entry-description {
115+
display: block;
116+
color: #5c6965;
117+
font-size: 13px;
118+
line-height: 1.5;
119+
}
120+
121+
.entry-action {
122+
min-width: 54px;
123+
color: #087a58;
124+
font-size: 14px;
125+
font-weight: 650;
126+
text-align: right;
127+
}
128+
129+
footer {
130+
margin-top: 22px;
131+
color: #687570;
132+
font-size: 12px;
133+
line-height: 1.5;
134+
text-align: center;
135+
}
136+
137+
@media (max-width: 440px) {
138+
main {
139+
padding-right: max(14px, var(--safe-area-inset-right, env(safe-area-inset-right)));
140+
padding-left: max(14px, var(--safe-area-inset-left, env(safe-area-inset-left)));
141+
}
142+
143+
.entry {
144+
grid-template-columns: 42px minmax(0, 1fr);
145+
gap: 12px;
146+
padding: 14px;
147+
}
148+
149+
.entry-icon {
150+
width: 42px;
151+
height: 42px;
152+
}
153+
154+
.entry-action {
155+
display: none;
156+
}
157+
}
158+
159+
@media (prefers-color-scheme: dark) {
160+
:root,
161+
body {
162+
background: #151a18;
163+
color: #edf4f1;
164+
}
165+
166+
.subtitle,
167+
.entry-description,
168+
footer {
169+
color: #aab6b1;
170+
}
171+
172+
.entry {
173+
background: #202724;
174+
border-color: #39443f;
175+
}
176+
177+
.entry:hover,
178+
.entry:focus-visible {
179+
background: #25302c;
180+
border-color: #4ec9a0;
181+
}
182+
183+
.entry-action {
184+
color: #72dbb6;
185+
}
186+
}
187+
</style>
188+
</head>
189+
<body>
190+
<main>
191+
<header>
192+
<img class="brand-icon" src="./netproxy/favicon.svg" alt="" />
193+
<div>
194+
<h1>NetProxy</h1>
195+
<p class="subtitle">选择管理界面</p>
196+
</div>
197+
</header>
198+
199+
<nav class="entries" aria-label="管理界面">
200+
<a class="entry" href="./netproxy/index.html#/dashboard">
201+
<img class="entry-icon" src="./netproxy/favicon.svg" alt="" />
202+
<span>
203+
<span class="entry-title">NetProxy WebUI</span>
204+
<span class="entry-description">管理服务、节点、应用与模块设置</span>
205+
</span>
206+
<span class="entry-action">打开</span>
207+
</a>
208+
209+
<a class="entry" href="http://127.0.0.1:9999/ui/">
210+
<picture>
211+
<source media="(prefers-color-scheme: dark)" srcset="./zashboard/favicon-dark.svg" />
212+
<img class="entry-icon" src="./zashboard/favicon.svg" alt="" />
213+
</picture>
214+
<span>
215+
<span class="entry-title">zashboard</span>
216+
<span class="entry-description">查看代理组、连接、规则与实时流量</span>
217+
</span>
218+
<span class="entry-action">打开</span>
219+
</a>
220+
221+
<a class="entry" href="http://127.0.0.1:9090/dashboard/">
222+
<img class="entry-icon" src="./sing-box-dashboard/favicon.svg" alt="" />
223+
<span>
224+
<span class="entry-title">sing-box Dashboard</span>
225+
<span class="entry-description">查看内核状态、日志、连接与网络测试</span>
226+
</span>
227+
<span class="entry-action">打开</span>
228+
</a>
229+
</nav>
230+
231+
<footer>控制面板需要 sing-box 服务运行</footer>
232+
</main>
233+
</body>
234+
</html>

src/module/webroot/sing-box-dashboard/.nojekyll

Whitespace-only changes.

0 commit comments

Comments
 (0)