Files
sub2api/backend/internal/service/gemini_upstream_url.go
T
李建琦 6d655c9903
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
2026-08-21 18:30:13 +08:00

52 lines
1.9 KiB
Go

package service
import (
"errors"
"fmt"
"strings"
)
// geminiAIStudioActions 是允许出现在上游 URL 里的 action 集合,与 ForwardNative
// 的入站校验保持一致,同时避免 compat 侧把可变字符串直接拼进 path。
var geminiAIStudioActions = map[string]struct{}{
"generateContent": {},
"streamGenerateContent": {},
"countTokens": {},
}
// buildGeminiAIStudioModelActionURL 组装 AI Studio 的
// /v1beta/models/{model}:{action} 上游 URL。
//
// model 是客户端可控的(native 路由取自 URL 片段,compat 路由取自请求体的 model
// 字段,之后可能再经渠道映射),因此必须先过路径片段护栏才能拼进 path,
// 见 upstream_path_guard.go。新增 AI Studio 端点请一律走本函数。
func buildGeminiAIStudioModelActionURL(baseURL, model, action string, stream bool) (string, error) {
trimmedBase := strings.TrimRight(strings.TrimSpace(baseURL), "/")
if trimmedBase == "" {
return "", errors.New("gemini base url is required")
}
trimmedModel := strings.TrimSpace(model)
if trimmedModel == "" {
return "", errors.New("gemini model is required")
}
if err := validateUpstreamPathSegment("gemini model", trimmedModel); err != nil {
return "", err
}
trimmedAction := strings.TrimSpace(action)
if _, ok := geminiAIStudioActions[trimmedAction]; !ok {
return "", fmt.Errorf("unsupported gemini action: %s", trimmedAction)
}
fullURL := fmt.Sprintf("%s/v1beta/models/%s:%s", trimmedBase, trimmedModel, trimmedAction)
if stream {
fullURL += "?alt=sse"
}
return fullURL, nil
}
// IsSafeGeminiModelPathSegment 供 handler 层在解析出 URL 里的模型名后立刻校验,
// 让客户端拿到明确的 400,而不是等到构造上游请求时才报错。
func IsSafeGeminiModelPathSegment(model string) bool {
return isSafeUpstreamPathSegment(strings.TrimSpace(model))
}