- 1.6 + 16.4.7:安全入口=路径式 `/<ADMIN_PATH>`(env `ADMIN_PATH`,默认 `admin`;`xxx/admin` 亦可,
非法值回退 `/admin`)。命中前缀 → 送注入了 `<base href="/">` + `window.__CYRENE_ADMIN_ENTRY__` 的面板入口页
(深链直达);**形如面板入口但前缀不符 → 404**(不回落用户端聊天壳);`/api/*`、`/ws`、webhook 先放行
⇒ **API 不藏**。前端路由改**路径式**(`router.ts` 只读注入,无硬编兜底;移除 hash 入口)
- 1.5 / 12.4.3 核对:暴露面默认 `0.0.0.0` 与裁决一致,无需改代码(仅补注释)
- 12.4.2:`DATA_DIR` 支持 `platform` 哨兵(大小写不敏感)→ 平台标准目录;默认 `./data` 与显式空值行为不变
(显式空值保持原兜底,避免正在运行实例突然换库)
- 复核:build/vet 绿 + config/gateway 包测试绿 + linux/darwin 交叉编译绿;新增 7 条用例,
并用真实构建产物(`webui.Assets()`)跑通「浏览器直开 /admin 能启动面板」(`<base>` 必须在资源引用之前)
- 变异 2 条(关掉错前缀 404 / 前端默认前缀兜底)已还原
- ⚠️ 运行时序:前端已生效而后端 hatch 未生效期间**面板打不开** ⇒ 需紧接一次「build 到位 + restart」
73 lines
2.1 KiB
Go
73 lines
2.1 KiB
Go
package config
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/platform"
|
||
)
|
||
|
||
// TestLoad_DataDirPlatformSentinel 覆盖数据目录的两种取法(design/13 §2,
|
||
// 裁决 12.4.2 + 15.4.1):未配置走默认 `./data`;显式哨兵 `platform` 才走
|
||
// 平台标准数据目录;显式路径原样保留。
|
||
func TestLoad_DataDirPlatformSentinel(t *testing.T) {
|
||
t.Setenv("JWT_SECRET", strings.Repeat("s", 40))
|
||
unsetEnvForTest(t, "DATA_DIR")
|
||
|
||
cfg, err := Load("")
|
||
if err != nil {
|
||
t.Fatalf("Load() error: %v", err)
|
||
}
|
||
if cfg.DataDir != "./data" {
|
||
t.Errorf("DataDir = %q, want ./data (unset keeps the historic default)", cfg.DataDir)
|
||
}
|
||
|
||
t.Setenv("DATA_DIR", "platform")
|
||
cfg, err = Load("")
|
||
if err != nil {
|
||
t.Fatalf("Load() with sentinel error: %v", err)
|
||
}
|
||
if want := platform.DefaultDataDir(); cfg.DataDir != want {
|
||
t.Errorf("DataDir = %q, want the platform standard dir %q", cfg.DataDir, want)
|
||
}
|
||
|
||
// 显式路径(含显式 ./data)不被改写。
|
||
for _, p := range []string{"./data", "/var/lib/cyrene"} {
|
||
t.Setenv("DATA_DIR", p)
|
||
cfg, err = Load("")
|
||
if err != nil {
|
||
t.Fatalf("Load() with DATA_DIR=%q error: %v", p, err)
|
||
}
|
||
if cfg.DataDir != p {
|
||
t.Errorf("DataDir = %q, want %q verbatim", cfg.DataDir, p)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestAdminPathPrefix 覆盖安全入口前缀的归一化(裁决 1.6 / 16.4.7,
|
||
// design/07 §0.2):默认 `admin` → `/admin`,自定义 `xxx/admin` →
|
||
// `/xxx/admin`(即 `host/xxx/admin` 形态),非法值一律挂回默认。
|
||
func TestAdminPathPrefix(t *testing.T) {
|
||
want := map[string]string{
|
||
"": "/admin",
|
||
"admin": "/admin",
|
||
"/admin": "/admin",
|
||
"/admin/": "/admin",
|
||
"xxx/admin": "/xxx/admin",
|
||
"/xxx/admin/": "/xxx/admin",
|
||
" a / b ": "/a/b",
|
||
"../etc": "/admin",
|
||
"a/../b": "/admin",
|
||
"a//b": "/admin",
|
||
"a?b": "/admin",
|
||
"a#b": "/admin",
|
||
" ": "/admin",
|
||
}
|
||
for in, exp := range want {
|
||
cfg := ServerConfig{AdminPath: in}
|
||
if got := cfg.AdminPathPrefix(); got != exp {
|
||
t.Errorf("AdminPath %q → %q, want %q", in, got, exp)
|
||
}
|
||
}
|
||
}
|