Skip to content

Commit d674889

Browse files
committed
fix(web): 修上传报告"返回"失效(blob用绝对地址) + 界面美化 v0.7.12
- 上传报告经blob URL打开,返回链接改绝对 http://localhost:端口/ (相对/在blob下失效) - 文件选择器美化为虚线投放区+红色按钮;卡片加品牌色顶条
1 parent 956e5a6 commit d674889

3 files changed

Lines changed: 33 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/),
66
and this project adheres to [Semantic Versioning](https://semver.org/).
77

8+
## [0.7.12] - 2026-06-21
9+
10+
### Fixed — 上传报告"返回"失效
11+
- 上传文件夹的报告经 `blob:` URL 打开,原"返回"链接是相对 `/` 在 blob 页面下失效(点了没反应)。本地模式改用**绝对地址** `http://localhost:端口/`,blob 报告也能正确返回首页
12+
13+
### Changed — 界面美化(UI)
14+
- 文件选择器美化为**虚线投放区 + 红色按钮**(替代浏览器默认丑控件)
15+
- 卡片加顶部品牌色条、字号/间距/对比度微调,更精致
16+
817
## [0.7.11] - 2026-06-21
918

1019
### Changed — 能静态查的就直接判,不再"待核验"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "shellward",
3-
"version": "0.7.11",
3+
"version": "0.7.12",
44
"mcpName": "io.github.jnMetaCode/shellward",
55
"description": "AI agent security & MCP security middleware — prompt injection detection, AI firewall, runtime guardrails & data-loss prevention for LLM tool calls. 8-layer defense against data exfiltration & dangerous commands. Zero dependencies. SDK + OpenClaw plugin. Supports LangChain, AutoGPT, Claude Code, Cursor, OpenAI Agents, Hermes Agent.",
66
"keywords": [

src/web/scan-server.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@ export function validateRepoUrl(input: string): { ok: true; url: string } | { ok
4141
return { ok: true, url }
4242
}
4343

44+
// 报告「返回」链接:本地模式用绝对地址(上传报告经 blob: URL 打开,相对 '/' 会失效)
45+
let SERVER_BASE = '/'
46+
4447
export function startWebServer(opts: WebServerOptions): void {
4548
const locale = resolveLocale(DEFAULT_CONFIG)
4649
const host = opts.local ? '127.0.0.1' : '0.0.0.0'
50+
SERVER_BASE = opts.local ? `http://localhost:${opts.port}/` : '/'
4751
let active = 0
4852

4953
const server = createServer(async (req, res) => {
@@ -113,7 +117,7 @@ async function handleRepo(res: any, repo: string, locale: 'zh' | 'en', inc: () =
113117
try {
114118
await cloneRepo(v.url, dir)
115119
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, dir)
116-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: v.url, backLink: '/' }))
120+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: v.url, backLink: SERVER_BASE }))
117121
} catch (e: any) {
118122
const msg = esc(e?.message || String(e))
119123
send(res, 502, 'text/html', errorPage(
@@ -133,7 +137,7 @@ async function handleLocal(res: any, path: string, locale: 'zh' | 'en', inc: ()
133137
inc()
134138
try {
135139
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, root)
136-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root, backLink: '/' }))
140+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root, backLink: SERVER_BASE }))
137141
} finally {
138142
dec()
139143
}
@@ -176,7 +180,7 @@ async function handleUpload(req: any, res: any, locale: 'zh' | 'en', inc: () =>
176180
}
177181
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, dir)
178182
const rootName = typeof payload.root === 'string' && payload.root ? payload.root : '(uploaded folder)'
179-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: rootName, backLink: '/' }))
183+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: rootName, backLink: SERVER_BASE }))
180184
} catch (e: any) {
181185
send(res, 500, 'text/html', errorPage('扫描失败:' + esc(e?.message || String(e))))
182186
} finally {
@@ -226,7 +230,7 @@ function handleDemo(res: any, locale: 'zh' | 'en', inc: () => void, dec: () => v
226230
writeFileSync(join(dir, '.env'),
227231
'AWS_ACCESS_KEY=AKIARZ9MKP2QWLS7YV3N\nDB_PASSWORD=Sup3rS3cretProdPwd2026\n')
228232
const { report, scan } = runProjectComplianceAudit(DEFAULT_CONFIG, dir)
229-
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: '示例项目(含风险)/ demo-ai-app', backLink: '/' }))
233+
send(res, 200, 'text/html', renderHtmlReport(report, scan, locale, { root: '示例项目(含风险)/ demo-ai-app', backLink: SERVER_BASE }))
230234
} catch (e: any) {
231235
send(res, 500, 'text/html', errorPage('演示失败:' + esc(e?.message || String(e))))
232236
} finally {
@@ -373,20 +377,26 @@ function page(title: string, body: string): string {
373377
*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;
374378
background:linear-gradient(135deg,#eef1f6,#e2e8f0);color:#0f172a;
375379
font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif}
376-
.hero{background:#fff;max-width:560px;width:92%;margin:40px;padding:40px;border-radius:18px;
377-
box-shadow:0 12px 40px rgba(15,23,42,.12);text-align:center}
378-
.logo{font-weight:800;font-size:15px}.logo span{color:#cb0000}
379-
h1{font-size:30px;margin:14px 0 8px;letter-spacing:-.5px}
380-
.sub{color:#64748b;margin:0 0 26px}
380+
.hero{background:#fff;max-width:580px;width:92%;margin:40px;padding:38px 40px 34px;border-radius:18px;
381+
box-shadow:0 12px 40px rgba(15,23,42,.12);text-align:center;border-top:4px solid #cb0000}
382+
.logo{font-weight:800;font-size:15px;letter-spacing:.2px}.logo span{color:#cb0000}
383+
h1{font-size:29px;margin:12px 0 8px;letter-spacing:-.5px}
384+
.sub{color:#64748b;margin:0 0 24px;font-size:15px}
381385
form{display:flex;flex-direction:column;gap:10px;text-align:left}
382-
label{font-size:13px;font-weight:600;color:#475569}
386+
label{font-size:13px;font-weight:700;color:#334155}
383387
input{padding:14px 16px;border:1px solid #cbd5e1;border-radius:10px;font-size:16px;width:100%}
384388
input:focus{outline:none;border-color:#cb0000;box-shadow:0 0 0 3px rgba(203,0,0,.12)}
385-
.hint{font-size:12.5px;color:#64748b;margin:2px 0 6px}
389+
.hint{font-size:12.5px;color:#64748b;margin:2px 0 6px;line-height:1.55}
386390
.hint code{background:#f1f5f9;padding:1px 6px;border-radius:5px}
387-
input[type=file]{padding:12px;background:#f8fafc;cursor:pointer}
391+
/* 文件选择器:美化成虚线投放区 + 红色按钮 */
392+
input[type=file]{width:100%;padding:20px 16px;border:2px dashed #cbd5e1;border-radius:12px;
393+
background:#f8fafc;cursor:pointer;font-size:14px;color:#64748b;transition:.15s}
394+
input[type=file]:hover{border-color:#cb0000;background:#fff}
395+
input[type=file]::file-selector-button{background:#cb0000;color:#fff;border:0;border-radius:8px;
396+
padding:9px 18px;margin-right:14px;font-weight:700;font-size:14px;cursor:pointer}
397+
input[type=file]::file-selector-button:hover{background:#a80000}
388398
button{background:#cb0000;color:#fff;border:0;border-radius:10px;padding:14px;font-size:16px;
389-
font-weight:700;cursor:pointer;margin-top:4px}button:hover{background:#a80000}
399+
font-weight:700;cursor:pointer;margin-top:4px;transition:.15s}button:hover{background:#a80000}
390400
button:disabled{background:#94a3b8;cursor:default}
391401
form{margin:0 0 14px}.or{text-align:center;color:#94a3b8;font-size:13px;margin:6px 0 14px}
392402
.status{display:none;margin:10px 0 0;padding:10px 14px;border-radius:8px;background:#f1f5f9;

0 commit comments

Comments
 (0)