新增 admin「渠道监控」模块(参考 BingZi-233/check-cx),独立于现有 Channel 体系。
admin 配置 + 后台定时调用上游 LLM chat completions 健康检查 + 所有登录用户只读可见。
后端:
- ent: channel_monitor + channel_monitor_history(AES-256-GCM 加密 api_key)
- service 按职责拆分:service/aggregator/validate/checker/runner/ssrf
- provider strategy map 替代 switch(openai/anthropic/gemini)
- repository batch 聚合(ListLatestForMonitorIDs + ComputeAvailabilityForMonitors)消除 N+1
- runner: ticker(5s) + pond worker pool(5) + inFlight 防并发 + TrySubmit 防雪崩
+ 凌晨 3 点 cron 清理 30 天历史
- SSRF 防护:强制 https + 私网/loopback/云元数据 IP 拒绝(127/8、10/8、172.16/12、
192.168/16、169.254/16、100.64/10、::1、fc00::/7、fe80::/10)+ DialContext
在 socket 层防 DNS rebinding
- API key sanitize:擦除 url.Error 与上游响应 body 中的 sk-/sk-ant-/AIza/JWT 模式
- APIKeyDecryptFailed 标志位 + 单 monitor 路径检测,避免空 key 调用上游
handler:
- admin: CRUD + 手动触发 + 历史接口(api_key 脱敏)
- user: 只读列表 + 状态详情(去除 api_key/endpoint)
- ParseChannelMonitorID 共用 + dto.ChannelMonitorExtraModelStatus 共用
前端:
- 路由 /admin/channels/{pricing,monitor} + /monitor(用户只读)
- AppSidebar 父项 expandOnly 支持
- ChannelMonitorView 拆为 8 个子组件 + ChannelStatusView 拆出 detail dialog
- composables/useChannelMonitorFormat + constants/channelMonitor 共享
- i18n monitorCommon namespace 消除 admin/user 两 view 重复
合规:所有文件符合 CLAUDE.md(Go ≤ 500 行 / Vue ≤ 300 行 / 函数 ≤ 30 行)
CI: go build / gofmt / golangci-lint(0 issues) / make test-unit / pnpm build 全绿
224 lines
8.3 KiB
Go
224 lines
8.3 KiB
Go
// Code generated by ent, DO NOT EDIT.
|
|
|
|
package channelmonitor
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"entgo.io/ent/dialect/sql"
|
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
|
)
|
|
|
|
const (
|
|
// Label holds the string label denoting the channelmonitor type in the database.
|
|
Label = "channel_monitor"
|
|
// FieldID holds the string denoting the id field in the database.
|
|
FieldID = "id"
|
|
// FieldCreatedAt holds the string denoting the created_at field in the database.
|
|
FieldCreatedAt = "created_at"
|
|
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
|
|
FieldUpdatedAt = "updated_at"
|
|
// FieldName holds the string denoting the name field in the database.
|
|
FieldName = "name"
|
|
// FieldProvider holds the string denoting the provider field in the database.
|
|
FieldProvider = "provider"
|
|
// FieldEndpoint holds the string denoting the endpoint field in the database.
|
|
FieldEndpoint = "endpoint"
|
|
// FieldAPIKeyEncrypted holds the string denoting the api_key_encrypted field in the database.
|
|
FieldAPIKeyEncrypted = "api_key_encrypted"
|
|
// FieldPrimaryModel holds the string denoting the primary_model field in the database.
|
|
FieldPrimaryModel = "primary_model"
|
|
// FieldExtraModels holds the string denoting the extra_models field in the database.
|
|
FieldExtraModels = "extra_models"
|
|
// FieldGroupName holds the string denoting the group_name field in the database.
|
|
FieldGroupName = "group_name"
|
|
// FieldEnabled holds the string denoting the enabled field in the database.
|
|
FieldEnabled = "enabled"
|
|
// FieldIntervalSeconds holds the string denoting the interval_seconds field in the database.
|
|
FieldIntervalSeconds = "interval_seconds"
|
|
// FieldLastCheckedAt holds the string denoting the last_checked_at field in the database.
|
|
FieldLastCheckedAt = "last_checked_at"
|
|
// FieldCreatedBy holds the string denoting the created_by field in the database.
|
|
FieldCreatedBy = "created_by"
|
|
// EdgeHistory holds the string denoting the history edge name in mutations.
|
|
EdgeHistory = "history"
|
|
// Table holds the table name of the channelmonitor in the database.
|
|
Table = "channel_monitors"
|
|
// HistoryTable is the table that holds the history relation/edge.
|
|
HistoryTable = "channel_monitor_histories"
|
|
// HistoryInverseTable is the table name for the ChannelMonitorHistory entity.
|
|
// It exists in this package in order to avoid circular dependency with the "channelmonitorhistory" package.
|
|
HistoryInverseTable = "channel_monitor_histories"
|
|
// HistoryColumn is the table column denoting the history relation/edge.
|
|
HistoryColumn = "monitor_id"
|
|
)
|
|
|
|
// Columns holds all SQL columns for channelmonitor fields.
|
|
var Columns = []string{
|
|
FieldID,
|
|
FieldCreatedAt,
|
|
FieldUpdatedAt,
|
|
FieldName,
|
|
FieldProvider,
|
|
FieldEndpoint,
|
|
FieldAPIKeyEncrypted,
|
|
FieldPrimaryModel,
|
|
FieldExtraModels,
|
|
FieldGroupName,
|
|
FieldEnabled,
|
|
FieldIntervalSeconds,
|
|
FieldLastCheckedAt,
|
|
FieldCreatedBy,
|
|
}
|
|
|
|
// ValidColumn reports if the column name is valid (part of the table columns).
|
|
func ValidColumn(column string) bool {
|
|
for i := range Columns {
|
|
if column == Columns[i] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
var (
|
|
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
|
|
DefaultCreatedAt func() time.Time
|
|
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
|
|
DefaultUpdatedAt func() time.Time
|
|
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
|
|
UpdateDefaultUpdatedAt func() time.Time
|
|
// NameValidator is a validator for the "name" field. It is called by the builders before save.
|
|
NameValidator func(string) error
|
|
// EndpointValidator is a validator for the "endpoint" field. It is called by the builders before save.
|
|
EndpointValidator func(string) error
|
|
// APIKeyEncryptedValidator is a validator for the "api_key_encrypted" field. It is called by the builders before save.
|
|
APIKeyEncryptedValidator func(string) error
|
|
// PrimaryModelValidator is a validator for the "primary_model" field. It is called by the builders before save.
|
|
PrimaryModelValidator func(string) error
|
|
// DefaultExtraModels holds the default value on creation for the "extra_models" field.
|
|
DefaultExtraModels []string
|
|
// DefaultGroupName holds the default value on creation for the "group_name" field.
|
|
DefaultGroupName string
|
|
// GroupNameValidator is a validator for the "group_name" field. It is called by the builders before save.
|
|
GroupNameValidator func(string) error
|
|
// DefaultEnabled holds the default value on creation for the "enabled" field.
|
|
DefaultEnabled bool
|
|
// IntervalSecondsValidator is a validator for the "interval_seconds" field. It is called by the builders before save.
|
|
IntervalSecondsValidator func(int) error
|
|
)
|
|
|
|
// Provider defines the type for the "provider" enum field.
|
|
type Provider string
|
|
|
|
// Provider values.
|
|
const (
|
|
ProviderOpenai Provider = "openai"
|
|
ProviderAnthropic Provider = "anthropic"
|
|
ProviderGemini Provider = "gemini"
|
|
)
|
|
|
|
func (pr Provider) String() string {
|
|
return string(pr)
|
|
}
|
|
|
|
// ProviderValidator is a validator for the "provider" field enum values. It is called by the builders before save.
|
|
func ProviderValidator(pr Provider) error {
|
|
switch pr {
|
|
case ProviderOpenai, ProviderAnthropic, ProviderGemini:
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("channelmonitor: invalid enum value for provider field: %q", pr)
|
|
}
|
|
}
|
|
|
|
// OrderOption defines the ordering options for the ChannelMonitor queries.
|
|
type OrderOption func(*sql.Selector)
|
|
|
|
// ByID orders the results by the id field.
|
|
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldID, opts...).ToFunc()
|
|
}
|
|
|
|
// ByCreatedAt orders the results by the created_at field.
|
|
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
|
}
|
|
|
|
// ByUpdatedAt orders the results by the updated_at field.
|
|
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
|
|
}
|
|
|
|
// ByName orders the results by the name field.
|
|
func ByName(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldName, opts...).ToFunc()
|
|
}
|
|
|
|
// ByProvider orders the results by the provider field.
|
|
func ByProvider(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldProvider, opts...).ToFunc()
|
|
}
|
|
|
|
// ByEndpoint orders the results by the endpoint field.
|
|
func ByEndpoint(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldEndpoint, opts...).ToFunc()
|
|
}
|
|
|
|
// ByAPIKeyEncrypted orders the results by the api_key_encrypted field.
|
|
func ByAPIKeyEncrypted(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldAPIKeyEncrypted, opts...).ToFunc()
|
|
}
|
|
|
|
// ByPrimaryModel orders the results by the primary_model field.
|
|
func ByPrimaryModel(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldPrimaryModel, opts...).ToFunc()
|
|
}
|
|
|
|
// ByGroupName orders the results by the group_name field.
|
|
func ByGroupName(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldGroupName, opts...).ToFunc()
|
|
}
|
|
|
|
// ByEnabled orders the results by the enabled field.
|
|
func ByEnabled(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldEnabled, opts...).ToFunc()
|
|
}
|
|
|
|
// ByIntervalSeconds orders the results by the interval_seconds field.
|
|
func ByIntervalSeconds(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldIntervalSeconds, opts...).ToFunc()
|
|
}
|
|
|
|
// ByLastCheckedAt orders the results by the last_checked_at field.
|
|
func ByLastCheckedAt(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldLastCheckedAt, opts...).ToFunc()
|
|
}
|
|
|
|
// ByCreatedBy orders the results by the created_by field.
|
|
func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption {
|
|
return sql.OrderByField(FieldCreatedBy, opts...).ToFunc()
|
|
}
|
|
|
|
// ByHistoryCount orders the results by history count.
|
|
func ByHistoryCount(opts ...sql.OrderTermOption) OrderOption {
|
|
return func(s *sql.Selector) {
|
|
sqlgraph.OrderByNeighborsCount(s, newHistoryStep(), opts...)
|
|
}
|
|
}
|
|
|
|
// ByHistory orders the results by history terms.
|
|
func ByHistory(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
|
|
return func(s *sql.Selector) {
|
|
sqlgraph.OrderByNeighborTerms(s, newHistoryStep(), append([]sql.OrderTerm{term}, terms...)...)
|
|
}
|
|
}
|
|
func newHistoryStep() *sqlgraph.Step {
|
|
return sqlgraph.NewStep(
|
|
sqlgraph.From(Table, FieldID),
|
|
sqlgraph.To(HistoryInverseTable, FieldID),
|
|
sqlgraph.Edge(sqlgraph.O2M, false, HistoryTable, HistoryColumn),
|
|
)
|
|
}
|