将全局启用、用户组基线、API Key 专属或排除规则及 scope 按固定顺序求值,避免 Key 越过所属用户组权限,并让运行时候选与模型列表共用同一权限链。 新增 Key 级可分配模型与失效规则诊断接口、OpenAI 兼容 /v1/models 及 rich 列表迁移路径;前端权限弹窗改为按当前 Key 实时加载并支持清理失效规则。 验证:Go 全量测试与 go vet 通过;Web 22 个测试文件共 142 项通过;pnpm lint、pnpm openapi、pnpm build、Compose 配置、gofmt、ShellCheck 和 git diff --check 通过;独立 PostgreSQL 真实配置验收通过。
81 lines
1.8 KiB
Go
81 lines
1.8 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
gatewaydocs "github.com/easyai/easyai-ai-gateway/apps/api/docs"
|
|
)
|
|
|
|
func TestOpenAPIPublicRoutesUseCanonicalV1Prefix(t *testing.T) {
|
|
var document struct {
|
|
Paths map[string]any `json:"paths"`
|
|
}
|
|
if err := json.Unmarshal(gatewaydocs.SwaggerJSON, &document); err != nil {
|
|
t.Fatalf("decode embedded OpenAPI document: %v", err)
|
|
}
|
|
|
|
legacyPrefixes := []string{
|
|
"/v1/",
|
|
"/v1beta/",
|
|
"/kling/",
|
|
"/upload/",
|
|
"/api/v3/",
|
|
"/chat/",
|
|
"/images/",
|
|
"/song/",
|
|
"/music/",
|
|
"/speech/",
|
|
"/voice_clone",
|
|
"/tasks",
|
|
}
|
|
legacyExact := map[string]bool{
|
|
"/healthz": true,
|
|
"/readyz": true,
|
|
"/api-docs-json": true,
|
|
"/api-docs-yaml": true,
|
|
"/responses": true,
|
|
"/embeddings": true,
|
|
"/reranks": true,
|
|
}
|
|
for route := range document.Paths {
|
|
if legacyExact[route] {
|
|
t.Errorf("legacy public route must not be advertised in OpenAPI: %s", route)
|
|
}
|
|
for _, prefix := range legacyPrefixes {
|
|
if route == "/v1/models" {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(route, prefix) {
|
|
t.Errorf("legacy public route must not be advertised in OpenAPI: %s", route)
|
|
}
|
|
}
|
|
}
|
|
|
|
required := []string{
|
|
"/api/v1/healthz",
|
|
"/api/v1/readyz",
|
|
"/api/v1/openapi.json",
|
|
"/api/v1/chat/completions",
|
|
"/api/v1/responses",
|
|
"/api/v1/images/generations",
|
|
"/api/v1/images/vectorize",
|
|
"/api/v1/videos/generations",
|
|
"/api/v1/videos/upscales",
|
|
"/api/v1/pricing/estimate",
|
|
"/v1/models",
|
|
"/api/workspace/token-usage/daily",
|
|
"/api/v1/models/{model}:generateContent",
|
|
"/api/v1/videos/omni-video",
|
|
"/api/v1/kling/v1/videos/omni-video",
|
|
"/api/v1/kling/v2/omni-video/{model}",
|
|
"/api/v1/contents/generations/tasks",
|
|
}
|
|
for _, route := range required {
|
|
if _, ok := document.Paths[route]; !ok {
|
|
t.Errorf("canonical public route missing from OpenAPI: %s", route)
|
|
}
|
|
}
|
|
}
|