Files
ttstd-infinite-canvas/backend/internal/platform/runtime_policy.go
ddcat666 4272b0e499 fix(media): 资源访问 - 签名地址有效期跟随后台策略并放开服务器访问地址限制
平台展示与厂商读取的签名地址有效期改为读取「资源与请求策略」,默认均为 4 小时,不再硬编码 5 分钟。服务器访问地址只校验 http/https 格式,允许本机、局域网或 HTTP 地址,模型读取平台资源不再强制 HTTPS 公网地址。
2026-09-30 03:38:31 +08:00

501 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package platform
import (
"context"
"encoding/json"
"errors"
"fmt"
"infinite-canvas/backend/internal/kernel"
"strings"
"time"
"infinite-canvas/backend/internal/model"
)
const runtimePolicySettingKey = "runtime_policy"
const (
maxRuntimeUploadMB int64 = 999
maxRuntimeStorageGB int64 = 999
maxRuntimeDataMB int64 = 999_999
maxRuntimeCount int64 = 999_999_999
maxRuntimeRate = 999_999
maxRuntimeConcurrency = 999
maxRuntimeTimeoutMinutes = 9_999
defaultWorkerConcurrency = 8
defaultChannelConcurrency = 8
)
// 画布 Agent 单步的输出上限与执行时限。上限是"每次模型调用"的边界(思考 + 正文 + 工具参数),
// 时限是"这一次调用最多等多久"的墙钟;两者一起决定单步最坏耗时。0 在两个口径下都是"该边界不生效":
// 上限为 0 表示不限制输出(只由执行时限兜底),时限为 0 表示沿用文本任务超时。
const (
MinRuntimeAgentStepOutputTokens = 256
MaxRuntimeAgentStepOutputTokens = 131_072
DefaultRuntimeAgentStepOutputTokens = 16_384
MinRuntimeAgentStepTimeoutSeconds = 30
MaxRuntimeAgentStepTimeoutSeconds = 3_600
DefaultRuntimeAgentStepTimeout = 0
MinRuntimeAgentSessions = 1
MaxRuntimeAgentSessions = 64
DefaultRuntimeAgentSessions = 30
)
// agentStepMaxOutputTokensDefault 读取部署级覆盖并按同一套范围夹取;环境变量无法表达"不限制"(0),
// 那个档位只在管理端配置里设置。
func agentStepMaxOutputTokensDefault() int {
value := envInt("CANVAS_AGENT_STEP_MAX_OUTPUT_TOKENS", DefaultRuntimeAgentStepOutputTokens)
return min(max(value, MinRuntimeAgentStepOutputTokens), MaxRuntimeAgentStepOutputTokens)
}
func agentStepTimeoutSecondsDefault() int {
value := envInt("CANVAS_AGENT_STEP_TIMEOUT_SECONDS", DefaultRuntimeAgentStepTimeout)
if value <= 0 {
return DefaultRuntimeAgentStepTimeout
}
return min(max(value, MinRuntimeAgentStepTimeoutSeconds), MaxRuntimeAgentStepTimeoutSeconds)
}
type RuntimeResourcePolicy struct {
ResourceUploadMB int64 `json:"resourceUploadMB"`
GeneratedFileMB int64 `json:"generatedFileMB"`
DailyUploadMB int64 `json:"dailyUploadMB"`
StoredFileGB int64 `json:"storedFileGB"`
StructuredDataMB int64 `json:"structuredDataMB"`
TaskDataGB int64 `json:"taskDataGB"`
AssetCount int64 `json:"assetCount"`
CanvasCount int64 `json:"canvasCount"`
TaskCount int64 `json:"taskCount"`
APICallLogCount int64 `json:"apiCallLogCount"`
RecycleBinRetentionDays int `json:"recycleBinRetentionDays"`
}
type RuntimeStoragePolicy struct {
TransferTimeoutSeconds int `json:"transferTimeoutSeconds"`
AccessURLTTLSeconds int `json:"accessURLTTLSeconds"`
ProviderAccessURLTTLSeconds int `json:"providerAccessURLTTLSeconds"`
NonSeekableBufferMB int64 `json:"nonSeekableBufferMB"`
ErrorBodyKB int64 `json:"errorBodyKB"`
}
type RuntimeTaskPolicy struct {
WorkerConcurrency int `json:"workerConcurrency"`
ChannelConcurrency int `json:"channelConcurrency"`
ActiveTaskLimit int `json:"activeTaskLimit"`
ImageTimeoutMinutes int `json:"imageTimeoutMinutes"`
TextTimeoutMinutes int `json:"textTimeoutMinutes"`
AudioTimeoutMinutes int `json:"audioTimeoutMinutes"`
VideoTimeoutMinutes int `json:"videoTimeoutMinutes"`
StoryboardTimeoutMinutes int `json:"storyboardTimeoutMinutes"`
DefaultTimeoutMinutes int `json:"defaultTimeoutMinutes"`
// AgentStepMaxOutputTokens 是画布 Agent 单步模型调用的输出上限(思考 + 正文 + 工具调用参数)。
// 0 表示不限制输出,此时单步最坏耗时只由 AgentStepTimeoutSeconds 兜底。
AgentStepMaxOutputTokens int `json:"agentStepMaxOutputTokens"`
// AgentStepTimeoutSeconds 是画布 Agent 单步模型调用的秒级墙钟;0 表示沿用文本任务超时。
AgentStepTimeoutSeconds int `json:"agentStepTimeoutSeconds"`
// AgentMaxSessions 是同时进行中的画布 Agent 轮次上限。审批等待不占用名额。
AgentMaxSessions int `json:"agentMaxSessions"`
}
type RuntimeRequestPolicy struct {
TaskCreatePerMinute int `json:"taskCreatePerMinute"`
ResourceUploadPerMinute int `json:"resourceUploadPerMinute"`
ResourceImportPerMinute int `json:"resourceImportPerMinute"`
AssetWritePerMinute int `json:"assetWritePerMinute"`
CanvasWritePerMinute int `json:"canvasWritePerMinute"`
RegisterPerHour int `json:"registerPerHour"`
EmailCodePerHour int `json:"emailCodePerHour"`
LoginIPPerTenMinutes int `json:"loginIPPerTenMinutes"`
LoginAccountPerTenMinutes int `json:"loginAccountPerTenMinutes"`
SystemRelayPerMinute int `json:"systemRelayPerMinute"`
CustomRelayPerMinute int `json:"customRelayPerMinute"`
CustomRelayConcurrency int `json:"customRelayConcurrency"`
CustomRelayRequestMB int64 `json:"customRelayRequestMB"`
CustomRelayResponseMB int64 `json:"customRelayResponseMB"`
CustomRelayTimeoutMinutes int `json:"customRelayTimeoutMinutes"`
SystemRelayRequestMB int64 `json:"systemRelayRequestMB"`
SystemRelayResponseMB int64 `json:"systemRelayResponseMB"`
ChannelCircuitFailureCount int `json:"channelCircuitFailureCount"`
ChannelCircuitOpenSeconds int `json:"channelCircuitOpenSeconds"`
}
type RuntimePolicySetting struct {
Resource RuntimeResourcePolicy `json:"resource"`
Storage RuntimeStoragePolicy `json:"storage"`
Task RuntimeTaskPolicy `json:"task"`
Request RuntimeRequestPolicy `json:"request"`
}
type PublicRuntimePolicySetting struct {
RuntimePolicySetting
Configured bool `json:"configured"`
UpdatedBy string `json:"updatedBy"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type PublicRuntimeLimits struct {
ActiveTaskLimit int `json:"activeTaskLimit"`
ResourceUploadMB int64 `json:"resourceUploadMB"`
RecycleBinRetentionDays int `json:"recycleBinRetentionDays"`
}
// DefaultRuntimePolicy returns the effective baseline policy, including
// deployment-level environment overrides. It is exported so composition-root
// compatibility code and domain tests do not need to reach into platform's
// private implementation.
func DefaultRuntimePolicy() RuntimePolicySetting {
return RuntimePolicySetting{
Resource: RuntimeResourcePolicy{
ResourceUploadMB: 50,
GeneratedFileMB: 64,
DailyUploadMB: 2048,
StoredFileGB: 20,
StructuredDataMB: 256,
TaskDataGB: 1,
AssetCount: 2_000,
CanvasCount: 1_000,
TaskCount: 20_000,
APICallLogCount: 100_000,
RecycleBinRetentionDays: 30,
},
Storage: RuntimeStoragePolicy{
TransferTimeoutSeconds: 120,
AccessURLTTLSeconds: 14_400,
ProviderAccessURLTTLSeconds: 14_400,
NonSeekableBufferMB: 64,
ErrorBodyKB: 1,
},
Task: RuntimeTaskPolicy{
WorkerConcurrency: defaultWorkerConcurrencyLimit(),
ChannelConcurrency: defaultChannelConcurrencyLimit(),
ActiveTaskLimit: 5,
ImageTimeoutMinutes: 8,
TextTimeoutMinutes: 8,
AudioTimeoutMinutes: 8,
VideoTimeoutMinutes: 60,
StoryboardTimeoutMinutes: 20,
DefaultTimeoutMinutes: 10,
AgentStepMaxOutputTokens: agentStepMaxOutputTokensDefault(),
AgentStepTimeoutSeconds: agentStepTimeoutSecondsDefault(),
AgentMaxSessions: DefaultRuntimeAgentSessions,
},
Request: RuntimeRequestPolicy{
TaskCreatePerMinute: 30,
ResourceUploadPerMinute: 30,
ResourceImportPerMinute: 30,
AssetWritePerMinute: 120,
CanvasWritePerMinute: 120,
RegisterPerHour: 30,
EmailCodePerHour: 60,
LoginIPPerTenMinutes: 50,
LoginAccountPerTenMinutes: 10,
SystemRelayPerMinute: 120,
CustomRelayPerMinute: 120,
CustomRelayConcurrency: 4,
CustomRelayRequestMB: 32,
CustomRelayResponseMB: 32,
CustomRelayTimeoutMinutes: 10,
SystemRelayRequestMB: 64,
SystemRelayResponseMB: 128,
ChannelCircuitFailureCount: min(envInt("CANVAS_CHANNEL_CIRCUIT_FAILURES", 5), maxRuntimeConcurrency),
ChannelCircuitOpenSeconds: min(envInt("CANVAS_CHANNEL_CIRCUIT_SECONDS", 60), 86_400),
},
}
}
func selfUseRuntimePolicy() RuntimePolicySetting {
value := DefaultRuntimePolicy()
value.Resource = RuntimeResourcePolicy{
ResourceUploadMB: maxRuntimeUploadMB, GeneratedFileMB: maxRuntimeUploadMB,
DailyUploadMB: maxRuntimeDataMB, StoredFileGB: maxRuntimeStorageGB, StructuredDataMB: maxRuntimeDataMB,
TaskDataGB: maxRuntimeStorageGB, AssetCount: maxRuntimeCount, CanvasCount: maxRuntimeCount,
TaskCount: maxRuntimeCount, APICallLogCount: maxRuntimeCount,
RecycleBinRetentionDays: 0,
}
value.Task = RuntimeTaskPolicy{
WorkerConcurrency: maxRuntimeConcurrency, ChannelConcurrency: maxRuntimeConcurrency, ActiveTaskLimit: maxRuntimeConcurrency,
ImageTimeoutMinutes: maxRuntimeTimeoutMinutes, TextTimeoutMinutes: maxRuntimeTimeoutMinutes,
AudioTimeoutMinutes: maxRuntimeTimeoutMinutes, VideoTimeoutMinutes: maxRuntimeTimeoutMinutes,
StoryboardTimeoutMinutes: maxRuntimeTimeoutMinutes, DefaultTimeoutMinutes: maxRuntimeTimeoutMinutes,
AgentStepMaxOutputTokens: MaxRuntimeAgentStepOutputTokens, AgentStepTimeoutSeconds: MaxRuntimeAgentStepTimeoutSeconds,
AgentMaxSessions: MaxRuntimeAgentSessions,
}
value.Request = RuntimeRequestPolicy{
TaskCreatePerMinute: maxRuntimeRate,
ResourceUploadPerMinute: maxRuntimeRate, ResourceImportPerMinute: maxRuntimeRate,
AssetWritePerMinute: maxRuntimeRate, CanvasWritePerMinute: maxRuntimeRate,
RegisterPerHour: maxRuntimeRate, EmailCodePerHour: maxRuntimeRate,
LoginIPPerTenMinutes: maxRuntimeRate, LoginAccountPerTenMinutes: maxRuntimeRate,
SystemRelayPerMinute: maxRuntimeRate, CustomRelayPerMinute: maxRuntimeRate,
CustomRelayConcurrency: maxRuntimeConcurrency, CustomRelayRequestMB: maxRuntimeUploadMB,
CustomRelayResponseMB: maxRuntimeUploadMB, CustomRelayTimeoutMinutes: maxRuntimeTimeoutMinutes,
SystemRelayRequestMB: maxRuntimeUploadMB, SystemRelayResponseMB: maxRuntimeUploadMB,
ChannelCircuitFailureCount: maxRuntimeConcurrency, ChannelCircuitOpenSeconds: 1,
}
return value
}
func SelfUseRuntimePolicy() RuntimePolicySetting { return selfUseRuntimePolicy() }
func (s *Service) RuntimePolicy() (RuntimePolicySetting, error) {
if s.runtimePolicyCache == nil {
_, value, err := s.readRuntimePolicy()
return value, err
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
return s.runtimePolicyCache.Get(ctx, runtimePolicySettingKey, func(ctx context.Context) (RuntimePolicySetting, int, error) {
reader := &Service{
repo: s.repo.WithContext(ctx),
host: s.host,
Coordinator: s.Coordinator,
}
_, value, err := reader.readRuntimePolicy()
return value, 2048, err
})
}
func (s *Service) RuntimeConcurrencySetting() (RuntimeTaskPolicy, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
return s.concurrencyCache.Get(ctx, runtimePolicySettingKey, func(ctx context.Context) (RuntimeTaskPolicy, int, error) {
reader := &Service{repo: s.repo.WithContext(ctx), host: s.host, Coordinator: s.Coordinator, concurrencyCache: s.concurrencyCache, runtimePolicyCache: s.runtimePolicyCache}
policy, err := reader.RuntimePolicy()
return policy.Task, 256, err
})
}
func (s *Service) PublicRuntimeLimits() (*PublicRuntimeLimits, error) {
policy, err := s.RuntimePolicy()
if err != nil {
return nil, err
}
return &PublicRuntimeLimits{
ActiveTaskLimit: policy.Task.ActiveTaskLimit, ResourceUploadMB: policy.Resource.ResourceUploadMB,
RecycleBinRetentionDays: policy.Resource.RecycleBinRetentionDays,
}, nil
}
func (s *Service) AdminRuntimePolicySetting(actor *model.User) (*PublicRuntimePolicySetting, error) {
if err := s.requireAdmin(actor); err != nil {
return nil, err
}
setting, value, err := s.readRuntimePolicy()
if err != nil {
return nil, err
}
return publicRuntimePolicy(setting, value), nil
}
func (s *Service) AdminSelfUseRuntimePolicy(actor *model.User) (*PublicRuntimePolicySetting, error) {
if err := s.requireAdmin(actor); err != nil {
return nil, err
}
return publicRuntimePolicy(nil, selfUseRuntimePolicy()), nil
}
func (s *Service) UpdateRuntimePolicySetting(actor *model.User, value RuntimePolicySetting) (*PublicRuntimePolicySetting, error) {
if err := s.requireAdmin(actor); err != nil {
return nil, err
}
if err := validateRuntimePolicy(value); err != nil {
return nil, err
}
current, before, err := s.readRuntimePolicy()
if err != nil {
return nil, err
}
encoded, err := json.Marshal(value)
if err != nil {
return nil, err
}
setting := model.SystemSetting{Key: runtimePolicySettingKey, ValueJSON: string(encoded), UpdatedBy: actor.ID}
if current != nil {
setting.CreatedAt = current.CreatedAt
}
if err := s.repo.SaveSystemSetting(&setting); err != nil {
return nil, err
}
s.concurrencyCache.Clear()
if s.runtimePolicyCache != nil {
s.runtimePolicyCache.Clear()
}
if err := s.appendAdminAudit(actor, "runtime_policy.update", "system_setting", runtimePolicySettingKey, "更新资源与请求策略", map[string]any{"before": before, "after": value}); err != nil {
return nil, err
}
return publicRuntimePolicy(&setting, value), nil
}
func (s *Service) ResetRuntimePolicySetting(actor *model.User) (*PublicRuntimePolicySetting, error) {
if err := s.requireAdmin(actor); err != nil {
return nil, err
}
_, before, err := s.readRuntimePolicy()
if err != nil {
return nil, err
}
if err := s.repo.DeleteSystemSetting(runtimePolicySettingKey); err != nil {
return nil, err
}
s.concurrencyCache.Clear()
if s.runtimePolicyCache != nil {
s.runtimePolicyCache.Clear()
}
after := DefaultRuntimePolicy()
if err := s.appendAdminAudit(actor, "runtime_policy.reset", "system_setting", runtimePolicySettingKey, "重置资源与请求策略", map[string]any{"before": before, "after": after}); err != nil {
return nil, err
}
return publicRuntimePolicy(nil, after), nil
}
func (s *Service) readRuntimePolicy() (*model.SystemSetting, RuntimePolicySetting, error) {
setting, err := s.repo.SystemSettingOptional(runtimePolicySettingKey)
if err != nil {
return nil, RuntimePolicySetting{}, err
}
if setting == nil {
value := DefaultRuntimePolicy()
return nil, value, validateRuntimePolicy(value)
}
value := DefaultRuntimePolicy()
if strings.TrimSpace(setting.ValueJSON) == "" || json.Unmarshal([]byte(setting.ValueJSON), &value) != nil {
return nil, RuntimePolicySetting{}, errors.New("资源与请求策略配置格式无效")
}
if err := validateRuntimePolicy(value); err != nil {
return nil, RuntimePolicySetting{}, err
}
return setting, value, nil
}
func validateRuntimePolicy(value RuntimePolicySetting) error {
resource := value.Resource
for label, item := range map[string]int64{
"普通资源单文件": resource.ResourceUploadMB,
"单个生成资源": resource.GeneratedFileMB,
} {
if item < 1 || item > maxRuntimeUploadMB {
return kernel.BadAuthRequest(fmt.Sprintf("%s必须是 1-%d MB 的整数", label, maxRuntimeUploadMB))
}
}
if resource.DailyUploadMB < 1 || resource.DailyUploadMB > maxRuntimeDataMB {
return kernel.BadAuthRequest(fmt.Sprintf("每日上传量必须是 1-%d MB 的整数", maxRuntimeDataMB))
}
if resource.StoredFileGB < 1 || resource.StoredFileGB > maxRuntimeStorageGB || resource.TaskDataGB < 1 || resource.TaskDataGB > maxRuntimeStorageGB {
return kernel.BadAuthRequest(fmt.Sprintf("账号文件与任务数据容量必须是 1-%d GB 的整数", maxRuntimeStorageGB))
}
if resource.StructuredDataMB < 1 || resource.StructuredDataMB > maxRuntimeDataMB {
return kernel.BadAuthRequest(fmt.Sprintf("结构化数据容量必须是 1-%d MB 的整数", maxRuntimeDataMB))
}
storedMB := resource.StoredFileGB * 1024
if resource.ResourceUploadMB > storedMB || resource.GeneratedFileMB > storedMB {
return kernel.BadAuthRequest("单文件上限不能大于账号文件总容量")
}
for label, item := range map[string]int64{
"素材数量": resource.AssetCount, "画布数量": resource.CanvasCount,
"任务历史数量": resource.TaskCount, "请求日志数量": resource.APICallLogCount,
} {
if item < 1 || item > maxRuntimeCount {
return kernel.BadAuthRequest(fmt.Sprintf("%s必须是 1-%d 的整数", label, maxRuntimeCount))
}
}
if resource.RecycleBinRetentionDays < 0 || resource.RecycleBinRetentionDays > 365 {
return kernel.BadAuthRequest("回收站保留天数必须是 0-365 的整数 (0 表示不自动清理)")
}
storage := value.Storage
if storage.TransferTimeoutSeconds < 10 || storage.TransferTimeoutSeconds > 3_600 {
return kernel.BadAuthRequest("对象存储传输超时必须是 10-3600 秒的整数")
}
if storage.AccessURLTTLSeconds < 60 || storage.AccessURLTTLSeconds > 86_400 {
return kernel.BadAuthRequest("对象存储访问地址有效期必须是 60-86400 秒的整数")
}
if storage.ProviderAccessURLTTLSeconds < 300 || storage.ProviderAccessURLTTLSeconds > 86_400 {
return kernel.BadAuthRequest("模型输入资源地址有效期必须是 300-86400 秒的整数")
}
if storage.NonSeekableBufferMB < 1 || storage.NonSeekableBufferMB > maxRuntimeUploadMB {
return kernel.BadAuthRequest(fmt.Sprintf("对象存储非可寻址上传缓冲必须是 1-%d MB 的整数", maxRuntimeUploadMB))
}
if storage.ErrorBodyKB < 1 || storage.ErrorBodyKB > 1024 {
return kernel.BadAuthRequest("对象存储错误响应保留必须是 1-1024 KB 的整数")
}
task := value.Task
for label, item := range map[string]int{
"Worker 并发数": task.WorkerConcurrency, "全局渠道并发数": task.ChannelConcurrency, "活动任务上限": task.ActiveTaskLimit,
} {
if item < 1 || item > maxRuntimeConcurrency {
return kernel.BadAuthRequest(fmt.Sprintf("%s必须是 1-%d 的整数", label, maxRuntimeConcurrency))
}
}
for label, item := range map[string]int{
"图片任务超时": task.ImageTimeoutMinutes, "文本任务超时": task.TextTimeoutMinutes,
"音频任务超时": task.AudioTimeoutMinutes, "视频任务超时": task.VideoTimeoutMinutes,
"分镜任务超时": task.StoryboardTimeoutMinutes, "默认任务超时": task.DefaultTimeoutMinutes,
} {
if item < 1 || item > maxRuntimeTimeoutMinutes {
return kernel.BadAuthRequest(fmt.Sprintf("%s必须是 1-%d 分钟的整数", label, maxRuntimeTimeoutMinutes))
}
}
if task.AgentStepMaxOutputTokens != 0 && (task.AgentStepMaxOutputTokens < MinRuntimeAgentStepOutputTokens || task.AgentStepMaxOutputTokens > MaxRuntimeAgentStepOutputTokens) {
return kernel.BadAuthRequest(fmt.Sprintf("Agent 单步输出上限必须是 0 或 %d-%d 的整数 (0 表示不限制)", MinRuntimeAgentStepOutputTokens, MaxRuntimeAgentStepOutputTokens))
}
if task.AgentStepTimeoutSeconds != 0 && (task.AgentStepTimeoutSeconds < MinRuntimeAgentStepTimeoutSeconds || task.AgentStepTimeoutSeconds > MaxRuntimeAgentStepTimeoutSeconds) {
return kernel.BadAuthRequest(fmt.Sprintf("Agent 单步超时必须是 0 或 %d-%d 秒的整数 (0 表示沿用文本任务超时)", MinRuntimeAgentStepTimeoutSeconds, MaxRuntimeAgentStepTimeoutSeconds))
}
if task.AgentMaxSessions < MinRuntimeAgentSessions || task.AgentMaxSessions > MaxRuntimeAgentSessions {
return kernel.BadAuthRequest(fmt.Sprintf("Agent 同时对话上限必须是 %d-%d 的整数", MinRuntimeAgentSessions, MaxRuntimeAgentSessions))
}
request := value.Request
for label, item := range map[string]int{
"任务创建频控": request.TaskCreatePerMinute,
"资源上传频控": request.ResourceUploadPerMinute, "资源导入频控": request.ResourceImportPerMinute,
"素材写入频控": request.AssetWritePerMinute,
"画布写入频控": request.CanvasWritePerMinute, "注册频控": request.RegisterPerHour,
"验证码频控": request.EmailCodePerHour, "登录 IP 频控": request.LoginIPPerTenMinutes,
"登录账号频控": request.LoginAccountPerTenMinutes, "系统渠道频控": request.SystemRelayPerMinute,
"自定义渠道频控": request.CustomRelayPerMinute,
} {
if item < 1 || item > maxRuntimeRate {
return kernel.BadAuthRequest(fmt.Sprintf("%s必须是 1-%d 的整数", label, maxRuntimeRate))
}
}
if request.CustomRelayConcurrency < 1 || request.CustomRelayConcurrency > maxRuntimeConcurrency {
return kernel.BadAuthRequest(fmt.Sprintf("自定义渠道并发必须是 1-%d 的整数", maxRuntimeConcurrency))
}
for label, item := range map[string]int64{
"自定义渠道请求体": request.CustomRelayRequestMB, "自定义渠道响应体": request.CustomRelayResponseMB,
"系统渠道请求体": request.SystemRelayRequestMB, "系统渠道响应体": request.SystemRelayResponseMB,
} {
if item < 1 || item > maxRuntimeUploadMB {
return kernel.BadAuthRequest(fmt.Sprintf("%s必须是 1-%d MB 的整数", label, maxRuntimeUploadMB))
}
}
if request.CustomRelayTimeoutMinutes < 1 || request.CustomRelayTimeoutMinutes > maxRuntimeTimeoutMinutes {
return kernel.BadAuthRequest(fmt.Sprintf("自定义渠道超时必须是 1-%d 分钟的整数", maxRuntimeTimeoutMinutes))
}
if request.ChannelCircuitFailureCount < 1 || request.ChannelCircuitFailureCount > maxRuntimeConcurrency {
return kernel.BadAuthRequest(fmt.Sprintf("渠道熔断失败次数必须是 1-%d 的整数", maxRuntimeConcurrency))
}
if request.ChannelCircuitOpenSeconds < 1 || request.ChannelCircuitOpenSeconds > 86_400 {
return kernel.BadAuthRequest("渠道熔断时长必须是 1-86400 秒的整数")
}
return nil
}
func ValidateRuntimePolicy(value RuntimePolicySetting) error { return validateRuntimePolicy(value) }
func publicRuntimePolicy(setting *model.SystemSetting, value RuntimePolicySetting) *PublicRuntimePolicySetting {
result := &PublicRuntimePolicySetting{RuntimePolicySetting: value, Configured: setting != nil}
if setting != nil {
result.UpdatedBy = setting.UpdatedBy
result.CreatedAt = setting.CreatedAt
result.UpdatedAt = setting.UpdatedAt
}
return result
}
func megabytes(value int64) int64 { return kernel.Megabytes(value) }
func gigabytes(value int64) int64 { return kernel.Gigabytes(value) }