fix: 管理会话自动恢复与访问来源统计优化

- 新增 AdminSessionRecover:后台离线/后端重启时自动重试恢复会话,明确失效才回跳登录
- 认证 cookie 契约调整(SameSite=Lax 与刷新轮转适配),路由与守卫适配
- 访问来源统计查询优化与测试、管理端 analytics sources 页面适配
- 移动端布局修正:覆盖 body min-h-screen 避免内容区高度异常
This commit is contained in:
2026-09-28 03:33:52 +08:00
parent 0c3b3a710c
commit 3e0689fe98
15 changed files with 548 additions and 85 deletions

View File

@@ -29,3 +29,26 @@ func TestClassifyReferrer(t *testing.T) {
}
}
}
func TestDecodeSearchTerm(t *testing.T) {
// 「新人报道」的 UTF-8 百分号编码前缀,模拟 512 字节截断
news := "%E6%96%B0%E4%BA%BA%E6%8A%A5%E9%81%93"
cases := []struct {
raw string
want string
}{
{"%E4%BD%A0%E5%A5%BD", "你好"}, // 完整编码
{"a+b", "a b"}, // + 按空格解码
{"plain", "plain"}, // 无编码
{"2222%E5%A6%82%E6%9E%9C", "2222如果"}, // 完整转义结尾
{news + "%E6%96", "新人报道"}, // 截断后结尾是完整转义但只是半截字符 → 去非法 UTF-8 尾巴
{news + "%E6%96%9", "新人报道"}, // 一位十六进制的残缺转义 → 剥尾重试 + 去半截字符
{news + "%", "新人报道"}, // 截断在孤立的 %
{news + "%E", "新人报道"}, // 截断在 1 位十六进制
}
for _, c := range cases {
if got := decodeSearchTerm(c.raw); got != c.want {
t.Errorf("decodeSearchTerm(%q) = %q, want %q", c.raw, got, c.want)
}
}
}