feat: 统一管理台主题与 Key 展示

This commit is contained in:
chuan
2026-08-15 18:35:33 +08:00
parent ddebe3c4a2
commit d2a0bebe16
9 changed files with 113 additions and 62 deletions
+21
View File
@@ -186,6 +186,27 @@ func TestCreateKeyCopiesDefaultRuleSnapshot(t *testing.T) {
}
}
func TestCreateKeyRejectsShortSecret(t *testing.T) {
app := NewApp()
if _, err := app.HandleMethod(MethodPluginRegister, lifecycleRequest(t, SchemaVersion, testConfig(t, "enabled: true\n"))); err != nil {
t.Fatal(err)
}
response := managementCallBody(t, app, http.MethodPost, managementBase+routeKeys, []byte(`{"name":"short","secret":"12345"}`))
if response.StatusCode != http.StatusBadRequest || !strings.Contains(string(response.Body), "6-256") {
t.Fatalf("short Key response=%d body=%s", response.StatusCode, response.Body)
}
}
func TestManagedSecretValidation(t *testing.T) {
for secret, want := range map[string]bool{
"12345": false, "000000": true, "alice-000000": true, "with space": false, strings.Repeat("x", 257): false,
} {
if got := validManagedSecret(secret); got != want {
t.Fatalf("validManagedSecret(%q)=%v, want %v", secret, got, want)
}
}
}
func TestBillingAdmissionConcurrencyAndUsageSettlement(t *testing.T) {
app := NewApp()
if _, err := app.HandleMethod(MethodPluginRegister, lifecycleRequest(t, SchemaVersion, testConfig(t, "enabled: true\ncodex_only: false\n"))); err != nil {
+6
View File
@@ -65,6 +65,12 @@ func TestLifecycleConfigYAMLUsesBase64WireEncoding(t *testing.T) {
}
}
func TestConfigRejectsShortBootstrapKey(t *testing.T) {
if _, err := decodeConfig([]byte("bootstrap_key: 12345\n")); err == nil {
t.Fatal("short bootstrap_key unexpectedly accepted")
}
}
func TestUsageRecordWireFieldsMatchTargetContract(t *testing.T) {
raw, err := json.Marshal(UsageRecord{})
if err != nil {
+5 -2
View File
@@ -33,8 +33,11 @@ func decodeConfig(raw []byte) (Config, error) {
if strings.TrimSpace(cfg.DatabasePath) == "" {
return Config{}, fmt.Errorf("database_path 不能为空")
}
if strings.TrimSpace(cfg.BootstrapName) == "" || strings.TrimSpace(cfg.BootstrapKey) == "" {
return Config{}, fmt.Errorf("bootstrap_name 和 bootstrap_key 不能为空")
if strings.TrimSpace(cfg.BootstrapName) == "" {
return Config{}, fmt.Errorf("bootstrap_name 不能为空")
}
if !validManagedSecret(cfg.BootstrapKey) {
return Config{}, fmt.Errorf("bootstrap_key 必须为 6-256 个不含空白或控制字符的字符")
}
return cfg, nil
}
+2 -2
View File
@@ -124,7 +124,7 @@ func (a *App) createManagedKey(body []byte) ManagementResponse {
}
}
if !validManagedSecret(secret) {
return managementError(http.StatusBadRequest, "invalid_key", "Key 必须为 1-256 个不含空白或控制字符的字符")
return managementError(http.StatusBadRequest, "invalid_key", "Key 必须为 6-256 个不含空白或控制字符的字符")
}
keyID, err := generatedToken("key_")
if err != nil {
@@ -465,7 +465,7 @@ func generatedToken(prefix string) (string, error) {
}
func validManagedSecret(secret string) bool {
if secret == "" || len(secret) > 256 {
if len(secret) < 6 || len(secret) > 256 {
return false
}
for _, value := range secret {
+6 -1
View File
@@ -454,11 +454,16 @@ func TestUsageResourceServesTablePage(t *testing.T) {
if strings.Contains(page, "<script src=") || strings.Contains(page, "<link rel=\"stylesheet\" href=") {
t.Fatal("UI unexpectedly depends on external assets")
}
for _, removed := range []string{"下游 Keys", "最近用量记录", "显示 102 条", "最多展示", "1100 /", "一个 Key 对应一个用户", "查看请求结果、实际上游", "维护模型基础价格"} {
for _, removed := range []string{"下游 Keys", "最近用量记录", "显示 102 条", "最多展示", "1100 /", "一个 Key 对应一个用户", "查看请求结果、实际上游", "维护模型基础价格", "点击“管理”修改访问、路由与额度", "由 CPA 调度", `copy.textContent = "复制"`} {
if strings.Contains(page, removed) {
t.Fatalf("UI still contains removed description %q", removed)
}
}
for _, feature := range []string{`minlength="6"`, `maskedSecret(key.secret)`, `className = "credential-copy"`, `return "自由选择"`} {
if !strings.Contains(page, feature) {
t.Fatalf("UI does not contain compact Key display feature %q", feature)
}
}
if strings.Index(page, `id="page-buttons"`) > strings.Index(page, `id="headers"`) {
t.Fatal("UI pagination controls are not above the usage table")
}