mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-16 07:33:06 +08:00
- System One 校验拒绝重复键与大小写/Unicode 折叠变体键(请求顶层、
questions、问题对象):encoding/json 大小写不敏感且取最后一个重复键,
而原始 body 原样转发,此前 {"model":"x","MODEL":"jev-latest"} 可绕过
jev-latest 限制、模型白名单与流式限制。
- Prompt 审计与旧审核同时收集对象键名、问题 ID 与未知扩展字段(不含规范
字段名与已校验的 type),此前把内容放进键名即可完全绕过审计。
- 上游成功响应的 usage/model 宽松解码(浮点、数字字符串),避免上游已
计费而网关整体 502 不记账;解码失败记录 response_error ops 事件。
- 上游 413 与 400/422 同视为请求错误:透传状态码、不切换、不改账号状态。
- TypeSafe base_url 末尾的 /v1 自动剥离,避免拼出 /v1/v1/systemone;
/v1/systemone 改挂文本请求体上限。
- Composite 静态兜底模型列表、管理端候选列表恢复为不含 jev-latest;
Codex 清单永不列出 TypeSafe 模型;TypeSafe 分组候选默认 jev-latest。
- 创建账号从 Grok 切到 TypeSafe 时重置为白名单模式;错误透传平台列表补 typesafe。
99 lines
3.7 KiB
Go
99 lines
3.7 KiB
Go
// Package model 定义服务层使用的数据模型。
|
|
package model
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/domain"
|
|
)
|
|
|
|
// ErrorPassthroughRule 全局错误透传规则
|
|
// 用于控制上游错误如何返回给客户端
|
|
type ErrorPassthroughRule struct {
|
|
ID int64 `json:"id"`
|
|
Name string `json:"name"` // 规则名称
|
|
Enabled bool `json:"enabled"` // 是否启用
|
|
Priority int `json:"priority"` // 优先级(数字越小优先级越高)
|
|
ErrorCodes []int `json:"error_codes"` // 匹配的错误码列表(OR关系)
|
|
Keywords []string `json:"keywords"` // 匹配的关键词列表(OR关系)
|
|
MatchMode string `json:"match_mode"` // "any"(任一条件) 或 "all"(所有条件)
|
|
Platforms []string `json:"platforms"` // 适用平台列表
|
|
PassthroughCode bool `json:"passthrough_code"` // 是否透传原始状态码
|
|
ResponseCode *int `json:"response_code"` // 自定义状态码(passthrough_code=false 时使用)
|
|
PassthroughBody bool `json:"passthrough_body"` // 是否透传原始错误信息
|
|
CustomMessage *string `json:"custom_message"` // 自定义错误信息(passthrough_body=false 时使用)
|
|
SkipMonitoring bool `json:"skip_monitoring"` // 是否跳过运维监控记录
|
|
Description *string `json:"description"` // 规则描述
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// MatchModeAny 表示任一条件匹配即可
|
|
const MatchModeAny = "any"
|
|
|
|
// MatchModeAll 表示所有条件都必须匹配
|
|
const MatchModeAll = "all"
|
|
|
|
// 支持的平台常量
|
|
const (
|
|
PlatformAnthropic = domain.PlatformAnthropic
|
|
PlatformOpenAI = domain.PlatformOpenAI
|
|
PlatformGemini = domain.PlatformGemini
|
|
PlatformAntigravity = domain.PlatformAntigravity
|
|
PlatformGrok = domain.PlatformGrok
|
|
PlatformKimi = domain.PlatformKimi
|
|
PlatformZhipu = domain.PlatformZhipu
|
|
PlatformDeepseek = domain.PlatformDeepseek
|
|
PlatformMiniMax = domain.PlatformMiniMax
|
|
PlatformOpenCodeGo = domain.PlatformOpenCodeGo
|
|
PlatformTypeSafe = domain.PlatformTypeSafe
|
|
)
|
|
|
|
// AllPlatforms 返回所有支持的平台列表
|
|
func AllPlatforms() []string {
|
|
return []string{
|
|
PlatformAnthropic,
|
|
PlatformOpenAI,
|
|
PlatformGemini,
|
|
PlatformAntigravity,
|
|
PlatformGrok,
|
|
PlatformKimi,
|
|
PlatformZhipu,
|
|
PlatformDeepseek,
|
|
PlatformMiniMax,
|
|
PlatformOpenCodeGo,
|
|
PlatformTypeSafe,
|
|
}
|
|
}
|
|
|
|
// Validate 验证规则配置的有效性
|
|
func (r *ErrorPassthroughRule) Validate() error {
|
|
if r.Name == "" {
|
|
return &ValidationError{Field: "name", Message: "name is required"}
|
|
}
|
|
if r.MatchMode != MatchModeAny && r.MatchMode != MatchModeAll {
|
|
return &ValidationError{Field: "match_mode", Message: "match_mode must be 'any' or 'all'"}
|
|
}
|
|
// 至少需要配置一个匹配条件(错误码或关键词)
|
|
if len(r.ErrorCodes) == 0 && len(r.Keywords) == 0 {
|
|
return &ValidationError{Field: "conditions", Message: "at least one error_code or keyword is required"}
|
|
}
|
|
if !r.PassthroughCode && (r.ResponseCode == nil || *r.ResponseCode <= 0) {
|
|
return &ValidationError{Field: "response_code", Message: "response_code is required when passthrough_code is false"}
|
|
}
|
|
if !r.PassthroughBody && (r.CustomMessage == nil || *r.CustomMessage == "") {
|
|
return &ValidationError{Field: "custom_message", Message: "custom_message is required when passthrough_body is false"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidationError 表示验证错误
|
|
type ValidationError struct {
|
|
Field string
|
|
Message string
|
|
}
|
|
|
|
func (e *ValidationError) Error() string {
|
|
return e.Field + ": " + e.Message
|
|
}
|