Merge pull request #1850 from touwaeriol/feat/channel-insights

feat(monitor): channel monitor with available channels & feature flags
This commit is contained in:
Wesley Liddick 2026-04-24 08:31:21 +08:00 committed by GitHub
commit ac11473833
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
151 changed files with 35316 additions and 615 deletions

View File

@ -97,6 +97,7 @@ func provideCleanup(
scheduledTestRunner *service.ScheduledTestRunnerService,
backupSvc *service.BackupService,
paymentOrderExpiry *service.PaymentOrderExpiryService,
channelMonitorRunner *service.ChannelMonitorRunner,
) func() {
return func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@ -239,6 +240,12 @@ func provideCleanup(
}
return nil
}},
{"ChannelMonitorRunner", func() error {
if channelMonitorRunner != nil {
channelMonitorRunner.Stop()
}
return nil
}},
}
infraSteps := []cleanupStep{

View File

@ -176,7 +176,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
claudeTokenProvider := service.ProvideClaudeTokenProvider(accountRepository, geminiTokenCache, oAuthService, oAuthRefreshAPI)
digestSessionStore := service.NewDigestSessionStore()
channelRepository := repository.NewChannelRepository(db)
channelService := service.NewChannelService(channelRepository, apiKeyAuthCacheInvalidator)
channelService := service.NewChannelService(channelRepository, groupRepository, apiKeyAuthCacheInvalidator, pricingService)
modelPricingResolver := service.NewModelPricingResolver(channelService, billingService)
balanceNotifyService := service.ProvideBalanceNotifyService(emailService, settingRepository, accountRepository)
gatewayService := service.NewGatewayService(accountRepository, groupRepository, usageLogRepository, usageBillingRepository, userRepository, userSubscriptionRepository, userGroupRateRepository, gatewayCache, configConfig, schedulerSnapshotService, concurrencyService, billingService, rateLimitService, billingCacheService, identityService, httpUpstream, deferredService, claudeTokenProvider, sessionLimitCache, rpmCache, digestSessionStore, settingService, tlsFingerprintProfileService, channelService, modelPricingResolver, balanceNotifyService)
@ -221,8 +221,21 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
scheduledTestService := service.ProvideScheduledTestService(scheduledTestPlanRepository, scheduledTestResultRepository)
scheduledTestHandler := admin.NewScheduledTestHandler(scheduledTestService)
channelHandler := admin.NewChannelHandler(channelService, billingService)
sqlDB, err := repository.ProvideSQLDB(client)
if err != nil {
return nil, err
}
channelMonitorRepository := repository.NewChannelMonitorRepository(client, sqlDB)
channelMonitorRequestTemplateRepository := repository.NewChannelMonitorRequestTemplateRepository(client, sqlDB)
channelMonitorRequestTemplateService := service.NewChannelMonitorRequestTemplateService(channelMonitorRequestTemplateRepository)
channelMonitorRequestTemplateHandler := admin.NewChannelMonitorRequestTemplateHandler(channelMonitorRequestTemplateService)
channelMonitorService := service.ProvideChannelMonitorService(channelMonitorRepository, secretEncryptor)
channelMonitorHandler := admin.NewChannelMonitorHandler(channelMonitorService)
channelMonitorUserHandler := handler.NewChannelMonitorUserHandler(channelMonitorService, settingService)
channelMonitorRunner := service.ProvideChannelMonitorRunner(channelMonitorService, settingService)
paymentHandler := admin.NewPaymentHandler(paymentService, paymentConfigService)
adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, dataManagementHandler, backupHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, tlsFingerprintProfileHandler, adminAPIKeyHandler, scheduledTestHandler, channelHandler, paymentHandler)
availableChannelUserHandler := handler.NewAvailableChannelHandler(channelService, apiKeyService, settingService)
adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, dataManagementHandler, backupHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, tlsFingerprintProfileHandler, adminAPIKeyHandler, scheduledTestHandler, channelHandler, channelMonitorHandler, channelMonitorRequestTemplateHandler, paymentHandler)
usageRecordWorkerPool := service.NewUsageRecordWorkerPool(configConfig)
userMsgQueueCache := repository.NewUserMsgQueueCache(redisClient)
userMessageQueueService := service.ProvideUserMessageQueueService(userMsgQueueCache, rpmCache, configConfig)
@ -234,7 +247,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
paymentWebhookHandler := handler.NewPaymentWebhookHandler(paymentService, registry)
idempotencyCoordinator := service.ProvideIdempotencyCoordinator(idempotencyRepository, configConfig)
idempotencyCleanupService := service.ProvideIdempotencyCleanupService(idempotencyRepository, configConfig)
handlers := handler.ProvideHandlers(authHandler, userHandler, apiKeyHandler, usageHandler, redeemHandler, subscriptionHandler, announcementHandler, adminHandlers, gatewayHandler, openAIGatewayHandler, handlerSettingHandler, totpHandler, handlerPaymentHandler, paymentWebhookHandler, idempotencyCoordinator, idempotencyCleanupService)
handlers := handler.ProvideHandlers(authHandler, userHandler, apiKeyHandler, usageHandler, redeemHandler, subscriptionHandler, announcementHandler, channelMonitorUserHandler, adminHandlers, gatewayHandler, openAIGatewayHandler, handlerSettingHandler, totpHandler, handlerPaymentHandler, paymentWebhookHandler, availableChannelUserHandler, idempotencyCoordinator, idempotencyCleanupService)
jwtAuthMiddleware := middleware.NewJWTAuthMiddleware(authService, userService)
adminAuthMiddleware := middleware.NewAdminAuthMiddleware(authService, userService, settingService)
apiKeyAuthMiddleware := middleware.NewAPIKeyAuthMiddleware(apiKeyService, subscriptionService, configConfig)
@ -243,14 +256,14 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
opsMetricsCollector := service.ProvideOpsMetricsCollector(opsRepository, settingRepository, accountRepository, concurrencyService, db, redisClient, configConfig)
opsAggregationService := service.ProvideOpsAggregationService(opsRepository, settingRepository, db, redisClient, configConfig)
opsAlertEvaluatorService := service.ProvideOpsAlertEvaluatorService(opsService, opsRepository, emailService, redisClient, configConfig)
opsCleanupService := service.ProvideOpsCleanupService(opsRepository, db, redisClient, configConfig)
opsCleanupService := service.ProvideOpsCleanupService(opsRepository, db, redisClient, configConfig, channelMonitorService)
opsScheduledReportService := service.ProvideOpsScheduledReportService(opsService, userService, emailService, redisClient, configConfig)
tokenRefreshService := service.ProvideTokenRefreshService(accountRepository, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService, compositeTokenCacheInvalidator, schedulerCache, configConfig, tempUnschedCache, privacyClientFactory, proxyRepository, oAuthRefreshAPI)
accountExpiryService := service.ProvideAccountExpiryService(accountRepository)
subscriptionExpiryService := service.ProvideSubscriptionExpiryService(userSubscriptionRepository)
scheduledTestRunnerService := service.ProvideScheduledTestRunnerService(scheduledTestPlanRepository, scheduledTestService, accountTestService, rateLimitService, configConfig)
paymentOrderExpiryService := service.ProvidePaymentOrderExpiryService(paymentService)
v := provideCleanup(client, redisClient, opsMetricsCollector, opsAggregationService, opsAlertEvaluatorService, opsCleanupService, opsScheduledReportService, opsSystemLogSink, schedulerSnapshotService, tokenRefreshService, accountExpiryService, subscriptionExpiryService, usageCleanupService, idempotencyCleanupService, pricingService, emailQueueService, billingCacheService, usageRecordWorkerPool, subscriptionService, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService, openAIGatewayService, scheduledTestRunnerService, backupService, paymentOrderExpiryService)
v := provideCleanup(client, redisClient, opsMetricsCollector, opsAggregationService, opsAlertEvaluatorService, opsCleanupService, opsScheduledReportService, opsSystemLogSink, schedulerSnapshotService, tokenRefreshService, accountExpiryService, subscriptionExpiryService, usageCleanupService, idempotencyCleanupService, pricingService, emailQueueService, billingCacheService, usageRecordWorkerPool, subscriptionService, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService, openAIGatewayService, scheduledTestRunnerService, backupService, paymentOrderExpiryService, channelMonitorRunner)
application := &Application{
Server: httpServer,
Cleanup: v,
@ -304,6 +317,7 @@ func provideCleanup(
scheduledTestRunner *service.ScheduledTestRunnerService,
backupSvc *service.BackupService,
paymentOrderExpiry *service.PaymentOrderExpiryService,
channelMonitorRunner *service.ChannelMonitorRunner,
) func() {
return func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@ -445,6 +459,12 @@ func provideCleanup(
}
return nil
}},
{"ChannelMonitorRunner", func() error {
if channelMonitorRunner != nil {
channelMonitorRunner.Stop()
}
return nil
}},
}
infraSteps := []cleanupStep{

View File

@ -76,6 +76,7 @@ func TestProvideCleanup_WithMinimalDependencies_NoPanic(t *testing.T) {
nil, // scheduledTestRunner
nil, // backupSvc
nil, // paymentOrderExpiry
nil, // channelMonitorRunner
)
require.NotPanics(t, func() {

View File

@ -0,0 +1,359 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"encoding/json"
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
)
// ChannelMonitor is the model entity for the ChannelMonitor schema.
type ChannelMonitor struct {
config `json:"-"`
// ID of the ent.
ID int64 `json:"id,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// UpdatedAt holds the value of the "updated_at" field.
UpdatedAt time.Time `json:"updated_at,omitempty"`
// Name holds the value of the "name" field.
Name string `json:"name,omitempty"`
// Provider holds the value of the "provider" field.
Provider channelmonitor.Provider `json:"provider,omitempty"`
// Provider base origin, e.g. https://api.openai.com
Endpoint string `json:"endpoint,omitempty"`
// AES-256-GCM encrypted API key
APIKeyEncrypted string `json:"-"`
// PrimaryModel holds the value of the "primary_model" field.
PrimaryModel string `json:"primary_model,omitempty"`
// Additional model names to test alongside primary_model
ExtraModels []string `json:"extra_models,omitempty"`
// GroupName holds the value of the "group_name" field.
GroupName string `json:"group_name,omitempty"`
// Enabled holds the value of the "enabled" field.
Enabled bool `json:"enabled,omitempty"`
// IntervalSeconds holds the value of the "interval_seconds" field.
IntervalSeconds int `json:"interval_seconds,omitempty"`
// LastCheckedAt holds the value of the "last_checked_at" field.
LastCheckedAt *time.Time `json:"last_checked_at,omitempty"`
// CreatedBy holds the value of the "created_by" field.
CreatedBy int64 `json:"created_by,omitempty"`
// TemplateID holds the value of the "template_id" field.
TemplateID *int64 `json:"template_id,omitempty"`
// ExtraHeaders holds the value of the "extra_headers" field.
ExtraHeaders map[string]string `json:"extra_headers,omitempty"`
// BodyOverrideMode holds the value of the "body_override_mode" field.
BodyOverrideMode string `json:"body_override_mode,omitempty"`
// BodyOverride holds the value of the "body_override" field.
BodyOverride map[string]interface{} `json:"body_override,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the ChannelMonitorQuery when eager-loading is set.
Edges ChannelMonitorEdges `json:"edges"`
selectValues sql.SelectValues
}
// ChannelMonitorEdges holds the relations/edges for other nodes in the graph.
type ChannelMonitorEdges struct {
// History holds the value of the history edge.
History []*ChannelMonitorHistory `json:"history,omitempty"`
// DailyRollups holds the value of the daily_rollups edge.
DailyRollups []*ChannelMonitorDailyRollup `json:"daily_rollups,omitempty"`
// RequestTemplate holds the value of the request_template edge.
RequestTemplate *ChannelMonitorRequestTemplate `json:"request_template,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [3]bool
}
// HistoryOrErr returns the History value or an error if the edge
// was not loaded in eager-loading.
func (e ChannelMonitorEdges) HistoryOrErr() ([]*ChannelMonitorHistory, error) {
if e.loadedTypes[0] {
return e.History, nil
}
return nil, &NotLoadedError{edge: "history"}
}
// DailyRollupsOrErr returns the DailyRollups value or an error if the edge
// was not loaded in eager-loading.
func (e ChannelMonitorEdges) DailyRollupsOrErr() ([]*ChannelMonitorDailyRollup, error) {
if e.loadedTypes[1] {
return e.DailyRollups, nil
}
return nil, &NotLoadedError{edge: "daily_rollups"}
}
// RequestTemplateOrErr returns the RequestTemplate value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e ChannelMonitorEdges) RequestTemplateOrErr() (*ChannelMonitorRequestTemplate, error) {
if e.RequestTemplate != nil {
return e.RequestTemplate, nil
} else if e.loadedTypes[2] {
return nil, &NotFoundError{label: channelmonitorrequesttemplate.Label}
}
return nil, &NotLoadedError{edge: "request_template"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*ChannelMonitor) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case channelmonitor.FieldExtraModels, channelmonitor.FieldExtraHeaders, channelmonitor.FieldBodyOverride:
values[i] = new([]byte)
case channelmonitor.FieldEnabled:
values[i] = new(sql.NullBool)
case channelmonitor.FieldID, channelmonitor.FieldIntervalSeconds, channelmonitor.FieldCreatedBy, channelmonitor.FieldTemplateID:
values[i] = new(sql.NullInt64)
case channelmonitor.FieldName, channelmonitor.FieldProvider, channelmonitor.FieldEndpoint, channelmonitor.FieldAPIKeyEncrypted, channelmonitor.FieldPrimaryModel, channelmonitor.FieldGroupName, channelmonitor.FieldBodyOverrideMode:
values[i] = new(sql.NullString)
case channelmonitor.FieldCreatedAt, channelmonitor.FieldUpdatedAt, channelmonitor.FieldLastCheckedAt:
values[i] = new(sql.NullTime)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the ChannelMonitor fields.
func (_m *ChannelMonitor) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case channelmonitor.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
_m.ID = int64(value.Int64)
case channelmonitor.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
_m.CreatedAt = value.Time
}
case channelmonitor.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
_m.UpdatedAt = value.Time
}
case channelmonitor.FieldName:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field name", values[i])
} else if value.Valid {
_m.Name = value.String
}
case channelmonitor.FieldProvider:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field provider", values[i])
} else if value.Valid {
_m.Provider = channelmonitor.Provider(value.String)
}
case channelmonitor.FieldEndpoint:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field endpoint", values[i])
} else if value.Valid {
_m.Endpoint = value.String
}
case channelmonitor.FieldAPIKeyEncrypted:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field api_key_encrypted", values[i])
} else if value.Valid {
_m.APIKeyEncrypted = value.String
}
case channelmonitor.FieldPrimaryModel:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field primary_model", values[i])
} else if value.Valid {
_m.PrimaryModel = value.String
}
case channelmonitor.FieldExtraModels:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field extra_models", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &_m.ExtraModels); err != nil {
return fmt.Errorf("unmarshal field extra_models: %w", err)
}
}
case channelmonitor.FieldGroupName:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field group_name", values[i])
} else if value.Valid {
_m.GroupName = value.String
}
case channelmonitor.FieldEnabled:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field enabled", values[i])
} else if value.Valid {
_m.Enabled = value.Bool
}
case channelmonitor.FieldIntervalSeconds:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field interval_seconds", values[i])
} else if value.Valid {
_m.IntervalSeconds = int(value.Int64)
}
case channelmonitor.FieldLastCheckedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field last_checked_at", values[i])
} else if value.Valid {
_m.LastCheckedAt = new(time.Time)
*_m.LastCheckedAt = value.Time
}
case channelmonitor.FieldCreatedBy:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field created_by", values[i])
} else if value.Valid {
_m.CreatedBy = value.Int64
}
case channelmonitor.FieldTemplateID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field template_id", values[i])
} else if value.Valid {
_m.TemplateID = new(int64)
*_m.TemplateID = value.Int64
}
case channelmonitor.FieldExtraHeaders:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field extra_headers", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &_m.ExtraHeaders); err != nil {
return fmt.Errorf("unmarshal field extra_headers: %w", err)
}
}
case channelmonitor.FieldBodyOverrideMode:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field body_override_mode", values[i])
} else if value.Valid {
_m.BodyOverrideMode = value.String
}
case channelmonitor.FieldBodyOverride:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field body_override", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &_m.BodyOverride); err != nil {
return fmt.Errorf("unmarshal field body_override: %w", err)
}
}
default:
_m.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the ChannelMonitor.
// This includes values selected through modifiers, order, etc.
func (_m *ChannelMonitor) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryHistory queries the "history" edge of the ChannelMonitor entity.
func (_m *ChannelMonitor) QueryHistory() *ChannelMonitorHistoryQuery {
return NewChannelMonitorClient(_m.config).QueryHistory(_m)
}
// QueryDailyRollups queries the "daily_rollups" edge of the ChannelMonitor entity.
func (_m *ChannelMonitor) QueryDailyRollups() *ChannelMonitorDailyRollupQuery {
return NewChannelMonitorClient(_m.config).QueryDailyRollups(_m)
}
// QueryRequestTemplate queries the "request_template" edge of the ChannelMonitor entity.
func (_m *ChannelMonitor) QueryRequestTemplate() *ChannelMonitorRequestTemplateQuery {
return NewChannelMonitorClient(_m.config).QueryRequestTemplate(_m)
}
// Update returns a builder for updating this ChannelMonitor.
// Note that you need to call ChannelMonitor.Unwrap() before calling this method if this ChannelMonitor
// was returned from a transaction, and the transaction was committed or rolled back.
func (_m *ChannelMonitor) Update() *ChannelMonitorUpdateOne {
return NewChannelMonitorClient(_m.config).UpdateOne(_m)
}
// Unwrap unwraps the ChannelMonitor entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (_m *ChannelMonitor) Unwrap() *ChannelMonitor {
_tx, ok := _m.config.driver.(*txDriver)
if !ok {
panic("ent: ChannelMonitor is not a transactional entity")
}
_m.config.driver = _tx.drv
return _m
}
// String implements the fmt.Stringer.
func (_m *ChannelMonitor) String() string {
var builder strings.Builder
builder.WriteString("ChannelMonitor(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("created_at=")
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("updated_at=")
builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("name=")
builder.WriteString(_m.Name)
builder.WriteString(", ")
builder.WriteString("provider=")
builder.WriteString(fmt.Sprintf("%v", _m.Provider))
builder.WriteString(", ")
builder.WriteString("endpoint=")
builder.WriteString(_m.Endpoint)
builder.WriteString(", ")
builder.WriteString("api_key_encrypted=<sensitive>")
builder.WriteString(", ")
builder.WriteString("primary_model=")
builder.WriteString(_m.PrimaryModel)
builder.WriteString(", ")
builder.WriteString("extra_models=")
builder.WriteString(fmt.Sprintf("%v", _m.ExtraModels))
builder.WriteString(", ")
builder.WriteString("group_name=")
builder.WriteString(_m.GroupName)
builder.WriteString(", ")
builder.WriteString("enabled=")
builder.WriteString(fmt.Sprintf("%v", _m.Enabled))
builder.WriteString(", ")
builder.WriteString("interval_seconds=")
builder.WriteString(fmt.Sprintf("%v", _m.IntervalSeconds))
builder.WriteString(", ")
if v := _m.LastCheckedAt; v != nil {
builder.WriteString("last_checked_at=")
builder.WriteString(v.Format(time.ANSIC))
}
builder.WriteString(", ")
builder.WriteString("created_by=")
builder.WriteString(fmt.Sprintf("%v", _m.CreatedBy))
builder.WriteString(", ")
if v := _m.TemplateID; v != nil {
builder.WriteString("template_id=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
builder.WriteString("extra_headers=")
builder.WriteString(fmt.Sprintf("%v", _m.ExtraHeaders))
builder.WriteString(", ")
builder.WriteString("body_override_mode=")
builder.WriteString(_m.BodyOverrideMode)
builder.WriteString(", ")
builder.WriteString("body_override=")
builder.WriteString(fmt.Sprintf("%v", _m.BodyOverride))
builder.WriteByte(')')
return builder.String()
}
// ChannelMonitors is a parsable slice of ChannelMonitor.
type ChannelMonitors []*ChannelMonitor

View File

@ -0,0 +1,304 @@
// 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"
// FieldTemplateID holds the string denoting the template_id field in the database.
FieldTemplateID = "template_id"
// FieldExtraHeaders holds the string denoting the extra_headers field in the database.
FieldExtraHeaders = "extra_headers"
// FieldBodyOverrideMode holds the string denoting the body_override_mode field in the database.
FieldBodyOverrideMode = "body_override_mode"
// FieldBodyOverride holds the string denoting the body_override field in the database.
FieldBodyOverride = "body_override"
// EdgeHistory holds the string denoting the history edge name in mutations.
EdgeHistory = "history"
// EdgeDailyRollups holds the string denoting the daily_rollups edge name in mutations.
EdgeDailyRollups = "daily_rollups"
// EdgeRequestTemplate holds the string denoting the request_template edge name in mutations.
EdgeRequestTemplate = "request_template"
// 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"
// DailyRollupsTable is the table that holds the daily_rollups relation/edge.
DailyRollupsTable = "channel_monitor_daily_rollups"
// DailyRollupsInverseTable is the table name for the ChannelMonitorDailyRollup entity.
// It exists in this package in order to avoid circular dependency with the "channelmonitordailyrollup" package.
DailyRollupsInverseTable = "channel_monitor_daily_rollups"
// DailyRollupsColumn is the table column denoting the daily_rollups relation/edge.
DailyRollupsColumn = "monitor_id"
// RequestTemplateTable is the table that holds the request_template relation/edge.
RequestTemplateTable = "channel_monitors"
// RequestTemplateInverseTable is the table name for the ChannelMonitorRequestTemplate entity.
// It exists in this package in order to avoid circular dependency with the "channelmonitorrequesttemplate" package.
RequestTemplateInverseTable = "channel_monitor_request_templates"
// RequestTemplateColumn is the table column denoting the request_template relation/edge.
RequestTemplateColumn = "template_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,
FieldTemplateID,
FieldExtraHeaders,
FieldBodyOverrideMode,
FieldBodyOverride,
}
// 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
// DefaultExtraHeaders holds the default value on creation for the "extra_headers" field.
DefaultExtraHeaders map[string]string
// DefaultBodyOverrideMode holds the default value on creation for the "body_override_mode" field.
DefaultBodyOverrideMode string
// BodyOverrideModeValidator is a validator for the "body_override_mode" field. It is called by the builders before save.
BodyOverrideModeValidator func(string) 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()
}
// ByTemplateID orders the results by the template_id field.
func ByTemplateID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldTemplateID, opts...).ToFunc()
}
// ByBodyOverrideMode orders the results by the body_override_mode field.
func ByBodyOverrideMode(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldBodyOverrideMode, 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...)...)
}
}
// ByDailyRollupsCount orders the results by daily_rollups count.
func ByDailyRollupsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newDailyRollupsStep(), opts...)
}
}
// ByDailyRollups orders the results by daily_rollups terms.
func ByDailyRollups(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newDailyRollupsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByRequestTemplateField orders the results by request_template field.
func ByRequestTemplateField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newRequestTemplateStep(), sql.OrderByField(field, opts...))
}
}
func newHistoryStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(HistoryInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, HistoryTable, HistoryColumn),
)
}
func newDailyRollupsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DailyRollupsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, DailyRollupsTable, DailyRollupsColumn),
)
}
func newRequestTemplateStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(RequestTemplateInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, RequestTemplateTable, RequestTemplateColumn),
)
}

View File

@ -0,0 +1,885 @@
// Code generated by ent, DO NOT EDIT.
package channelmonitor
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldID, id))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldCreatedAt, v))
}
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldUpdatedAt, v))
}
// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
func Name(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldName, v))
}
// Endpoint applies equality check predicate on the "endpoint" field. It's identical to EndpointEQ.
func Endpoint(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldEndpoint, v))
}
// APIKeyEncrypted applies equality check predicate on the "api_key_encrypted" field. It's identical to APIKeyEncryptedEQ.
func APIKeyEncrypted(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldAPIKeyEncrypted, v))
}
// PrimaryModel applies equality check predicate on the "primary_model" field. It's identical to PrimaryModelEQ.
func PrimaryModel(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldPrimaryModel, v))
}
// GroupName applies equality check predicate on the "group_name" field. It's identical to GroupNameEQ.
func GroupName(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldGroupName, v))
}
// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ.
func Enabled(v bool) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldEnabled, v))
}
// IntervalSeconds applies equality check predicate on the "interval_seconds" field. It's identical to IntervalSecondsEQ.
func IntervalSeconds(v int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldIntervalSeconds, v))
}
// LastCheckedAt applies equality check predicate on the "last_checked_at" field. It's identical to LastCheckedAtEQ.
func LastCheckedAt(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldLastCheckedAt, v))
}
// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ.
func CreatedBy(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldCreatedBy, v))
}
// TemplateID applies equality check predicate on the "template_id" field. It's identical to TemplateIDEQ.
func TemplateID(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldTemplateID, v))
}
// BodyOverrideMode applies equality check predicate on the "body_override_mode" field. It's identical to BodyOverrideModeEQ.
func BodyOverrideMode(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldBodyOverrideMode, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldCreatedAt, v))
}
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldCreatedAt, v))
}
// CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldCreatedAt, vs...))
}
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldCreatedAt, vs...))
}
// CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldCreatedAt, v))
}
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldCreatedAt, v))
}
// CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldCreatedAt, v))
}
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldCreatedAt, v))
}
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldUpdatedAt, v))
}
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldUpdatedAt, v))
}
// UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldUpdatedAt, vs...))
}
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldUpdatedAt, vs...))
}
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldUpdatedAt, v))
}
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldUpdatedAt, v))
}
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldUpdatedAt, v))
}
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldUpdatedAt, v))
}
// NameEQ applies the EQ predicate on the "name" field.
func NameEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldName, v))
}
// NameNEQ applies the NEQ predicate on the "name" field.
func NameNEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldName, v))
}
// NameIn applies the In predicate on the "name" field.
func NameIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldName, vs...))
}
// NameNotIn applies the NotIn predicate on the "name" field.
func NameNotIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldName, vs...))
}
// NameGT applies the GT predicate on the "name" field.
func NameGT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldName, v))
}
// NameGTE applies the GTE predicate on the "name" field.
func NameGTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldName, v))
}
// NameLT applies the LT predicate on the "name" field.
func NameLT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldName, v))
}
// NameLTE applies the LTE predicate on the "name" field.
func NameLTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldName, v))
}
// NameContains applies the Contains predicate on the "name" field.
func NameContains(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContains(FieldName, v))
}
// NameHasPrefix applies the HasPrefix predicate on the "name" field.
func NameHasPrefix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasPrefix(FieldName, v))
}
// NameHasSuffix applies the HasSuffix predicate on the "name" field.
func NameHasSuffix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasSuffix(FieldName, v))
}
// NameEqualFold applies the EqualFold predicate on the "name" field.
func NameEqualFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEqualFold(FieldName, v))
}
// NameContainsFold applies the ContainsFold predicate on the "name" field.
func NameContainsFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContainsFold(FieldName, v))
}
// ProviderEQ applies the EQ predicate on the "provider" field.
func ProviderEQ(v Provider) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldProvider, v))
}
// ProviderNEQ applies the NEQ predicate on the "provider" field.
func ProviderNEQ(v Provider) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldProvider, v))
}
// ProviderIn applies the In predicate on the "provider" field.
func ProviderIn(vs ...Provider) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldProvider, vs...))
}
// ProviderNotIn applies the NotIn predicate on the "provider" field.
func ProviderNotIn(vs ...Provider) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldProvider, vs...))
}
// EndpointEQ applies the EQ predicate on the "endpoint" field.
func EndpointEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldEndpoint, v))
}
// EndpointNEQ applies the NEQ predicate on the "endpoint" field.
func EndpointNEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldEndpoint, v))
}
// EndpointIn applies the In predicate on the "endpoint" field.
func EndpointIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldEndpoint, vs...))
}
// EndpointNotIn applies the NotIn predicate on the "endpoint" field.
func EndpointNotIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldEndpoint, vs...))
}
// EndpointGT applies the GT predicate on the "endpoint" field.
func EndpointGT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldEndpoint, v))
}
// EndpointGTE applies the GTE predicate on the "endpoint" field.
func EndpointGTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldEndpoint, v))
}
// EndpointLT applies the LT predicate on the "endpoint" field.
func EndpointLT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldEndpoint, v))
}
// EndpointLTE applies the LTE predicate on the "endpoint" field.
func EndpointLTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldEndpoint, v))
}
// EndpointContains applies the Contains predicate on the "endpoint" field.
func EndpointContains(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContains(FieldEndpoint, v))
}
// EndpointHasPrefix applies the HasPrefix predicate on the "endpoint" field.
func EndpointHasPrefix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasPrefix(FieldEndpoint, v))
}
// EndpointHasSuffix applies the HasSuffix predicate on the "endpoint" field.
func EndpointHasSuffix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasSuffix(FieldEndpoint, v))
}
// EndpointEqualFold applies the EqualFold predicate on the "endpoint" field.
func EndpointEqualFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEqualFold(FieldEndpoint, v))
}
// EndpointContainsFold applies the ContainsFold predicate on the "endpoint" field.
func EndpointContainsFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContainsFold(FieldEndpoint, v))
}
// APIKeyEncryptedEQ applies the EQ predicate on the "api_key_encrypted" field.
func APIKeyEncryptedEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedNEQ applies the NEQ predicate on the "api_key_encrypted" field.
func APIKeyEncryptedNEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedIn applies the In predicate on the "api_key_encrypted" field.
func APIKeyEncryptedIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldAPIKeyEncrypted, vs...))
}
// APIKeyEncryptedNotIn applies the NotIn predicate on the "api_key_encrypted" field.
func APIKeyEncryptedNotIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldAPIKeyEncrypted, vs...))
}
// APIKeyEncryptedGT applies the GT predicate on the "api_key_encrypted" field.
func APIKeyEncryptedGT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedGTE applies the GTE predicate on the "api_key_encrypted" field.
func APIKeyEncryptedGTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedLT applies the LT predicate on the "api_key_encrypted" field.
func APIKeyEncryptedLT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedLTE applies the LTE predicate on the "api_key_encrypted" field.
func APIKeyEncryptedLTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedContains applies the Contains predicate on the "api_key_encrypted" field.
func APIKeyEncryptedContains(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContains(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedHasPrefix applies the HasPrefix predicate on the "api_key_encrypted" field.
func APIKeyEncryptedHasPrefix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasPrefix(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedHasSuffix applies the HasSuffix predicate on the "api_key_encrypted" field.
func APIKeyEncryptedHasSuffix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasSuffix(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedEqualFold applies the EqualFold predicate on the "api_key_encrypted" field.
func APIKeyEncryptedEqualFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEqualFold(FieldAPIKeyEncrypted, v))
}
// APIKeyEncryptedContainsFold applies the ContainsFold predicate on the "api_key_encrypted" field.
func APIKeyEncryptedContainsFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContainsFold(FieldAPIKeyEncrypted, v))
}
// PrimaryModelEQ applies the EQ predicate on the "primary_model" field.
func PrimaryModelEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldPrimaryModel, v))
}
// PrimaryModelNEQ applies the NEQ predicate on the "primary_model" field.
func PrimaryModelNEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldPrimaryModel, v))
}
// PrimaryModelIn applies the In predicate on the "primary_model" field.
func PrimaryModelIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldPrimaryModel, vs...))
}
// PrimaryModelNotIn applies the NotIn predicate on the "primary_model" field.
func PrimaryModelNotIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldPrimaryModel, vs...))
}
// PrimaryModelGT applies the GT predicate on the "primary_model" field.
func PrimaryModelGT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldPrimaryModel, v))
}
// PrimaryModelGTE applies the GTE predicate on the "primary_model" field.
func PrimaryModelGTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldPrimaryModel, v))
}
// PrimaryModelLT applies the LT predicate on the "primary_model" field.
func PrimaryModelLT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldPrimaryModel, v))
}
// PrimaryModelLTE applies the LTE predicate on the "primary_model" field.
func PrimaryModelLTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldPrimaryModel, v))
}
// PrimaryModelContains applies the Contains predicate on the "primary_model" field.
func PrimaryModelContains(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContains(FieldPrimaryModel, v))
}
// PrimaryModelHasPrefix applies the HasPrefix predicate on the "primary_model" field.
func PrimaryModelHasPrefix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasPrefix(FieldPrimaryModel, v))
}
// PrimaryModelHasSuffix applies the HasSuffix predicate on the "primary_model" field.
func PrimaryModelHasSuffix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasSuffix(FieldPrimaryModel, v))
}
// PrimaryModelEqualFold applies the EqualFold predicate on the "primary_model" field.
func PrimaryModelEqualFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEqualFold(FieldPrimaryModel, v))
}
// PrimaryModelContainsFold applies the ContainsFold predicate on the "primary_model" field.
func PrimaryModelContainsFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContainsFold(FieldPrimaryModel, v))
}
// GroupNameEQ applies the EQ predicate on the "group_name" field.
func GroupNameEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldGroupName, v))
}
// GroupNameNEQ applies the NEQ predicate on the "group_name" field.
func GroupNameNEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldGroupName, v))
}
// GroupNameIn applies the In predicate on the "group_name" field.
func GroupNameIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldGroupName, vs...))
}
// GroupNameNotIn applies the NotIn predicate on the "group_name" field.
func GroupNameNotIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldGroupName, vs...))
}
// GroupNameGT applies the GT predicate on the "group_name" field.
func GroupNameGT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldGroupName, v))
}
// GroupNameGTE applies the GTE predicate on the "group_name" field.
func GroupNameGTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldGroupName, v))
}
// GroupNameLT applies the LT predicate on the "group_name" field.
func GroupNameLT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldGroupName, v))
}
// GroupNameLTE applies the LTE predicate on the "group_name" field.
func GroupNameLTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldGroupName, v))
}
// GroupNameContains applies the Contains predicate on the "group_name" field.
func GroupNameContains(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContains(FieldGroupName, v))
}
// GroupNameHasPrefix applies the HasPrefix predicate on the "group_name" field.
func GroupNameHasPrefix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasPrefix(FieldGroupName, v))
}
// GroupNameHasSuffix applies the HasSuffix predicate on the "group_name" field.
func GroupNameHasSuffix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasSuffix(FieldGroupName, v))
}
// GroupNameIsNil applies the IsNil predicate on the "group_name" field.
func GroupNameIsNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIsNull(FieldGroupName))
}
// GroupNameNotNil applies the NotNil predicate on the "group_name" field.
func GroupNameNotNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotNull(FieldGroupName))
}
// GroupNameEqualFold applies the EqualFold predicate on the "group_name" field.
func GroupNameEqualFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEqualFold(FieldGroupName, v))
}
// GroupNameContainsFold applies the ContainsFold predicate on the "group_name" field.
func GroupNameContainsFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContainsFold(FieldGroupName, v))
}
// EnabledEQ applies the EQ predicate on the "enabled" field.
func EnabledEQ(v bool) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldEnabled, v))
}
// EnabledNEQ applies the NEQ predicate on the "enabled" field.
func EnabledNEQ(v bool) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldEnabled, v))
}
// IntervalSecondsEQ applies the EQ predicate on the "interval_seconds" field.
func IntervalSecondsEQ(v int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldIntervalSeconds, v))
}
// IntervalSecondsNEQ applies the NEQ predicate on the "interval_seconds" field.
func IntervalSecondsNEQ(v int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldIntervalSeconds, v))
}
// IntervalSecondsIn applies the In predicate on the "interval_seconds" field.
func IntervalSecondsIn(vs ...int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldIntervalSeconds, vs...))
}
// IntervalSecondsNotIn applies the NotIn predicate on the "interval_seconds" field.
func IntervalSecondsNotIn(vs ...int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldIntervalSeconds, vs...))
}
// IntervalSecondsGT applies the GT predicate on the "interval_seconds" field.
func IntervalSecondsGT(v int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldIntervalSeconds, v))
}
// IntervalSecondsGTE applies the GTE predicate on the "interval_seconds" field.
func IntervalSecondsGTE(v int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldIntervalSeconds, v))
}
// IntervalSecondsLT applies the LT predicate on the "interval_seconds" field.
func IntervalSecondsLT(v int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldIntervalSeconds, v))
}
// IntervalSecondsLTE applies the LTE predicate on the "interval_seconds" field.
func IntervalSecondsLTE(v int) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldIntervalSeconds, v))
}
// LastCheckedAtEQ applies the EQ predicate on the "last_checked_at" field.
func LastCheckedAtEQ(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldLastCheckedAt, v))
}
// LastCheckedAtNEQ applies the NEQ predicate on the "last_checked_at" field.
func LastCheckedAtNEQ(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldLastCheckedAt, v))
}
// LastCheckedAtIn applies the In predicate on the "last_checked_at" field.
func LastCheckedAtIn(vs ...time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldLastCheckedAt, vs...))
}
// LastCheckedAtNotIn applies the NotIn predicate on the "last_checked_at" field.
func LastCheckedAtNotIn(vs ...time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldLastCheckedAt, vs...))
}
// LastCheckedAtGT applies the GT predicate on the "last_checked_at" field.
func LastCheckedAtGT(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldLastCheckedAt, v))
}
// LastCheckedAtGTE applies the GTE predicate on the "last_checked_at" field.
func LastCheckedAtGTE(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldLastCheckedAt, v))
}
// LastCheckedAtLT applies the LT predicate on the "last_checked_at" field.
func LastCheckedAtLT(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldLastCheckedAt, v))
}
// LastCheckedAtLTE applies the LTE predicate on the "last_checked_at" field.
func LastCheckedAtLTE(v time.Time) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldLastCheckedAt, v))
}
// LastCheckedAtIsNil applies the IsNil predicate on the "last_checked_at" field.
func LastCheckedAtIsNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIsNull(FieldLastCheckedAt))
}
// LastCheckedAtNotNil applies the NotNil predicate on the "last_checked_at" field.
func LastCheckedAtNotNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotNull(FieldLastCheckedAt))
}
// CreatedByEQ applies the EQ predicate on the "created_by" field.
func CreatedByEQ(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldCreatedBy, v))
}
// CreatedByNEQ applies the NEQ predicate on the "created_by" field.
func CreatedByNEQ(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldCreatedBy, v))
}
// CreatedByIn applies the In predicate on the "created_by" field.
func CreatedByIn(vs ...int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldCreatedBy, vs...))
}
// CreatedByNotIn applies the NotIn predicate on the "created_by" field.
func CreatedByNotIn(vs ...int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldCreatedBy, vs...))
}
// CreatedByGT applies the GT predicate on the "created_by" field.
func CreatedByGT(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldCreatedBy, v))
}
// CreatedByGTE applies the GTE predicate on the "created_by" field.
func CreatedByGTE(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldCreatedBy, v))
}
// CreatedByLT applies the LT predicate on the "created_by" field.
func CreatedByLT(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldCreatedBy, v))
}
// CreatedByLTE applies the LTE predicate on the "created_by" field.
func CreatedByLTE(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldCreatedBy, v))
}
// TemplateIDEQ applies the EQ predicate on the "template_id" field.
func TemplateIDEQ(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldTemplateID, v))
}
// TemplateIDNEQ applies the NEQ predicate on the "template_id" field.
func TemplateIDNEQ(v int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldTemplateID, v))
}
// TemplateIDIn applies the In predicate on the "template_id" field.
func TemplateIDIn(vs ...int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldTemplateID, vs...))
}
// TemplateIDNotIn applies the NotIn predicate on the "template_id" field.
func TemplateIDNotIn(vs ...int64) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldTemplateID, vs...))
}
// TemplateIDIsNil applies the IsNil predicate on the "template_id" field.
func TemplateIDIsNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIsNull(FieldTemplateID))
}
// TemplateIDNotNil applies the NotNil predicate on the "template_id" field.
func TemplateIDNotNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotNull(FieldTemplateID))
}
// BodyOverrideModeEQ applies the EQ predicate on the "body_override_mode" field.
func BodyOverrideModeEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEQ(FieldBodyOverrideMode, v))
}
// BodyOverrideModeNEQ applies the NEQ predicate on the "body_override_mode" field.
func BodyOverrideModeNEQ(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNEQ(FieldBodyOverrideMode, v))
}
// BodyOverrideModeIn applies the In predicate on the "body_override_mode" field.
func BodyOverrideModeIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIn(FieldBodyOverrideMode, vs...))
}
// BodyOverrideModeNotIn applies the NotIn predicate on the "body_override_mode" field.
func BodyOverrideModeNotIn(vs ...string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotIn(FieldBodyOverrideMode, vs...))
}
// BodyOverrideModeGT applies the GT predicate on the "body_override_mode" field.
func BodyOverrideModeGT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGT(FieldBodyOverrideMode, v))
}
// BodyOverrideModeGTE applies the GTE predicate on the "body_override_mode" field.
func BodyOverrideModeGTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldGTE(FieldBodyOverrideMode, v))
}
// BodyOverrideModeLT applies the LT predicate on the "body_override_mode" field.
func BodyOverrideModeLT(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLT(FieldBodyOverrideMode, v))
}
// BodyOverrideModeLTE applies the LTE predicate on the "body_override_mode" field.
func BodyOverrideModeLTE(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldLTE(FieldBodyOverrideMode, v))
}
// BodyOverrideModeContains applies the Contains predicate on the "body_override_mode" field.
func BodyOverrideModeContains(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContains(FieldBodyOverrideMode, v))
}
// BodyOverrideModeHasPrefix applies the HasPrefix predicate on the "body_override_mode" field.
func BodyOverrideModeHasPrefix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasPrefix(FieldBodyOverrideMode, v))
}
// BodyOverrideModeHasSuffix applies the HasSuffix predicate on the "body_override_mode" field.
func BodyOverrideModeHasSuffix(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldHasSuffix(FieldBodyOverrideMode, v))
}
// BodyOverrideModeEqualFold applies the EqualFold predicate on the "body_override_mode" field.
func BodyOverrideModeEqualFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldEqualFold(FieldBodyOverrideMode, v))
}
// BodyOverrideModeContainsFold applies the ContainsFold predicate on the "body_override_mode" field.
func BodyOverrideModeContainsFold(v string) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldContainsFold(FieldBodyOverrideMode, v))
}
// BodyOverrideIsNil applies the IsNil predicate on the "body_override" field.
func BodyOverrideIsNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldIsNull(FieldBodyOverride))
}
// BodyOverrideNotNil applies the NotNil predicate on the "body_override" field.
func BodyOverrideNotNil() predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.FieldNotNull(FieldBodyOverride))
}
// HasHistory applies the HasEdge predicate on the "history" edge.
func HasHistory() predicate.ChannelMonitor {
return predicate.ChannelMonitor(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, HistoryTable, HistoryColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasHistoryWith applies the HasEdge predicate on the "history" edge with a given conditions (other predicates).
func HasHistoryWith(preds ...predicate.ChannelMonitorHistory) predicate.ChannelMonitor {
return predicate.ChannelMonitor(func(s *sql.Selector) {
step := newHistoryStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// HasDailyRollups applies the HasEdge predicate on the "daily_rollups" edge.
func HasDailyRollups() predicate.ChannelMonitor {
return predicate.ChannelMonitor(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, DailyRollupsTable, DailyRollupsColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasDailyRollupsWith applies the HasEdge predicate on the "daily_rollups" edge with a given conditions (other predicates).
func HasDailyRollupsWith(preds ...predicate.ChannelMonitorDailyRollup) predicate.ChannelMonitor {
return predicate.ChannelMonitor(func(s *sql.Selector) {
step := newDailyRollupsStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// HasRequestTemplate applies the HasEdge predicate on the "request_template" edge.
func HasRequestTemplate() predicate.ChannelMonitor {
return predicate.ChannelMonitor(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, RequestTemplateTable, RequestTemplateColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasRequestTemplateWith applies the HasEdge predicate on the "request_template" edge with a given conditions (other predicates).
func HasRequestTemplateWith(preds ...predicate.ChannelMonitorRequestTemplate) predicate.ChannelMonitor {
return predicate.ChannelMonitor(func(s *sql.Selector) {
step := newRequestTemplateStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.ChannelMonitor) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.ChannelMonitor) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.ChannelMonitor) predicate.ChannelMonitor {
return predicate.ChannelMonitor(sql.NotPredicates(p))
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorDelete is the builder for deleting a ChannelMonitor entity.
type ChannelMonitorDelete struct {
config
hooks []Hook
mutation *ChannelMonitorMutation
}
// Where appends a list predicates to the ChannelMonitorDelete builder.
func (_d *ChannelMonitorDelete) Where(ps ...predicate.ChannelMonitor) *ChannelMonitorDelete {
_d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (_d *ChannelMonitorDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorDelete) ExecX(ctx context.Context) int {
n, err := _d.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (_d *ChannelMonitorDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(channelmonitor.Table, sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64))
if ps := _d.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
_d.mutation.done = true
return affected, err
}
// ChannelMonitorDeleteOne is the builder for deleting a single ChannelMonitor entity.
type ChannelMonitorDeleteOne struct {
_d *ChannelMonitorDelete
}
// Where appends a list predicates to the ChannelMonitorDelete builder.
func (_d *ChannelMonitorDeleteOne) Where(ps ...predicate.ChannelMonitor) *ChannelMonitorDeleteOne {
_d._d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query.
func (_d *ChannelMonitorDeleteOne) Exec(ctx context.Context) error {
n, err := _d._d.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{channelmonitor.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorDeleteOne) ExecX(ctx context.Context) {
if err := _d.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,797 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"database/sql/driver"
"fmt"
"math"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorQuery is the builder for querying ChannelMonitor entities.
type ChannelMonitorQuery struct {
config
ctx *QueryContext
order []channelmonitor.OrderOption
inters []Interceptor
predicates []predicate.ChannelMonitor
withHistory *ChannelMonitorHistoryQuery
withDailyRollups *ChannelMonitorDailyRollupQuery
withRequestTemplate *ChannelMonitorRequestTemplateQuery
modifiers []func(*sql.Selector)
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the ChannelMonitorQuery builder.
func (_q *ChannelMonitorQuery) Where(ps ...predicate.ChannelMonitor) *ChannelMonitorQuery {
_q.predicates = append(_q.predicates, ps...)
return _q
}
// Limit the number of records to be returned by this query.
func (_q *ChannelMonitorQuery) Limit(limit int) *ChannelMonitorQuery {
_q.ctx.Limit = &limit
return _q
}
// Offset to start from.
func (_q *ChannelMonitorQuery) Offset(offset int) *ChannelMonitorQuery {
_q.ctx.Offset = &offset
return _q
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (_q *ChannelMonitorQuery) Unique(unique bool) *ChannelMonitorQuery {
_q.ctx.Unique = &unique
return _q
}
// Order specifies how the records should be ordered.
func (_q *ChannelMonitorQuery) Order(o ...channelmonitor.OrderOption) *ChannelMonitorQuery {
_q.order = append(_q.order, o...)
return _q
}
// QueryHistory chains the current query on the "history" edge.
func (_q *ChannelMonitorQuery) QueryHistory() *ChannelMonitorHistoryQuery {
query := (&ChannelMonitorHistoryClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitor.Table, channelmonitor.FieldID, selector),
sqlgraph.To(channelmonitorhistory.Table, channelmonitorhistory.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, channelmonitor.HistoryTable, channelmonitor.HistoryColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// QueryDailyRollups chains the current query on the "daily_rollups" edge.
func (_q *ChannelMonitorQuery) QueryDailyRollups() *ChannelMonitorDailyRollupQuery {
query := (&ChannelMonitorDailyRollupClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitor.Table, channelmonitor.FieldID, selector),
sqlgraph.To(channelmonitordailyrollup.Table, channelmonitordailyrollup.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, channelmonitor.DailyRollupsTable, channelmonitor.DailyRollupsColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// QueryRequestTemplate chains the current query on the "request_template" edge.
func (_q *ChannelMonitorQuery) QueryRequestTemplate() *ChannelMonitorRequestTemplateQuery {
query := (&ChannelMonitorRequestTemplateClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitor.Table, channelmonitor.FieldID, selector),
sqlgraph.To(channelmonitorrequesttemplate.Table, channelmonitorrequesttemplate.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, channelmonitor.RequestTemplateTable, channelmonitor.RequestTemplateColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first ChannelMonitor entity from the query.
// Returns a *NotFoundError when no ChannelMonitor was found.
func (_q *ChannelMonitorQuery) First(ctx context.Context) (*ChannelMonitor, error) {
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{channelmonitor.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (_q *ChannelMonitorQuery) FirstX(ctx context.Context) *ChannelMonitor {
node, err := _q.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first ChannelMonitor ID from the query.
// Returns a *NotFoundError when no ChannelMonitor ID was found.
func (_q *ChannelMonitorQuery) FirstID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{channelmonitor.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (_q *ChannelMonitorQuery) FirstIDX(ctx context.Context) int64 {
id, err := _q.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single ChannelMonitor entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one ChannelMonitor entity is found.
// Returns a *NotFoundError when no ChannelMonitor entities are found.
func (_q *ChannelMonitorQuery) Only(ctx context.Context) (*ChannelMonitor, error) {
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{channelmonitor.Label}
default:
return nil, &NotSingularError{channelmonitor.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (_q *ChannelMonitorQuery) OnlyX(ctx context.Context) *ChannelMonitor {
node, err := _q.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only ChannelMonitor ID in the query.
// Returns a *NotSingularError when more than one ChannelMonitor ID is found.
// Returns a *NotFoundError when no entities are found.
func (_q *ChannelMonitorQuery) OnlyID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{channelmonitor.Label}
default:
err = &NotSingularError{channelmonitor.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (_q *ChannelMonitorQuery) OnlyIDX(ctx context.Context) int64 {
id, err := _q.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of ChannelMonitors.
func (_q *ChannelMonitorQuery) All(ctx context.Context) ([]*ChannelMonitor, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*ChannelMonitor, *ChannelMonitorQuery]()
return withInterceptors[[]*ChannelMonitor](ctx, _q, qr, _q.inters)
}
// AllX is like All, but panics if an error occurs.
func (_q *ChannelMonitorQuery) AllX(ctx context.Context) []*ChannelMonitor {
nodes, err := _q.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of ChannelMonitor IDs.
func (_q *ChannelMonitorQuery) IDs(ctx context.Context) (ids []int64, err error) {
if _q.ctx.Unique == nil && _q.path != nil {
_q.Unique(true)
}
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
if err = _q.Select(channelmonitor.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (_q *ChannelMonitorQuery) IDsX(ctx context.Context) []int64 {
ids, err := _q.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (_q *ChannelMonitorQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
if err := _q.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, _q, querierCount[*ChannelMonitorQuery](), _q.inters)
}
// CountX is like Count, but panics if an error occurs.
func (_q *ChannelMonitorQuery) CountX(ctx context.Context) int {
count, err := _q.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (_q *ChannelMonitorQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
switch _, err := _q.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (_q *ChannelMonitorQuery) ExistX(ctx context.Context) bool {
exist, err := _q.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the ChannelMonitorQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (_q *ChannelMonitorQuery) Clone() *ChannelMonitorQuery {
if _q == nil {
return nil
}
return &ChannelMonitorQuery{
config: _q.config,
ctx: _q.ctx.Clone(),
order: append([]channelmonitor.OrderOption{}, _q.order...),
inters: append([]Interceptor{}, _q.inters...),
predicates: append([]predicate.ChannelMonitor{}, _q.predicates...),
withHistory: _q.withHistory.Clone(),
withDailyRollups: _q.withDailyRollups.Clone(),
withRequestTemplate: _q.withRequestTemplate.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
path: _q.path,
}
}
// WithHistory tells the query-builder to eager-load the nodes that are connected to
// the "history" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *ChannelMonitorQuery) WithHistory(opts ...func(*ChannelMonitorHistoryQuery)) *ChannelMonitorQuery {
query := (&ChannelMonitorHistoryClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withHistory = query
return _q
}
// WithDailyRollups tells the query-builder to eager-load the nodes that are connected to
// the "daily_rollups" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *ChannelMonitorQuery) WithDailyRollups(opts ...func(*ChannelMonitorDailyRollupQuery)) *ChannelMonitorQuery {
query := (&ChannelMonitorDailyRollupClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withDailyRollups = query
return _q
}
// WithRequestTemplate tells the query-builder to eager-load the nodes that are connected to
// the "request_template" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *ChannelMonitorQuery) WithRequestTemplate(opts ...func(*ChannelMonitorRequestTemplateQuery)) *ChannelMonitorQuery {
query := (&ChannelMonitorRequestTemplateClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withRequestTemplate = query
return _q
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.ChannelMonitor.Query().
// GroupBy(channelmonitor.FieldCreatedAt).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (_q *ChannelMonitorQuery) GroupBy(field string, fields ...string) *ChannelMonitorGroupBy {
_q.ctx.Fields = append([]string{field}, fields...)
grbuild := &ChannelMonitorGroupBy{build: _q}
grbuild.flds = &_q.ctx.Fields
grbuild.label = channelmonitor.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// }
//
// client.ChannelMonitor.Query().
// Select(channelmonitor.FieldCreatedAt).
// Scan(ctx, &v)
func (_q *ChannelMonitorQuery) Select(fields ...string) *ChannelMonitorSelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
sbuild := &ChannelMonitorSelect{ChannelMonitorQuery: _q}
sbuild.label = channelmonitor.Label
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a ChannelMonitorSelect configured with the given aggregations.
func (_q *ChannelMonitorQuery) Aggregate(fns ...AggregateFunc) *ChannelMonitorSelect {
return _q.Select().Aggregate(fns...)
}
func (_q *ChannelMonitorQuery) prepareQuery(ctx context.Context) error {
for _, inter := range _q.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, _q); err != nil {
return err
}
}
}
for _, f := range _q.ctx.Fields {
if !channelmonitor.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if _q.path != nil {
prev, err := _q.path(ctx)
if err != nil {
return err
}
_q.sql = prev
}
return nil
}
func (_q *ChannelMonitorQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ChannelMonitor, error) {
var (
nodes = []*ChannelMonitor{}
_spec = _q.querySpec()
loadedTypes = [3]bool{
_q.withHistory != nil,
_q.withDailyRollups != nil,
_q.withRequestTemplate != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*ChannelMonitor).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &ChannelMonitor{config: _q.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := _q.withHistory; query != nil {
if err := _q.loadHistory(ctx, query, nodes,
func(n *ChannelMonitor) { n.Edges.History = []*ChannelMonitorHistory{} },
func(n *ChannelMonitor, e *ChannelMonitorHistory) { n.Edges.History = append(n.Edges.History, e) }); err != nil {
return nil, err
}
}
if query := _q.withDailyRollups; query != nil {
if err := _q.loadDailyRollups(ctx, query, nodes,
func(n *ChannelMonitor) { n.Edges.DailyRollups = []*ChannelMonitorDailyRollup{} },
func(n *ChannelMonitor, e *ChannelMonitorDailyRollup) {
n.Edges.DailyRollups = append(n.Edges.DailyRollups, e)
}); err != nil {
return nil, err
}
}
if query := _q.withRequestTemplate; query != nil {
if err := _q.loadRequestTemplate(ctx, query, nodes, nil,
func(n *ChannelMonitor, e *ChannelMonitorRequestTemplate) { n.Edges.RequestTemplate = e }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (_q *ChannelMonitorQuery) loadHistory(ctx context.Context, query *ChannelMonitorHistoryQuery, nodes []*ChannelMonitor, init func(*ChannelMonitor), assign func(*ChannelMonitor, *ChannelMonitorHistory)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*ChannelMonitor)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
if init != nil {
init(nodes[i])
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(channelmonitorhistory.FieldMonitorID)
}
query.Where(predicate.ChannelMonitorHistory(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(channelmonitor.HistoryColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.MonitorID
node, ok := nodeids[fk]
if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "monitor_id" returned %v for node %v`, fk, n.ID)
}
assign(node, n)
}
return nil
}
func (_q *ChannelMonitorQuery) loadDailyRollups(ctx context.Context, query *ChannelMonitorDailyRollupQuery, nodes []*ChannelMonitor, init func(*ChannelMonitor), assign func(*ChannelMonitor, *ChannelMonitorDailyRollup)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*ChannelMonitor)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
if init != nil {
init(nodes[i])
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(channelmonitordailyrollup.FieldMonitorID)
}
query.Where(predicate.ChannelMonitorDailyRollup(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(channelmonitor.DailyRollupsColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.MonitorID
node, ok := nodeids[fk]
if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "monitor_id" returned %v for node %v`, fk, n.ID)
}
assign(node, n)
}
return nil
}
func (_q *ChannelMonitorQuery) loadRequestTemplate(ctx context.Context, query *ChannelMonitorRequestTemplateQuery, nodes []*ChannelMonitor, init func(*ChannelMonitor), assign func(*ChannelMonitor, *ChannelMonitorRequestTemplate)) error {
ids := make([]int64, 0, len(nodes))
nodeids := make(map[int64][]*ChannelMonitor)
for i := range nodes {
if nodes[i].TemplateID == nil {
continue
}
fk := *nodes[i].TemplateID
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(channelmonitorrequesttemplate.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nodeids[n.ID]
if !ok {
return fmt.Errorf(`unexpected foreign-key "template_id" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (_q *ChannelMonitorQuery) sqlCount(ctx context.Context) (int, error) {
_spec := _q.querySpec()
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
_spec.Node.Columns = _q.ctx.Fields
if len(_q.ctx.Fields) > 0 {
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
}
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
}
func (_q *ChannelMonitorQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(channelmonitor.Table, channelmonitor.Columns, sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64))
_spec.From = _q.sql
if unique := _q.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if _q.path != nil {
_spec.Unique = true
}
if fields := _q.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, channelmonitor.FieldID)
for i := range fields {
if fields[i] != channelmonitor.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
if _q.withRequestTemplate != nil {
_spec.Node.AddColumnOnce(channelmonitor.FieldTemplateID)
}
}
if ps := _q.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := _q.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := _q.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := _q.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (_q *ChannelMonitorQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(_q.driver.Dialect())
t1 := builder.Table(channelmonitor.Table)
columns := _q.ctx.Fields
if len(columns) == 0 {
columns = channelmonitor.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if _q.sql != nil {
selector = _q.sql
selector.Select(selector.Columns(columns...)...)
}
if _q.ctx.Unique != nil && *_q.ctx.Unique {
selector.Distinct()
}
for _, m := range _q.modifiers {
m(selector)
}
for _, p := range _q.predicates {
p(selector)
}
for _, p := range _q.order {
p(selector)
}
if offset := _q.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := _q.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
// either committed or rolled-back.
func (_q *ChannelMonitorQuery) ForUpdate(opts ...sql.LockOption) *ChannelMonitorQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForUpdate(opts...)
})
return _q
}
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
// on any rows that are read. Other sessions can read the rows, but cannot modify them
// until your transaction commits.
func (_q *ChannelMonitorQuery) ForShare(opts ...sql.LockOption) *ChannelMonitorQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForShare(opts...)
})
return _q
}
// ChannelMonitorGroupBy is the group-by builder for ChannelMonitor entities.
type ChannelMonitorGroupBy struct {
selector
build *ChannelMonitorQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (_g *ChannelMonitorGroupBy) Aggregate(fns ...AggregateFunc) *ChannelMonitorGroupBy {
_g.fns = append(_g.fns, fns...)
return _g
}
// Scan applies the selector query and scans the result into the given value.
func (_g *ChannelMonitorGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
if err := _g.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorQuery, *ChannelMonitorGroupBy](ctx, _g.build, _g, _g.build.inters, v)
}
func (_g *ChannelMonitorGroupBy) sqlScan(ctx context.Context, root *ChannelMonitorQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(_g.fns))
for _, fn := range _g.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
for _, f := range *_g.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*_g.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// ChannelMonitorSelect is the builder for selecting fields of ChannelMonitor entities.
type ChannelMonitorSelect struct {
*ChannelMonitorQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (_s *ChannelMonitorSelect) Aggregate(fns ...AggregateFunc) *ChannelMonitorSelect {
_s.fns = append(_s.fns, fns...)
return _s
}
// Scan applies the selector query and scans the result into the given value.
func (_s *ChannelMonitorSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
if err := _s.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorQuery, *ChannelMonitorSelect](ctx, _s.ChannelMonitorQuery, _s, _s.inters, v)
}
func (_s *ChannelMonitorSelect) sqlScan(ctx context.Context, root *ChannelMonitorQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(_s.fns))
for _, fn := range _s.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*_s.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,278 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
)
// ChannelMonitorDailyRollup is the model entity for the ChannelMonitorDailyRollup schema.
type ChannelMonitorDailyRollup struct {
config `json:"-"`
// ID of the ent.
ID int64 `json:"id,omitempty"`
// MonitorID holds the value of the "monitor_id" field.
MonitorID int64 `json:"monitor_id,omitempty"`
// Model holds the value of the "model" field.
Model string `json:"model,omitempty"`
// BucketDate holds the value of the "bucket_date" field.
BucketDate time.Time `json:"bucket_date,omitempty"`
// TotalChecks holds the value of the "total_checks" field.
TotalChecks int `json:"total_checks,omitempty"`
// OkCount holds the value of the "ok_count" field.
OkCount int `json:"ok_count,omitempty"`
// OperationalCount holds the value of the "operational_count" field.
OperationalCount int `json:"operational_count,omitempty"`
// DegradedCount holds the value of the "degraded_count" field.
DegradedCount int `json:"degraded_count,omitempty"`
// FailedCount holds the value of the "failed_count" field.
FailedCount int `json:"failed_count,omitempty"`
// ErrorCount holds the value of the "error_count" field.
ErrorCount int `json:"error_count,omitempty"`
// SumLatencyMs holds the value of the "sum_latency_ms" field.
SumLatencyMs int64 `json:"sum_latency_ms,omitempty"`
// CountLatency holds the value of the "count_latency" field.
CountLatency int `json:"count_latency,omitempty"`
// SumPingLatencyMs holds the value of the "sum_ping_latency_ms" field.
SumPingLatencyMs int64 `json:"sum_ping_latency_ms,omitempty"`
// CountPingLatency holds the value of the "count_ping_latency" field.
CountPingLatency int `json:"count_ping_latency,omitempty"`
// ComputedAt holds the value of the "computed_at" field.
ComputedAt time.Time `json:"computed_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the ChannelMonitorDailyRollupQuery when eager-loading is set.
Edges ChannelMonitorDailyRollupEdges `json:"edges"`
selectValues sql.SelectValues
}
// ChannelMonitorDailyRollupEdges holds the relations/edges for other nodes in the graph.
type ChannelMonitorDailyRollupEdges struct {
// Monitor holds the value of the monitor edge.
Monitor *ChannelMonitor `json:"monitor,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// MonitorOrErr returns the Monitor value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e ChannelMonitorDailyRollupEdges) MonitorOrErr() (*ChannelMonitor, error) {
if e.Monitor != nil {
return e.Monitor, nil
} else if e.loadedTypes[0] {
return nil, &NotFoundError{label: channelmonitor.Label}
}
return nil, &NotLoadedError{edge: "monitor"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*ChannelMonitorDailyRollup) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case channelmonitordailyrollup.FieldID, channelmonitordailyrollup.FieldMonitorID, channelmonitordailyrollup.FieldTotalChecks, channelmonitordailyrollup.FieldOkCount, channelmonitordailyrollup.FieldOperationalCount, channelmonitordailyrollup.FieldDegradedCount, channelmonitordailyrollup.FieldFailedCount, channelmonitordailyrollup.FieldErrorCount, channelmonitordailyrollup.FieldSumLatencyMs, channelmonitordailyrollup.FieldCountLatency, channelmonitordailyrollup.FieldSumPingLatencyMs, channelmonitordailyrollup.FieldCountPingLatency:
values[i] = new(sql.NullInt64)
case channelmonitordailyrollup.FieldModel:
values[i] = new(sql.NullString)
case channelmonitordailyrollup.FieldBucketDate, channelmonitordailyrollup.FieldComputedAt:
values[i] = new(sql.NullTime)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the ChannelMonitorDailyRollup fields.
func (_m *ChannelMonitorDailyRollup) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case channelmonitordailyrollup.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
_m.ID = int64(value.Int64)
case channelmonitordailyrollup.FieldMonitorID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field monitor_id", values[i])
} else if value.Valid {
_m.MonitorID = value.Int64
}
case channelmonitordailyrollup.FieldModel:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field model", values[i])
} else if value.Valid {
_m.Model = value.String
}
case channelmonitordailyrollup.FieldBucketDate:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field bucket_date", values[i])
} else if value.Valid {
_m.BucketDate = value.Time
}
case channelmonitordailyrollup.FieldTotalChecks:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field total_checks", values[i])
} else if value.Valid {
_m.TotalChecks = int(value.Int64)
}
case channelmonitordailyrollup.FieldOkCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field ok_count", values[i])
} else if value.Valid {
_m.OkCount = int(value.Int64)
}
case channelmonitordailyrollup.FieldOperationalCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field operational_count", values[i])
} else if value.Valid {
_m.OperationalCount = int(value.Int64)
}
case channelmonitordailyrollup.FieldDegradedCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field degraded_count", values[i])
} else if value.Valid {
_m.DegradedCount = int(value.Int64)
}
case channelmonitordailyrollup.FieldFailedCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field failed_count", values[i])
} else if value.Valid {
_m.FailedCount = int(value.Int64)
}
case channelmonitordailyrollup.FieldErrorCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field error_count", values[i])
} else if value.Valid {
_m.ErrorCount = int(value.Int64)
}
case channelmonitordailyrollup.FieldSumLatencyMs:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field sum_latency_ms", values[i])
} else if value.Valid {
_m.SumLatencyMs = value.Int64
}
case channelmonitordailyrollup.FieldCountLatency:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field count_latency", values[i])
} else if value.Valid {
_m.CountLatency = int(value.Int64)
}
case channelmonitordailyrollup.FieldSumPingLatencyMs:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field sum_ping_latency_ms", values[i])
} else if value.Valid {
_m.SumPingLatencyMs = value.Int64
}
case channelmonitordailyrollup.FieldCountPingLatency:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field count_ping_latency", values[i])
} else if value.Valid {
_m.CountPingLatency = int(value.Int64)
}
case channelmonitordailyrollup.FieldComputedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field computed_at", values[i])
} else if value.Valid {
_m.ComputedAt = value.Time
}
default:
_m.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the ChannelMonitorDailyRollup.
// This includes values selected through modifiers, order, etc.
func (_m *ChannelMonitorDailyRollup) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryMonitor queries the "monitor" edge of the ChannelMonitorDailyRollup entity.
func (_m *ChannelMonitorDailyRollup) QueryMonitor() *ChannelMonitorQuery {
return NewChannelMonitorDailyRollupClient(_m.config).QueryMonitor(_m)
}
// Update returns a builder for updating this ChannelMonitorDailyRollup.
// Note that you need to call ChannelMonitorDailyRollup.Unwrap() before calling this method if this ChannelMonitorDailyRollup
// was returned from a transaction, and the transaction was committed or rolled back.
func (_m *ChannelMonitorDailyRollup) Update() *ChannelMonitorDailyRollupUpdateOne {
return NewChannelMonitorDailyRollupClient(_m.config).UpdateOne(_m)
}
// Unwrap unwraps the ChannelMonitorDailyRollup entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (_m *ChannelMonitorDailyRollup) Unwrap() *ChannelMonitorDailyRollup {
_tx, ok := _m.config.driver.(*txDriver)
if !ok {
panic("ent: ChannelMonitorDailyRollup is not a transactional entity")
}
_m.config.driver = _tx.drv
return _m
}
// String implements the fmt.Stringer.
func (_m *ChannelMonitorDailyRollup) String() string {
var builder strings.Builder
builder.WriteString("ChannelMonitorDailyRollup(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("monitor_id=")
builder.WriteString(fmt.Sprintf("%v", _m.MonitorID))
builder.WriteString(", ")
builder.WriteString("model=")
builder.WriteString(_m.Model)
builder.WriteString(", ")
builder.WriteString("bucket_date=")
builder.WriteString(_m.BucketDate.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("total_checks=")
builder.WriteString(fmt.Sprintf("%v", _m.TotalChecks))
builder.WriteString(", ")
builder.WriteString("ok_count=")
builder.WriteString(fmt.Sprintf("%v", _m.OkCount))
builder.WriteString(", ")
builder.WriteString("operational_count=")
builder.WriteString(fmt.Sprintf("%v", _m.OperationalCount))
builder.WriteString(", ")
builder.WriteString("degraded_count=")
builder.WriteString(fmt.Sprintf("%v", _m.DegradedCount))
builder.WriteString(", ")
builder.WriteString("failed_count=")
builder.WriteString(fmt.Sprintf("%v", _m.FailedCount))
builder.WriteString(", ")
builder.WriteString("error_count=")
builder.WriteString(fmt.Sprintf("%v", _m.ErrorCount))
builder.WriteString(", ")
builder.WriteString("sum_latency_ms=")
builder.WriteString(fmt.Sprintf("%v", _m.SumLatencyMs))
builder.WriteString(", ")
builder.WriteString("count_latency=")
builder.WriteString(fmt.Sprintf("%v", _m.CountLatency))
builder.WriteString(", ")
builder.WriteString("sum_ping_latency_ms=")
builder.WriteString(fmt.Sprintf("%v", _m.SumPingLatencyMs))
builder.WriteString(", ")
builder.WriteString("count_ping_latency=")
builder.WriteString(fmt.Sprintf("%v", _m.CountPingLatency))
builder.WriteString(", ")
builder.WriteString("computed_at=")
builder.WriteString(_m.ComputedAt.Format(time.ANSIC))
builder.WriteByte(')')
return builder.String()
}
// ChannelMonitorDailyRollups is a parsable slice of ChannelMonitorDailyRollup.
type ChannelMonitorDailyRollups []*ChannelMonitorDailyRollup

View File

@ -0,0 +1,206 @@
// Code generated by ent, DO NOT EDIT.
package channelmonitordailyrollup
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
const (
// Label holds the string label denoting the channelmonitordailyrollup type in the database.
Label = "channel_monitor_daily_rollup"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldMonitorID holds the string denoting the monitor_id field in the database.
FieldMonitorID = "monitor_id"
// FieldModel holds the string denoting the model field in the database.
FieldModel = "model"
// FieldBucketDate holds the string denoting the bucket_date field in the database.
FieldBucketDate = "bucket_date"
// FieldTotalChecks holds the string denoting the total_checks field in the database.
FieldTotalChecks = "total_checks"
// FieldOkCount holds the string denoting the ok_count field in the database.
FieldOkCount = "ok_count"
// FieldOperationalCount holds the string denoting the operational_count field in the database.
FieldOperationalCount = "operational_count"
// FieldDegradedCount holds the string denoting the degraded_count field in the database.
FieldDegradedCount = "degraded_count"
// FieldFailedCount holds the string denoting the failed_count field in the database.
FieldFailedCount = "failed_count"
// FieldErrorCount holds the string denoting the error_count field in the database.
FieldErrorCount = "error_count"
// FieldSumLatencyMs holds the string denoting the sum_latency_ms field in the database.
FieldSumLatencyMs = "sum_latency_ms"
// FieldCountLatency holds the string denoting the count_latency field in the database.
FieldCountLatency = "count_latency"
// FieldSumPingLatencyMs holds the string denoting the sum_ping_latency_ms field in the database.
FieldSumPingLatencyMs = "sum_ping_latency_ms"
// FieldCountPingLatency holds the string denoting the count_ping_latency field in the database.
FieldCountPingLatency = "count_ping_latency"
// FieldComputedAt holds the string denoting the computed_at field in the database.
FieldComputedAt = "computed_at"
// EdgeMonitor holds the string denoting the monitor edge name in mutations.
EdgeMonitor = "monitor"
// Table holds the table name of the channelmonitordailyrollup in the database.
Table = "channel_monitor_daily_rollups"
// MonitorTable is the table that holds the monitor relation/edge.
MonitorTable = "channel_monitor_daily_rollups"
// MonitorInverseTable is the table name for the ChannelMonitor entity.
// It exists in this package in order to avoid circular dependency with the "channelmonitor" package.
MonitorInverseTable = "channel_monitors"
// MonitorColumn is the table column denoting the monitor relation/edge.
MonitorColumn = "monitor_id"
)
// Columns holds all SQL columns for channelmonitordailyrollup fields.
var Columns = []string{
FieldID,
FieldMonitorID,
FieldModel,
FieldBucketDate,
FieldTotalChecks,
FieldOkCount,
FieldOperationalCount,
FieldDegradedCount,
FieldFailedCount,
FieldErrorCount,
FieldSumLatencyMs,
FieldCountLatency,
FieldSumPingLatencyMs,
FieldCountPingLatency,
FieldComputedAt,
}
// 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 (
// ModelValidator is a validator for the "model" field. It is called by the builders before save.
ModelValidator func(string) error
// DefaultTotalChecks holds the default value on creation for the "total_checks" field.
DefaultTotalChecks int
// DefaultOkCount holds the default value on creation for the "ok_count" field.
DefaultOkCount int
// DefaultOperationalCount holds the default value on creation for the "operational_count" field.
DefaultOperationalCount int
// DefaultDegradedCount holds the default value on creation for the "degraded_count" field.
DefaultDegradedCount int
// DefaultFailedCount holds the default value on creation for the "failed_count" field.
DefaultFailedCount int
// DefaultErrorCount holds the default value on creation for the "error_count" field.
DefaultErrorCount int
// DefaultSumLatencyMs holds the default value on creation for the "sum_latency_ms" field.
DefaultSumLatencyMs int64
// DefaultCountLatency holds the default value on creation for the "count_latency" field.
DefaultCountLatency int
// DefaultSumPingLatencyMs holds the default value on creation for the "sum_ping_latency_ms" field.
DefaultSumPingLatencyMs int64
// DefaultCountPingLatency holds the default value on creation for the "count_ping_latency" field.
DefaultCountPingLatency int
// DefaultComputedAt holds the default value on creation for the "computed_at" field.
DefaultComputedAt func() time.Time
// UpdateDefaultComputedAt holds the default value on update for the "computed_at" field.
UpdateDefaultComputedAt func() time.Time
)
// OrderOption defines the ordering options for the ChannelMonitorDailyRollup 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()
}
// ByMonitorID orders the results by the monitor_id field.
func ByMonitorID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldMonitorID, opts...).ToFunc()
}
// ByModel orders the results by the model field.
func ByModel(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldModel, opts...).ToFunc()
}
// ByBucketDate orders the results by the bucket_date field.
func ByBucketDate(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldBucketDate, opts...).ToFunc()
}
// ByTotalChecks orders the results by the total_checks field.
func ByTotalChecks(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldTotalChecks, opts...).ToFunc()
}
// ByOkCount orders the results by the ok_count field.
func ByOkCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldOkCount, opts...).ToFunc()
}
// ByOperationalCount orders the results by the operational_count field.
func ByOperationalCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldOperationalCount, opts...).ToFunc()
}
// ByDegradedCount orders the results by the degraded_count field.
func ByDegradedCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDegradedCount, opts...).ToFunc()
}
// ByFailedCount orders the results by the failed_count field.
func ByFailedCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldFailedCount, opts...).ToFunc()
}
// ByErrorCount orders the results by the error_count field.
func ByErrorCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldErrorCount, opts...).ToFunc()
}
// BySumLatencyMs orders the results by the sum_latency_ms field.
func BySumLatencyMs(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSumLatencyMs, opts...).ToFunc()
}
// ByCountLatency orders the results by the count_latency field.
func ByCountLatency(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCountLatency, opts...).ToFunc()
}
// BySumPingLatencyMs orders the results by the sum_ping_latency_ms field.
func BySumPingLatencyMs(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSumPingLatencyMs, opts...).ToFunc()
}
// ByCountPingLatency orders the results by the count_ping_latency field.
func ByCountPingLatency(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCountPingLatency, opts...).ToFunc()
}
// ByComputedAt orders the results by the computed_at field.
func ByComputedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldComputedAt, opts...).ToFunc()
}
// ByMonitorField orders the results by monitor field.
func ByMonitorField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newMonitorStep(), sql.OrderByField(field, opts...))
}
}
func newMonitorStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(MonitorInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, MonitorTable, MonitorColumn),
)
}

View File

@ -0,0 +1,729 @@
// Code generated by ent, DO NOT EDIT.
package channelmonitordailyrollup
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldID, id))
}
// MonitorID applies equality check predicate on the "monitor_id" field. It's identical to MonitorIDEQ.
func MonitorID(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldMonitorID, v))
}
// Model applies equality check predicate on the "model" field. It's identical to ModelEQ.
func Model(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldModel, v))
}
// BucketDate applies equality check predicate on the "bucket_date" field. It's identical to BucketDateEQ.
func BucketDate(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldBucketDate, v))
}
// TotalChecks applies equality check predicate on the "total_checks" field. It's identical to TotalChecksEQ.
func TotalChecks(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldTotalChecks, v))
}
// OkCount applies equality check predicate on the "ok_count" field. It's identical to OkCountEQ.
func OkCount(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldOkCount, v))
}
// OperationalCount applies equality check predicate on the "operational_count" field. It's identical to OperationalCountEQ.
func OperationalCount(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldOperationalCount, v))
}
// DegradedCount applies equality check predicate on the "degraded_count" field. It's identical to DegradedCountEQ.
func DegradedCount(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldDegradedCount, v))
}
// FailedCount applies equality check predicate on the "failed_count" field. It's identical to FailedCountEQ.
func FailedCount(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldFailedCount, v))
}
// ErrorCount applies equality check predicate on the "error_count" field. It's identical to ErrorCountEQ.
func ErrorCount(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldErrorCount, v))
}
// SumLatencyMs applies equality check predicate on the "sum_latency_ms" field. It's identical to SumLatencyMsEQ.
func SumLatencyMs(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldSumLatencyMs, v))
}
// CountLatency applies equality check predicate on the "count_latency" field. It's identical to CountLatencyEQ.
func CountLatency(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldCountLatency, v))
}
// SumPingLatencyMs applies equality check predicate on the "sum_ping_latency_ms" field. It's identical to SumPingLatencyMsEQ.
func SumPingLatencyMs(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldSumPingLatencyMs, v))
}
// CountPingLatency applies equality check predicate on the "count_ping_latency" field. It's identical to CountPingLatencyEQ.
func CountPingLatency(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldCountPingLatency, v))
}
// ComputedAt applies equality check predicate on the "computed_at" field. It's identical to ComputedAtEQ.
func ComputedAt(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldComputedAt, v))
}
// MonitorIDEQ applies the EQ predicate on the "monitor_id" field.
func MonitorIDEQ(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldMonitorID, v))
}
// MonitorIDNEQ applies the NEQ predicate on the "monitor_id" field.
func MonitorIDNEQ(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldMonitorID, v))
}
// MonitorIDIn applies the In predicate on the "monitor_id" field.
func MonitorIDIn(vs ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldMonitorID, vs...))
}
// MonitorIDNotIn applies the NotIn predicate on the "monitor_id" field.
func MonitorIDNotIn(vs ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldMonitorID, vs...))
}
// ModelEQ applies the EQ predicate on the "model" field.
func ModelEQ(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldModel, v))
}
// ModelNEQ applies the NEQ predicate on the "model" field.
func ModelNEQ(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldModel, v))
}
// ModelIn applies the In predicate on the "model" field.
func ModelIn(vs ...string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldModel, vs...))
}
// ModelNotIn applies the NotIn predicate on the "model" field.
func ModelNotIn(vs ...string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldModel, vs...))
}
// ModelGT applies the GT predicate on the "model" field.
func ModelGT(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldModel, v))
}
// ModelGTE applies the GTE predicate on the "model" field.
func ModelGTE(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldModel, v))
}
// ModelLT applies the LT predicate on the "model" field.
func ModelLT(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldModel, v))
}
// ModelLTE applies the LTE predicate on the "model" field.
func ModelLTE(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldModel, v))
}
// ModelContains applies the Contains predicate on the "model" field.
func ModelContains(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldContains(FieldModel, v))
}
// ModelHasPrefix applies the HasPrefix predicate on the "model" field.
func ModelHasPrefix(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldHasPrefix(FieldModel, v))
}
// ModelHasSuffix applies the HasSuffix predicate on the "model" field.
func ModelHasSuffix(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldHasSuffix(FieldModel, v))
}
// ModelEqualFold applies the EqualFold predicate on the "model" field.
func ModelEqualFold(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEqualFold(FieldModel, v))
}
// ModelContainsFold applies the ContainsFold predicate on the "model" field.
func ModelContainsFold(v string) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldContainsFold(FieldModel, v))
}
// BucketDateEQ applies the EQ predicate on the "bucket_date" field.
func BucketDateEQ(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldBucketDate, v))
}
// BucketDateNEQ applies the NEQ predicate on the "bucket_date" field.
func BucketDateNEQ(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldBucketDate, v))
}
// BucketDateIn applies the In predicate on the "bucket_date" field.
func BucketDateIn(vs ...time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldBucketDate, vs...))
}
// BucketDateNotIn applies the NotIn predicate on the "bucket_date" field.
func BucketDateNotIn(vs ...time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldBucketDate, vs...))
}
// BucketDateGT applies the GT predicate on the "bucket_date" field.
func BucketDateGT(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldBucketDate, v))
}
// BucketDateGTE applies the GTE predicate on the "bucket_date" field.
func BucketDateGTE(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldBucketDate, v))
}
// BucketDateLT applies the LT predicate on the "bucket_date" field.
func BucketDateLT(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldBucketDate, v))
}
// BucketDateLTE applies the LTE predicate on the "bucket_date" field.
func BucketDateLTE(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldBucketDate, v))
}
// TotalChecksEQ applies the EQ predicate on the "total_checks" field.
func TotalChecksEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldTotalChecks, v))
}
// TotalChecksNEQ applies the NEQ predicate on the "total_checks" field.
func TotalChecksNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldTotalChecks, v))
}
// TotalChecksIn applies the In predicate on the "total_checks" field.
func TotalChecksIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldTotalChecks, vs...))
}
// TotalChecksNotIn applies the NotIn predicate on the "total_checks" field.
func TotalChecksNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldTotalChecks, vs...))
}
// TotalChecksGT applies the GT predicate on the "total_checks" field.
func TotalChecksGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldTotalChecks, v))
}
// TotalChecksGTE applies the GTE predicate on the "total_checks" field.
func TotalChecksGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldTotalChecks, v))
}
// TotalChecksLT applies the LT predicate on the "total_checks" field.
func TotalChecksLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldTotalChecks, v))
}
// TotalChecksLTE applies the LTE predicate on the "total_checks" field.
func TotalChecksLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldTotalChecks, v))
}
// OkCountEQ applies the EQ predicate on the "ok_count" field.
func OkCountEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldOkCount, v))
}
// OkCountNEQ applies the NEQ predicate on the "ok_count" field.
func OkCountNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldOkCount, v))
}
// OkCountIn applies the In predicate on the "ok_count" field.
func OkCountIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldOkCount, vs...))
}
// OkCountNotIn applies the NotIn predicate on the "ok_count" field.
func OkCountNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldOkCount, vs...))
}
// OkCountGT applies the GT predicate on the "ok_count" field.
func OkCountGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldOkCount, v))
}
// OkCountGTE applies the GTE predicate on the "ok_count" field.
func OkCountGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldOkCount, v))
}
// OkCountLT applies the LT predicate on the "ok_count" field.
func OkCountLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldOkCount, v))
}
// OkCountLTE applies the LTE predicate on the "ok_count" field.
func OkCountLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldOkCount, v))
}
// OperationalCountEQ applies the EQ predicate on the "operational_count" field.
func OperationalCountEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldOperationalCount, v))
}
// OperationalCountNEQ applies the NEQ predicate on the "operational_count" field.
func OperationalCountNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldOperationalCount, v))
}
// OperationalCountIn applies the In predicate on the "operational_count" field.
func OperationalCountIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldOperationalCount, vs...))
}
// OperationalCountNotIn applies the NotIn predicate on the "operational_count" field.
func OperationalCountNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldOperationalCount, vs...))
}
// OperationalCountGT applies the GT predicate on the "operational_count" field.
func OperationalCountGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldOperationalCount, v))
}
// OperationalCountGTE applies the GTE predicate on the "operational_count" field.
func OperationalCountGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldOperationalCount, v))
}
// OperationalCountLT applies the LT predicate on the "operational_count" field.
func OperationalCountLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldOperationalCount, v))
}
// OperationalCountLTE applies the LTE predicate on the "operational_count" field.
func OperationalCountLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldOperationalCount, v))
}
// DegradedCountEQ applies the EQ predicate on the "degraded_count" field.
func DegradedCountEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldDegradedCount, v))
}
// DegradedCountNEQ applies the NEQ predicate on the "degraded_count" field.
func DegradedCountNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldDegradedCount, v))
}
// DegradedCountIn applies the In predicate on the "degraded_count" field.
func DegradedCountIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldDegradedCount, vs...))
}
// DegradedCountNotIn applies the NotIn predicate on the "degraded_count" field.
func DegradedCountNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldDegradedCount, vs...))
}
// DegradedCountGT applies the GT predicate on the "degraded_count" field.
func DegradedCountGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldDegradedCount, v))
}
// DegradedCountGTE applies the GTE predicate on the "degraded_count" field.
func DegradedCountGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldDegradedCount, v))
}
// DegradedCountLT applies the LT predicate on the "degraded_count" field.
func DegradedCountLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldDegradedCount, v))
}
// DegradedCountLTE applies the LTE predicate on the "degraded_count" field.
func DegradedCountLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldDegradedCount, v))
}
// FailedCountEQ applies the EQ predicate on the "failed_count" field.
func FailedCountEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldFailedCount, v))
}
// FailedCountNEQ applies the NEQ predicate on the "failed_count" field.
func FailedCountNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldFailedCount, v))
}
// FailedCountIn applies the In predicate on the "failed_count" field.
func FailedCountIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldFailedCount, vs...))
}
// FailedCountNotIn applies the NotIn predicate on the "failed_count" field.
func FailedCountNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldFailedCount, vs...))
}
// FailedCountGT applies the GT predicate on the "failed_count" field.
func FailedCountGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldFailedCount, v))
}
// FailedCountGTE applies the GTE predicate on the "failed_count" field.
func FailedCountGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldFailedCount, v))
}
// FailedCountLT applies the LT predicate on the "failed_count" field.
func FailedCountLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldFailedCount, v))
}
// FailedCountLTE applies the LTE predicate on the "failed_count" field.
func FailedCountLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldFailedCount, v))
}
// ErrorCountEQ applies the EQ predicate on the "error_count" field.
func ErrorCountEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldErrorCount, v))
}
// ErrorCountNEQ applies the NEQ predicate on the "error_count" field.
func ErrorCountNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldErrorCount, v))
}
// ErrorCountIn applies the In predicate on the "error_count" field.
func ErrorCountIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldErrorCount, vs...))
}
// ErrorCountNotIn applies the NotIn predicate on the "error_count" field.
func ErrorCountNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldErrorCount, vs...))
}
// ErrorCountGT applies the GT predicate on the "error_count" field.
func ErrorCountGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldErrorCount, v))
}
// ErrorCountGTE applies the GTE predicate on the "error_count" field.
func ErrorCountGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldErrorCount, v))
}
// ErrorCountLT applies the LT predicate on the "error_count" field.
func ErrorCountLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldErrorCount, v))
}
// ErrorCountLTE applies the LTE predicate on the "error_count" field.
func ErrorCountLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldErrorCount, v))
}
// SumLatencyMsEQ applies the EQ predicate on the "sum_latency_ms" field.
func SumLatencyMsEQ(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldSumLatencyMs, v))
}
// SumLatencyMsNEQ applies the NEQ predicate on the "sum_latency_ms" field.
func SumLatencyMsNEQ(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldSumLatencyMs, v))
}
// SumLatencyMsIn applies the In predicate on the "sum_latency_ms" field.
func SumLatencyMsIn(vs ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldSumLatencyMs, vs...))
}
// SumLatencyMsNotIn applies the NotIn predicate on the "sum_latency_ms" field.
func SumLatencyMsNotIn(vs ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldSumLatencyMs, vs...))
}
// SumLatencyMsGT applies the GT predicate on the "sum_latency_ms" field.
func SumLatencyMsGT(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldSumLatencyMs, v))
}
// SumLatencyMsGTE applies the GTE predicate on the "sum_latency_ms" field.
func SumLatencyMsGTE(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldSumLatencyMs, v))
}
// SumLatencyMsLT applies the LT predicate on the "sum_latency_ms" field.
func SumLatencyMsLT(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldSumLatencyMs, v))
}
// SumLatencyMsLTE applies the LTE predicate on the "sum_latency_ms" field.
func SumLatencyMsLTE(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldSumLatencyMs, v))
}
// CountLatencyEQ applies the EQ predicate on the "count_latency" field.
func CountLatencyEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldCountLatency, v))
}
// CountLatencyNEQ applies the NEQ predicate on the "count_latency" field.
func CountLatencyNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldCountLatency, v))
}
// CountLatencyIn applies the In predicate on the "count_latency" field.
func CountLatencyIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldCountLatency, vs...))
}
// CountLatencyNotIn applies the NotIn predicate on the "count_latency" field.
func CountLatencyNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldCountLatency, vs...))
}
// CountLatencyGT applies the GT predicate on the "count_latency" field.
func CountLatencyGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldCountLatency, v))
}
// CountLatencyGTE applies the GTE predicate on the "count_latency" field.
func CountLatencyGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldCountLatency, v))
}
// CountLatencyLT applies the LT predicate on the "count_latency" field.
func CountLatencyLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldCountLatency, v))
}
// CountLatencyLTE applies the LTE predicate on the "count_latency" field.
func CountLatencyLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldCountLatency, v))
}
// SumPingLatencyMsEQ applies the EQ predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsEQ(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldSumPingLatencyMs, v))
}
// SumPingLatencyMsNEQ applies the NEQ predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsNEQ(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldSumPingLatencyMs, v))
}
// SumPingLatencyMsIn applies the In predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsIn(vs ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldSumPingLatencyMs, vs...))
}
// SumPingLatencyMsNotIn applies the NotIn predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsNotIn(vs ...int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldSumPingLatencyMs, vs...))
}
// SumPingLatencyMsGT applies the GT predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsGT(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldSumPingLatencyMs, v))
}
// SumPingLatencyMsGTE applies the GTE predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsGTE(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldSumPingLatencyMs, v))
}
// SumPingLatencyMsLT applies the LT predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsLT(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldSumPingLatencyMs, v))
}
// SumPingLatencyMsLTE applies the LTE predicate on the "sum_ping_latency_ms" field.
func SumPingLatencyMsLTE(v int64) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldSumPingLatencyMs, v))
}
// CountPingLatencyEQ applies the EQ predicate on the "count_ping_latency" field.
func CountPingLatencyEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldCountPingLatency, v))
}
// CountPingLatencyNEQ applies the NEQ predicate on the "count_ping_latency" field.
func CountPingLatencyNEQ(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldCountPingLatency, v))
}
// CountPingLatencyIn applies the In predicate on the "count_ping_latency" field.
func CountPingLatencyIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldCountPingLatency, vs...))
}
// CountPingLatencyNotIn applies the NotIn predicate on the "count_ping_latency" field.
func CountPingLatencyNotIn(vs ...int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldCountPingLatency, vs...))
}
// CountPingLatencyGT applies the GT predicate on the "count_ping_latency" field.
func CountPingLatencyGT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldCountPingLatency, v))
}
// CountPingLatencyGTE applies the GTE predicate on the "count_ping_latency" field.
func CountPingLatencyGTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldCountPingLatency, v))
}
// CountPingLatencyLT applies the LT predicate on the "count_ping_latency" field.
func CountPingLatencyLT(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldCountPingLatency, v))
}
// CountPingLatencyLTE applies the LTE predicate on the "count_ping_latency" field.
func CountPingLatencyLTE(v int) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldCountPingLatency, v))
}
// ComputedAtEQ applies the EQ predicate on the "computed_at" field.
func ComputedAtEQ(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldEQ(FieldComputedAt, v))
}
// ComputedAtNEQ applies the NEQ predicate on the "computed_at" field.
func ComputedAtNEQ(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNEQ(FieldComputedAt, v))
}
// ComputedAtIn applies the In predicate on the "computed_at" field.
func ComputedAtIn(vs ...time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldIn(FieldComputedAt, vs...))
}
// ComputedAtNotIn applies the NotIn predicate on the "computed_at" field.
func ComputedAtNotIn(vs ...time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldNotIn(FieldComputedAt, vs...))
}
// ComputedAtGT applies the GT predicate on the "computed_at" field.
func ComputedAtGT(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGT(FieldComputedAt, v))
}
// ComputedAtGTE applies the GTE predicate on the "computed_at" field.
func ComputedAtGTE(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldGTE(FieldComputedAt, v))
}
// ComputedAtLT applies the LT predicate on the "computed_at" field.
func ComputedAtLT(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLT(FieldComputedAt, v))
}
// ComputedAtLTE applies the LTE predicate on the "computed_at" field.
func ComputedAtLTE(v time.Time) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.FieldLTE(FieldComputedAt, v))
}
// HasMonitor applies the HasEdge predicate on the "monitor" edge.
func HasMonitor() predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, MonitorTable, MonitorColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasMonitorWith applies the HasEdge predicate on the "monitor" edge with a given conditions (other predicates).
func HasMonitorWith(preds ...predicate.ChannelMonitor) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(func(s *sql.Selector) {
step := newMonitorStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.ChannelMonitorDailyRollup) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.ChannelMonitorDailyRollup) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.ChannelMonitorDailyRollup) predicate.ChannelMonitorDailyRollup {
return predicate.ChannelMonitorDailyRollup(sql.NotPredicates(p))
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorDailyRollupDelete is the builder for deleting a ChannelMonitorDailyRollup entity.
type ChannelMonitorDailyRollupDelete struct {
config
hooks []Hook
mutation *ChannelMonitorDailyRollupMutation
}
// Where appends a list predicates to the ChannelMonitorDailyRollupDelete builder.
func (_d *ChannelMonitorDailyRollupDelete) Where(ps ...predicate.ChannelMonitorDailyRollup) *ChannelMonitorDailyRollupDelete {
_d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (_d *ChannelMonitorDailyRollupDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorDailyRollupDelete) ExecX(ctx context.Context) int {
n, err := _d.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (_d *ChannelMonitorDailyRollupDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(channelmonitordailyrollup.Table, sqlgraph.NewFieldSpec(channelmonitordailyrollup.FieldID, field.TypeInt64))
if ps := _d.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
_d.mutation.done = true
return affected, err
}
// ChannelMonitorDailyRollupDeleteOne is the builder for deleting a single ChannelMonitorDailyRollup entity.
type ChannelMonitorDailyRollupDeleteOne struct {
_d *ChannelMonitorDailyRollupDelete
}
// Where appends a list predicates to the ChannelMonitorDailyRollupDelete builder.
func (_d *ChannelMonitorDailyRollupDeleteOne) Where(ps ...predicate.ChannelMonitorDailyRollup) *ChannelMonitorDailyRollupDeleteOne {
_d._d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query.
func (_d *ChannelMonitorDailyRollupDeleteOne) Exec(ctx context.Context) error {
n, err := _d._d.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{channelmonitordailyrollup.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorDailyRollupDeleteOne) ExecX(ctx context.Context) {
if err := _d.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,643 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"fmt"
"math"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorDailyRollupQuery is the builder for querying ChannelMonitorDailyRollup entities.
type ChannelMonitorDailyRollupQuery struct {
config
ctx *QueryContext
order []channelmonitordailyrollup.OrderOption
inters []Interceptor
predicates []predicate.ChannelMonitorDailyRollup
withMonitor *ChannelMonitorQuery
modifiers []func(*sql.Selector)
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the ChannelMonitorDailyRollupQuery builder.
func (_q *ChannelMonitorDailyRollupQuery) Where(ps ...predicate.ChannelMonitorDailyRollup) *ChannelMonitorDailyRollupQuery {
_q.predicates = append(_q.predicates, ps...)
return _q
}
// Limit the number of records to be returned by this query.
func (_q *ChannelMonitorDailyRollupQuery) Limit(limit int) *ChannelMonitorDailyRollupQuery {
_q.ctx.Limit = &limit
return _q
}
// Offset to start from.
func (_q *ChannelMonitorDailyRollupQuery) Offset(offset int) *ChannelMonitorDailyRollupQuery {
_q.ctx.Offset = &offset
return _q
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (_q *ChannelMonitorDailyRollupQuery) Unique(unique bool) *ChannelMonitorDailyRollupQuery {
_q.ctx.Unique = &unique
return _q
}
// Order specifies how the records should be ordered.
func (_q *ChannelMonitorDailyRollupQuery) Order(o ...channelmonitordailyrollup.OrderOption) *ChannelMonitorDailyRollupQuery {
_q.order = append(_q.order, o...)
return _q
}
// QueryMonitor chains the current query on the "monitor" edge.
func (_q *ChannelMonitorDailyRollupQuery) QueryMonitor() *ChannelMonitorQuery {
query := (&ChannelMonitorClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitordailyrollup.Table, channelmonitordailyrollup.FieldID, selector),
sqlgraph.To(channelmonitor.Table, channelmonitor.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, channelmonitordailyrollup.MonitorTable, channelmonitordailyrollup.MonitorColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first ChannelMonitorDailyRollup entity from the query.
// Returns a *NotFoundError when no ChannelMonitorDailyRollup was found.
func (_q *ChannelMonitorDailyRollupQuery) First(ctx context.Context) (*ChannelMonitorDailyRollup, error) {
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{channelmonitordailyrollup.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) FirstX(ctx context.Context) *ChannelMonitorDailyRollup {
node, err := _q.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first ChannelMonitorDailyRollup ID from the query.
// Returns a *NotFoundError when no ChannelMonitorDailyRollup ID was found.
func (_q *ChannelMonitorDailyRollupQuery) FirstID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{channelmonitordailyrollup.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) FirstIDX(ctx context.Context) int64 {
id, err := _q.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single ChannelMonitorDailyRollup entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one ChannelMonitorDailyRollup entity is found.
// Returns a *NotFoundError when no ChannelMonitorDailyRollup entities are found.
func (_q *ChannelMonitorDailyRollupQuery) Only(ctx context.Context) (*ChannelMonitorDailyRollup, error) {
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{channelmonitordailyrollup.Label}
default:
return nil, &NotSingularError{channelmonitordailyrollup.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) OnlyX(ctx context.Context) *ChannelMonitorDailyRollup {
node, err := _q.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only ChannelMonitorDailyRollup ID in the query.
// Returns a *NotSingularError when more than one ChannelMonitorDailyRollup ID is found.
// Returns a *NotFoundError when no entities are found.
func (_q *ChannelMonitorDailyRollupQuery) OnlyID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{channelmonitordailyrollup.Label}
default:
err = &NotSingularError{channelmonitordailyrollup.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) OnlyIDX(ctx context.Context) int64 {
id, err := _q.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of ChannelMonitorDailyRollups.
func (_q *ChannelMonitorDailyRollupQuery) All(ctx context.Context) ([]*ChannelMonitorDailyRollup, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*ChannelMonitorDailyRollup, *ChannelMonitorDailyRollupQuery]()
return withInterceptors[[]*ChannelMonitorDailyRollup](ctx, _q, qr, _q.inters)
}
// AllX is like All, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) AllX(ctx context.Context) []*ChannelMonitorDailyRollup {
nodes, err := _q.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of ChannelMonitorDailyRollup IDs.
func (_q *ChannelMonitorDailyRollupQuery) IDs(ctx context.Context) (ids []int64, err error) {
if _q.ctx.Unique == nil && _q.path != nil {
_q.Unique(true)
}
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
if err = _q.Select(channelmonitordailyrollup.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) IDsX(ctx context.Context) []int64 {
ids, err := _q.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (_q *ChannelMonitorDailyRollupQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
if err := _q.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, _q, querierCount[*ChannelMonitorDailyRollupQuery](), _q.inters)
}
// CountX is like Count, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) CountX(ctx context.Context) int {
count, err := _q.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (_q *ChannelMonitorDailyRollupQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
switch _, err := _q.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (_q *ChannelMonitorDailyRollupQuery) ExistX(ctx context.Context) bool {
exist, err := _q.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the ChannelMonitorDailyRollupQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (_q *ChannelMonitorDailyRollupQuery) Clone() *ChannelMonitorDailyRollupQuery {
if _q == nil {
return nil
}
return &ChannelMonitorDailyRollupQuery{
config: _q.config,
ctx: _q.ctx.Clone(),
order: append([]channelmonitordailyrollup.OrderOption{}, _q.order...),
inters: append([]Interceptor{}, _q.inters...),
predicates: append([]predicate.ChannelMonitorDailyRollup{}, _q.predicates...),
withMonitor: _q.withMonitor.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
path: _q.path,
}
}
// WithMonitor tells the query-builder to eager-load the nodes that are connected to
// the "monitor" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *ChannelMonitorDailyRollupQuery) WithMonitor(opts ...func(*ChannelMonitorQuery)) *ChannelMonitorDailyRollupQuery {
query := (&ChannelMonitorClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withMonitor = query
return _q
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// MonitorID int64 `json:"monitor_id,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.ChannelMonitorDailyRollup.Query().
// GroupBy(channelmonitordailyrollup.FieldMonitorID).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (_q *ChannelMonitorDailyRollupQuery) GroupBy(field string, fields ...string) *ChannelMonitorDailyRollupGroupBy {
_q.ctx.Fields = append([]string{field}, fields...)
grbuild := &ChannelMonitorDailyRollupGroupBy{build: _q}
grbuild.flds = &_q.ctx.Fields
grbuild.label = channelmonitordailyrollup.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// MonitorID int64 `json:"monitor_id,omitempty"`
// }
//
// client.ChannelMonitorDailyRollup.Query().
// Select(channelmonitordailyrollup.FieldMonitorID).
// Scan(ctx, &v)
func (_q *ChannelMonitorDailyRollupQuery) Select(fields ...string) *ChannelMonitorDailyRollupSelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
sbuild := &ChannelMonitorDailyRollupSelect{ChannelMonitorDailyRollupQuery: _q}
sbuild.label = channelmonitordailyrollup.Label
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a ChannelMonitorDailyRollupSelect configured with the given aggregations.
func (_q *ChannelMonitorDailyRollupQuery) Aggregate(fns ...AggregateFunc) *ChannelMonitorDailyRollupSelect {
return _q.Select().Aggregate(fns...)
}
func (_q *ChannelMonitorDailyRollupQuery) prepareQuery(ctx context.Context) error {
for _, inter := range _q.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, _q); err != nil {
return err
}
}
}
for _, f := range _q.ctx.Fields {
if !channelmonitordailyrollup.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if _q.path != nil {
prev, err := _q.path(ctx)
if err != nil {
return err
}
_q.sql = prev
}
return nil
}
func (_q *ChannelMonitorDailyRollupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ChannelMonitorDailyRollup, error) {
var (
nodes = []*ChannelMonitorDailyRollup{}
_spec = _q.querySpec()
loadedTypes = [1]bool{
_q.withMonitor != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*ChannelMonitorDailyRollup).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &ChannelMonitorDailyRollup{config: _q.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := _q.withMonitor; query != nil {
if err := _q.loadMonitor(ctx, query, nodes, nil,
func(n *ChannelMonitorDailyRollup, e *ChannelMonitor) { n.Edges.Monitor = e }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (_q *ChannelMonitorDailyRollupQuery) loadMonitor(ctx context.Context, query *ChannelMonitorQuery, nodes []*ChannelMonitorDailyRollup, init func(*ChannelMonitorDailyRollup), assign func(*ChannelMonitorDailyRollup, *ChannelMonitor)) error {
ids := make([]int64, 0, len(nodes))
nodeids := make(map[int64][]*ChannelMonitorDailyRollup)
for i := range nodes {
fk := nodes[i].MonitorID
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(channelmonitor.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nodeids[n.ID]
if !ok {
return fmt.Errorf(`unexpected foreign-key "monitor_id" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (_q *ChannelMonitorDailyRollupQuery) sqlCount(ctx context.Context) (int, error) {
_spec := _q.querySpec()
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
_spec.Node.Columns = _q.ctx.Fields
if len(_q.ctx.Fields) > 0 {
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
}
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
}
func (_q *ChannelMonitorDailyRollupQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(channelmonitordailyrollup.Table, channelmonitordailyrollup.Columns, sqlgraph.NewFieldSpec(channelmonitordailyrollup.FieldID, field.TypeInt64))
_spec.From = _q.sql
if unique := _q.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if _q.path != nil {
_spec.Unique = true
}
if fields := _q.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, channelmonitordailyrollup.FieldID)
for i := range fields {
if fields[i] != channelmonitordailyrollup.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
if _q.withMonitor != nil {
_spec.Node.AddColumnOnce(channelmonitordailyrollup.FieldMonitorID)
}
}
if ps := _q.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := _q.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := _q.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := _q.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (_q *ChannelMonitorDailyRollupQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(_q.driver.Dialect())
t1 := builder.Table(channelmonitordailyrollup.Table)
columns := _q.ctx.Fields
if len(columns) == 0 {
columns = channelmonitordailyrollup.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if _q.sql != nil {
selector = _q.sql
selector.Select(selector.Columns(columns...)...)
}
if _q.ctx.Unique != nil && *_q.ctx.Unique {
selector.Distinct()
}
for _, m := range _q.modifiers {
m(selector)
}
for _, p := range _q.predicates {
p(selector)
}
for _, p := range _q.order {
p(selector)
}
if offset := _q.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := _q.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
// either committed or rolled-back.
func (_q *ChannelMonitorDailyRollupQuery) ForUpdate(opts ...sql.LockOption) *ChannelMonitorDailyRollupQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForUpdate(opts...)
})
return _q
}
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
// on any rows that are read. Other sessions can read the rows, but cannot modify them
// until your transaction commits.
func (_q *ChannelMonitorDailyRollupQuery) ForShare(opts ...sql.LockOption) *ChannelMonitorDailyRollupQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForShare(opts...)
})
return _q
}
// ChannelMonitorDailyRollupGroupBy is the group-by builder for ChannelMonitorDailyRollup entities.
type ChannelMonitorDailyRollupGroupBy struct {
selector
build *ChannelMonitorDailyRollupQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (_g *ChannelMonitorDailyRollupGroupBy) Aggregate(fns ...AggregateFunc) *ChannelMonitorDailyRollupGroupBy {
_g.fns = append(_g.fns, fns...)
return _g
}
// Scan applies the selector query and scans the result into the given value.
func (_g *ChannelMonitorDailyRollupGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
if err := _g.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorDailyRollupQuery, *ChannelMonitorDailyRollupGroupBy](ctx, _g.build, _g, _g.build.inters, v)
}
func (_g *ChannelMonitorDailyRollupGroupBy) sqlScan(ctx context.Context, root *ChannelMonitorDailyRollupQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(_g.fns))
for _, fn := range _g.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
for _, f := range *_g.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*_g.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// ChannelMonitorDailyRollupSelect is the builder for selecting fields of ChannelMonitorDailyRollup entities.
type ChannelMonitorDailyRollupSelect struct {
*ChannelMonitorDailyRollupQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (_s *ChannelMonitorDailyRollupSelect) Aggregate(fns ...AggregateFunc) *ChannelMonitorDailyRollupSelect {
_s.fns = append(_s.fns, fns...)
return _s
}
// Scan applies the selector query and scans the result into the given value.
func (_s *ChannelMonitorDailyRollupSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
if err := _s.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorDailyRollupQuery, *ChannelMonitorDailyRollupSelect](ctx, _s.ChannelMonitorDailyRollupQuery, _s, _s.inters, v)
}
func (_s *ChannelMonitorDailyRollupSelect) sqlScan(ctx context.Context, root *ChannelMonitorDailyRollupQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(_s.fns))
for _, fn := range _s.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*_s.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

View File

@ -0,0 +1,961 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorDailyRollupUpdate is the builder for updating ChannelMonitorDailyRollup entities.
type ChannelMonitorDailyRollupUpdate struct {
config
hooks []Hook
mutation *ChannelMonitorDailyRollupMutation
}
// Where appends a list predicates to the ChannelMonitorDailyRollupUpdate builder.
func (_u *ChannelMonitorDailyRollupUpdate) Where(ps ...predicate.ChannelMonitorDailyRollup) *ChannelMonitorDailyRollupUpdate {
_u.mutation.Where(ps...)
return _u
}
// SetMonitorID sets the "monitor_id" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetMonitorID(v int64) *ChannelMonitorDailyRollupUpdate {
_u.mutation.SetMonitorID(v)
return _u
}
// SetNillableMonitorID sets the "monitor_id" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableMonitorID(v *int64) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetMonitorID(*v)
}
return _u
}
// SetModel sets the "model" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetModel(v string) *ChannelMonitorDailyRollupUpdate {
_u.mutation.SetModel(v)
return _u
}
// SetNillableModel sets the "model" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableModel(v *string) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetModel(*v)
}
return _u
}
// SetBucketDate sets the "bucket_date" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetBucketDate(v time.Time) *ChannelMonitorDailyRollupUpdate {
_u.mutation.SetBucketDate(v)
return _u
}
// SetNillableBucketDate sets the "bucket_date" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableBucketDate(v *time.Time) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetBucketDate(*v)
}
return _u
}
// SetTotalChecks sets the "total_checks" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetTotalChecks(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetTotalChecks()
_u.mutation.SetTotalChecks(v)
return _u
}
// SetNillableTotalChecks sets the "total_checks" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableTotalChecks(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetTotalChecks(*v)
}
return _u
}
// AddTotalChecks adds value to the "total_checks" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddTotalChecks(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddTotalChecks(v)
return _u
}
// SetOkCount sets the "ok_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetOkCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetOkCount()
_u.mutation.SetOkCount(v)
return _u
}
// SetNillableOkCount sets the "ok_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableOkCount(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetOkCount(*v)
}
return _u
}
// AddOkCount adds value to the "ok_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddOkCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddOkCount(v)
return _u
}
// SetOperationalCount sets the "operational_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetOperationalCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetOperationalCount()
_u.mutation.SetOperationalCount(v)
return _u
}
// SetNillableOperationalCount sets the "operational_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableOperationalCount(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetOperationalCount(*v)
}
return _u
}
// AddOperationalCount adds value to the "operational_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddOperationalCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddOperationalCount(v)
return _u
}
// SetDegradedCount sets the "degraded_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetDegradedCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetDegradedCount()
_u.mutation.SetDegradedCount(v)
return _u
}
// SetNillableDegradedCount sets the "degraded_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableDegradedCount(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetDegradedCount(*v)
}
return _u
}
// AddDegradedCount adds value to the "degraded_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddDegradedCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddDegradedCount(v)
return _u
}
// SetFailedCount sets the "failed_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetFailedCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetFailedCount()
_u.mutation.SetFailedCount(v)
return _u
}
// SetNillableFailedCount sets the "failed_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableFailedCount(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetFailedCount(*v)
}
return _u
}
// AddFailedCount adds value to the "failed_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddFailedCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddFailedCount(v)
return _u
}
// SetErrorCount sets the "error_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetErrorCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetErrorCount()
_u.mutation.SetErrorCount(v)
return _u
}
// SetNillableErrorCount sets the "error_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableErrorCount(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetErrorCount(*v)
}
return _u
}
// AddErrorCount adds value to the "error_count" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddErrorCount(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddErrorCount(v)
return _u
}
// SetSumLatencyMs sets the "sum_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetSumLatencyMs(v int64) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetSumLatencyMs()
_u.mutation.SetSumLatencyMs(v)
return _u
}
// SetNillableSumLatencyMs sets the "sum_latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableSumLatencyMs(v *int64) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetSumLatencyMs(*v)
}
return _u
}
// AddSumLatencyMs adds value to the "sum_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddSumLatencyMs(v int64) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddSumLatencyMs(v)
return _u
}
// SetCountLatency sets the "count_latency" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetCountLatency(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetCountLatency()
_u.mutation.SetCountLatency(v)
return _u
}
// SetNillableCountLatency sets the "count_latency" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableCountLatency(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetCountLatency(*v)
}
return _u
}
// AddCountLatency adds value to the "count_latency" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddCountLatency(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddCountLatency(v)
return _u
}
// SetSumPingLatencyMs sets the "sum_ping_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetSumPingLatencyMs(v int64) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetSumPingLatencyMs()
_u.mutation.SetSumPingLatencyMs(v)
return _u
}
// SetNillableSumPingLatencyMs sets the "sum_ping_latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableSumPingLatencyMs(v *int64) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetSumPingLatencyMs(*v)
}
return _u
}
// AddSumPingLatencyMs adds value to the "sum_ping_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddSumPingLatencyMs(v int64) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddSumPingLatencyMs(v)
return _u
}
// SetCountPingLatency sets the "count_ping_latency" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetCountPingLatency(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.ResetCountPingLatency()
_u.mutation.SetCountPingLatency(v)
return _u
}
// SetNillableCountPingLatency sets the "count_ping_latency" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdate) SetNillableCountPingLatency(v *int) *ChannelMonitorDailyRollupUpdate {
if v != nil {
_u.SetCountPingLatency(*v)
}
return _u
}
// AddCountPingLatency adds value to the "count_ping_latency" field.
func (_u *ChannelMonitorDailyRollupUpdate) AddCountPingLatency(v int) *ChannelMonitorDailyRollupUpdate {
_u.mutation.AddCountPingLatency(v)
return _u
}
// SetComputedAt sets the "computed_at" field.
func (_u *ChannelMonitorDailyRollupUpdate) SetComputedAt(v time.Time) *ChannelMonitorDailyRollupUpdate {
_u.mutation.SetComputedAt(v)
return _u
}
// SetMonitor sets the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorDailyRollupUpdate) SetMonitor(v *ChannelMonitor) *ChannelMonitorDailyRollupUpdate {
return _u.SetMonitorID(v.ID)
}
// Mutation returns the ChannelMonitorDailyRollupMutation object of the builder.
func (_u *ChannelMonitorDailyRollupUpdate) Mutation() *ChannelMonitorDailyRollupMutation {
return _u.mutation
}
// ClearMonitor clears the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorDailyRollupUpdate) ClearMonitor() *ChannelMonitorDailyRollupUpdate {
_u.mutation.ClearMonitor()
return _u
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *ChannelMonitorDailyRollupUpdate) Save(ctx context.Context) (int, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *ChannelMonitorDailyRollupUpdate) SaveX(ctx context.Context) int {
affected, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (_u *ChannelMonitorDailyRollupUpdate) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *ChannelMonitorDailyRollupUpdate) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *ChannelMonitorDailyRollupUpdate) defaults() {
if _, ok := _u.mutation.ComputedAt(); !ok {
v := channelmonitordailyrollup.UpdateDefaultComputedAt()
_u.mutation.SetComputedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *ChannelMonitorDailyRollupUpdate) check() error {
if v, ok := _u.mutation.Model(); ok {
if err := channelmonitordailyrollup.ModelValidator(v); err != nil {
return &ValidationError{Name: "model", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorDailyRollup.model": %w`, err)}
}
}
if _u.mutation.MonitorCleared() && len(_u.mutation.MonitorIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "ChannelMonitorDailyRollup.monitor"`)
}
return nil
}
func (_u *ChannelMonitorDailyRollupUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(channelmonitordailyrollup.Table, channelmonitordailyrollup.Columns, sqlgraph.NewFieldSpec(channelmonitordailyrollup.FieldID, field.TypeInt64))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.Model(); ok {
_spec.SetField(channelmonitordailyrollup.FieldModel, field.TypeString, value)
}
if value, ok := _u.mutation.BucketDate(); ok {
_spec.SetField(channelmonitordailyrollup.FieldBucketDate, field.TypeTime, value)
}
if value, ok := _u.mutation.TotalChecks(); ok {
_spec.SetField(channelmonitordailyrollup.FieldTotalChecks, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedTotalChecks(); ok {
_spec.AddField(channelmonitordailyrollup.FieldTotalChecks, field.TypeInt, value)
}
if value, ok := _u.mutation.OkCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldOkCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedOkCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldOkCount, field.TypeInt, value)
}
if value, ok := _u.mutation.OperationalCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldOperationalCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedOperationalCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldOperationalCount, field.TypeInt, value)
}
if value, ok := _u.mutation.DegradedCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldDegradedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedDegradedCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldDegradedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.FailedCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldFailedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedFailedCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldFailedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.ErrorCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldErrorCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedErrorCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldErrorCount, field.TypeInt, value)
}
if value, ok := _u.mutation.SumLatencyMs(); ok {
_spec.SetField(channelmonitordailyrollup.FieldSumLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.AddedSumLatencyMs(); ok {
_spec.AddField(channelmonitordailyrollup.FieldSumLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.CountLatency(); ok {
_spec.SetField(channelmonitordailyrollup.FieldCountLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedCountLatency(); ok {
_spec.AddField(channelmonitordailyrollup.FieldCountLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.SumPingLatencyMs(); ok {
_spec.SetField(channelmonitordailyrollup.FieldSumPingLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.AddedSumPingLatencyMs(); ok {
_spec.AddField(channelmonitordailyrollup.FieldSumPingLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.CountPingLatency(); ok {
_spec.SetField(channelmonitordailyrollup.FieldCountPingLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedCountPingLatency(); ok {
_spec.AddField(channelmonitordailyrollup.FieldCountPingLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.ComputedAt(); ok {
_spec.SetField(channelmonitordailyrollup.FieldComputedAt, field.TypeTime, value)
}
if _u.mutation.MonitorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitordailyrollup.MonitorTable,
Columns: []string{channelmonitordailyrollup.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.MonitorIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitordailyrollup.MonitorTable,
Columns: []string{channelmonitordailyrollup.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{channelmonitordailyrollup.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
_u.mutation.done = true
return _node, nil
}
// ChannelMonitorDailyRollupUpdateOne is the builder for updating a single ChannelMonitorDailyRollup entity.
type ChannelMonitorDailyRollupUpdateOne struct {
config
fields []string
hooks []Hook
mutation *ChannelMonitorDailyRollupMutation
}
// SetMonitorID sets the "monitor_id" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetMonitorID(v int64) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.SetMonitorID(v)
return _u
}
// SetNillableMonitorID sets the "monitor_id" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableMonitorID(v *int64) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetMonitorID(*v)
}
return _u
}
// SetModel sets the "model" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetModel(v string) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.SetModel(v)
return _u
}
// SetNillableModel sets the "model" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableModel(v *string) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetModel(*v)
}
return _u
}
// SetBucketDate sets the "bucket_date" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetBucketDate(v time.Time) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.SetBucketDate(v)
return _u
}
// SetNillableBucketDate sets the "bucket_date" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableBucketDate(v *time.Time) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetBucketDate(*v)
}
return _u
}
// SetTotalChecks sets the "total_checks" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetTotalChecks(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetTotalChecks()
_u.mutation.SetTotalChecks(v)
return _u
}
// SetNillableTotalChecks sets the "total_checks" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableTotalChecks(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetTotalChecks(*v)
}
return _u
}
// AddTotalChecks adds value to the "total_checks" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddTotalChecks(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddTotalChecks(v)
return _u
}
// SetOkCount sets the "ok_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetOkCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetOkCount()
_u.mutation.SetOkCount(v)
return _u
}
// SetNillableOkCount sets the "ok_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableOkCount(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetOkCount(*v)
}
return _u
}
// AddOkCount adds value to the "ok_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddOkCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddOkCount(v)
return _u
}
// SetOperationalCount sets the "operational_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetOperationalCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetOperationalCount()
_u.mutation.SetOperationalCount(v)
return _u
}
// SetNillableOperationalCount sets the "operational_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableOperationalCount(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetOperationalCount(*v)
}
return _u
}
// AddOperationalCount adds value to the "operational_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddOperationalCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddOperationalCount(v)
return _u
}
// SetDegradedCount sets the "degraded_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetDegradedCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetDegradedCount()
_u.mutation.SetDegradedCount(v)
return _u
}
// SetNillableDegradedCount sets the "degraded_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableDegradedCount(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetDegradedCount(*v)
}
return _u
}
// AddDegradedCount adds value to the "degraded_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddDegradedCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddDegradedCount(v)
return _u
}
// SetFailedCount sets the "failed_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetFailedCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetFailedCount()
_u.mutation.SetFailedCount(v)
return _u
}
// SetNillableFailedCount sets the "failed_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableFailedCount(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetFailedCount(*v)
}
return _u
}
// AddFailedCount adds value to the "failed_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddFailedCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddFailedCount(v)
return _u
}
// SetErrorCount sets the "error_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetErrorCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetErrorCount()
_u.mutation.SetErrorCount(v)
return _u
}
// SetNillableErrorCount sets the "error_count" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableErrorCount(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetErrorCount(*v)
}
return _u
}
// AddErrorCount adds value to the "error_count" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddErrorCount(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddErrorCount(v)
return _u
}
// SetSumLatencyMs sets the "sum_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetSumLatencyMs(v int64) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetSumLatencyMs()
_u.mutation.SetSumLatencyMs(v)
return _u
}
// SetNillableSumLatencyMs sets the "sum_latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableSumLatencyMs(v *int64) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetSumLatencyMs(*v)
}
return _u
}
// AddSumLatencyMs adds value to the "sum_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddSumLatencyMs(v int64) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddSumLatencyMs(v)
return _u
}
// SetCountLatency sets the "count_latency" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetCountLatency(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetCountLatency()
_u.mutation.SetCountLatency(v)
return _u
}
// SetNillableCountLatency sets the "count_latency" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableCountLatency(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetCountLatency(*v)
}
return _u
}
// AddCountLatency adds value to the "count_latency" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddCountLatency(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddCountLatency(v)
return _u
}
// SetSumPingLatencyMs sets the "sum_ping_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetSumPingLatencyMs(v int64) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetSumPingLatencyMs()
_u.mutation.SetSumPingLatencyMs(v)
return _u
}
// SetNillableSumPingLatencyMs sets the "sum_ping_latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableSumPingLatencyMs(v *int64) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetSumPingLatencyMs(*v)
}
return _u
}
// AddSumPingLatencyMs adds value to the "sum_ping_latency_ms" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddSumPingLatencyMs(v int64) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddSumPingLatencyMs(v)
return _u
}
// SetCountPingLatency sets the "count_ping_latency" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetCountPingLatency(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ResetCountPingLatency()
_u.mutation.SetCountPingLatency(v)
return _u
}
// SetNillableCountPingLatency sets the "count_ping_latency" field if the given value is not nil.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetNillableCountPingLatency(v *int) *ChannelMonitorDailyRollupUpdateOne {
if v != nil {
_u.SetCountPingLatency(*v)
}
return _u
}
// AddCountPingLatency adds value to the "count_ping_latency" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) AddCountPingLatency(v int) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.AddCountPingLatency(v)
return _u
}
// SetComputedAt sets the "computed_at" field.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetComputedAt(v time.Time) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.SetComputedAt(v)
return _u
}
// SetMonitor sets the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorDailyRollupUpdateOne) SetMonitor(v *ChannelMonitor) *ChannelMonitorDailyRollupUpdateOne {
return _u.SetMonitorID(v.ID)
}
// Mutation returns the ChannelMonitorDailyRollupMutation object of the builder.
func (_u *ChannelMonitorDailyRollupUpdateOne) Mutation() *ChannelMonitorDailyRollupMutation {
return _u.mutation
}
// ClearMonitor clears the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorDailyRollupUpdateOne) ClearMonitor() *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.ClearMonitor()
return _u
}
// Where appends a list predicates to the ChannelMonitorDailyRollupUpdate builder.
func (_u *ChannelMonitorDailyRollupUpdateOne) Where(ps ...predicate.ChannelMonitorDailyRollup) *ChannelMonitorDailyRollupUpdateOne {
_u.mutation.Where(ps...)
return _u
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (_u *ChannelMonitorDailyRollupUpdateOne) Select(field string, fields ...string) *ChannelMonitorDailyRollupUpdateOne {
_u.fields = append([]string{field}, fields...)
return _u
}
// Save executes the query and returns the updated ChannelMonitorDailyRollup entity.
func (_u *ChannelMonitorDailyRollupUpdateOne) Save(ctx context.Context) (*ChannelMonitorDailyRollup, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *ChannelMonitorDailyRollupUpdateOne) SaveX(ctx context.Context) *ChannelMonitorDailyRollup {
node, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (_u *ChannelMonitorDailyRollupUpdateOne) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *ChannelMonitorDailyRollupUpdateOne) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *ChannelMonitorDailyRollupUpdateOne) defaults() {
if _, ok := _u.mutation.ComputedAt(); !ok {
v := channelmonitordailyrollup.UpdateDefaultComputedAt()
_u.mutation.SetComputedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *ChannelMonitorDailyRollupUpdateOne) check() error {
if v, ok := _u.mutation.Model(); ok {
if err := channelmonitordailyrollup.ModelValidator(v); err != nil {
return &ValidationError{Name: "model", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorDailyRollup.model": %w`, err)}
}
}
if _u.mutation.MonitorCleared() && len(_u.mutation.MonitorIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "ChannelMonitorDailyRollup.monitor"`)
}
return nil
}
func (_u *ChannelMonitorDailyRollupUpdateOne) sqlSave(ctx context.Context) (_node *ChannelMonitorDailyRollup, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(channelmonitordailyrollup.Table, channelmonitordailyrollup.Columns, sqlgraph.NewFieldSpec(channelmonitordailyrollup.FieldID, field.TypeInt64))
id, ok := _u.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ChannelMonitorDailyRollup.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, channelmonitordailyrollup.FieldID)
for _, f := range fields {
if !channelmonitordailyrollup.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != channelmonitordailyrollup.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.Model(); ok {
_spec.SetField(channelmonitordailyrollup.FieldModel, field.TypeString, value)
}
if value, ok := _u.mutation.BucketDate(); ok {
_spec.SetField(channelmonitordailyrollup.FieldBucketDate, field.TypeTime, value)
}
if value, ok := _u.mutation.TotalChecks(); ok {
_spec.SetField(channelmonitordailyrollup.FieldTotalChecks, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedTotalChecks(); ok {
_spec.AddField(channelmonitordailyrollup.FieldTotalChecks, field.TypeInt, value)
}
if value, ok := _u.mutation.OkCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldOkCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedOkCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldOkCount, field.TypeInt, value)
}
if value, ok := _u.mutation.OperationalCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldOperationalCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedOperationalCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldOperationalCount, field.TypeInt, value)
}
if value, ok := _u.mutation.DegradedCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldDegradedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedDegradedCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldDegradedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.FailedCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldFailedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedFailedCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldFailedCount, field.TypeInt, value)
}
if value, ok := _u.mutation.ErrorCount(); ok {
_spec.SetField(channelmonitordailyrollup.FieldErrorCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedErrorCount(); ok {
_spec.AddField(channelmonitordailyrollup.FieldErrorCount, field.TypeInt, value)
}
if value, ok := _u.mutation.SumLatencyMs(); ok {
_spec.SetField(channelmonitordailyrollup.FieldSumLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.AddedSumLatencyMs(); ok {
_spec.AddField(channelmonitordailyrollup.FieldSumLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.CountLatency(); ok {
_spec.SetField(channelmonitordailyrollup.FieldCountLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedCountLatency(); ok {
_spec.AddField(channelmonitordailyrollup.FieldCountLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.SumPingLatencyMs(); ok {
_spec.SetField(channelmonitordailyrollup.FieldSumPingLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.AddedSumPingLatencyMs(); ok {
_spec.AddField(channelmonitordailyrollup.FieldSumPingLatencyMs, field.TypeInt64, value)
}
if value, ok := _u.mutation.CountPingLatency(); ok {
_spec.SetField(channelmonitordailyrollup.FieldCountPingLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedCountPingLatency(); ok {
_spec.AddField(channelmonitordailyrollup.FieldCountPingLatency, field.TypeInt, value)
}
if value, ok := _u.mutation.ComputedAt(); ok {
_spec.SetField(channelmonitordailyrollup.FieldComputedAt, field.TypeTime, value)
}
if _u.mutation.MonitorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitordailyrollup.MonitorTable,
Columns: []string{channelmonitordailyrollup.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.MonitorIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitordailyrollup.MonitorTable,
Columns: []string{channelmonitordailyrollup.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &ChannelMonitorDailyRollup{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{channelmonitordailyrollup.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
_u.mutation.done = true
return _node, nil
}

View File

@ -0,0 +1,207 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
)
// ChannelMonitorHistory is the model entity for the ChannelMonitorHistory schema.
type ChannelMonitorHistory struct {
config `json:"-"`
// ID of the ent.
ID int64 `json:"id,omitempty"`
// MonitorID holds the value of the "monitor_id" field.
MonitorID int64 `json:"monitor_id,omitempty"`
// Model holds the value of the "model" field.
Model string `json:"model,omitempty"`
// Status holds the value of the "status" field.
Status channelmonitorhistory.Status `json:"status,omitempty"`
// LatencyMs holds the value of the "latency_ms" field.
LatencyMs *int `json:"latency_ms,omitempty"`
// PingLatencyMs holds the value of the "ping_latency_ms" field.
PingLatencyMs *int `json:"ping_latency_ms,omitempty"`
// Message holds the value of the "message" field.
Message string `json:"message,omitempty"`
// CheckedAt holds the value of the "checked_at" field.
CheckedAt time.Time `json:"checked_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the ChannelMonitorHistoryQuery when eager-loading is set.
Edges ChannelMonitorHistoryEdges `json:"edges"`
selectValues sql.SelectValues
}
// ChannelMonitorHistoryEdges holds the relations/edges for other nodes in the graph.
type ChannelMonitorHistoryEdges struct {
// Monitor holds the value of the monitor edge.
Monitor *ChannelMonitor `json:"monitor,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// MonitorOrErr returns the Monitor value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e ChannelMonitorHistoryEdges) MonitorOrErr() (*ChannelMonitor, error) {
if e.Monitor != nil {
return e.Monitor, nil
} else if e.loadedTypes[0] {
return nil, &NotFoundError{label: channelmonitor.Label}
}
return nil, &NotLoadedError{edge: "monitor"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*ChannelMonitorHistory) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case channelmonitorhistory.FieldID, channelmonitorhistory.FieldMonitorID, channelmonitorhistory.FieldLatencyMs, channelmonitorhistory.FieldPingLatencyMs:
values[i] = new(sql.NullInt64)
case channelmonitorhistory.FieldModel, channelmonitorhistory.FieldStatus, channelmonitorhistory.FieldMessage:
values[i] = new(sql.NullString)
case channelmonitorhistory.FieldCheckedAt:
values[i] = new(sql.NullTime)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the ChannelMonitorHistory fields.
func (_m *ChannelMonitorHistory) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case channelmonitorhistory.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
_m.ID = int64(value.Int64)
case channelmonitorhistory.FieldMonitorID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field monitor_id", values[i])
} else if value.Valid {
_m.MonitorID = value.Int64
}
case channelmonitorhistory.FieldModel:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field model", values[i])
} else if value.Valid {
_m.Model = value.String
}
case channelmonitorhistory.FieldStatus:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field status", values[i])
} else if value.Valid {
_m.Status = channelmonitorhistory.Status(value.String)
}
case channelmonitorhistory.FieldLatencyMs:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field latency_ms", values[i])
} else if value.Valid {
_m.LatencyMs = new(int)
*_m.LatencyMs = int(value.Int64)
}
case channelmonitorhistory.FieldPingLatencyMs:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field ping_latency_ms", values[i])
} else if value.Valid {
_m.PingLatencyMs = new(int)
*_m.PingLatencyMs = int(value.Int64)
}
case channelmonitorhistory.FieldMessage:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field message", values[i])
} else if value.Valid {
_m.Message = value.String
}
case channelmonitorhistory.FieldCheckedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field checked_at", values[i])
} else if value.Valid {
_m.CheckedAt = value.Time
}
default:
_m.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the ChannelMonitorHistory.
// This includes values selected through modifiers, order, etc.
func (_m *ChannelMonitorHistory) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryMonitor queries the "monitor" edge of the ChannelMonitorHistory entity.
func (_m *ChannelMonitorHistory) QueryMonitor() *ChannelMonitorQuery {
return NewChannelMonitorHistoryClient(_m.config).QueryMonitor(_m)
}
// Update returns a builder for updating this ChannelMonitorHistory.
// Note that you need to call ChannelMonitorHistory.Unwrap() before calling this method if this ChannelMonitorHistory
// was returned from a transaction, and the transaction was committed or rolled back.
func (_m *ChannelMonitorHistory) Update() *ChannelMonitorHistoryUpdateOne {
return NewChannelMonitorHistoryClient(_m.config).UpdateOne(_m)
}
// Unwrap unwraps the ChannelMonitorHistory entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (_m *ChannelMonitorHistory) Unwrap() *ChannelMonitorHistory {
_tx, ok := _m.config.driver.(*txDriver)
if !ok {
panic("ent: ChannelMonitorHistory is not a transactional entity")
}
_m.config.driver = _tx.drv
return _m
}
// String implements the fmt.Stringer.
func (_m *ChannelMonitorHistory) String() string {
var builder strings.Builder
builder.WriteString("ChannelMonitorHistory(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("monitor_id=")
builder.WriteString(fmt.Sprintf("%v", _m.MonitorID))
builder.WriteString(", ")
builder.WriteString("model=")
builder.WriteString(_m.Model)
builder.WriteString(", ")
builder.WriteString("status=")
builder.WriteString(fmt.Sprintf("%v", _m.Status))
builder.WriteString(", ")
if v := _m.LatencyMs; v != nil {
builder.WriteString("latency_ms=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
if v := _m.PingLatencyMs; v != nil {
builder.WriteString("ping_latency_ms=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
builder.WriteString(", ")
builder.WriteString("message=")
builder.WriteString(_m.Message)
builder.WriteString(", ")
builder.WriteString("checked_at=")
builder.WriteString(_m.CheckedAt.Format(time.ANSIC))
builder.WriteByte(')')
return builder.String()
}
// ChannelMonitorHistories is a parsable slice of ChannelMonitorHistory.
type ChannelMonitorHistories []*ChannelMonitorHistory

View File

@ -0,0 +1,158 @@
// Code generated by ent, DO NOT EDIT.
package channelmonitorhistory
import (
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
const (
// Label holds the string label denoting the channelmonitorhistory type in the database.
Label = "channel_monitor_history"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldMonitorID holds the string denoting the monitor_id field in the database.
FieldMonitorID = "monitor_id"
// FieldModel holds the string denoting the model field in the database.
FieldModel = "model"
// FieldStatus holds the string denoting the status field in the database.
FieldStatus = "status"
// FieldLatencyMs holds the string denoting the latency_ms field in the database.
FieldLatencyMs = "latency_ms"
// FieldPingLatencyMs holds the string denoting the ping_latency_ms field in the database.
FieldPingLatencyMs = "ping_latency_ms"
// FieldMessage holds the string denoting the message field in the database.
FieldMessage = "message"
// FieldCheckedAt holds the string denoting the checked_at field in the database.
FieldCheckedAt = "checked_at"
// EdgeMonitor holds the string denoting the monitor edge name in mutations.
EdgeMonitor = "monitor"
// Table holds the table name of the channelmonitorhistory in the database.
Table = "channel_monitor_histories"
// MonitorTable is the table that holds the monitor relation/edge.
MonitorTable = "channel_monitor_histories"
// MonitorInverseTable is the table name for the ChannelMonitor entity.
// It exists in this package in order to avoid circular dependency with the "channelmonitor" package.
MonitorInverseTable = "channel_monitors"
// MonitorColumn is the table column denoting the monitor relation/edge.
MonitorColumn = "monitor_id"
)
// Columns holds all SQL columns for channelmonitorhistory fields.
var Columns = []string{
FieldID,
FieldMonitorID,
FieldModel,
FieldStatus,
FieldLatencyMs,
FieldPingLatencyMs,
FieldMessage,
FieldCheckedAt,
}
// 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 (
// ModelValidator is a validator for the "model" field. It is called by the builders before save.
ModelValidator func(string) error
// DefaultMessage holds the default value on creation for the "message" field.
DefaultMessage string
// MessageValidator is a validator for the "message" field. It is called by the builders before save.
MessageValidator func(string) error
// DefaultCheckedAt holds the default value on creation for the "checked_at" field.
DefaultCheckedAt func() time.Time
)
// Status defines the type for the "status" enum field.
type Status string
// Status values.
const (
StatusOperational Status = "operational"
StatusDegraded Status = "degraded"
StatusFailed Status = "failed"
StatusError Status = "error"
)
func (s Status) String() string {
return string(s)
}
// StatusValidator is a validator for the "status" field enum values. It is called by the builders before save.
func StatusValidator(s Status) error {
switch s {
case StatusOperational, StatusDegraded, StatusFailed, StatusError:
return nil
default:
return fmt.Errorf("channelmonitorhistory: invalid enum value for status field: %q", s)
}
}
// OrderOption defines the ordering options for the ChannelMonitorHistory 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()
}
// ByMonitorID orders the results by the monitor_id field.
func ByMonitorID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldMonitorID, opts...).ToFunc()
}
// ByModel orders the results by the model field.
func ByModel(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldModel, opts...).ToFunc()
}
// ByStatus orders the results by the status field.
func ByStatus(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldStatus, opts...).ToFunc()
}
// ByLatencyMs orders the results by the latency_ms field.
func ByLatencyMs(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldLatencyMs, opts...).ToFunc()
}
// ByPingLatencyMs orders the results by the ping_latency_ms field.
func ByPingLatencyMs(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPingLatencyMs, opts...).ToFunc()
}
// ByMessage orders the results by the message field.
func ByMessage(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldMessage, opts...).ToFunc()
}
// ByCheckedAt orders the results by the checked_at field.
func ByCheckedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCheckedAt, opts...).ToFunc()
}
// ByMonitorField orders the results by monitor field.
func ByMonitorField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newMonitorStep(), sql.OrderByField(field, opts...))
}
}
func newMonitorStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(MonitorInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, MonitorTable, MonitorColumn),
)
}

View File

@ -0,0 +1,444 @@
// Code generated by ent, DO NOT EDIT.
package channelmonitorhistory
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLTE(FieldID, id))
}
// MonitorID applies equality check predicate on the "monitor_id" field. It's identical to MonitorIDEQ.
func MonitorID(v int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldMonitorID, v))
}
// Model applies equality check predicate on the "model" field. It's identical to ModelEQ.
func Model(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldModel, v))
}
// LatencyMs applies equality check predicate on the "latency_ms" field. It's identical to LatencyMsEQ.
func LatencyMs(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldLatencyMs, v))
}
// PingLatencyMs applies equality check predicate on the "ping_latency_ms" field. It's identical to PingLatencyMsEQ.
func PingLatencyMs(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldPingLatencyMs, v))
}
// Message applies equality check predicate on the "message" field. It's identical to MessageEQ.
func Message(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldMessage, v))
}
// CheckedAt applies equality check predicate on the "checked_at" field. It's identical to CheckedAtEQ.
func CheckedAt(v time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldCheckedAt, v))
}
// MonitorIDEQ applies the EQ predicate on the "monitor_id" field.
func MonitorIDEQ(v int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldMonitorID, v))
}
// MonitorIDNEQ applies the NEQ predicate on the "monitor_id" field.
func MonitorIDNEQ(v int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldMonitorID, v))
}
// MonitorIDIn applies the In predicate on the "monitor_id" field.
func MonitorIDIn(vs ...int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldMonitorID, vs...))
}
// MonitorIDNotIn applies the NotIn predicate on the "monitor_id" field.
func MonitorIDNotIn(vs ...int64) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldMonitorID, vs...))
}
// ModelEQ applies the EQ predicate on the "model" field.
func ModelEQ(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldModel, v))
}
// ModelNEQ applies the NEQ predicate on the "model" field.
func ModelNEQ(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldModel, v))
}
// ModelIn applies the In predicate on the "model" field.
func ModelIn(vs ...string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldModel, vs...))
}
// ModelNotIn applies the NotIn predicate on the "model" field.
func ModelNotIn(vs ...string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldModel, vs...))
}
// ModelGT applies the GT predicate on the "model" field.
func ModelGT(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGT(FieldModel, v))
}
// ModelGTE applies the GTE predicate on the "model" field.
func ModelGTE(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGTE(FieldModel, v))
}
// ModelLT applies the LT predicate on the "model" field.
func ModelLT(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLT(FieldModel, v))
}
// ModelLTE applies the LTE predicate on the "model" field.
func ModelLTE(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLTE(FieldModel, v))
}
// ModelContains applies the Contains predicate on the "model" field.
func ModelContains(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldContains(FieldModel, v))
}
// ModelHasPrefix applies the HasPrefix predicate on the "model" field.
func ModelHasPrefix(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldHasPrefix(FieldModel, v))
}
// ModelHasSuffix applies the HasSuffix predicate on the "model" field.
func ModelHasSuffix(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldHasSuffix(FieldModel, v))
}
// ModelEqualFold applies the EqualFold predicate on the "model" field.
func ModelEqualFold(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEqualFold(FieldModel, v))
}
// ModelContainsFold applies the ContainsFold predicate on the "model" field.
func ModelContainsFold(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldContainsFold(FieldModel, v))
}
// StatusEQ applies the EQ predicate on the "status" field.
func StatusEQ(v Status) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldStatus, v))
}
// StatusNEQ applies the NEQ predicate on the "status" field.
func StatusNEQ(v Status) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldStatus, v))
}
// StatusIn applies the In predicate on the "status" field.
func StatusIn(vs ...Status) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldStatus, vs...))
}
// StatusNotIn applies the NotIn predicate on the "status" field.
func StatusNotIn(vs ...Status) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldStatus, vs...))
}
// LatencyMsEQ applies the EQ predicate on the "latency_ms" field.
func LatencyMsEQ(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldLatencyMs, v))
}
// LatencyMsNEQ applies the NEQ predicate on the "latency_ms" field.
func LatencyMsNEQ(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldLatencyMs, v))
}
// LatencyMsIn applies the In predicate on the "latency_ms" field.
func LatencyMsIn(vs ...int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldLatencyMs, vs...))
}
// LatencyMsNotIn applies the NotIn predicate on the "latency_ms" field.
func LatencyMsNotIn(vs ...int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldLatencyMs, vs...))
}
// LatencyMsGT applies the GT predicate on the "latency_ms" field.
func LatencyMsGT(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGT(FieldLatencyMs, v))
}
// LatencyMsGTE applies the GTE predicate on the "latency_ms" field.
func LatencyMsGTE(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGTE(FieldLatencyMs, v))
}
// LatencyMsLT applies the LT predicate on the "latency_ms" field.
func LatencyMsLT(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLT(FieldLatencyMs, v))
}
// LatencyMsLTE applies the LTE predicate on the "latency_ms" field.
func LatencyMsLTE(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLTE(FieldLatencyMs, v))
}
// LatencyMsIsNil applies the IsNil predicate on the "latency_ms" field.
func LatencyMsIsNil() predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIsNull(FieldLatencyMs))
}
// LatencyMsNotNil applies the NotNil predicate on the "latency_ms" field.
func LatencyMsNotNil() predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotNull(FieldLatencyMs))
}
// PingLatencyMsEQ applies the EQ predicate on the "ping_latency_ms" field.
func PingLatencyMsEQ(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldPingLatencyMs, v))
}
// PingLatencyMsNEQ applies the NEQ predicate on the "ping_latency_ms" field.
func PingLatencyMsNEQ(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldPingLatencyMs, v))
}
// PingLatencyMsIn applies the In predicate on the "ping_latency_ms" field.
func PingLatencyMsIn(vs ...int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldPingLatencyMs, vs...))
}
// PingLatencyMsNotIn applies the NotIn predicate on the "ping_latency_ms" field.
func PingLatencyMsNotIn(vs ...int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldPingLatencyMs, vs...))
}
// PingLatencyMsGT applies the GT predicate on the "ping_latency_ms" field.
func PingLatencyMsGT(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGT(FieldPingLatencyMs, v))
}
// PingLatencyMsGTE applies the GTE predicate on the "ping_latency_ms" field.
func PingLatencyMsGTE(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGTE(FieldPingLatencyMs, v))
}
// PingLatencyMsLT applies the LT predicate on the "ping_latency_ms" field.
func PingLatencyMsLT(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLT(FieldPingLatencyMs, v))
}
// PingLatencyMsLTE applies the LTE predicate on the "ping_latency_ms" field.
func PingLatencyMsLTE(v int) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLTE(FieldPingLatencyMs, v))
}
// PingLatencyMsIsNil applies the IsNil predicate on the "ping_latency_ms" field.
func PingLatencyMsIsNil() predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIsNull(FieldPingLatencyMs))
}
// PingLatencyMsNotNil applies the NotNil predicate on the "ping_latency_ms" field.
func PingLatencyMsNotNil() predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotNull(FieldPingLatencyMs))
}
// MessageEQ applies the EQ predicate on the "message" field.
func MessageEQ(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldMessage, v))
}
// MessageNEQ applies the NEQ predicate on the "message" field.
func MessageNEQ(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldMessage, v))
}
// MessageIn applies the In predicate on the "message" field.
func MessageIn(vs ...string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldMessage, vs...))
}
// MessageNotIn applies the NotIn predicate on the "message" field.
func MessageNotIn(vs ...string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldMessage, vs...))
}
// MessageGT applies the GT predicate on the "message" field.
func MessageGT(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGT(FieldMessage, v))
}
// MessageGTE applies the GTE predicate on the "message" field.
func MessageGTE(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGTE(FieldMessage, v))
}
// MessageLT applies the LT predicate on the "message" field.
func MessageLT(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLT(FieldMessage, v))
}
// MessageLTE applies the LTE predicate on the "message" field.
func MessageLTE(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLTE(FieldMessage, v))
}
// MessageContains applies the Contains predicate on the "message" field.
func MessageContains(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldContains(FieldMessage, v))
}
// MessageHasPrefix applies the HasPrefix predicate on the "message" field.
func MessageHasPrefix(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldHasPrefix(FieldMessage, v))
}
// MessageHasSuffix applies the HasSuffix predicate on the "message" field.
func MessageHasSuffix(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldHasSuffix(FieldMessage, v))
}
// MessageIsNil applies the IsNil predicate on the "message" field.
func MessageIsNil() predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIsNull(FieldMessage))
}
// MessageNotNil applies the NotNil predicate on the "message" field.
func MessageNotNil() predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotNull(FieldMessage))
}
// MessageEqualFold applies the EqualFold predicate on the "message" field.
func MessageEqualFold(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEqualFold(FieldMessage, v))
}
// MessageContainsFold applies the ContainsFold predicate on the "message" field.
func MessageContainsFold(v string) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldContainsFold(FieldMessage, v))
}
// CheckedAtEQ applies the EQ predicate on the "checked_at" field.
func CheckedAtEQ(v time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldEQ(FieldCheckedAt, v))
}
// CheckedAtNEQ applies the NEQ predicate on the "checked_at" field.
func CheckedAtNEQ(v time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNEQ(FieldCheckedAt, v))
}
// CheckedAtIn applies the In predicate on the "checked_at" field.
func CheckedAtIn(vs ...time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldIn(FieldCheckedAt, vs...))
}
// CheckedAtNotIn applies the NotIn predicate on the "checked_at" field.
func CheckedAtNotIn(vs ...time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldNotIn(FieldCheckedAt, vs...))
}
// CheckedAtGT applies the GT predicate on the "checked_at" field.
func CheckedAtGT(v time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGT(FieldCheckedAt, v))
}
// CheckedAtGTE applies the GTE predicate on the "checked_at" field.
func CheckedAtGTE(v time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldGTE(FieldCheckedAt, v))
}
// CheckedAtLT applies the LT predicate on the "checked_at" field.
func CheckedAtLT(v time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLT(FieldCheckedAt, v))
}
// CheckedAtLTE applies the LTE predicate on the "checked_at" field.
func CheckedAtLTE(v time.Time) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.FieldLTE(FieldCheckedAt, v))
}
// HasMonitor applies the HasEdge predicate on the "monitor" edge.
func HasMonitor() predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, MonitorTable, MonitorColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasMonitorWith applies the HasEdge predicate on the "monitor" edge with a given conditions (other predicates).
func HasMonitorWith(preds ...predicate.ChannelMonitor) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(func(s *sql.Selector) {
step := newMonitorStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.ChannelMonitorHistory) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.ChannelMonitorHistory) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.ChannelMonitorHistory) predicate.ChannelMonitorHistory {
return predicate.ChannelMonitorHistory(sql.NotPredicates(p))
}

View File

@ -0,0 +1,947 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
)
// ChannelMonitorHistoryCreate is the builder for creating a ChannelMonitorHistory entity.
type ChannelMonitorHistoryCreate struct {
config
mutation *ChannelMonitorHistoryMutation
hooks []Hook
conflict []sql.ConflictOption
}
// SetMonitorID sets the "monitor_id" field.
func (_c *ChannelMonitorHistoryCreate) SetMonitorID(v int64) *ChannelMonitorHistoryCreate {
_c.mutation.SetMonitorID(v)
return _c
}
// SetModel sets the "model" field.
func (_c *ChannelMonitorHistoryCreate) SetModel(v string) *ChannelMonitorHistoryCreate {
_c.mutation.SetModel(v)
return _c
}
// SetStatus sets the "status" field.
func (_c *ChannelMonitorHistoryCreate) SetStatus(v channelmonitorhistory.Status) *ChannelMonitorHistoryCreate {
_c.mutation.SetStatus(v)
return _c
}
// SetLatencyMs sets the "latency_ms" field.
func (_c *ChannelMonitorHistoryCreate) SetLatencyMs(v int) *ChannelMonitorHistoryCreate {
_c.mutation.SetLatencyMs(v)
return _c
}
// SetNillableLatencyMs sets the "latency_ms" field if the given value is not nil.
func (_c *ChannelMonitorHistoryCreate) SetNillableLatencyMs(v *int) *ChannelMonitorHistoryCreate {
if v != nil {
_c.SetLatencyMs(*v)
}
return _c
}
// SetPingLatencyMs sets the "ping_latency_ms" field.
func (_c *ChannelMonitorHistoryCreate) SetPingLatencyMs(v int) *ChannelMonitorHistoryCreate {
_c.mutation.SetPingLatencyMs(v)
return _c
}
// SetNillablePingLatencyMs sets the "ping_latency_ms" field if the given value is not nil.
func (_c *ChannelMonitorHistoryCreate) SetNillablePingLatencyMs(v *int) *ChannelMonitorHistoryCreate {
if v != nil {
_c.SetPingLatencyMs(*v)
}
return _c
}
// SetMessage sets the "message" field.
func (_c *ChannelMonitorHistoryCreate) SetMessage(v string) *ChannelMonitorHistoryCreate {
_c.mutation.SetMessage(v)
return _c
}
// SetNillableMessage sets the "message" field if the given value is not nil.
func (_c *ChannelMonitorHistoryCreate) SetNillableMessage(v *string) *ChannelMonitorHistoryCreate {
if v != nil {
_c.SetMessage(*v)
}
return _c
}
// SetCheckedAt sets the "checked_at" field.
func (_c *ChannelMonitorHistoryCreate) SetCheckedAt(v time.Time) *ChannelMonitorHistoryCreate {
_c.mutation.SetCheckedAt(v)
return _c
}
// SetNillableCheckedAt sets the "checked_at" field if the given value is not nil.
func (_c *ChannelMonitorHistoryCreate) SetNillableCheckedAt(v *time.Time) *ChannelMonitorHistoryCreate {
if v != nil {
_c.SetCheckedAt(*v)
}
return _c
}
// SetMonitor sets the "monitor" edge to the ChannelMonitor entity.
func (_c *ChannelMonitorHistoryCreate) SetMonitor(v *ChannelMonitor) *ChannelMonitorHistoryCreate {
return _c.SetMonitorID(v.ID)
}
// Mutation returns the ChannelMonitorHistoryMutation object of the builder.
func (_c *ChannelMonitorHistoryCreate) Mutation() *ChannelMonitorHistoryMutation {
return _c.mutation
}
// Save creates the ChannelMonitorHistory in the database.
func (_c *ChannelMonitorHistoryCreate) Save(ctx context.Context) (*ChannelMonitorHistory, error) {
_c.defaults()
return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
}
// SaveX calls Save and panics if Save returns an error.
func (_c *ChannelMonitorHistoryCreate) SaveX(ctx context.Context) *ChannelMonitorHistory {
v, err := _c.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (_c *ChannelMonitorHistoryCreate) Exec(ctx context.Context) error {
_, err := _c.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_c *ChannelMonitorHistoryCreate) ExecX(ctx context.Context) {
if err := _c.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_c *ChannelMonitorHistoryCreate) defaults() {
if _, ok := _c.mutation.Message(); !ok {
v := channelmonitorhistory.DefaultMessage
_c.mutation.SetMessage(v)
}
if _, ok := _c.mutation.CheckedAt(); !ok {
v := channelmonitorhistory.DefaultCheckedAt()
_c.mutation.SetCheckedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_c *ChannelMonitorHistoryCreate) check() error {
if _, ok := _c.mutation.MonitorID(); !ok {
return &ValidationError{Name: "monitor_id", err: errors.New(`ent: missing required field "ChannelMonitorHistory.monitor_id"`)}
}
if _, ok := _c.mutation.Model(); !ok {
return &ValidationError{Name: "model", err: errors.New(`ent: missing required field "ChannelMonitorHistory.model"`)}
}
if v, ok := _c.mutation.Model(); ok {
if err := channelmonitorhistory.ModelValidator(v); err != nil {
return &ValidationError{Name: "model", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.model": %w`, err)}
}
}
if _, ok := _c.mutation.Status(); !ok {
return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "ChannelMonitorHistory.status"`)}
}
if v, ok := _c.mutation.Status(); ok {
if err := channelmonitorhistory.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.status": %w`, err)}
}
}
if v, ok := _c.mutation.Message(); ok {
if err := channelmonitorhistory.MessageValidator(v); err != nil {
return &ValidationError{Name: "message", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.message": %w`, err)}
}
}
if _, ok := _c.mutation.CheckedAt(); !ok {
return &ValidationError{Name: "checked_at", err: errors.New(`ent: missing required field "ChannelMonitorHistory.checked_at"`)}
}
if len(_c.mutation.MonitorIDs()) == 0 {
return &ValidationError{Name: "monitor", err: errors.New(`ent: missing required edge "ChannelMonitorHistory.monitor"`)}
}
return nil
}
func (_c *ChannelMonitorHistoryCreate) sqlSave(ctx context.Context) (*ChannelMonitorHistory, error) {
if err := _c.check(); err != nil {
return nil, err
}
_node, _spec := _c.createSpec()
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
id := _spec.ID.Value.(int64)
_node.ID = int64(id)
_c.mutation.id = &_node.ID
_c.mutation.done = true
return _node, nil
}
func (_c *ChannelMonitorHistoryCreate) createSpec() (*ChannelMonitorHistory, *sqlgraph.CreateSpec) {
var (
_node = &ChannelMonitorHistory{config: _c.config}
_spec = sqlgraph.NewCreateSpec(channelmonitorhistory.Table, sqlgraph.NewFieldSpec(channelmonitorhistory.FieldID, field.TypeInt64))
)
_spec.OnConflict = _c.conflict
if value, ok := _c.mutation.Model(); ok {
_spec.SetField(channelmonitorhistory.FieldModel, field.TypeString, value)
_node.Model = value
}
if value, ok := _c.mutation.Status(); ok {
_spec.SetField(channelmonitorhistory.FieldStatus, field.TypeEnum, value)
_node.Status = value
}
if value, ok := _c.mutation.LatencyMs(); ok {
_spec.SetField(channelmonitorhistory.FieldLatencyMs, field.TypeInt, value)
_node.LatencyMs = &value
}
if value, ok := _c.mutation.PingLatencyMs(); ok {
_spec.SetField(channelmonitorhistory.FieldPingLatencyMs, field.TypeInt, value)
_node.PingLatencyMs = &value
}
if value, ok := _c.mutation.Message(); ok {
_spec.SetField(channelmonitorhistory.FieldMessage, field.TypeString, value)
_node.Message = value
}
if value, ok := _c.mutation.CheckedAt(); ok {
_spec.SetField(channelmonitorhistory.FieldCheckedAt, field.TypeTime, value)
_node.CheckedAt = value
}
if nodes := _c.mutation.MonitorIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitorhistory.MonitorTable,
Columns: []string{channelmonitorhistory.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.MonitorID = nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
// of the `INSERT` statement. For example:
//
// client.ChannelMonitorHistory.Create().
// SetMonitorID(v).
// OnConflict(
// // Update the row with the new values
// // the was proposed for insertion.
// sql.ResolveWithNewValues(),
// ).
// // Override some of the fields with custom
// // update values.
// Update(func(u *ent.ChannelMonitorHistoryUpsert) {
// SetMonitorID(v+v).
// }).
// Exec(ctx)
func (_c *ChannelMonitorHistoryCreate) OnConflict(opts ...sql.ConflictOption) *ChannelMonitorHistoryUpsertOne {
_c.conflict = opts
return &ChannelMonitorHistoryUpsertOne{
create: _c,
}
}
// OnConflictColumns calls `OnConflict` and configures the columns
// as conflict target. Using this option is equivalent to using:
//
// client.ChannelMonitorHistory.Create().
// OnConflict(sql.ConflictColumns(columns...)).
// Exec(ctx)
func (_c *ChannelMonitorHistoryCreate) OnConflictColumns(columns ...string) *ChannelMonitorHistoryUpsertOne {
_c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
return &ChannelMonitorHistoryUpsertOne{
create: _c,
}
}
type (
// ChannelMonitorHistoryUpsertOne is the builder for "upsert"-ing
// one ChannelMonitorHistory node.
ChannelMonitorHistoryUpsertOne struct {
create *ChannelMonitorHistoryCreate
}
// ChannelMonitorHistoryUpsert is the "OnConflict" setter.
ChannelMonitorHistoryUpsert struct {
*sql.UpdateSet
}
)
// SetMonitorID sets the "monitor_id" field.
func (u *ChannelMonitorHistoryUpsert) SetMonitorID(v int64) *ChannelMonitorHistoryUpsert {
u.Set(channelmonitorhistory.FieldMonitorID, v)
return u
}
// UpdateMonitorID sets the "monitor_id" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsert) UpdateMonitorID() *ChannelMonitorHistoryUpsert {
u.SetExcluded(channelmonitorhistory.FieldMonitorID)
return u
}
// SetModel sets the "model" field.
func (u *ChannelMonitorHistoryUpsert) SetModel(v string) *ChannelMonitorHistoryUpsert {
u.Set(channelmonitorhistory.FieldModel, v)
return u
}
// UpdateModel sets the "model" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsert) UpdateModel() *ChannelMonitorHistoryUpsert {
u.SetExcluded(channelmonitorhistory.FieldModel)
return u
}
// SetStatus sets the "status" field.
func (u *ChannelMonitorHistoryUpsert) SetStatus(v channelmonitorhistory.Status) *ChannelMonitorHistoryUpsert {
u.Set(channelmonitorhistory.FieldStatus, v)
return u
}
// UpdateStatus sets the "status" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsert) UpdateStatus() *ChannelMonitorHistoryUpsert {
u.SetExcluded(channelmonitorhistory.FieldStatus)
return u
}
// SetLatencyMs sets the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsert) SetLatencyMs(v int) *ChannelMonitorHistoryUpsert {
u.Set(channelmonitorhistory.FieldLatencyMs, v)
return u
}
// UpdateLatencyMs sets the "latency_ms" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsert) UpdateLatencyMs() *ChannelMonitorHistoryUpsert {
u.SetExcluded(channelmonitorhistory.FieldLatencyMs)
return u
}
// AddLatencyMs adds v to the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsert) AddLatencyMs(v int) *ChannelMonitorHistoryUpsert {
u.Add(channelmonitorhistory.FieldLatencyMs, v)
return u
}
// ClearLatencyMs clears the value of the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsert) ClearLatencyMs() *ChannelMonitorHistoryUpsert {
u.SetNull(channelmonitorhistory.FieldLatencyMs)
return u
}
// SetPingLatencyMs sets the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsert) SetPingLatencyMs(v int) *ChannelMonitorHistoryUpsert {
u.Set(channelmonitorhistory.FieldPingLatencyMs, v)
return u
}
// UpdatePingLatencyMs sets the "ping_latency_ms" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsert) UpdatePingLatencyMs() *ChannelMonitorHistoryUpsert {
u.SetExcluded(channelmonitorhistory.FieldPingLatencyMs)
return u
}
// AddPingLatencyMs adds v to the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsert) AddPingLatencyMs(v int) *ChannelMonitorHistoryUpsert {
u.Add(channelmonitorhistory.FieldPingLatencyMs, v)
return u
}
// ClearPingLatencyMs clears the value of the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsert) ClearPingLatencyMs() *ChannelMonitorHistoryUpsert {
u.SetNull(channelmonitorhistory.FieldPingLatencyMs)
return u
}
// SetMessage sets the "message" field.
func (u *ChannelMonitorHistoryUpsert) SetMessage(v string) *ChannelMonitorHistoryUpsert {
u.Set(channelmonitorhistory.FieldMessage, v)
return u
}
// UpdateMessage sets the "message" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsert) UpdateMessage() *ChannelMonitorHistoryUpsert {
u.SetExcluded(channelmonitorhistory.FieldMessage)
return u
}
// ClearMessage clears the value of the "message" field.
func (u *ChannelMonitorHistoryUpsert) ClearMessage() *ChannelMonitorHistoryUpsert {
u.SetNull(channelmonitorhistory.FieldMessage)
return u
}
// SetCheckedAt sets the "checked_at" field.
func (u *ChannelMonitorHistoryUpsert) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpsert {
u.Set(channelmonitorhistory.FieldCheckedAt, v)
return u
}
// UpdateCheckedAt sets the "checked_at" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsert) UpdateCheckedAt() *ChannelMonitorHistoryUpsert {
u.SetExcluded(channelmonitorhistory.FieldCheckedAt)
return u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
// client.ChannelMonitorHistory.Create().
// OnConflict(
// sql.ResolveWithNewValues(),
// ).
// Exec(ctx)
func (u *ChannelMonitorHistoryUpsertOne) UpdateNewValues() *ChannelMonitorHistoryUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
return u
}
// Ignore sets each column to itself in case of conflict.
// Using this option is equivalent to using:
//
// client.ChannelMonitorHistory.Create().
// OnConflict(sql.ResolveWithIgnore()).
// Exec(ctx)
func (u *ChannelMonitorHistoryUpsertOne) Ignore() *ChannelMonitorHistoryUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
return u
}
// DoNothing configures the conflict_action to `DO NOTHING`.
// Supported only by SQLite and PostgreSQL.
func (u *ChannelMonitorHistoryUpsertOne) DoNothing() *ChannelMonitorHistoryUpsertOne {
u.create.conflict = append(u.create.conflict, sql.DoNothing())
return u
}
// Update allows overriding fields `UPDATE` values. See the ChannelMonitorHistoryCreate.OnConflict
// documentation for more info.
func (u *ChannelMonitorHistoryUpsertOne) Update(set func(*ChannelMonitorHistoryUpsert)) *ChannelMonitorHistoryUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
set(&ChannelMonitorHistoryUpsert{UpdateSet: update})
}))
return u
}
// SetMonitorID sets the "monitor_id" field.
func (u *ChannelMonitorHistoryUpsertOne) SetMonitorID(v int64) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetMonitorID(v)
})
}
// UpdateMonitorID sets the "monitor_id" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertOne) UpdateMonitorID() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateMonitorID()
})
}
// SetModel sets the "model" field.
func (u *ChannelMonitorHistoryUpsertOne) SetModel(v string) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetModel(v)
})
}
// UpdateModel sets the "model" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertOne) UpdateModel() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateModel()
})
}
// SetStatus sets the "status" field.
func (u *ChannelMonitorHistoryUpsertOne) SetStatus(v channelmonitorhistory.Status) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetStatus(v)
})
}
// UpdateStatus sets the "status" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertOne) UpdateStatus() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateStatus()
})
}
// SetLatencyMs sets the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsertOne) SetLatencyMs(v int) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetLatencyMs(v)
})
}
// AddLatencyMs adds v to the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsertOne) AddLatencyMs(v int) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.AddLatencyMs(v)
})
}
// UpdateLatencyMs sets the "latency_ms" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertOne) UpdateLatencyMs() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateLatencyMs()
})
}
// ClearLatencyMs clears the value of the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsertOne) ClearLatencyMs() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.ClearLatencyMs()
})
}
// SetPingLatencyMs sets the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsertOne) SetPingLatencyMs(v int) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetPingLatencyMs(v)
})
}
// AddPingLatencyMs adds v to the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsertOne) AddPingLatencyMs(v int) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.AddPingLatencyMs(v)
})
}
// UpdatePingLatencyMs sets the "ping_latency_ms" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertOne) UpdatePingLatencyMs() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdatePingLatencyMs()
})
}
// ClearPingLatencyMs clears the value of the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsertOne) ClearPingLatencyMs() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.ClearPingLatencyMs()
})
}
// SetMessage sets the "message" field.
func (u *ChannelMonitorHistoryUpsertOne) SetMessage(v string) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetMessage(v)
})
}
// UpdateMessage sets the "message" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertOne) UpdateMessage() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateMessage()
})
}
// ClearMessage clears the value of the "message" field.
func (u *ChannelMonitorHistoryUpsertOne) ClearMessage() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.ClearMessage()
})
}
// SetCheckedAt sets the "checked_at" field.
func (u *ChannelMonitorHistoryUpsertOne) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetCheckedAt(v)
})
}
// UpdateCheckedAt sets the "checked_at" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertOne) UpdateCheckedAt() *ChannelMonitorHistoryUpsertOne {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateCheckedAt()
})
}
// Exec executes the query.
func (u *ChannelMonitorHistoryUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
return errors.New("ent: missing options for ChannelMonitorHistoryCreate.OnConflict")
}
return u.create.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (u *ChannelMonitorHistoryUpsertOne) ExecX(ctx context.Context) {
if err := u.create.Exec(ctx); err != nil {
panic(err)
}
}
// Exec executes the UPSERT query and returns the inserted/updated ID.
func (u *ChannelMonitorHistoryUpsertOne) ID(ctx context.Context) (id int64, err error) {
node, err := u.create.Save(ctx)
if err != nil {
return id, err
}
return node.ID, nil
}
// IDX is like ID, but panics if an error occurs.
func (u *ChannelMonitorHistoryUpsertOne) IDX(ctx context.Context) int64 {
id, err := u.ID(ctx)
if err != nil {
panic(err)
}
return id
}
// ChannelMonitorHistoryCreateBulk is the builder for creating many ChannelMonitorHistory entities in bulk.
type ChannelMonitorHistoryCreateBulk struct {
config
err error
builders []*ChannelMonitorHistoryCreate
conflict []sql.ConflictOption
}
// Save creates the ChannelMonitorHistory entities in the database.
func (_c *ChannelMonitorHistoryCreateBulk) Save(ctx context.Context) ([]*ChannelMonitorHistory, error) {
if _c.err != nil {
return nil, _c.err
}
specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
nodes := make([]*ChannelMonitorHistory, len(_c.builders))
mutators := make([]Mutator, len(_c.builders))
for i := range _c.builders {
func(i int, root context.Context) {
builder := _c.builders[i]
builder.defaults()
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*ChannelMonitorHistoryMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err := builder.check(); err != nil {
return nil, err
}
builder.mutation = mutation
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
} else {
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
spec.OnConflict = _c.conflict
// Invoke the actual operation on the latest mutation in the chain.
if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
}
}
if err != nil {
return nil, err
}
mutation.id = &nodes[i].ID
if specs[i].ID.Value != nil {
id := specs[i].ID.Value.(int64)
nodes[i].ID = int64(id)
}
mutation.done = true
return nodes[i], nil
})
for i := len(builder.hooks) - 1; i >= 0; i-- {
mut = builder.hooks[i](mut)
}
mutators[i] = mut
}(i, ctx)
}
if len(mutators) > 0 {
if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
return nil, err
}
}
return nodes, nil
}
// SaveX is like Save, but panics if an error occurs.
func (_c *ChannelMonitorHistoryCreateBulk) SaveX(ctx context.Context) []*ChannelMonitorHistory {
v, err := _c.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (_c *ChannelMonitorHistoryCreateBulk) Exec(ctx context.Context) error {
_, err := _c.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_c *ChannelMonitorHistoryCreateBulk) ExecX(ctx context.Context) {
if err := _c.Exec(ctx); err != nil {
panic(err)
}
}
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
// of the `INSERT` statement. For example:
//
// client.ChannelMonitorHistory.CreateBulk(builders...).
// OnConflict(
// // Update the row with the new values
// // the was proposed for insertion.
// sql.ResolveWithNewValues(),
// ).
// // Override some of the fields with custom
// // update values.
// Update(func(u *ent.ChannelMonitorHistoryUpsert) {
// SetMonitorID(v+v).
// }).
// Exec(ctx)
func (_c *ChannelMonitorHistoryCreateBulk) OnConflict(opts ...sql.ConflictOption) *ChannelMonitorHistoryUpsertBulk {
_c.conflict = opts
return &ChannelMonitorHistoryUpsertBulk{
create: _c,
}
}
// OnConflictColumns calls `OnConflict` and configures the columns
// as conflict target. Using this option is equivalent to using:
//
// client.ChannelMonitorHistory.Create().
// OnConflict(sql.ConflictColumns(columns...)).
// Exec(ctx)
func (_c *ChannelMonitorHistoryCreateBulk) OnConflictColumns(columns ...string) *ChannelMonitorHistoryUpsertBulk {
_c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
return &ChannelMonitorHistoryUpsertBulk{
create: _c,
}
}
// ChannelMonitorHistoryUpsertBulk is the builder for "upsert"-ing
// a bulk of ChannelMonitorHistory nodes.
type ChannelMonitorHistoryUpsertBulk struct {
create *ChannelMonitorHistoryCreateBulk
}
// UpdateNewValues updates the mutable fields using the new values that
// were set on create. Using this option is equivalent to using:
//
// client.ChannelMonitorHistory.Create().
// OnConflict(
// sql.ResolveWithNewValues(),
// ).
// Exec(ctx)
func (u *ChannelMonitorHistoryUpsertBulk) UpdateNewValues() *ChannelMonitorHistoryUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
return u
}
// Ignore sets each column to itself in case of conflict.
// Using this option is equivalent to using:
//
// client.ChannelMonitorHistory.Create().
// OnConflict(sql.ResolveWithIgnore()).
// Exec(ctx)
func (u *ChannelMonitorHistoryUpsertBulk) Ignore() *ChannelMonitorHistoryUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
return u
}
// DoNothing configures the conflict_action to `DO NOTHING`.
// Supported only by SQLite and PostgreSQL.
func (u *ChannelMonitorHistoryUpsertBulk) DoNothing() *ChannelMonitorHistoryUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.DoNothing())
return u
}
// Update allows overriding fields `UPDATE` values. See the ChannelMonitorHistoryCreateBulk.OnConflict
// documentation for more info.
func (u *ChannelMonitorHistoryUpsertBulk) Update(set func(*ChannelMonitorHistoryUpsert)) *ChannelMonitorHistoryUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
set(&ChannelMonitorHistoryUpsert{UpdateSet: update})
}))
return u
}
// SetMonitorID sets the "monitor_id" field.
func (u *ChannelMonitorHistoryUpsertBulk) SetMonitorID(v int64) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetMonitorID(v)
})
}
// UpdateMonitorID sets the "monitor_id" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertBulk) UpdateMonitorID() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateMonitorID()
})
}
// SetModel sets the "model" field.
func (u *ChannelMonitorHistoryUpsertBulk) SetModel(v string) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetModel(v)
})
}
// UpdateModel sets the "model" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertBulk) UpdateModel() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateModel()
})
}
// SetStatus sets the "status" field.
func (u *ChannelMonitorHistoryUpsertBulk) SetStatus(v channelmonitorhistory.Status) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetStatus(v)
})
}
// UpdateStatus sets the "status" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertBulk) UpdateStatus() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateStatus()
})
}
// SetLatencyMs sets the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsertBulk) SetLatencyMs(v int) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetLatencyMs(v)
})
}
// AddLatencyMs adds v to the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsertBulk) AddLatencyMs(v int) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.AddLatencyMs(v)
})
}
// UpdateLatencyMs sets the "latency_ms" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertBulk) UpdateLatencyMs() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateLatencyMs()
})
}
// ClearLatencyMs clears the value of the "latency_ms" field.
func (u *ChannelMonitorHistoryUpsertBulk) ClearLatencyMs() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.ClearLatencyMs()
})
}
// SetPingLatencyMs sets the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsertBulk) SetPingLatencyMs(v int) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetPingLatencyMs(v)
})
}
// AddPingLatencyMs adds v to the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsertBulk) AddPingLatencyMs(v int) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.AddPingLatencyMs(v)
})
}
// UpdatePingLatencyMs sets the "ping_latency_ms" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertBulk) UpdatePingLatencyMs() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdatePingLatencyMs()
})
}
// ClearPingLatencyMs clears the value of the "ping_latency_ms" field.
func (u *ChannelMonitorHistoryUpsertBulk) ClearPingLatencyMs() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.ClearPingLatencyMs()
})
}
// SetMessage sets the "message" field.
func (u *ChannelMonitorHistoryUpsertBulk) SetMessage(v string) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetMessage(v)
})
}
// UpdateMessage sets the "message" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertBulk) UpdateMessage() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateMessage()
})
}
// ClearMessage clears the value of the "message" field.
func (u *ChannelMonitorHistoryUpsertBulk) ClearMessage() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.ClearMessage()
})
}
// SetCheckedAt sets the "checked_at" field.
func (u *ChannelMonitorHistoryUpsertBulk) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.SetCheckedAt(v)
})
}
// UpdateCheckedAt sets the "checked_at" field to the value that was provided on create.
func (u *ChannelMonitorHistoryUpsertBulk) UpdateCheckedAt() *ChannelMonitorHistoryUpsertBulk {
return u.Update(func(s *ChannelMonitorHistoryUpsert) {
s.UpdateCheckedAt()
})
}
// Exec executes the query.
func (u *ChannelMonitorHistoryUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {
return u.create.err
}
for i, b := range u.create.builders {
if len(b.conflict) != 0 {
return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ChannelMonitorHistoryCreateBulk instead", i)
}
}
if len(u.create.conflict) == 0 {
return errors.New("ent: missing options for ChannelMonitorHistoryCreateBulk.OnConflict")
}
return u.create.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (u *ChannelMonitorHistoryUpsertBulk) ExecX(ctx context.Context) {
if err := u.create.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorHistoryDelete is the builder for deleting a ChannelMonitorHistory entity.
type ChannelMonitorHistoryDelete struct {
config
hooks []Hook
mutation *ChannelMonitorHistoryMutation
}
// Where appends a list predicates to the ChannelMonitorHistoryDelete builder.
func (_d *ChannelMonitorHistoryDelete) Where(ps ...predicate.ChannelMonitorHistory) *ChannelMonitorHistoryDelete {
_d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (_d *ChannelMonitorHistoryDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorHistoryDelete) ExecX(ctx context.Context) int {
n, err := _d.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (_d *ChannelMonitorHistoryDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(channelmonitorhistory.Table, sqlgraph.NewFieldSpec(channelmonitorhistory.FieldID, field.TypeInt64))
if ps := _d.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
_d.mutation.done = true
return affected, err
}
// ChannelMonitorHistoryDeleteOne is the builder for deleting a single ChannelMonitorHistory entity.
type ChannelMonitorHistoryDeleteOne struct {
_d *ChannelMonitorHistoryDelete
}
// Where appends a list predicates to the ChannelMonitorHistoryDelete builder.
func (_d *ChannelMonitorHistoryDeleteOne) Where(ps ...predicate.ChannelMonitorHistory) *ChannelMonitorHistoryDeleteOne {
_d._d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query.
func (_d *ChannelMonitorHistoryDeleteOne) Exec(ctx context.Context) error {
n, err := _d._d.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{channelmonitorhistory.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorHistoryDeleteOne) ExecX(ctx context.Context) {
if err := _d.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,643 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"fmt"
"math"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorHistoryQuery is the builder for querying ChannelMonitorHistory entities.
type ChannelMonitorHistoryQuery struct {
config
ctx *QueryContext
order []channelmonitorhistory.OrderOption
inters []Interceptor
predicates []predicate.ChannelMonitorHistory
withMonitor *ChannelMonitorQuery
modifiers []func(*sql.Selector)
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the ChannelMonitorHistoryQuery builder.
func (_q *ChannelMonitorHistoryQuery) Where(ps ...predicate.ChannelMonitorHistory) *ChannelMonitorHistoryQuery {
_q.predicates = append(_q.predicates, ps...)
return _q
}
// Limit the number of records to be returned by this query.
func (_q *ChannelMonitorHistoryQuery) Limit(limit int) *ChannelMonitorHistoryQuery {
_q.ctx.Limit = &limit
return _q
}
// Offset to start from.
func (_q *ChannelMonitorHistoryQuery) Offset(offset int) *ChannelMonitorHistoryQuery {
_q.ctx.Offset = &offset
return _q
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (_q *ChannelMonitorHistoryQuery) Unique(unique bool) *ChannelMonitorHistoryQuery {
_q.ctx.Unique = &unique
return _q
}
// Order specifies how the records should be ordered.
func (_q *ChannelMonitorHistoryQuery) Order(o ...channelmonitorhistory.OrderOption) *ChannelMonitorHistoryQuery {
_q.order = append(_q.order, o...)
return _q
}
// QueryMonitor chains the current query on the "monitor" edge.
func (_q *ChannelMonitorHistoryQuery) QueryMonitor() *ChannelMonitorQuery {
query := (&ChannelMonitorClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitorhistory.Table, channelmonitorhistory.FieldID, selector),
sqlgraph.To(channelmonitor.Table, channelmonitor.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, channelmonitorhistory.MonitorTable, channelmonitorhistory.MonitorColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first ChannelMonitorHistory entity from the query.
// Returns a *NotFoundError when no ChannelMonitorHistory was found.
func (_q *ChannelMonitorHistoryQuery) First(ctx context.Context) (*ChannelMonitorHistory, error) {
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{channelmonitorhistory.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) FirstX(ctx context.Context) *ChannelMonitorHistory {
node, err := _q.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first ChannelMonitorHistory ID from the query.
// Returns a *NotFoundError when no ChannelMonitorHistory ID was found.
func (_q *ChannelMonitorHistoryQuery) FirstID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{channelmonitorhistory.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) FirstIDX(ctx context.Context) int64 {
id, err := _q.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single ChannelMonitorHistory entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one ChannelMonitorHistory entity is found.
// Returns a *NotFoundError when no ChannelMonitorHistory entities are found.
func (_q *ChannelMonitorHistoryQuery) Only(ctx context.Context) (*ChannelMonitorHistory, error) {
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{channelmonitorhistory.Label}
default:
return nil, &NotSingularError{channelmonitorhistory.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) OnlyX(ctx context.Context) *ChannelMonitorHistory {
node, err := _q.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only ChannelMonitorHistory ID in the query.
// Returns a *NotSingularError when more than one ChannelMonitorHistory ID is found.
// Returns a *NotFoundError when no entities are found.
func (_q *ChannelMonitorHistoryQuery) OnlyID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{channelmonitorhistory.Label}
default:
err = &NotSingularError{channelmonitorhistory.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) OnlyIDX(ctx context.Context) int64 {
id, err := _q.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of ChannelMonitorHistories.
func (_q *ChannelMonitorHistoryQuery) All(ctx context.Context) ([]*ChannelMonitorHistory, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*ChannelMonitorHistory, *ChannelMonitorHistoryQuery]()
return withInterceptors[[]*ChannelMonitorHistory](ctx, _q, qr, _q.inters)
}
// AllX is like All, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) AllX(ctx context.Context) []*ChannelMonitorHistory {
nodes, err := _q.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of ChannelMonitorHistory IDs.
func (_q *ChannelMonitorHistoryQuery) IDs(ctx context.Context) (ids []int64, err error) {
if _q.ctx.Unique == nil && _q.path != nil {
_q.Unique(true)
}
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
if err = _q.Select(channelmonitorhistory.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) IDsX(ctx context.Context) []int64 {
ids, err := _q.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (_q *ChannelMonitorHistoryQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
if err := _q.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, _q, querierCount[*ChannelMonitorHistoryQuery](), _q.inters)
}
// CountX is like Count, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) CountX(ctx context.Context) int {
count, err := _q.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (_q *ChannelMonitorHistoryQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
switch _, err := _q.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (_q *ChannelMonitorHistoryQuery) ExistX(ctx context.Context) bool {
exist, err := _q.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the ChannelMonitorHistoryQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (_q *ChannelMonitorHistoryQuery) Clone() *ChannelMonitorHistoryQuery {
if _q == nil {
return nil
}
return &ChannelMonitorHistoryQuery{
config: _q.config,
ctx: _q.ctx.Clone(),
order: append([]channelmonitorhistory.OrderOption{}, _q.order...),
inters: append([]Interceptor{}, _q.inters...),
predicates: append([]predicate.ChannelMonitorHistory{}, _q.predicates...),
withMonitor: _q.withMonitor.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
path: _q.path,
}
}
// WithMonitor tells the query-builder to eager-load the nodes that are connected to
// the "monitor" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *ChannelMonitorHistoryQuery) WithMonitor(opts ...func(*ChannelMonitorQuery)) *ChannelMonitorHistoryQuery {
query := (&ChannelMonitorClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withMonitor = query
return _q
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// MonitorID int64 `json:"monitor_id,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.ChannelMonitorHistory.Query().
// GroupBy(channelmonitorhistory.FieldMonitorID).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (_q *ChannelMonitorHistoryQuery) GroupBy(field string, fields ...string) *ChannelMonitorHistoryGroupBy {
_q.ctx.Fields = append([]string{field}, fields...)
grbuild := &ChannelMonitorHistoryGroupBy{build: _q}
grbuild.flds = &_q.ctx.Fields
grbuild.label = channelmonitorhistory.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// MonitorID int64 `json:"monitor_id,omitempty"`
// }
//
// client.ChannelMonitorHistory.Query().
// Select(channelmonitorhistory.FieldMonitorID).
// Scan(ctx, &v)
func (_q *ChannelMonitorHistoryQuery) Select(fields ...string) *ChannelMonitorHistorySelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
sbuild := &ChannelMonitorHistorySelect{ChannelMonitorHistoryQuery: _q}
sbuild.label = channelmonitorhistory.Label
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a ChannelMonitorHistorySelect configured with the given aggregations.
func (_q *ChannelMonitorHistoryQuery) Aggregate(fns ...AggregateFunc) *ChannelMonitorHistorySelect {
return _q.Select().Aggregate(fns...)
}
func (_q *ChannelMonitorHistoryQuery) prepareQuery(ctx context.Context) error {
for _, inter := range _q.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, _q); err != nil {
return err
}
}
}
for _, f := range _q.ctx.Fields {
if !channelmonitorhistory.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if _q.path != nil {
prev, err := _q.path(ctx)
if err != nil {
return err
}
_q.sql = prev
}
return nil
}
func (_q *ChannelMonitorHistoryQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ChannelMonitorHistory, error) {
var (
nodes = []*ChannelMonitorHistory{}
_spec = _q.querySpec()
loadedTypes = [1]bool{
_q.withMonitor != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*ChannelMonitorHistory).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &ChannelMonitorHistory{config: _q.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := _q.withMonitor; query != nil {
if err := _q.loadMonitor(ctx, query, nodes, nil,
func(n *ChannelMonitorHistory, e *ChannelMonitor) { n.Edges.Monitor = e }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (_q *ChannelMonitorHistoryQuery) loadMonitor(ctx context.Context, query *ChannelMonitorQuery, nodes []*ChannelMonitorHistory, init func(*ChannelMonitorHistory), assign func(*ChannelMonitorHistory, *ChannelMonitor)) error {
ids := make([]int64, 0, len(nodes))
nodeids := make(map[int64][]*ChannelMonitorHistory)
for i := range nodes {
fk := nodes[i].MonitorID
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(channelmonitor.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nodeids[n.ID]
if !ok {
return fmt.Errorf(`unexpected foreign-key "monitor_id" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (_q *ChannelMonitorHistoryQuery) sqlCount(ctx context.Context) (int, error) {
_spec := _q.querySpec()
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
_spec.Node.Columns = _q.ctx.Fields
if len(_q.ctx.Fields) > 0 {
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
}
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
}
func (_q *ChannelMonitorHistoryQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(channelmonitorhistory.Table, channelmonitorhistory.Columns, sqlgraph.NewFieldSpec(channelmonitorhistory.FieldID, field.TypeInt64))
_spec.From = _q.sql
if unique := _q.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if _q.path != nil {
_spec.Unique = true
}
if fields := _q.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, channelmonitorhistory.FieldID)
for i := range fields {
if fields[i] != channelmonitorhistory.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
if _q.withMonitor != nil {
_spec.Node.AddColumnOnce(channelmonitorhistory.FieldMonitorID)
}
}
if ps := _q.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := _q.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := _q.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := _q.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (_q *ChannelMonitorHistoryQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(_q.driver.Dialect())
t1 := builder.Table(channelmonitorhistory.Table)
columns := _q.ctx.Fields
if len(columns) == 0 {
columns = channelmonitorhistory.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if _q.sql != nil {
selector = _q.sql
selector.Select(selector.Columns(columns...)...)
}
if _q.ctx.Unique != nil && *_q.ctx.Unique {
selector.Distinct()
}
for _, m := range _q.modifiers {
m(selector)
}
for _, p := range _q.predicates {
p(selector)
}
for _, p := range _q.order {
p(selector)
}
if offset := _q.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := _q.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
// either committed or rolled-back.
func (_q *ChannelMonitorHistoryQuery) ForUpdate(opts ...sql.LockOption) *ChannelMonitorHistoryQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForUpdate(opts...)
})
return _q
}
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
// on any rows that are read. Other sessions can read the rows, but cannot modify them
// until your transaction commits.
func (_q *ChannelMonitorHistoryQuery) ForShare(opts ...sql.LockOption) *ChannelMonitorHistoryQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForShare(opts...)
})
return _q
}
// ChannelMonitorHistoryGroupBy is the group-by builder for ChannelMonitorHistory entities.
type ChannelMonitorHistoryGroupBy struct {
selector
build *ChannelMonitorHistoryQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (_g *ChannelMonitorHistoryGroupBy) Aggregate(fns ...AggregateFunc) *ChannelMonitorHistoryGroupBy {
_g.fns = append(_g.fns, fns...)
return _g
}
// Scan applies the selector query and scans the result into the given value.
func (_g *ChannelMonitorHistoryGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
if err := _g.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorHistoryQuery, *ChannelMonitorHistoryGroupBy](ctx, _g.build, _g, _g.build.inters, v)
}
func (_g *ChannelMonitorHistoryGroupBy) sqlScan(ctx context.Context, root *ChannelMonitorHistoryQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(_g.fns))
for _, fn := range _g.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
for _, f := range *_g.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*_g.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// ChannelMonitorHistorySelect is the builder for selecting fields of ChannelMonitorHistory entities.
type ChannelMonitorHistorySelect struct {
*ChannelMonitorHistoryQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (_s *ChannelMonitorHistorySelect) Aggregate(fns ...AggregateFunc) *ChannelMonitorHistorySelect {
_s.fns = append(_s.fns, fns...)
return _s
}
// Scan applies the selector query and scans the result into the given value.
func (_s *ChannelMonitorHistorySelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
if err := _s.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorHistoryQuery, *ChannelMonitorHistorySelect](ctx, _s.ChannelMonitorHistoryQuery, _s, _s.inters, v)
}
func (_s *ChannelMonitorHistorySelect) sqlScan(ctx context.Context, root *ChannelMonitorHistoryQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(_s.fns))
for _, fn := range _s.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*_s.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

View File

@ -0,0 +1,635 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorHistoryUpdate is the builder for updating ChannelMonitorHistory entities.
type ChannelMonitorHistoryUpdate struct {
config
hooks []Hook
mutation *ChannelMonitorHistoryMutation
}
// Where appends a list predicates to the ChannelMonitorHistoryUpdate builder.
func (_u *ChannelMonitorHistoryUpdate) Where(ps ...predicate.ChannelMonitorHistory) *ChannelMonitorHistoryUpdate {
_u.mutation.Where(ps...)
return _u
}
// SetMonitorID sets the "monitor_id" field.
func (_u *ChannelMonitorHistoryUpdate) SetMonitorID(v int64) *ChannelMonitorHistoryUpdate {
_u.mutation.SetMonitorID(v)
return _u
}
// SetNillableMonitorID sets the "monitor_id" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdate) SetNillableMonitorID(v *int64) *ChannelMonitorHistoryUpdate {
if v != nil {
_u.SetMonitorID(*v)
}
return _u
}
// SetModel sets the "model" field.
func (_u *ChannelMonitorHistoryUpdate) SetModel(v string) *ChannelMonitorHistoryUpdate {
_u.mutation.SetModel(v)
return _u
}
// SetNillableModel sets the "model" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdate) SetNillableModel(v *string) *ChannelMonitorHistoryUpdate {
if v != nil {
_u.SetModel(*v)
}
return _u
}
// SetStatus sets the "status" field.
func (_u *ChannelMonitorHistoryUpdate) SetStatus(v channelmonitorhistory.Status) *ChannelMonitorHistoryUpdate {
_u.mutation.SetStatus(v)
return _u
}
// SetNillableStatus sets the "status" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdate) SetNillableStatus(v *channelmonitorhistory.Status) *ChannelMonitorHistoryUpdate {
if v != nil {
_u.SetStatus(*v)
}
return _u
}
// SetLatencyMs sets the "latency_ms" field.
func (_u *ChannelMonitorHistoryUpdate) SetLatencyMs(v int) *ChannelMonitorHistoryUpdate {
_u.mutation.ResetLatencyMs()
_u.mutation.SetLatencyMs(v)
return _u
}
// SetNillableLatencyMs sets the "latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdate) SetNillableLatencyMs(v *int) *ChannelMonitorHistoryUpdate {
if v != nil {
_u.SetLatencyMs(*v)
}
return _u
}
// AddLatencyMs adds value to the "latency_ms" field.
func (_u *ChannelMonitorHistoryUpdate) AddLatencyMs(v int) *ChannelMonitorHistoryUpdate {
_u.mutation.AddLatencyMs(v)
return _u
}
// ClearLatencyMs clears the value of the "latency_ms" field.
func (_u *ChannelMonitorHistoryUpdate) ClearLatencyMs() *ChannelMonitorHistoryUpdate {
_u.mutation.ClearLatencyMs()
return _u
}
// SetPingLatencyMs sets the "ping_latency_ms" field.
func (_u *ChannelMonitorHistoryUpdate) SetPingLatencyMs(v int) *ChannelMonitorHistoryUpdate {
_u.mutation.ResetPingLatencyMs()
_u.mutation.SetPingLatencyMs(v)
return _u
}
// SetNillablePingLatencyMs sets the "ping_latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdate) SetNillablePingLatencyMs(v *int) *ChannelMonitorHistoryUpdate {
if v != nil {
_u.SetPingLatencyMs(*v)
}
return _u
}
// AddPingLatencyMs adds value to the "ping_latency_ms" field.
func (_u *ChannelMonitorHistoryUpdate) AddPingLatencyMs(v int) *ChannelMonitorHistoryUpdate {
_u.mutation.AddPingLatencyMs(v)
return _u
}
// ClearPingLatencyMs clears the value of the "ping_latency_ms" field.
func (_u *ChannelMonitorHistoryUpdate) ClearPingLatencyMs() *ChannelMonitorHistoryUpdate {
_u.mutation.ClearPingLatencyMs()
return _u
}
// SetMessage sets the "message" field.
func (_u *ChannelMonitorHistoryUpdate) SetMessage(v string) *ChannelMonitorHistoryUpdate {
_u.mutation.SetMessage(v)
return _u
}
// SetNillableMessage sets the "message" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdate) SetNillableMessage(v *string) *ChannelMonitorHistoryUpdate {
if v != nil {
_u.SetMessage(*v)
}
return _u
}
// ClearMessage clears the value of the "message" field.
func (_u *ChannelMonitorHistoryUpdate) ClearMessage() *ChannelMonitorHistoryUpdate {
_u.mutation.ClearMessage()
return _u
}
// SetCheckedAt sets the "checked_at" field.
func (_u *ChannelMonitorHistoryUpdate) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpdate {
_u.mutation.SetCheckedAt(v)
return _u
}
// SetNillableCheckedAt sets the "checked_at" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdate) SetNillableCheckedAt(v *time.Time) *ChannelMonitorHistoryUpdate {
if v != nil {
_u.SetCheckedAt(*v)
}
return _u
}
// SetMonitor sets the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorHistoryUpdate) SetMonitor(v *ChannelMonitor) *ChannelMonitorHistoryUpdate {
return _u.SetMonitorID(v.ID)
}
// Mutation returns the ChannelMonitorHistoryMutation object of the builder.
func (_u *ChannelMonitorHistoryUpdate) Mutation() *ChannelMonitorHistoryMutation {
return _u.mutation
}
// ClearMonitor clears the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorHistoryUpdate) ClearMonitor() *ChannelMonitorHistoryUpdate {
_u.mutation.ClearMonitor()
return _u
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *ChannelMonitorHistoryUpdate) Save(ctx context.Context) (int, error) {
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *ChannelMonitorHistoryUpdate) SaveX(ctx context.Context) int {
affected, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (_u *ChannelMonitorHistoryUpdate) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *ChannelMonitorHistoryUpdate) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *ChannelMonitorHistoryUpdate) check() error {
if v, ok := _u.mutation.Model(); ok {
if err := channelmonitorhistory.ModelValidator(v); err != nil {
return &ValidationError{Name: "model", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.model": %w`, err)}
}
}
if v, ok := _u.mutation.Status(); ok {
if err := channelmonitorhistory.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.status": %w`, err)}
}
}
if v, ok := _u.mutation.Message(); ok {
if err := channelmonitorhistory.MessageValidator(v); err != nil {
return &ValidationError{Name: "message", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.message": %w`, err)}
}
}
if _u.mutation.MonitorCleared() && len(_u.mutation.MonitorIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "ChannelMonitorHistory.monitor"`)
}
return nil
}
func (_u *ChannelMonitorHistoryUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(channelmonitorhistory.Table, channelmonitorhistory.Columns, sqlgraph.NewFieldSpec(channelmonitorhistory.FieldID, field.TypeInt64))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.Model(); ok {
_spec.SetField(channelmonitorhistory.FieldModel, field.TypeString, value)
}
if value, ok := _u.mutation.Status(); ok {
_spec.SetField(channelmonitorhistory.FieldStatus, field.TypeEnum, value)
}
if value, ok := _u.mutation.LatencyMs(); ok {
_spec.SetField(channelmonitorhistory.FieldLatencyMs, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedLatencyMs(); ok {
_spec.AddField(channelmonitorhistory.FieldLatencyMs, field.TypeInt, value)
}
if _u.mutation.LatencyMsCleared() {
_spec.ClearField(channelmonitorhistory.FieldLatencyMs, field.TypeInt)
}
if value, ok := _u.mutation.PingLatencyMs(); ok {
_spec.SetField(channelmonitorhistory.FieldPingLatencyMs, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedPingLatencyMs(); ok {
_spec.AddField(channelmonitorhistory.FieldPingLatencyMs, field.TypeInt, value)
}
if _u.mutation.PingLatencyMsCleared() {
_spec.ClearField(channelmonitorhistory.FieldPingLatencyMs, field.TypeInt)
}
if value, ok := _u.mutation.Message(); ok {
_spec.SetField(channelmonitorhistory.FieldMessage, field.TypeString, value)
}
if _u.mutation.MessageCleared() {
_spec.ClearField(channelmonitorhistory.FieldMessage, field.TypeString)
}
if value, ok := _u.mutation.CheckedAt(); ok {
_spec.SetField(channelmonitorhistory.FieldCheckedAt, field.TypeTime, value)
}
if _u.mutation.MonitorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitorhistory.MonitorTable,
Columns: []string{channelmonitorhistory.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.MonitorIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitorhistory.MonitorTable,
Columns: []string{channelmonitorhistory.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{channelmonitorhistory.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
_u.mutation.done = true
return _node, nil
}
// ChannelMonitorHistoryUpdateOne is the builder for updating a single ChannelMonitorHistory entity.
type ChannelMonitorHistoryUpdateOne struct {
config
fields []string
hooks []Hook
mutation *ChannelMonitorHistoryMutation
}
// SetMonitorID sets the "monitor_id" field.
func (_u *ChannelMonitorHistoryUpdateOne) SetMonitorID(v int64) *ChannelMonitorHistoryUpdateOne {
_u.mutation.SetMonitorID(v)
return _u
}
// SetNillableMonitorID sets the "monitor_id" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdateOne) SetNillableMonitorID(v *int64) *ChannelMonitorHistoryUpdateOne {
if v != nil {
_u.SetMonitorID(*v)
}
return _u
}
// SetModel sets the "model" field.
func (_u *ChannelMonitorHistoryUpdateOne) SetModel(v string) *ChannelMonitorHistoryUpdateOne {
_u.mutation.SetModel(v)
return _u
}
// SetNillableModel sets the "model" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdateOne) SetNillableModel(v *string) *ChannelMonitorHistoryUpdateOne {
if v != nil {
_u.SetModel(*v)
}
return _u
}
// SetStatus sets the "status" field.
func (_u *ChannelMonitorHistoryUpdateOne) SetStatus(v channelmonitorhistory.Status) *ChannelMonitorHistoryUpdateOne {
_u.mutation.SetStatus(v)
return _u
}
// SetNillableStatus sets the "status" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdateOne) SetNillableStatus(v *channelmonitorhistory.Status) *ChannelMonitorHistoryUpdateOne {
if v != nil {
_u.SetStatus(*v)
}
return _u
}
// SetLatencyMs sets the "latency_ms" field.
func (_u *ChannelMonitorHistoryUpdateOne) SetLatencyMs(v int) *ChannelMonitorHistoryUpdateOne {
_u.mutation.ResetLatencyMs()
_u.mutation.SetLatencyMs(v)
return _u
}
// SetNillableLatencyMs sets the "latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdateOne) SetNillableLatencyMs(v *int) *ChannelMonitorHistoryUpdateOne {
if v != nil {
_u.SetLatencyMs(*v)
}
return _u
}
// AddLatencyMs adds value to the "latency_ms" field.
func (_u *ChannelMonitorHistoryUpdateOne) AddLatencyMs(v int) *ChannelMonitorHistoryUpdateOne {
_u.mutation.AddLatencyMs(v)
return _u
}
// ClearLatencyMs clears the value of the "latency_ms" field.
func (_u *ChannelMonitorHistoryUpdateOne) ClearLatencyMs() *ChannelMonitorHistoryUpdateOne {
_u.mutation.ClearLatencyMs()
return _u
}
// SetPingLatencyMs sets the "ping_latency_ms" field.
func (_u *ChannelMonitorHistoryUpdateOne) SetPingLatencyMs(v int) *ChannelMonitorHistoryUpdateOne {
_u.mutation.ResetPingLatencyMs()
_u.mutation.SetPingLatencyMs(v)
return _u
}
// SetNillablePingLatencyMs sets the "ping_latency_ms" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdateOne) SetNillablePingLatencyMs(v *int) *ChannelMonitorHistoryUpdateOne {
if v != nil {
_u.SetPingLatencyMs(*v)
}
return _u
}
// AddPingLatencyMs adds value to the "ping_latency_ms" field.
func (_u *ChannelMonitorHistoryUpdateOne) AddPingLatencyMs(v int) *ChannelMonitorHistoryUpdateOne {
_u.mutation.AddPingLatencyMs(v)
return _u
}
// ClearPingLatencyMs clears the value of the "ping_latency_ms" field.
func (_u *ChannelMonitorHistoryUpdateOne) ClearPingLatencyMs() *ChannelMonitorHistoryUpdateOne {
_u.mutation.ClearPingLatencyMs()
return _u
}
// SetMessage sets the "message" field.
func (_u *ChannelMonitorHistoryUpdateOne) SetMessage(v string) *ChannelMonitorHistoryUpdateOne {
_u.mutation.SetMessage(v)
return _u
}
// SetNillableMessage sets the "message" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdateOne) SetNillableMessage(v *string) *ChannelMonitorHistoryUpdateOne {
if v != nil {
_u.SetMessage(*v)
}
return _u
}
// ClearMessage clears the value of the "message" field.
func (_u *ChannelMonitorHistoryUpdateOne) ClearMessage() *ChannelMonitorHistoryUpdateOne {
_u.mutation.ClearMessage()
return _u
}
// SetCheckedAt sets the "checked_at" field.
func (_u *ChannelMonitorHistoryUpdateOne) SetCheckedAt(v time.Time) *ChannelMonitorHistoryUpdateOne {
_u.mutation.SetCheckedAt(v)
return _u
}
// SetNillableCheckedAt sets the "checked_at" field if the given value is not nil.
func (_u *ChannelMonitorHistoryUpdateOne) SetNillableCheckedAt(v *time.Time) *ChannelMonitorHistoryUpdateOne {
if v != nil {
_u.SetCheckedAt(*v)
}
return _u
}
// SetMonitor sets the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorHistoryUpdateOne) SetMonitor(v *ChannelMonitor) *ChannelMonitorHistoryUpdateOne {
return _u.SetMonitorID(v.ID)
}
// Mutation returns the ChannelMonitorHistoryMutation object of the builder.
func (_u *ChannelMonitorHistoryUpdateOne) Mutation() *ChannelMonitorHistoryMutation {
return _u.mutation
}
// ClearMonitor clears the "monitor" edge to the ChannelMonitor entity.
func (_u *ChannelMonitorHistoryUpdateOne) ClearMonitor() *ChannelMonitorHistoryUpdateOne {
_u.mutation.ClearMonitor()
return _u
}
// Where appends a list predicates to the ChannelMonitorHistoryUpdate builder.
func (_u *ChannelMonitorHistoryUpdateOne) Where(ps ...predicate.ChannelMonitorHistory) *ChannelMonitorHistoryUpdateOne {
_u.mutation.Where(ps...)
return _u
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (_u *ChannelMonitorHistoryUpdateOne) Select(field string, fields ...string) *ChannelMonitorHistoryUpdateOne {
_u.fields = append([]string{field}, fields...)
return _u
}
// Save executes the query and returns the updated ChannelMonitorHistory entity.
func (_u *ChannelMonitorHistoryUpdateOne) Save(ctx context.Context) (*ChannelMonitorHistory, error) {
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *ChannelMonitorHistoryUpdateOne) SaveX(ctx context.Context) *ChannelMonitorHistory {
node, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (_u *ChannelMonitorHistoryUpdateOne) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *ChannelMonitorHistoryUpdateOne) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *ChannelMonitorHistoryUpdateOne) check() error {
if v, ok := _u.mutation.Model(); ok {
if err := channelmonitorhistory.ModelValidator(v); err != nil {
return &ValidationError{Name: "model", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.model": %w`, err)}
}
}
if v, ok := _u.mutation.Status(); ok {
if err := channelmonitorhistory.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.status": %w`, err)}
}
}
if v, ok := _u.mutation.Message(); ok {
if err := channelmonitorhistory.MessageValidator(v); err != nil {
return &ValidationError{Name: "message", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorHistory.message": %w`, err)}
}
}
if _u.mutation.MonitorCleared() && len(_u.mutation.MonitorIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "ChannelMonitorHistory.monitor"`)
}
return nil
}
func (_u *ChannelMonitorHistoryUpdateOne) sqlSave(ctx context.Context) (_node *ChannelMonitorHistory, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(channelmonitorhistory.Table, channelmonitorhistory.Columns, sqlgraph.NewFieldSpec(channelmonitorhistory.FieldID, field.TypeInt64))
id, ok := _u.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ChannelMonitorHistory.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, channelmonitorhistory.FieldID)
for _, f := range fields {
if !channelmonitorhistory.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != channelmonitorhistory.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.Model(); ok {
_spec.SetField(channelmonitorhistory.FieldModel, field.TypeString, value)
}
if value, ok := _u.mutation.Status(); ok {
_spec.SetField(channelmonitorhistory.FieldStatus, field.TypeEnum, value)
}
if value, ok := _u.mutation.LatencyMs(); ok {
_spec.SetField(channelmonitorhistory.FieldLatencyMs, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedLatencyMs(); ok {
_spec.AddField(channelmonitorhistory.FieldLatencyMs, field.TypeInt, value)
}
if _u.mutation.LatencyMsCleared() {
_spec.ClearField(channelmonitorhistory.FieldLatencyMs, field.TypeInt)
}
if value, ok := _u.mutation.PingLatencyMs(); ok {
_spec.SetField(channelmonitorhistory.FieldPingLatencyMs, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedPingLatencyMs(); ok {
_spec.AddField(channelmonitorhistory.FieldPingLatencyMs, field.TypeInt, value)
}
if _u.mutation.PingLatencyMsCleared() {
_spec.ClearField(channelmonitorhistory.FieldPingLatencyMs, field.TypeInt)
}
if value, ok := _u.mutation.Message(); ok {
_spec.SetField(channelmonitorhistory.FieldMessage, field.TypeString, value)
}
if _u.mutation.MessageCleared() {
_spec.ClearField(channelmonitorhistory.FieldMessage, field.TypeString)
}
if value, ok := _u.mutation.CheckedAt(); ok {
_spec.SetField(channelmonitorhistory.FieldCheckedAt, field.TypeTime, value)
}
if _u.mutation.MonitorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitorhistory.MonitorTable,
Columns: []string{channelmonitorhistory.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.MonitorIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: channelmonitorhistory.MonitorTable,
Columns: []string{channelmonitorhistory.MonitorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &ChannelMonitorHistory{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{channelmonitorhistory.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
_u.mutation.done = true
return _node, nil
}

View File

@ -0,0 +1,216 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"encoding/json"
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
)
// ChannelMonitorRequestTemplate is the model entity for the ChannelMonitorRequestTemplate schema.
type ChannelMonitorRequestTemplate struct {
config `json:"-"`
// ID of the ent.
ID int64 `json:"id,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// UpdatedAt holds the value of the "updated_at" field.
UpdatedAt time.Time `json:"updated_at,omitempty"`
// Name holds the value of the "name" field.
Name string `json:"name,omitempty"`
// Provider holds the value of the "provider" field.
Provider channelmonitorrequesttemplate.Provider `json:"provider,omitempty"`
// Description holds the value of the "description" field.
Description string `json:"description,omitempty"`
// ExtraHeaders holds the value of the "extra_headers" field.
ExtraHeaders map[string]string `json:"extra_headers,omitempty"`
// BodyOverrideMode holds the value of the "body_override_mode" field.
BodyOverrideMode string `json:"body_override_mode,omitempty"`
// BodyOverride holds the value of the "body_override" field.
BodyOverride map[string]interface{} `json:"body_override,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the ChannelMonitorRequestTemplateQuery when eager-loading is set.
Edges ChannelMonitorRequestTemplateEdges `json:"edges"`
selectValues sql.SelectValues
}
// ChannelMonitorRequestTemplateEdges holds the relations/edges for other nodes in the graph.
type ChannelMonitorRequestTemplateEdges struct {
// Monitors holds the value of the monitors edge.
Monitors []*ChannelMonitor `json:"monitors,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// MonitorsOrErr returns the Monitors value or an error if the edge
// was not loaded in eager-loading.
func (e ChannelMonitorRequestTemplateEdges) MonitorsOrErr() ([]*ChannelMonitor, error) {
if e.loadedTypes[0] {
return e.Monitors, nil
}
return nil, &NotLoadedError{edge: "monitors"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*ChannelMonitorRequestTemplate) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case channelmonitorrequesttemplate.FieldExtraHeaders, channelmonitorrequesttemplate.FieldBodyOverride:
values[i] = new([]byte)
case channelmonitorrequesttemplate.FieldID:
values[i] = new(sql.NullInt64)
case channelmonitorrequesttemplate.FieldName, channelmonitorrequesttemplate.FieldProvider, channelmonitorrequesttemplate.FieldDescription, channelmonitorrequesttemplate.FieldBodyOverrideMode:
values[i] = new(sql.NullString)
case channelmonitorrequesttemplate.FieldCreatedAt, channelmonitorrequesttemplate.FieldUpdatedAt:
values[i] = new(sql.NullTime)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the ChannelMonitorRequestTemplate fields.
func (_m *ChannelMonitorRequestTemplate) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case channelmonitorrequesttemplate.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
_m.ID = int64(value.Int64)
case channelmonitorrequesttemplate.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
_m.CreatedAt = value.Time
}
case channelmonitorrequesttemplate.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
_m.UpdatedAt = value.Time
}
case channelmonitorrequesttemplate.FieldName:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field name", values[i])
} else if value.Valid {
_m.Name = value.String
}
case channelmonitorrequesttemplate.FieldProvider:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field provider", values[i])
} else if value.Valid {
_m.Provider = channelmonitorrequesttemplate.Provider(value.String)
}
case channelmonitorrequesttemplate.FieldDescription:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field description", values[i])
} else if value.Valid {
_m.Description = value.String
}
case channelmonitorrequesttemplate.FieldExtraHeaders:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field extra_headers", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &_m.ExtraHeaders); err != nil {
return fmt.Errorf("unmarshal field extra_headers: %w", err)
}
}
case channelmonitorrequesttemplate.FieldBodyOverrideMode:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field body_override_mode", values[i])
} else if value.Valid {
_m.BodyOverrideMode = value.String
}
case channelmonitorrequesttemplate.FieldBodyOverride:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field body_override", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &_m.BodyOverride); err != nil {
return fmt.Errorf("unmarshal field body_override: %w", err)
}
}
default:
_m.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the ChannelMonitorRequestTemplate.
// This includes values selected through modifiers, order, etc.
func (_m *ChannelMonitorRequestTemplate) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryMonitors queries the "monitors" edge of the ChannelMonitorRequestTemplate entity.
func (_m *ChannelMonitorRequestTemplate) QueryMonitors() *ChannelMonitorQuery {
return NewChannelMonitorRequestTemplateClient(_m.config).QueryMonitors(_m)
}
// Update returns a builder for updating this ChannelMonitorRequestTemplate.
// Note that you need to call ChannelMonitorRequestTemplate.Unwrap() before calling this method if this ChannelMonitorRequestTemplate
// was returned from a transaction, and the transaction was committed or rolled back.
func (_m *ChannelMonitorRequestTemplate) Update() *ChannelMonitorRequestTemplateUpdateOne {
return NewChannelMonitorRequestTemplateClient(_m.config).UpdateOne(_m)
}
// Unwrap unwraps the ChannelMonitorRequestTemplate entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (_m *ChannelMonitorRequestTemplate) Unwrap() *ChannelMonitorRequestTemplate {
_tx, ok := _m.config.driver.(*txDriver)
if !ok {
panic("ent: ChannelMonitorRequestTemplate is not a transactional entity")
}
_m.config.driver = _tx.drv
return _m
}
// String implements the fmt.Stringer.
func (_m *ChannelMonitorRequestTemplate) String() string {
var builder strings.Builder
builder.WriteString("ChannelMonitorRequestTemplate(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("created_at=")
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("updated_at=")
builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("name=")
builder.WriteString(_m.Name)
builder.WriteString(", ")
builder.WriteString("provider=")
builder.WriteString(fmt.Sprintf("%v", _m.Provider))
builder.WriteString(", ")
builder.WriteString("description=")
builder.WriteString(_m.Description)
builder.WriteString(", ")
builder.WriteString("extra_headers=")
builder.WriteString(fmt.Sprintf("%v", _m.ExtraHeaders))
builder.WriteString(", ")
builder.WriteString("body_override_mode=")
builder.WriteString(_m.BodyOverrideMode)
builder.WriteString(", ")
builder.WriteString("body_override=")
builder.WriteString(fmt.Sprintf("%v", _m.BodyOverride))
builder.WriteByte(')')
return builder.String()
}
// ChannelMonitorRequestTemplates is a parsable slice of ChannelMonitorRequestTemplate.
type ChannelMonitorRequestTemplates []*ChannelMonitorRequestTemplate

View File

@ -0,0 +1,172 @@
// Code generated by ent, DO NOT EDIT.
package channelmonitorrequesttemplate
import (
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
const (
// Label holds the string label denoting the channelmonitorrequesttemplate type in the database.
Label = "channel_monitor_request_template"
// 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"
// FieldDescription holds the string denoting the description field in the database.
FieldDescription = "description"
// FieldExtraHeaders holds the string denoting the extra_headers field in the database.
FieldExtraHeaders = "extra_headers"
// FieldBodyOverrideMode holds the string denoting the body_override_mode field in the database.
FieldBodyOverrideMode = "body_override_mode"
// FieldBodyOverride holds the string denoting the body_override field in the database.
FieldBodyOverride = "body_override"
// EdgeMonitors holds the string denoting the monitors edge name in mutations.
EdgeMonitors = "monitors"
// Table holds the table name of the channelmonitorrequesttemplate in the database.
Table = "channel_monitor_request_templates"
// MonitorsTable is the table that holds the monitors relation/edge.
MonitorsTable = "channel_monitors"
// MonitorsInverseTable is the table name for the ChannelMonitor entity.
// It exists in this package in order to avoid circular dependency with the "channelmonitor" package.
MonitorsInverseTable = "channel_monitors"
// MonitorsColumn is the table column denoting the monitors relation/edge.
MonitorsColumn = "template_id"
)
// Columns holds all SQL columns for channelmonitorrequesttemplate fields.
var Columns = []string{
FieldID,
FieldCreatedAt,
FieldUpdatedAt,
FieldName,
FieldProvider,
FieldDescription,
FieldExtraHeaders,
FieldBodyOverrideMode,
FieldBodyOverride,
}
// 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
// DefaultDescription holds the default value on creation for the "description" field.
DefaultDescription string
// DescriptionValidator is a validator for the "description" field. It is called by the builders before save.
DescriptionValidator func(string) error
// DefaultExtraHeaders holds the default value on creation for the "extra_headers" field.
DefaultExtraHeaders map[string]string
// DefaultBodyOverrideMode holds the default value on creation for the "body_override_mode" field.
DefaultBodyOverrideMode string
// BodyOverrideModeValidator is a validator for the "body_override_mode" field. It is called by the builders before save.
BodyOverrideModeValidator func(string) 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("channelmonitorrequesttemplate: invalid enum value for provider field: %q", pr)
}
}
// OrderOption defines the ordering options for the ChannelMonitorRequestTemplate 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()
}
// ByDescription orders the results by the description field.
func ByDescription(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDescription, opts...).ToFunc()
}
// ByBodyOverrideMode orders the results by the body_override_mode field.
func ByBodyOverrideMode(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldBodyOverrideMode, opts...).ToFunc()
}
// ByMonitorsCount orders the results by monitors count.
func ByMonitorsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newMonitorsStep(), opts...)
}
}
// ByMonitors orders the results by monitors terms.
func ByMonitors(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newMonitorsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newMonitorsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(MonitorsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, MonitorsTable, MonitorsColumn),
)
}

View File

@ -0,0 +1,434 @@
// Code generated by ent, DO NOT EDIT.
package channelmonitorrequesttemplate
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int64) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLTE(FieldID, id))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldCreatedAt, v))
}
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldUpdatedAt, v))
}
// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
func Name(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldName, v))
}
// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ.
func Description(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldDescription, v))
}
// BodyOverrideMode applies equality check predicate on the "body_override_mode" field. It's identical to BodyOverrideModeEQ.
func BodyOverrideMode(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldBodyOverrideMode, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldCreatedAt, v))
}
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNEQ(FieldCreatedAt, v))
}
// CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIn(FieldCreatedAt, vs...))
}
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotIn(FieldCreatedAt, vs...))
}
// CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGT(FieldCreatedAt, v))
}
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGTE(FieldCreatedAt, v))
}
// CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLT(FieldCreatedAt, v))
}
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLTE(FieldCreatedAt, v))
}
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldUpdatedAt, v))
}
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNEQ(FieldUpdatedAt, v))
}
// UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIn(FieldUpdatedAt, vs...))
}
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotIn(FieldUpdatedAt, vs...))
}
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGT(FieldUpdatedAt, v))
}
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGTE(FieldUpdatedAt, v))
}
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLT(FieldUpdatedAt, v))
}
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLTE(FieldUpdatedAt, v))
}
// NameEQ applies the EQ predicate on the "name" field.
func NameEQ(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldName, v))
}
// NameNEQ applies the NEQ predicate on the "name" field.
func NameNEQ(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNEQ(FieldName, v))
}
// NameIn applies the In predicate on the "name" field.
func NameIn(vs ...string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIn(FieldName, vs...))
}
// NameNotIn applies the NotIn predicate on the "name" field.
func NameNotIn(vs ...string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotIn(FieldName, vs...))
}
// NameGT applies the GT predicate on the "name" field.
func NameGT(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGT(FieldName, v))
}
// NameGTE applies the GTE predicate on the "name" field.
func NameGTE(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGTE(FieldName, v))
}
// NameLT applies the LT predicate on the "name" field.
func NameLT(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLT(FieldName, v))
}
// NameLTE applies the LTE predicate on the "name" field.
func NameLTE(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLTE(FieldName, v))
}
// NameContains applies the Contains predicate on the "name" field.
func NameContains(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldContains(FieldName, v))
}
// NameHasPrefix applies the HasPrefix predicate on the "name" field.
func NameHasPrefix(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldHasPrefix(FieldName, v))
}
// NameHasSuffix applies the HasSuffix predicate on the "name" field.
func NameHasSuffix(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldHasSuffix(FieldName, v))
}
// NameEqualFold applies the EqualFold predicate on the "name" field.
func NameEqualFold(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEqualFold(FieldName, v))
}
// NameContainsFold applies the ContainsFold predicate on the "name" field.
func NameContainsFold(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldContainsFold(FieldName, v))
}
// ProviderEQ applies the EQ predicate on the "provider" field.
func ProviderEQ(v Provider) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldProvider, v))
}
// ProviderNEQ applies the NEQ predicate on the "provider" field.
func ProviderNEQ(v Provider) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNEQ(FieldProvider, v))
}
// ProviderIn applies the In predicate on the "provider" field.
func ProviderIn(vs ...Provider) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIn(FieldProvider, vs...))
}
// ProviderNotIn applies the NotIn predicate on the "provider" field.
func ProviderNotIn(vs ...Provider) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotIn(FieldProvider, vs...))
}
// DescriptionEQ applies the EQ predicate on the "description" field.
func DescriptionEQ(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldDescription, v))
}
// DescriptionNEQ applies the NEQ predicate on the "description" field.
func DescriptionNEQ(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNEQ(FieldDescription, v))
}
// DescriptionIn applies the In predicate on the "description" field.
func DescriptionIn(vs ...string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIn(FieldDescription, vs...))
}
// DescriptionNotIn applies the NotIn predicate on the "description" field.
func DescriptionNotIn(vs ...string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotIn(FieldDescription, vs...))
}
// DescriptionGT applies the GT predicate on the "description" field.
func DescriptionGT(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGT(FieldDescription, v))
}
// DescriptionGTE applies the GTE predicate on the "description" field.
func DescriptionGTE(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGTE(FieldDescription, v))
}
// DescriptionLT applies the LT predicate on the "description" field.
func DescriptionLT(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLT(FieldDescription, v))
}
// DescriptionLTE applies the LTE predicate on the "description" field.
func DescriptionLTE(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLTE(FieldDescription, v))
}
// DescriptionContains applies the Contains predicate on the "description" field.
func DescriptionContains(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldContains(FieldDescription, v))
}
// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field.
func DescriptionHasPrefix(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldHasPrefix(FieldDescription, v))
}
// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field.
func DescriptionHasSuffix(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldHasSuffix(FieldDescription, v))
}
// DescriptionIsNil applies the IsNil predicate on the "description" field.
func DescriptionIsNil() predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIsNull(FieldDescription))
}
// DescriptionNotNil applies the NotNil predicate on the "description" field.
func DescriptionNotNil() predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotNull(FieldDescription))
}
// DescriptionEqualFold applies the EqualFold predicate on the "description" field.
func DescriptionEqualFold(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEqualFold(FieldDescription, v))
}
// DescriptionContainsFold applies the ContainsFold predicate on the "description" field.
func DescriptionContainsFold(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldContainsFold(FieldDescription, v))
}
// BodyOverrideModeEQ applies the EQ predicate on the "body_override_mode" field.
func BodyOverrideModeEQ(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEQ(FieldBodyOverrideMode, v))
}
// BodyOverrideModeNEQ applies the NEQ predicate on the "body_override_mode" field.
func BodyOverrideModeNEQ(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNEQ(FieldBodyOverrideMode, v))
}
// BodyOverrideModeIn applies the In predicate on the "body_override_mode" field.
func BodyOverrideModeIn(vs ...string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIn(FieldBodyOverrideMode, vs...))
}
// BodyOverrideModeNotIn applies the NotIn predicate on the "body_override_mode" field.
func BodyOverrideModeNotIn(vs ...string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotIn(FieldBodyOverrideMode, vs...))
}
// BodyOverrideModeGT applies the GT predicate on the "body_override_mode" field.
func BodyOverrideModeGT(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGT(FieldBodyOverrideMode, v))
}
// BodyOverrideModeGTE applies the GTE predicate on the "body_override_mode" field.
func BodyOverrideModeGTE(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldGTE(FieldBodyOverrideMode, v))
}
// BodyOverrideModeLT applies the LT predicate on the "body_override_mode" field.
func BodyOverrideModeLT(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLT(FieldBodyOverrideMode, v))
}
// BodyOverrideModeLTE applies the LTE predicate on the "body_override_mode" field.
func BodyOverrideModeLTE(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldLTE(FieldBodyOverrideMode, v))
}
// BodyOverrideModeContains applies the Contains predicate on the "body_override_mode" field.
func BodyOverrideModeContains(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldContains(FieldBodyOverrideMode, v))
}
// BodyOverrideModeHasPrefix applies the HasPrefix predicate on the "body_override_mode" field.
func BodyOverrideModeHasPrefix(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldHasPrefix(FieldBodyOverrideMode, v))
}
// BodyOverrideModeHasSuffix applies the HasSuffix predicate on the "body_override_mode" field.
func BodyOverrideModeHasSuffix(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldHasSuffix(FieldBodyOverrideMode, v))
}
// BodyOverrideModeEqualFold applies the EqualFold predicate on the "body_override_mode" field.
func BodyOverrideModeEqualFold(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldEqualFold(FieldBodyOverrideMode, v))
}
// BodyOverrideModeContainsFold applies the ContainsFold predicate on the "body_override_mode" field.
func BodyOverrideModeContainsFold(v string) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldContainsFold(FieldBodyOverrideMode, v))
}
// BodyOverrideIsNil applies the IsNil predicate on the "body_override" field.
func BodyOverrideIsNil() predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldIsNull(FieldBodyOverride))
}
// BodyOverrideNotNil applies the NotNil predicate on the "body_override" field.
func BodyOverrideNotNil() predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.FieldNotNull(FieldBodyOverride))
}
// HasMonitors applies the HasEdge predicate on the "monitors" edge.
func HasMonitors() predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, MonitorsTable, MonitorsColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasMonitorsWith applies the HasEdge predicate on the "monitors" edge with a given conditions (other predicates).
func HasMonitorsWith(preds ...predicate.ChannelMonitor) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(func(s *sql.Selector) {
step := newMonitorsStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.ChannelMonitorRequestTemplate) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.ChannelMonitorRequestTemplate) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.ChannelMonitorRequestTemplate) predicate.ChannelMonitorRequestTemplate {
return predicate.ChannelMonitorRequestTemplate(sql.NotPredicates(p))
}

View File

@ -0,0 +1,942 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
)
// ChannelMonitorRequestTemplateCreate is the builder for creating a ChannelMonitorRequestTemplate entity.
type ChannelMonitorRequestTemplateCreate struct {
config
mutation *ChannelMonitorRequestTemplateMutation
hooks []Hook
conflict []sql.ConflictOption
}
// SetCreatedAt sets the "created_at" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetCreatedAt(v time.Time) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetCreatedAt(v)
return _c
}
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
func (_c *ChannelMonitorRequestTemplateCreate) SetNillableCreatedAt(v *time.Time) *ChannelMonitorRequestTemplateCreate {
if v != nil {
_c.SetCreatedAt(*v)
}
return _c
}
// SetUpdatedAt sets the "updated_at" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetUpdatedAt(v time.Time) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetUpdatedAt(v)
return _c
}
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
func (_c *ChannelMonitorRequestTemplateCreate) SetNillableUpdatedAt(v *time.Time) *ChannelMonitorRequestTemplateCreate {
if v != nil {
_c.SetUpdatedAt(*v)
}
return _c
}
// SetName sets the "name" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetName(v string) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetName(v)
return _c
}
// SetProvider sets the "provider" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetProvider(v channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetProvider(v)
return _c
}
// SetDescription sets the "description" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetDescription(v string) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetDescription(v)
return _c
}
// SetNillableDescription sets the "description" field if the given value is not nil.
func (_c *ChannelMonitorRequestTemplateCreate) SetNillableDescription(v *string) *ChannelMonitorRequestTemplateCreate {
if v != nil {
_c.SetDescription(*v)
}
return _c
}
// SetExtraHeaders sets the "extra_headers" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetExtraHeaders(v map[string]string) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetExtraHeaders(v)
return _c
}
// SetBodyOverrideMode sets the "body_override_mode" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetBodyOverrideMode(v string) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetBodyOverrideMode(v)
return _c
}
// SetNillableBodyOverrideMode sets the "body_override_mode" field if the given value is not nil.
func (_c *ChannelMonitorRequestTemplateCreate) SetNillableBodyOverrideMode(v *string) *ChannelMonitorRequestTemplateCreate {
if v != nil {
_c.SetBodyOverrideMode(*v)
}
return _c
}
// SetBodyOverride sets the "body_override" field.
func (_c *ChannelMonitorRequestTemplateCreate) SetBodyOverride(v map[string]interface{}) *ChannelMonitorRequestTemplateCreate {
_c.mutation.SetBodyOverride(v)
return _c
}
// AddMonitorIDs adds the "monitors" edge to the ChannelMonitor entity by IDs.
func (_c *ChannelMonitorRequestTemplateCreate) AddMonitorIDs(ids ...int64) *ChannelMonitorRequestTemplateCreate {
_c.mutation.AddMonitorIDs(ids...)
return _c
}
// AddMonitors adds the "monitors" edges to the ChannelMonitor entity.
func (_c *ChannelMonitorRequestTemplateCreate) AddMonitors(v ...*ChannelMonitor) *ChannelMonitorRequestTemplateCreate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _c.AddMonitorIDs(ids...)
}
// Mutation returns the ChannelMonitorRequestTemplateMutation object of the builder.
func (_c *ChannelMonitorRequestTemplateCreate) Mutation() *ChannelMonitorRequestTemplateMutation {
return _c.mutation
}
// Save creates the ChannelMonitorRequestTemplate in the database.
func (_c *ChannelMonitorRequestTemplateCreate) Save(ctx context.Context) (*ChannelMonitorRequestTemplate, error) {
_c.defaults()
return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
}
// SaveX calls Save and panics if Save returns an error.
func (_c *ChannelMonitorRequestTemplateCreate) SaveX(ctx context.Context) *ChannelMonitorRequestTemplate {
v, err := _c.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (_c *ChannelMonitorRequestTemplateCreate) Exec(ctx context.Context) error {
_, err := _c.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_c *ChannelMonitorRequestTemplateCreate) ExecX(ctx context.Context) {
if err := _c.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_c *ChannelMonitorRequestTemplateCreate) defaults() {
if _, ok := _c.mutation.CreatedAt(); !ok {
v := channelmonitorrequesttemplate.DefaultCreatedAt()
_c.mutation.SetCreatedAt(v)
}
if _, ok := _c.mutation.UpdatedAt(); !ok {
v := channelmonitorrequesttemplate.DefaultUpdatedAt()
_c.mutation.SetUpdatedAt(v)
}
if _, ok := _c.mutation.Description(); !ok {
v := channelmonitorrequesttemplate.DefaultDescription
_c.mutation.SetDescription(v)
}
if _, ok := _c.mutation.ExtraHeaders(); !ok {
v := channelmonitorrequesttemplate.DefaultExtraHeaders
_c.mutation.SetExtraHeaders(v)
}
if _, ok := _c.mutation.BodyOverrideMode(); !ok {
v := channelmonitorrequesttemplate.DefaultBodyOverrideMode
_c.mutation.SetBodyOverrideMode(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_c *ChannelMonitorRequestTemplateCreate) check() error {
if _, ok := _c.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ChannelMonitorRequestTemplate.created_at"`)}
}
if _, ok := _c.mutation.UpdatedAt(); !ok {
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ChannelMonitorRequestTemplate.updated_at"`)}
}
if _, ok := _c.mutation.Name(); !ok {
return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "ChannelMonitorRequestTemplate.name"`)}
}
if v, ok := _c.mutation.Name(); ok {
if err := channelmonitorrequesttemplate.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.name": %w`, err)}
}
}
if _, ok := _c.mutation.Provider(); !ok {
return &ValidationError{Name: "provider", err: errors.New(`ent: missing required field "ChannelMonitorRequestTemplate.provider"`)}
}
if v, ok := _c.mutation.Provider(); ok {
if err := channelmonitorrequesttemplate.ProviderValidator(v); err != nil {
return &ValidationError{Name: "provider", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.provider": %w`, err)}
}
}
if v, ok := _c.mutation.Description(); ok {
if err := channelmonitorrequesttemplate.DescriptionValidator(v); err != nil {
return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.description": %w`, err)}
}
}
if _, ok := _c.mutation.ExtraHeaders(); !ok {
return &ValidationError{Name: "extra_headers", err: errors.New(`ent: missing required field "ChannelMonitorRequestTemplate.extra_headers"`)}
}
if _, ok := _c.mutation.BodyOverrideMode(); !ok {
return &ValidationError{Name: "body_override_mode", err: errors.New(`ent: missing required field "ChannelMonitorRequestTemplate.body_override_mode"`)}
}
if v, ok := _c.mutation.BodyOverrideMode(); ok {
if err := channelmonitorrequesttemplate.BodyOverrideModeValidator(v); err != nil {
return &ValidationError{Name: "body_override_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.body_override_mode": %w`, err)}
}
}
return nil
}
func (_c *ChannelMonitorRequestTemplateCreate) sqlSave(ctx context.Context) (*ChannelMonitorRequestTemplate, error) {
if err := _c.check(); err != nil {
return nil, err
}
_node, _spec := _c.createSpec()
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
id := _spec.ID.Value.(int64)
_node.ID = int64(id)
_c.mutation.id = &_node.ID
_c.mutation.done = true
return _node, nil
}
func (_c *ChannelMonitorRequestTemplateCreate) createSpec() (*ChannelMonitorRequestTemplate, *sqlgraph.CreateSpec) {
var (
_node = &ChannelMonitorRequestTemplate{config: _c.config}
_spec = sqlgraph.NewCreateSpec(channelmonitorrequesttemplate.Table, sqlgraph.NewFieldSpec(channelmonitorrequesttemplate.FieldID, field.TypeInt64))
)
_spec.OnConflict = _c.conflict
if value, ok := _c.mutation.CreatedAt(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldCreatedAt, field.TypeTime, value)
_node.CreatedAt = value
}
if value, ok := _c.mutation.UpdatedAt(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldUpdatedAt, field.TypeTime, value)
_node.UpdatedAt = value
}
if value, ok := _c.mutation.Name(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldName, field.TypeString, value)
_node.Name = value
}
if value, ok := _c.mutation.Provider(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldProvider, field.TypeEnum, value)
_node.Provider = value
}
if value, ok := _c.mutation.Description(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldDescription, field.TypeString, value)
_node.Description = value
}
if value, ok := _c.mutation.ExtraHeaders(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldExtraHeaders, field.TypeJSON, value)
_node.ExtraHeaders = value
}
if value, ok := _c.mutation.BodyOverrideMode(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldBodyOverrideMode, field.TypeString, value)
_node.BodyOverrideMode = value
}
if value, ok := _c.mutation.BodyOverride(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldBodyOverride, field.TypeJSON, value)
_node.BodyOverride = value
}
if nodes := _c.mutation.MonitorsIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: true,
Table: channelmonitorrequesttemplate.MonitorsTable,
Columns: []string{channelmonitorrequesttemplate.MonitorsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
// of the `INSERT` statement. For example:
//
// client.ChannelMonitorRequestTemplate.Create().
// SetCreatedAt(v).
// OnConflict(
// // Update the row with the new values
// // the was proposed for insertion.
// sql.ResolveWithNewValues(),
// ).
// // Override some of the fields with custom
// // update values.
// Update(func(u *ent.ChannelMonitorRequestTemplateUpsert) {
// SetCreatedAt(v+v).
// }).
// Exec(ctx)
func (_c *ChannelMonitorRequestTemplateCreate) OnConflict(opts ...sql.ConflictOption) *ChannelMonitorRequestTemplateUpsertOne {
_c.conflict = opts
return &ChannelMonitorRequestTemplateUpsertOne{
create: _c,
}
}
// OnConflictColumns calls `OnConflict` and configures the columns
// as conflict target. Using this option is equivalent to using:
//
// client.ChannelMonitorRequestTemplate.Create().
// OnConflict(sql.ConflictColumns(columns...)).
// Exec(ctx)
func (_c *ChannelMonitorRequestTemplateCreate) OnConflictColumns(columns ...string) *ChannelMonitorRequestTemplateUpsertOne {
_c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
return &ChannelMonitorRequestTemplateUpsertOne{
create: _c,
}
}
type (
// ChannelMonitorRequestTemplateUpsertOne is the builder for "upsert"-ing
// one ChannelMonitorRequestTemplate node.
ChannelMonitorRequestTemplateUpsertOne struct {
create *ChannelMonitorRequestTemplateCreate
}
// ChannelMonitorRequestTemplateUpsert is the "OnConflict" setter.
ChannelMonitorRequestTemplateUpsert struct {
*sql.UpdateSet
}
)
// SetUpdatedAt sets the "updated_at" field.
func (u *ChannelMonitorRequestTemplateUpsert) SetUpdatedAt(v time.Time) *ChannelMonitorRequestTemplateUpsert {
u.Set(channelmonitorrequesttemplate.FieldUpdatedAt, v)
return u
}
// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsert) UpdateUpdatedAt() *ChannelMonitorRequestTemplateUpsert {
u.SetExcluded(channelmonitorrequesttemplate.FieldUpdatedAt)
return u
}
// SetName sets the "name" field.
func (u *ChannelMonitorRequestTemplateUpsert) SetName(v string) *ChannelMonitorRequestTemplateUpsert {
u.Set(channelmonitorrequesttemplate.FieldName, v)
return u
}
// UpdateName sets the "name" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsert) UpdateName() *ChannelMonitorRequestTemplateUpsert {
u.SetExcluded(channelmonitorrequesttemplate.FieldName)
return u
}
// SetProvider sets the "provider" field.
func (u *ChannelMonitorRequestTemplateUpsert) SetProvider(v channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateUpsert {
u.Set(channelmonitorrequesttemplate.FieldProvider, v)
return u
}
// UpdateProvider sets the "provider" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsert) UpdateProvider() *ChannelMonitorRequestTemplateUpsert {
u.SetExcluded(channelmonitorrequesttemplate.FieldProvider)
return u
}
// SetDescription sets the "description" field.
func (u *ChannelMonitorRequestTemplateUpsert) SetDescription(v string) *ChannelMonitorRequestTemplateUpsert {
u.Set(channelmonitorrequesttemplate.FieldDescription, v)
return u
}
// UpdateDescription sets the "description" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsert) UpdateDescription() *ChannelMonitorRequestTemplateUpsert {
u.SetExcluded(channelmonitorrequesttemplate.FieldDescription)
return u
}
// ClearDescription clears the value of the "description" field.
func (u *ChannelMonitorRequestTemplateUpsert) ClearDescription() *ChannelMonitorRequestTemplateUpsert {
u.SetNull(channelmonitorrequesttemplate.FieldDescription)
return u
}
// SetExtraHeaders sets the "extra_headers" field.
func (u *ChannelMonitorRequestTemplateUpsert) SetExtraHeaders(v map[string]string) *ChannelMonitorRequestTemplateUpsert {
u.Set(channelmonitorrequesttemplate.FieldExtraHeaders, v)
return u
}
// UpdateExtraHeaders sets the "extra_headers" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsert) UpdateExtraHeaders() *ChannelMonitorRequestTemplateUpsert {
u.SetExcluded(channelmonitorrequesttemplate.FieldExtraHeaders)
return u
}
// SetBodyOverrideMode sets the "body_override_mode" field.
func (u *ChannelMonitorRequestTemplateUpsert) SetBodyOverrideMode(v string) *ChannelMonitorRequestTemplateUpsert {
u.Set(channelmonitorrequesttemplate.FieldBodyOverrideMode, v)
return u
}
// UpdateBodyOverrideMode sets the "body_override_mode" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsert) UpdateBodyOverrideMode() *ChannelMonitorRequestTemplateUpsert {
u.SetExcluded(channelmonitorrequesttemplate.FieldBodyOverrideMode)
return u
}
// SetBodyOverride sets the "body_override" field.
func (u *ChannelMonitorRequestTemplateUpsert) SetBodyOverride(v map[string]interface{}) *ChannelMonitorRequestTemplateUpsert {
u.Set(channelmonitorrequesttemplate.FieldBodyOverride, v)
return u
}
// UpdateBodyOverride sets the "body_override" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsert) UpdateBodyOverride() *ChannelMonitorRequestTemplateUpsert {
u.SetExcluded(channelmonitorrequesttemplate.FieldBodyOverride)
return u
}
// ClearBodyOverride clears the value of the "body_override" field.
func (u *ChannelMonitorRequestTemplateUpsert) ClearBodyOverride() *ChannelMonitorRequestTemplateUpsert {
u.SetNull(channelmonitorrequesttemplate.FieldBodyOverride)
return u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
// client.ChannelMonitorRequestTemplate.Create().
// OnConflict(
// sql.ResolveWithNewValues(),
// ).
// Exec(ctx)
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateNewValues() *ChannelMonitorRequestTemplateUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
if _, exists := u.create.mutation.CreatedAt(); exists {
s.SetIgnore(channelmonitorrequesttemplate.FieldCreatedAt)
}
}))
return u
}
// Ignore sets each column to itself in case of conflict.
// Using this option is equivalent to using:
//
// client.ChannelMonitorRequestTemplate.Create().
// OnConflict(sql.ResolveWithIgnore()).
// Exec(ctx)
func (u *ChannelMonitorRequestTemplateUpsertOne) Ignore() *ChannelMonitorRequestTemplateUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
return u
}
// DoNothing configures the conflict_action to `DO NOTHING`.
// Supported only by SQLite and PostgreSQL.
func (u *ChannelMonitorRequestTemplateUpsertOne) DoNothing() *ChannelMonitorRequestTemplateUpsertOne {
u.create.conflict = append(u.create.conflict, sql.DoNothing())
return u
}
// Update allows overriding fields `UPDATE` values. See the ChannelMonitorRequestTemplateCreate.OnConflict
// documentation for more info.
func (u *ChannelMonitorRequestTemplateUpsertOne) Update(set func(*ChannelMonitorRequestTemplateUpsert)) *ChannelMonitorRequestTemplateUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
set(&ChannelMonitorRequestTemplateUpsert{UpdateSet: update})
}))
return u
}
// SetUpdatedAt sets the "updated_at" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) SetUpdatedAt(v time.Time) *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetUpdatedAt(v)
})
}
// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateUpdatedAt() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateUpdatedAt()
})
}
// SetName sets the "name" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) SetName(v string) *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetName(v)
})
}
// UpdateName sets the "name" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateName() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateName()
})
}
// SetProvider sets the "provider" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) SetProvider(v channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetProvider(v)
})
}
// UpdateProvider sets the "provider" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateProvider() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateProvider()
})
}
// SetDescription sets the "description" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) SetDescription(v string) *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetDescription(v)
})
}
// UpdateDescription sets the "description" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateDescription() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateDescription()
})
}
// ClearDescription clears the value of the "description" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) ClearDescription() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.ClearDescription()
})
}
// SetExtraHeaders sets the "extra_headers" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) SetExtraHeaders(v map[string]string) *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetExtraHeaders(v)
})
}
// UpdateExtraHeaders sets the "extra_headers" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateExtraHeaders() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateExtraHeaders()
})
}
// SetBodyOverrideMode sets the "body_override_mode" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) SetBodyOverrideMode(v string) *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetBodyOverrideMode(v)
})
}
// UpdateBodyOverrideMode sets the "body_override_mode" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateBodyOverrideMode() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateBodyOverrideMode()
})
}
// SetBodyOverride sets the "body_override" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) SetBodyOverride(v map[string]interface{}) *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetBodyOverride(v)
})
}
// UpdateBodyOverride sets the "body_override" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertOne) UpdateBodyOverride() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateBodyOverride()
})
}
// ClearBodyOverride clears the value of the "body_override" field.
func (u *ChannelMonitorRequestTemplateUpsertOne) ClearBodyOverride() *ChannelMonitorRequestTemplateUpsertOne {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.ClearBodyOverride()
})
}
// Exec executes the query.
func (u *ChannelMonitorRequestTemplateUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
return errors.New("ent: missing options for ChannelMonitorRequestTemplateCreate.OnConflict")
}
return u.create.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (u *ChannelMonitorRequestTemplateUpsertOne) ExecX(ctx context.Context) {
if err := u.create.Exec(ctx); err != nil {
panic(err)
}
}
// Exec executes the UPSERT query and returns the inserted/updated ID.
func (u *ChannelMonitorRequestTemplateUpsertOne) ID(ctx context.Context) (id int64, err error) {
node, err := u.create.Save(ctx)
if err != nil {
return id, err
}
return node.ID, nil
}
// IDX is like ID, but panics if an error occurs.
func (u *ChannelMonitorRequestTemplateUpsertOne) IDX(ctx context.Context) int64 {
id, err := u.ID(ctx)
if err != nil {
panic(err)
}
return id
}
// ChannelMonitorRequestTemplateCreateBulk is the builder for creating many ChannelMonitorRequestTemplate entities in bulk.
type ChannelMonitorRequestTemplateCreateBulk struct {
config
err error
builders []*ChannelMonitorRequestTemplateCreate
conflict []sql.ConflictOption
}
// Save creates the ChannelMonitorRequestTemplate entities in the database.
func (_c *ChannelMonitorRequestTemplateCreateBulk) Save(ctx context.Context) ([]*ChannelMonitorRequestTemplate, error) {
if _c.err != nil {
return nil, _c.err
}
specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
nodes := make([]*ChannelMonitorRequestTemplate, len(_c.builders))
mutators := make([]Mutator, len(_c.builders))
for i := range _c.builders {
func(i int, root context.Context) {
builder := _c.builders[i]
builder.defaults()
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*ChannelMonitorRequestTemplateMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err := builder.check(); err != nil {
return nil, err
}
builder.mutation = mutation
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
} else {
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
spec.OnConflict = _c.conflict
// Invoke the actual operation on the latest mutation in the chain.
if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
}
}
if err != nil {
return nil, err
}
mutation.id = &nodes[i].ID
if specs[i].ID.Value != nil {
id := specs[i].ID.Value.(int64)
nodes[i].ID = int64(id)
}
mutation.done = true
return nodes[i], nil
})
for i := len(builder.hooks) - 1; i >= 0; i-- {
mut = builder.hooks[i](mut)
}
mutators[i] = mut
}(i, ctx)
}
if len(mutators) > 0 {
if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
return nil, err
}
}
return nodes, nil
}
// SaveX is like Save, but panics if an error occurs.
func (_c *ChannelMonitorRequestTemplateCreateBulk) SaveX(ctx context.Context) []*ChannelMonitorRequestTemplate {
v, err := _c.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (_c *ChannelMonitorRequestTemplateCreateBulk) Exec(ctx context.Context) error {
_, err := _c.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_c *ChannelMonitorRequestTemplateCreateBulk) ExecX(ctx context.Context) {
if err := _c.Exec(ctx); err != nil {
panic(err)
}
}
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
// of the `INSERT` statement. For example:
//
// client.ChannelMonitorRequestTemplate.CreateBulk(builders...).
// OnConflict(
// // Update the row with the new values
// // the was proposed for insertion.
// sql.ResolveWithNewValues(),
// ).
// // Override some of the fields with custom
// // update values.
// Update(func(u *ent.ChannelMonitorRequestTemplateUpsert) {
// SetCreatedAt(v+v).
// }).
// Exec(ctx)
func (_c *ChannelMonitorRequestTemplateCreateBulk) OnConflict(opts ...sql.ConflictOption) *ChannelMonitorRequestTemplateUpsertBulk {
_c.conflict = opts
return &ChannelMonitorRequestTemplateUpsertBulk{
create: _c,
}
}
// OnConflictColumns calls `OnConflict` and configures the columns
// as conflict target. Using this option is equivalent to using:
//
// client.ChannelMonitorRequestTemplate.Create().
// OnConflict(sql.ConflictColumns(columns...)).
// Exec(ctx)
func (_c *ChannelMonitorRequestTemplateCreateBulk) OnConflictColumns(columns ...string) *ChannelMonitorRequestTemplateUpsertBulk {
_c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
return &ChannelMonitorRequestTemplateUpsertBulk{
create: _c,
}
}
// ChannelMonitorRequestTemplateUpsertBulk is the builder for "upsert"-ing
// a bulk of ChannelMonitorRequestTemplate nodes.
type ChannelMonitorRequestTemplateUpsertBulk struct {
create *ChannelMonitorRequestTemplateCreateBulk
}
// UpdateNewValues updates the mutable fields using the new values that
// were set on create. Using this option is equivalent to using:
//
// client.ChannelMonitorRequestTemplate.Create().
// OnConflict(
// sql.ResolveWithNewValues(),
// ).
// Exec(ctx)
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateNewValues() *ChannelMonitorRequestTemplateUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
for _, b := range u.create.builders {
if _, exists := b.mutation.CreatedAt(); exists {
s.SetIgnore(channelmonitorrequesttemplate.FieldCreatedAt)
}
}
}))
return u
}
// Ignore sets each column to itself in case of conflict.
// Using this option is equivalent to using:
//
// client.ChannelMonitorRequestTemplate.Create().
// OnConflict(sql.ResolveWithIgnore()).
// Exec(ctx)
func (u *ChannelMonitorRequestTemplateUpsertBulk) Ignore() *ChannelMonitorRequestTemplateUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
return u
}
// DoNothing configures the conflict_action to `DO NOTHING`.
// Supported only by SQLite and PostgreSQL.
func (u *ChannelMonitorRequestTemplateUpsertBulk) DoNothing() *ChannelMonitorRequestTemplateUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.DoNothing())
return u
}
// Update allows overriding fields `UPDATE` values. See the ChannelMonitorRequestTemplateCreateBulk.OnConflict
// documentation for more info.
func (u *ChannelMonitorRequestTemplateUpsertBulk) Update(set func(*ChannelMonitorRequestTemplateUpsert)) *ChannelMonitorRequestTemplateUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
set(&ChannelMonitorRequestTemplateUpsert{UpdateSet: update})
}))
return u
}
// SetUpdatedAt sets the "updated_at" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) SetUpdatedAt(v time.Time) *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetUpdatedAt(v)
})
}
// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateUpdatedAt() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateUpdatedAt()
})
}
// SetName sets the "name" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) SetName(v string) *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetName(v)
})
}
// UpdateName sets the "name" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateName() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateName()
})
}
// SetProvider sets the "provider" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) SetProvider(v channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetProvider(v)
})
}
// UpdateProvider sets the "provider" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateProvider() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateProvider()
})
}
// SetDescription sets the "description" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) SetDescription(v string) *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetDescription(v)
})
}
// UpdateDescription sets the "description" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateDescription() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateDescription()
})
}
// ClearDescription clears the value of the "description" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) ClearDescription() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.ClearDescription()
})
}
// SetExtraHeaders sets the "extra_headers" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) SetExtraHeaders(v map[string]string) *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetExtraHeaders(v)
})
}
// UpdateExtraHeaders sets the "extra_headers" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateExtraHeaders() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateExtraHeaders()
})
}
// SetBodyOverrideMode sets the "body_override_mode" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) SetBodyOverrideMode(v string) *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetBodyOverrideMode(v)
})
}
// UpdateBodyOverrideMode sets the "body_override_mode" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateBodyOverrideMode() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateBodyOverrideMode()
})
}
// SetBodyOverride sets the "body_override" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) SetBodyOverride(v map[string]interface{}) *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.SetBodyOverride(v)
})
}
// UpdateBodyOverride sets the "body_override" field to the value that was provided on create.
func (u *ChannelMonitorRequestTemplateUpsertBulk) UpdateBodyOverride() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.UpdateBodyOverride()
})
}
// ClearBodyOverride clears the value of the "body_override" field.
func (u *ChannelMonitorRequestTemplateUpsertBulk) ClearBodyOverride() *ChannelMonitorRequestTemplateUpsertBulk {
return u.Update(func(s *ChannelMonitorRequestTemplateUpsert) {
s.ClearBodyOverride()
})
}
// Exec executes the query.
func (u *ChannelMonitorRequestTemplateUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {
return u.create.err
}
for i, b := range u.create.builders {
if len(b.conflict) != 0 {
return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ChannelMonitorRequestTemplateCreateBulk instead", i)
}
}
if len(u.create.conflict) == 0 {
return errors.New("ent: missing options for ChannelMonitorRequestTemplateCreateBulk.OnConflict")
}
return u.create.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (u *ChannelMonitorRequestTemplateUpsertBulk) ExecX(ctx context.Context) {
if err := u.create.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorRequestTemplateDelete is the builder for deleting a ChannelMonitorRequestTemplate entity.
type ChannelMonitorRequestTemplateDelete struct {
config
hooks []Hook
mutation *ChannelMonitorRequestTemplateMutation
}
// Where appends a list predicates to the ChannelMonitorRequestTemplateDelete builder.
func (_d *ChannelMonitorRequestTemplateDelete) Where(ps ...predicate.ChannelMonitorRequestTemplate) *ChannelMonitorRequestTemplateDelete {
_d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (_d *ChannelMonitorRequestTemplateDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorRequestTemplateDelete) ExecX(ctx context.Context) int {
n, err := _d.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (_d *ChannelMonitorRequestTemplateDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(channelmonitorrequesttemplate.Table, sqlgraph.NewFieldSpec(channelmonitorrequesttemplate.FieldID, field.TypeInt64))
if ps := _d.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
_d.mutation.done = true
return affected, err
}
// ChannelMonitorRequestTemplateDeleteOne is the builder for deleting a single ChannelMonitorRequestTemplate entity.
type ChannelMonitorRequestTemplateDeleteOne struct {
_d *ChannelMonitorRequestTemplateDelete
}
// Where appends a list predicates to the ChannelMonitorRequestTemplateDelete builder.
func (_d *ChannelMonitorRequestTemplateDeleteOne) Where(ps ...predicate.ChannelMonitorRequestTemplate) *ChannelMonitorRequestTemplateDeleteOne {
_d._d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query.
func (_d *ChannelMonitorRequestTemplateDeleteOne) Exec(ctx context.Context) error {
n, err := _d._d.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{channelmonitorrequesttemplate.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *ChannelMonitorRequestTemplateDeleteOne) ExecX(ctx context.Context) {
if err := _d.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,648 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"database/sql/driver"
"fmt"
"math"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorRequestTemplateQuery is the builder for querying ChannelMonitorRequestTemplate entities.
type ChannelMonitorRequestTemplateQuery struct {
config
ctx *QueryContext
order []channelmonitorrequesttemplate.OrderOption
inters []Interceptor
predicates []predicate.ChannelMonitorRequestTemplate
withMonitors *ChannelMonitorQuery
modifiers []func(*sql.Selector)
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the ChannelMonitorRequestTemplateQuery builder.
func (_q *ChannelMonitorRequestTemplateQuery) Where(ps ...predicate.ChannelMonitorRequestTemplate) *ChannelMonitorRequestTemplateQuery {
_q.predicates = append(_q.predicates, ps...)
return _q
}
// Limit the number of records to be returned by this query.
func (_q *ChannelMonitorRequestTemplateQuery) Limit(limit int) *ChannelMonitorRequestTemplateQuery {
_q.ctx.Limit = &limit
return _q
}
// Offset to start from.
func (_q *ChannelMonitorRequestTemplateQuery) Offset(offset int) *ChannelMonitorRequestTemplateQuery {
_q.ctx.Offset = &offset
return _q
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (_q *ChannelMonitorRequestTemplateQuery) Unique(unique bool) *ChannelMonitorRequestTemplateQuery {
_q.ctx.Unique = &unique
return _q
}
// Order specifies how the records should be ordered.
func (_q *ChannelMonitorRequestTemplateQuery) Order(o ...channelmonitorrequesttemplate.OrderOption) *ChannelMonitorRequestTemplateQuery {
_q.order = append(_q.order, o...)
return _q
}
// QueryMonitors chains the current query on the "monitors" edge.
func (_q *ChannelMonitorRequestTemplateQuery) QueryMonitors() *ChannelMonitorQuery {
query := (&ChannelMonitorClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitorrequesttemplate.Table, channelmonitorrequesttemplate.FieldID, selector),
sqlgraph.To(channelmonitor.Table, channelmonitor.FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, channelmonitorrequesttemplate.MonitorsTable, channelmonitorrequesttemplate.MonitorsColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first ChannelMonitorRequestTemplate entity from the query.
// Returns a *NotFoundError when no ChannelMonitorRequestTemplate was found.
func (_q *ChannelMonitorRequestTemplateQuery) First(ctx context.Context) (*ChannelMonitorRequestTemplate, error) {
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{channelmonitorrequesttemplate.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) FirstX(ctx context.Context) *ChannelMonitorRequestTemplate {
node, err := _q.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first ChannelMonitorRequestTemplate ID from the query.
// Returns a *NotFoundError when no ChannelMonitorRequestTemplate ID was found.
func (_q *ChannelMonitorRequestTemplateQuery) FirstID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{channelmonitorrequesttemplate.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) FirstIDX(ctx context.Context) int64 {
id, err := _q.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single ChannelMonitorRequestTemplate entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one ChannelMonitorRequestTemplate entity is found.
// Returns a *NotFoundError when no ChannelMonitorRequestTemplate entities are found.
func (_q *ChannelMonitorRequestTemplateQuery) Only(ctx context.Context) (*ChannelMonitorRequestTemplate, error) {
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{channelmonitorrequesttemplate.Label}
default:
return nil, &NotSingularError{channelmonitorrequesttemplate.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) OnlyX(ctx context.Context) *ChannelMonitorRequestTemplate {
node, err := _q.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only ChannelMonitorRequestTemplate ID in the query.
// Returns a *NotSingularError when more than one ChannelMonitorRequestTemplate ID is found.
// Returns a *NotFoundError when no entities are found.
func (_q *ChannelMonitorRequestTemplateQuery) OnlyID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{channelmonitorrequesttemplate.Label}
default:
err = &NotSingularError{channelmonitorrequesttemplate.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) OnlyIDX(ctx context.Context) int64 {
id, err := _q.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of ChannelMonitorRequestTemplates.
func (_q *ChannelMonitorRequestTemplateQuery) All(ctx context.Context) ([]*ChannelMonitorRequestTemplate, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*ChannelMonitorRequestTemplate, *ChannelMonitorRequestTemplateQuery]()
return withInterceptors[[]*ChannelMonitorRequestTemplate](ctx, _q, qr, _q.inters)
}
// AllX is like All, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) AllX(ctx context.Context) []*ChannelMonitorRequestTemplate {
nodes, err := _q.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of ChannelMonitorRequestTemplate IDs.
func (_q *ChannelMonitorRequestTemplateQuery) IDs(ctx context.Context) (ids []int64, err error) {
if _q.ctx.Unique == nil && _q.path != nil {
_q.Unique(true)
}
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
if err = _q.Select(channelmonitorrequesttemplate.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) IDsX(ctx context.Context) []int64 {
ids, err := _q.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (_q *ChannelMonitorRequestTemplateQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
if err := _q.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, _q, querierCount[*ChannelMonitorRequestTemplateQuery](), _q.inters)
}
// CountX is like Count, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) CountX(ctx context.Context) int {
count, err := _q.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (_q *ChannelMonitorRequestTemplateQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
switch _, err := _q.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (_q *ChannelMonitorRequestTemplateQuery) ExistX(ctx context.Context) bool {
exist, err := _q.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the ChannelMonitorRequestTemplateQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (_q *ChannelMonitorRequestTemplateQuery) Clone() *ChannelMonitorRequestTemplateQuery {
if _q == nil {
return nil
}
return &ChannelMonitorRequestTemplateQuery{
config: _q.config,
ctx: _q.ctx.Clone(),
order: append([]channelmonitorrequesttemplate.OrderOption{}, _q.order...),
inters: append([]Interceptor{}, _q.inters...),
predicates: append([]predicate.ChannelMonitorRequestTemplate{}, _q.predicates...),
withMonitors: _q.withMonitors.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
path: _q.path,
}
}
// WithMonitors tells the query-builder to eager-load the nodes that are connected to
// the "monitors" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *ChannelMonitorRequestTemplateQuery) WithMonitors(opts ...func(*ChannelMonitorQuery)) *ChannelMonitorRequestTemplateQuery {
query := (&ChannelMonitorClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withMonitors = query
return _q
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.ChannelMonitorRequestTemplate.Query().
// GroupBy(channelmonitorrequesttemplate.FieldCreatedAt).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (_q *ChannelMonitorRequestTemplateQuery) GroupBy(field string, fields ...string) *ChannelMonitorRequestTemplateGroupBy {
_q.ctx.Fields = append([]string{field}, fields...)
grbuild := &ChannelMonitorRequestTemplateGroupBy{build: _q}
grbuild.flds = &_q.ctx.Fields
grbuild.label = channelmonitorrequesttemplate.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// }
//
// client.ChannelMonitorRequestTemplate.Query().
// Select(channelmonitorrequesttemplate.FieldCreatedAt).
// Scan(ctx, &v)
func (_q *ChannelMonitorRequestTemplateQuery) Select(fields ...string) *ChannelMonitorRequestTemplateSelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
sbuild := &ChannelMonitorRequestTemplateSelect{ChannelMonitorRequestTemplateQuery: _q}
sbuild.label = channelmonitorrequesttemplate.Label
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a ChannelMonitorRequestTemplateSelect configured with the given aggregations.
func (_q *ChannelMonitorRequestTemplateQuery) Aggregate(fns ...AggregateFunc) *ChannelMonitorRequestTemplateSelect {
return _q.Select().Aggregate(fns...)
}
func (_q *ChannelMonitorRequestTemplateQuery) prepareQuery(ctx context.Context) error {
for _, inter := range _q.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, _q); err != nil {
return err
}
}
}
for _, f := range _q.ctx.Fields {
if !channelmonitorrequesttemplate.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if _q.path != nil {
prev, err := _q.path(ctx)
if err != nil {
return err
}
_q.sql = prev
}
return nil
}
func (_q *ChannelMonitorRequestTemplateQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ChannelMonitorRequestTemplate, error) {
var (
nodes = []*ChannelMonitorRequestTemplate{}
_spec = _q.querySpec()
loadedTypes = [1]bool{
_q.withMonitors != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*ChannelMonitorRequestTemplate).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &ChannelMonitorRequestTemplate{config: _q.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := _q.withMonitors; query != nil {
if err := _q.loadMonitors(ctx, query, nodes,
func(n *ChannelMonitorRequestTemplate) { n.Edges.Monitors = []*ChannelMonitor{} },
func(n *ChannelMonitorRequestTemplate, e *ChannelMonitor) {
n.Edges.Monitors = append(n.Edges.Monitors, e)
}); err != nil {
return nil, err
}
}
return nodes, nil
}
func (_q *ChannelMonitorRequestTemplateQuery) loadMonitors(ctx context.Context, query *ChannelMonitorQuery, nodes []*ChannelMonitorRequestTemplate, init func(*ChannelMonitorRequestTemplate), assign func(*ChannelMonitorRequestTemplate, *ChannelMonitor)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*ChannelMonitorRequestTemplate)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
if init != nil {
init(nodes[i])
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(channelmonitor.FieldTemplateID)
}
query.Where(predicate.ChannelMonitor(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(channelmonitorrequesttemplate.MonitorsColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.TemplateID
if fk == nil {
return fmt.Errorf(`foreign-key "template_id" is nil for node %v`, n.ID)
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "template_id" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
return nil
}
func (_q *ChannelMonitorRequestTemplateQuery) sqlCount(ctx context.Context) (int, error) {
_spec := _q.querySpec()
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
_spec.Node.Columns = _q.ctx.Fields
if len(_q.ctx.Fields) > 0 {
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
}
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
}
func (_q *ChannelMonitorRequestTemplateQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(channelmonitorrequesttemplate.Table, channelmonitorrequesttemplate.Columns, sqlgraph.NewFieldSpec(channelmonitorrequesttemplate.FieldID, field.TypeInt64))
_spec.From = _q.sql
if unique := _q.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if _q.path != nil {
_spec.Unique = true
}
if fields := _q.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, channelmonitorrequesttemplate.FieldID)
for i := range fields {
if fields[i] != channelmonitorrequesttemplate.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
}
if ps := _q.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := _q.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := _q.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := _q.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (_q *ChannelMonitorRequestTemplateQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(_q.driver.Dialect())
t1 := builder.Table(channelmonitorrequesttemplate.Table)
columns := _q.ctx.Fields
if len(columns) == 0 {
columns = channelmonitorrequesttemplate.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if _q.sql != nil {
selector = _q.sql
selector.Select(selector.Columns(columns...)...)
}
if _q.ctx.Unique != nil && *_q.ctx.Unique {
selector.Distinct()
}
for _, m := range _q.modifiers {
m(selector)
}
for _, p := range _q.predicates {
p(selector)
}
for _, p := range _q.order {
p(selector)
}
if offset := _q.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := _q.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
// either committed or rolled-back.
func (_q *ChannelMonitorRequestTemplateQuery) ForUpdate(opts ...sql.LockOption) *ChannelMonitorRequestTemplateQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForUpdate(opts...)
})
return _q
}
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
// on any rows that are read. Other sessions can read the rows, but cannot modify them
// until your transaction commits.
func (_q *ChannelMonitorRequestTemplateQuery) ForShare(opts ...sql.LockOption) *ChannelMonitorRequestTemplateQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForShare(opts...)
})
return _q
}
// ChannelMonitorRequestTemplateGroupBy is the group-by builder for ChannelMonitorRequestTemplate entities.
type ChannelMonitorRequestTemplateGroupBy struct {
selector
build *ChannelMonitorRequestTemplateQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (_g *ChannelMonitorRequestTemplateGroupBy) Aggregate(fns ...AggregateFunc) *ChannelMonitorRequestTemplateGroupBy {
_g.fns = append(_g.fns, fns...)
return _g
}
// Scan applies the selector query and scans the result into the given value.
func (_g *ChannelMonitorRequestTemplateGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
if err := _g.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorRequestTemplateQuery, *ChannelMonitorRequestTemplateGroupBy](ctx, _g.build, _g, _g.build.inters, v)
}
func (_g *ChannelMonitorRequestTemplateGroupBy) sqlScan(ctx context.Context, root *ChannelMonitorRequestTemplateQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(_g.fns))
for _, fn := range _g.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
for _, f := range *_g.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*_g.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// ChannelMonitorRequestTemplateSelect is the builder for selecting fields of ChannelMonitorRequestTemplate entities.
type ChannelMonitorRequestTemplateSelect struct {
*ChannelMonitorRequestTemplateQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (_s *ChannelMonitorRequestTemplateSelect) Aggregate(fns ...AggregateFunc) *ChannelMonitorRequestTemplateSelect {
_s.fns = append(_s.fns, fns...)
return _s
}
// Scan applies the selector query and scans the result into the given value.
func (_s *ChannelMonitorRequestTemplateSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
if err := _s.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ChannelMonitorRequestTemplateQuery, *ChannelMonitorRequestTemplateSelect](ctx, _s.ChannelMonitorRequestTemplateQuery, _s, _s.inters, v)
}
func (_s *ChannelMonitorRequestTemplateSelect) sqlScan(ctx context.Context, root *ChannelMonitorRequestTemplateQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(_s.fns))
for _, fn := range _s.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*_s.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

View File

@ -0,0 +1,639 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ChannelMonitorRequestTemplateUpdate is the builder for updating ChannelMonitorRequestTemplate entities.
type ChannelMonitorRequestTemplateUpdate struct {
config
hooks []Hook
mutation *ChannelMonitorRequestTemplateMutation
}
// Where appends a list predicates to the ChannelMonitorRequestTemplateUpdate builder.
func (_u *ChannelMonitorRequestTemplateUpdate) Where(ps ...predicate.ChannelMonitorRequestTemplate) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.Where(ps...)
return _u
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *ChannelMonitorRequestTemplateUpdate) SetUpdatedAt(v time.Time) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetName sets the "name" field.
func (_u *ChannelMonitorRequestTemplateUpdate) SetName(v string) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.SetName(v)
return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdate) SetNillableName(v *string) *ChannelMonitorRequestTemplateUpdate {
if v != nil {
_u.SetName(*v)
}
return _u
}
// SetProvider sets the "provider" field.
func (_u *ChannelMonitorRequestTemplateUpdate) SetProvider(v channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.SetProvider(v)
return _u
}
// SetNillableProvider sets the "provider" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdate) SetNillableProvider(v *channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateUpdate {
if v != nil {
_u.SetProvider(*v)
}
return _u
}
// SetDescription sets the "description" field.
func (_u *ChannelMonitorRequestTemplateUpdate) SetDescription(v string) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.SetDescription(v)
return _u
}
// SetNillableDescription sets the "description" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdate) SetNillableDescription(v *string) *ChannelMonitorRequestTemplateUpdate {
if v != nil {
_u.SetDescription(*v)
}
return _u
}
// ClearDescription clears the value of the "description" field.
func (_u *ChannelMonitorRequestTemplateUpdate) ClearDescription() *ChannelMonitorRequestTemplateUpdate {
_u.mutation.ClearDescription()
return _u
}
// SetExtraHeaders sets the "extra_headers" field.
func (_u *ChannelMonitorRequestTemplateUpdate) SetExtraHeaders(v map[string]string) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.SetExtraHeaders(v)
return _u
}
// SetBodyOverrideMode sets the "body_override_mode" field.
func (_u *ChannelMonitorRequestTemplateUpdate) SetBodyOverrideMode(v string) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.SetBodyOverrideMode(v)
return _u
}
// SetNillableBodyOverrideMode sets the "body_override_mode" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdate) SetNillableBodyOverrideMode(v *string) *ChannelMonitorRequestTemplateUpdate {
if v != nil {
_u.SetBodyOverrideMode(*v)
}
return _u
}
// SetBodyOverride sets the "body_override" field.
func (_u *ChannelMonitorRequestTemplateUpdate) SetBodyOverride(v map[string]interface{}) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.SetBodyOverride(v)
return _u
}
// ClearBodyOverride clears the value of the "body_override" field.
func (_u *ChannelMonitorRequestTemplateUpdate) ClearBodyOverride() *ChannelMonitorRequestTemplateUpdate {
_u.mutation.ClearBodyOverride()
return _u
}
// AddMonitorIDs adds the "monitors" edge to the ChannelMonitor entity by IDs.
func (_u *ChannelMonitorRequestTemplateUpdate) AddMonitorIDs(ids ...int64) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.AddMonitorIDs(ids...)
return _u
}
// AddMonitors adds the "monitors" edges to the ChannelMonitor entity.
func (_u *ChannelMonitorRequestTemplateUpdate) AddMonitors(v ...*ChannelMonitor) *ChannelMonitorRequestTemplateUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.AddMonitorIDs(ids...)
}
// Mutation returns the ChannelMonitorRequestTemplateMutation object of the builder.
func (_u *ChannelMonitorRequestTemplateUpdate) Mutation() *ChannelMonitorRequestTemplateMutation {
return _u.mutation
}
// ClearMonitors clears all "monitors" edges to the ChannelMonitor entity.
func (_u *ChannelMonitorRequestTemplateUpdate) ClearMonitors() *ChannelMonitorRequestTemplateUpdate {
_u.mutation.ClearMonitors()
return _u
}
// RemoveMonitorIDs removes the "monitors" edge to ChannelMonitor entities by IDs.
func (_u *ChannelMonitorRequestTemplateUpdate) RemoveMonitorIDs(ids ...int64) *ChannelMonitorRequestTemplateUpdate {
_u.mutation.RemoveMonitorIDs(ids...)
return _u
}
// RemoveMonitors removes "monitors" edges to ChannelMonitor entities.
func (_u *ChannelMonitorRequestTemplateUpdate) RemoveMonitors(v ...*ChannelMonitor) *ChannelMonitorRequestTemplateUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.RemoveMonitorIDs(ids...)
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *ChannelMonitorRequestTemplateUpdate) Save(ctx context.Context) (int, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *ChannelMonitorRequestTemplateUpdate) SaveX(ctx context.Context) int {
affected, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (_u *ChannelMonitorRequestTemplateUpdate) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *ChannelMonitorRequestTemplateUpdate) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *ChannelMonitorRequestTemplateUpdate) defaults() {
if _, ok := _u.mutation.UpdatedAt(); !ok {
v := channelmonitorrequesttemplate.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *ChannelMonitorRequestTemplateUpdate) check() error {
if v, ok := _u.mutation.Name(); ok {
if err := channelmonitorrequesttemplate.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.name": %w`, err)}
}
}
if v, ok := _u.mutation.Provider(); ok {
if err := channelmonitorrequesttemplate.ProviderValidator(v); err != nil {
return &ValidationError{Name: "provider", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.provider": %w`, err)}
}
}
if v, ok := _u.mutation.Description(); ok {
if err := channelmonitorrequesttemplate.DescriptionValidator(v); err != nil {
return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.description": %w`, err)}
}
}
if v, ok := _u.mutation.BodyOverrideMode(); ok {
if err := channelmonitorrequesttemplate.BodyOverrideModeValidator(v); err != nil {
return &ValidationError{Name: "body_override_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.body_override_mode": %w`, err)}
}
}
return nil
}
func (_u *ChannelMonitorRequestTemplateUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(channelmonitorrequesttemplate.Table, channelmonitorrequesttemplate.Columns, sqlgraph.NewFieldSpec(channelmonitorrequesttemplate.FieldID, field.TypeInt64))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.Name(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldName, field.TypeString, value)
}
if value, ok := _u.mutation.Provider(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldProvider, field.TypeEnum, value)
}
if value, ok := _u.mutation.Description(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldDescription, field.TypeString, value)
}
if _u.mutation.DescriptionCleared() {
_spec.ClearField(channelmonitorrequesttemplate.FieldDescription, field.TypeString)
}
if value, ok := _u.mutation.ExtraHeaders(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldExtraHeaders, field.TypeJSON, value)
}
if value, ok := _u.mutation.BodyOverrideMode(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldBodyOverrideMode, field.TypeString, value)
}
if value, ok := _u.mutation.BodyOverride(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldBodyOverride, field.TypeJSON, value)
}
if _u.mutation.BodyOverrideCleared() {
_spec.ClearField(channelmonitorrequesttemplate.FieldBodyOverride, field.TypeJSON)
}
if _u.mutation.MonitorsCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: true,
Table: channelmonitorrequesttemplate.MonitorsTable,
Columns: []string{channelmonitorrequesttemplate.MonitorsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.RemovedMonitorsIDs(); len(nodes) > 0 && !_u.mutation.MonitorsCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: true,
Table: channelmonitorrequesttemplate.MonitorsTable,
Columns: []string{channelmonitorrequesttemplate.MonitorsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.MonitorsIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: true,
Table: channelmonitorrequesttemplate.MonitorsTable,
Columns: []string{channelmonitorrequesttemplate.MonitorsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{channelmonitorrequesttemplate.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
_u.mutation.done = true
return _node, nil
}
// ChannelMonitorRequestTemplateUpdateOne is the builder for updating a single ChannelMonitorRequestTemplate entity.
type ChannelMonitorRequestTemplateUpdateOne struct {
config
fields []string
hooks []Hook
mutation *ChannelMonitorRequestTemplateMutation
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetUpdatedAt(v time.Time) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetName sets the "name" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetName(v string) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.SetName(v)
return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetNillableName(v *string) *ChannelMonitorRequestTemplateUpdateOne {
if v != nil {
_u.SetName(*v)
}
return _u
}
// SetProvider sets the "provider" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetProvider(v channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.SetProvider(v)
return _u
}
// SetNillableProvider sets the "provider" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetNillableProvider(v *channelmonitorrequesttemplate.Provider) *ChannelMonitorRequestTemplateUpdateOne {
if v != nil {
_u.SetProvider(*v)
}
return _u
}
// SetDescription sets the "description" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetDescription(v string) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.SetDescription(v)
return _u
}
// SetNillableDescription sets the "description" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetNillableDescription(v *string) *ChannelMonitorRequestTemplateUpdateOne {
if v != nil {
_u.SetDescription(*v)
}
return _u
}
// ClearDescription clears the value of the "description" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) ClearDescription() *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.ClearDescription()
return _u
}
// SetExtraHeaders sets the "extra_headers" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetExtraHeaders(v map[string]string) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.SetExtraHeaders(v)
return _u
}
// SetBodyOverrideMode sets the "body_override_mode" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetBodyOverrideMode(v string) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.SetBodyOverrideMode(v)
return _u
}
// SetNillableBodyOverrideMode sets the "body_override_mode" field if the given value is not nil.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetNillableBodyOverrideMode(v *string) *ChannelMonitorRequestTemplateUpdateOne {
if v != nil {
_u.SetBodyOverrideMode(*v)
}
return _u
}
// SetBodyOverride sets the "body_override" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SetBodyOverride(v map[string]interface{}) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.SetBodyOverride(v)
return _u
}
// ClearBodyOverride clears the value of the "body_override" field.
func (_u *ChannelMonitorRequestTemplateUpdateOne) ClearBodyOverride() *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.ClearBodyOverride()
return _u
}
// AddMonitorIDs adds the "monitors" edge to the ChannelMonitor entity by IDs.
func (_u *ChannelMonitorRequestTemplateUpdateOne) AddMonitorIDs(ids ...int64) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.AddMonitorIDs(ids...)
return _u
}
// AddMonitors adds the "monitors" edges to the ChannelMonitor entity.
func (_u *ChannelMonitorRequestTemplateUpdateOne) AddMonitors(v ...*ChannelMonitor) *ChannelMonitorRequestTemplateUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.AddMonitorIDs(ids...)
}
// Mutation returns the ChannelMonitorRequestTemplateMutation object of the builder.
func (_u *ChannelMonitorRequestTemplateUpdateOne) Mutation() *ChannelMonitorRequestTemplateMutation {
return _u.mutation
}
// ClearMonitors clears all "monitors" edges to the ChannelMonitor entity.
func (_u *ChannelMonitorRequestTemplateUpdateOne) ClearMonitors() *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.ClearMonitors()
return _u
}
// RemoveMonitorIDs removes the "monitors" edge to ChannelMonitor entities by IDs.
func (_u *ChannelMonitorRequestTemplateUpdateOne) RemoveMonitorIDs(ids ...int64) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.RemoveMonitorIDs(ids...)
return _u
}
// RemoveMonitors removes "monitors" edges to ChannelMonitor entities.
func (_u *ChannelMonitorRequestTemplateUpdateOne) RemoveMonitors(v ...*ChannelMonitor) *ChannelMonitorRequestTemplateUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.RemoveMonitorIDs(ids...)
}
// Where appends a list predicates to the ChannelMonitorRequestTemplateUpdate builder.
func (_u *ChannelMonitorRequestTemplateUpdateOne) Where(ps ...predicate.ChannelMonitorRequestTemplate) *ChannelMonitorRequestTemplateUpdateOne {
_u.mutation.Where(ps...)
return _u
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (_u *ChannelMonitorRequestTemplateUpdateOne) Select(field string, fields ...string) *ChannelMonitorRequestTemplateUpdateOne {
_u.fields = append([]string{field}, fields...)
return _u
}
// Save executes the query and returns the updated ChannelMonitorRequestTemplate entity.
func (_u *ChannelMonitorRequestTemplateUpdateOne) Save(ctx context.Context) (*ChannelMonitorRequestTemplate, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *ChannelMonitorRequestTemplateUpdateOne) SaveX(ctx context.Context) *ChannelMonitorRequestTemplate {
node, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (_u *ChannelMonitorRequestTemplateUpdateOne) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *ChannelMonitorRequestTemplateUpdateOne) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *ChannelMonitorRequestTemplateUpdateOne) defaults() {
if _, ok := _u.mutation.UpdatedAt(); !ok {
v := channelmonitorrequesttemplate.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *ChannelMonitorRequestTemplateUpdateOne) check() error {
if v, ok := _u.mutation.Name(); ok {
if err := channelmonitorrequesttemplate.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.name": %w`, err)}
}
}
if v, ok := _u.mutation.Provider(); ok {
if err := channelmonitorrequesttemplate.ProviderValidator(v); err != nil {
return &ValidationError{Name: "provider", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.provider": %w`, err)}
}
}
if v, ok := _u.mutation.Description(); ok {
if err := channelmonitorrequesttemplate.DescriptionValidator(v); err != nil {
return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.description": %w`, err)}
}
}
if v, ok := _u.mutation.BodyOverrideMode(); ok {
if err := channelmonitorrequesttemplate.BodyOverrideModeValidator(v); err != nil {
return &ValidationError{Name: "body_override_mode", err: fmt.Errorf(`ent: validator failed for field "ChannelMonitorRequestTemplate.body_override_mode": %w`, err)}
}
}
return nil
}
func (_u *ChannelMonitorRequestTemplateUpdateOne) sqlSave(ctx context.Context) (_node *ChannelMonitorRequestTemplate, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(channelmonitorrequesttemplate.Table, channelmonitorrequesttemplate.Columns, sqlgraph.NewFieldSpec(channelmonitorrequesttemplate.FieldID, field.TypeInt64))
id, ok := _u.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ChannelMonitorRequestTemplate.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, channelmonitorrequesttemplate.FieldID)
for _, f := range fields {
if !channelmonitorrequesttemplate.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != channelmonitorrequesttemplate.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.Name(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldName, field.TypeString, value)
}
if value, ok := _u.mutation.Provider(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldProvider, field.TypeEnum, value)
}
if value, ok := _u.mutation.Description(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldDescription, field.TypeString, value)
}
if _u.mutation.DescriptionCleared() {
_spec.ClearField(channelmonitorrequesttemplate.FieldDescription, field.TypeString)
}
if value, ok := _u.mutation.ExtraHeaders(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldExtraHeaders, field.TypeJSON, value)
}
if value, ok := _u.mutation.BodyOverrideMode(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldBodyOverrideMode, field.TypeString, value)
}
if value, ok := _u.mutation.BodyOverride(); ok {
_spec.SetField(channelmonitorrequesttemplate.FieldBodyOverride, field.TypeJSON, value)
}
if _u.mutation.BodyOverrideCleared() {
_spec.ClearField(channelmonitorrequesttemplate.FieldBodyOverride, field.TypeJSON)
}
if _u.mutation.MonitorsCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: true,
Table: channelmonitorrequesttemplate.MonitorsTable,
Columns: []string{channelmonitorrequesttemplate.MonitorsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.RemovedMonitorsIDs(); len(nodes) > 0 && !_u.mutation.MonitorsCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: true,
Table: channelmonitorrequesttemplate.MonitorsTable,
Columns: []string{channelmonitorrequesttemplate.MonitorsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.MonitorsIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: true,
Table: channelmonitorrequesttemplate.MonitorsTable,
Columns: []string{channelmonitorrequesttemplate.MonitorsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(channelmonitor.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &ChannelMonitorRequestTemplate{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{channelmonitorrequesttemplate.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
_u.mutation.done = true
return _node, nil
}

View File

@ -22,6 +22,10 @@ import (
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/authidentity"
"github.com/Wei-Shaw/sub2api/ent/authidentitychannel"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@ -68,6 +72,14 @@ type Client struct {
AuthIdentity *AuthIdentityClient
// AuthIdentityChannel is the client for interacting with the AuthIdentityChannel builders.
AuthIdentityChannel *AuthIdentityChannelClient
// ChannelMonitor is the client for interacting with the ChannelMonitor builders.
ChannelMonitor *ChannelMonitorClient
// ChannelMonitorDailyRollup is the client for interacting with the ChannelMonitorDailyRollup builders.
ChannelMonitorDailyRollup *ChannelMonitorDailyRollupClient
// ChannelMonitorHistory is the client for interacting with the ChannelMonitorHistory builders.
ChannelMonitorHistory *ChannelMonitorHistoryClient
// ChannelMonitorRequestTemplate is the client for interacting with the ChannelMonitorRequestTemplate builders.
ChannelMonitorRequestTemplate *ChannelMonitorRequestTemplateClient
// ErrorPassthroughRule is the client for interacting with the ErrorPassthroughRule builders.
ErrorPassthroughRule *ErrorPassthroughRuleClient
// Group is the client for interacting with the Group builders.
@ -132,6 +144,10 @@ func (c *Client) init() {
c.AnnouncementRead = NewAnnouncementReadClient(c.config)
c.AuthIdentity = NewAuthIdentityClient(c.config)
c.AuthIdentityChannel = NewAuthIdentityChannelClient(c.config)
c.ChannelMonitor = NewChannelMonitorClient(c.config)
c.ChannelMonitorDailyRollup = NewChannelMonitorDailyRollupClient(c.config)
c.ChannelMonitorHistory = NewChannelMonitorHistoryClient(c.config)
c.ChannelMonitorRequestTemplate = NewChannelMonitorRequestTemplateClient(c.config)
c.ErrorPassthroughRule = NewErrorPassthroughRuleClient(c.config)
c.Group = NewGroupClient(c.config)
c.IdempotencyRecord = NewIdempotencyRecordClient(c.config)
@ -245,38 +261,42 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
cfg := c.config
cfg.driver = tx
return &Tx{
ctx: ctx,
config: cfg,
APIKey: NewAPIKeyClient(cfg),
Account: NewAccountClient(cfg),
AccountGroup: NewAccountGroupClient(cfg),
Announcement: NewAnnouncementClient(cfg),
AnnouncementRead: NewAnnouncementReadClient(cfg),
AuthIdentity: NewAuthIdentityClient(cfg),
AuthIdentityChannel: NewAuthIdentityChannelClient(cfg),
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
Group: NewGroupClient(cfg),
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
IdentityAdoptionDecision: NewIdentityAdoptionDecisionClient(cfg),
PaymentAuditLog: NewPaymentAuditLogClient(cfg),
PaymentOrder: NewPaymentOrderClient(cfg),
PaymentProviderInstance: NewPaymentProviderInstanceClient(cfg),
PendingAuthSession: NewPendingAuthSessionClient(cfg),
PromoCode: NewPromoCodeClient(cfg),
PromoCodeUsage: NewPromoCodeUsageClient(cfg),
Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg),
SecuritySecret: NewSecuritySecretClient(cfg),
Setting: NewSettingClient(cfg),
SubscriptionPlan: NewSubscriptionPlanClient(cfg),
TLSFingerprintProfile: NewTLSFingerprintProfileClient(cfg),
UsageCleanupTask: NewUsageCleanupTaskClient(cfg),
UsageLog: NewUsageLogClient(cfg),
User: NewUserClient(cfg),
UserAllowedGroup: NewUserAllowedGroupClient(cfg),
UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
UserAttributeValue: NewUserAttributeValueClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg),
ctx: ctx,
config: cfg,
APIKey: NewAPIKeyClient(cfg),
Account: NewAccountClient(cfg),
AccountGroup: NewAccountGroupClient(cfg),
Announcement: NewAnnouncementClient(cfg),
AnnouncementRead: NewAnnouncementReadClient(cfg),
AuthIdentity: NewAuthIdentityClient(cfg),
AuthIdentityChannel: NewAuthIdentityChannelClient(cfg),
ChannelMonitor: NewChannelMonitorClient(cfg),
ChannelMonitorDailyRollup: NewChannelMonitorDailyRollupClient(cfg),
ChannelMonitorHistory: NewChannelMonitorHistoryClient(cfg),
ChannelMonitorRequestTemplate: NewChannelMonitorRequestTemplateClient(cfg),
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
Group: NewGroupClient(cfg),
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
IdentityAdoptionDecision: NewIdentityAdoptionDecisionClient(cfg),
PaymentAuditLog: NewPaymentAuditLogClient(cfg),
PaymentOrder: NewPaymentOrderClient(cfg),
PaymentProviderInstance: NewPaymentProviderInstanceClient(cfg),
PendingAuthSession: NewPendingAuthSessionClient(cfg),
PromoCode: NewPromoCodeClient(cfg),
PromoCodeUsage: NewPromoCodeUsageClient(cfg),
Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg),
SecuritySecret: NewSecuritySecretClient(cfg),
Setting: NewSettingClient(cfg),
SubscriptionPlan: NewSubscriptionPlanClient(cfg),
TLSFingerprintProfile: NewTLSFingerprintProfileClient(cfg),
UsageCleanupTask: NewUsageCleanupTaskClient(cfg),
UsageLog: NewUsageLogClient(cfg),
User: NewUserClient(cfg),
UserAllowedGroup: NewUserAllowedGroupClient(cfg),
UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
UserAttributeValue: NewUserAttributeValueClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg),
}, nil
}
@ -294,38 +314,42 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
cfg := c.config
cfg.driver = &txDriver{tx: tx, drv: c.driver}
return &Tx{
ctx: ctx,
config: cfg,
APIKey: NewAPIKeyClient(cfg),
Account: NewAccountClient(cfg),
AccountGroup: NewAccountGroupClient(cfg),
Announcement: NewAnnouncementClient(cfg),
AnnouncementRead: NewAnnouncementReadClient(cfg),
AuthIdentity: NewAuthIdentityClient(cfg),
AuthIdentityChannel: NewAuthIdentityChannelClient(cfg),
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
Group: NewGroupClient(cfg),
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
IdentityAdoptionDecision: NewIdentityAdoptionDecisionClient(cfg),
PaymentAuditLog: NewPaymentAuditLogClient(cfg),
PaymentOrder: NewPaymentOrderClient(cfg),
PaymentProviderInstance: NewPaymentProviderInstanceClient(cfg),
PendingAuthSession: NewPendingAuthSessionClient(cfg),
PromoCode: NewPromoCodeClient(cfg),
PromoCodeUsage: NewPromoCodeUsageClient(cfg),
Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg),
SecuritySecret: NewSecuritySecretClient(cfg),
Setting: NewSettingClient(cfg),
SubscriptionPlan: NewSubscriptionPlanClient(cfg),
TLSFingerprintProfile: NewTLSFingerprintProfileClient(cfg),
UsageCleanupTask: NewUsageCleanupTaskClient(cfg),
UsageLog: NewUsageLogClient(cfg),
User: NewUserClient(cfg),
UserAllowedGroup: NewUserAllowedGroupClient(cfg),
UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
UserAttributeValue: NewUserAttributeValueClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg),
ctx: ctx,
config: cfg,
APIKey: NewAPIKeyClient(cfg),
Account: NewAccountClient(cfg),
AccountGroup: NewAccountGroupClient(cfg),
Announcement: NewAnnouncementClient(cfg),
AnnouncementRead: NewAnnouncementReadClient(cfg),
AuthIdentity: NewAuthIdentityClient(cfg),
AuthIdentityChannel: NewAuthIdentityChannelClient(cfg),
ChannelMonitor: NewChannelMonitorClient(cfg),
ChannelMonitorDailyRollup: NewChannelMonitorDailyRollupClient(cfg),
ChannelMonitorHistory: NewChannelMonitorHistoryClient(cfg),
ChannelMonitorRequestTemplate: NewChannelMonitorRequestTemplateClient(cfg),
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
Group: NewGroupClient(cfg),
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
IdentityAdoptionDecision: NewIdentityAdoptionDecisionClient(cfg),
PaymentAuditLog: NewPaymentAuditLogClient(cfg),
PaymentOrder: NewPaymentOrderClient(cfg),
PaymentProviderInstance: NewPaymentProviderInstanceClient(cfg),
PendingAuthSession: NewPendingAuthSessionClient(cfg),
PromoCode: NewPromoCodeClient(cfg),
PromoCodeUsage: NewPromoCodeUsageClient(cfg),
Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg),
SecuritySecret: NewSecuritySecretClient(cfg),
Setting: NewSettingClient(cfg),
SubscriptionPlan: NewSubscriptionPlanClient(cfg),
TLSFingerprintProfile: NewTLSFingerprintProfileClient(cfg),
UsageCleanupTask: NewUsageCleanupTaskClient(cfg),
UsageLog: NewUsageLogClient(cfg),
User: NewUserClient(cfg),
UserAllowedGroup: NewUserAllowedGroupClient(cfg),
UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
UserAttributeValue: NewUserAttributeValueClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg),
}, nil
}
@ -356,7 +380,9 @@ func (c *Client) Close() error {
func (c *Client) Use(hooks ...Hook) {
for _, n := range []interface{ Use(...Hook) }{
c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead,
c.AuthIdentity, c.AuthIdentityChannel, c.ErrorPassthroughRule, c.Group,
c.AuthIdentity, c.AuthIdentityChannel, c.ChannelMonitor,
c.ChannelMonitorDailyRollup, c.ChannelMonitorHistory,
c.ChannelMonitorRequestTemplate, c.ErrorPassthroughRule, c.Group,
c.IdempotencyRecord, c.IdentityAdoptionDecision, c.PaymentAuditLog,
c.PaymentOrder, c.PaymentProviderInstance, c.PendingAuthSession, c.PromoCode,
c.PromoCodeUsage, c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting,
@ -373,7 +399,9 @@ func (c *Client) Use(hooks ...Hook) {
func (c *Client) Intercept(interceptors ...Interceptor) {
for _, n := range []interface{ Intercept(...Interceptor) }{
c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead,
c.AuthIdentity, c.AuthIdentityChannel, c.ErrorPassthroughRule, c.Group,
c.AuthIdentity, c.AuthIdentityChannel, c.ChannelMonitor,
c.ChannelMonitorDailyRollup, c.ChannelMonitorHistory,
c.ChannelMonitorRequestTemplate, c.ErrorPassthroughRule, c.Group,
c.IdempotencyRecord, c.IdentityAdoptionDecision, c.PaymentAuditLog,
c.PaymentOrder, c.PaymentProviderInstance, c.PendingAuthSession, c.PromoCode,
c.PromoCodeUsage, c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting,
@ -402,6 +430,14 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
return c.AuthIdentity.mutate(ctx, m)
case *AuthIdentityChannelMutation:
return c.AuthIdentityChannel.mutate(ctx, m)
case *ChannelMonitorMutation:
return c.ChannelMonitor.mutate(ctx, m)
case *ChannelMonitorDailyRollupMutation:
return c.ChannelMonitorDailyRollup.mutate(ctx, m)
case *ChannelMonitorHistoryMutation:
return c.ChannelMonitorHistory.mutate(ctx, m)
case *ChannelMonitorRequestTemplateMutation:
return c.ChannelMonitorRequestTemplate.mutate(ctx, m)
case *ErrorPassthroughRuleMutation:
return c.ErrorPassthroughRule.mutate(ctx, m)
case *GroupMutation:
@ -1595,6 +1631,634 @@ func (c *AuthIdentityChannelClient) mutate(ctx context.Context, m *AuthIdentityC
}
}
// ChannelMonitorClient is a client for the ChannelMonitor schema.
type ChannelMonitorClient struct {
config
}
// NewChannelMonitorClient returns a client for the ChannelMonitor from the given config.
func NewChannelMonitorClient(c config) *ChannelMonitorClient {
return &ChannelMonitorClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `channelmonitor.Hooks(f(g(h())))`.
func (c *ChannelMonitorClient) Use(hooks ...Hook) {
c.hooks.ChannelMonitor = append(c.hooks.ChannelMonitor, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `channelmonitor.Intercept(f(g(h())))`.
func (c *ChannelMonitorClient) Intercept(interceptors ...Interceptor) {
c.inters.ChannelMonitor = append(c.inters.ChannelMonitor, interceptors...)
}
// Create returns a builder for creating a ChannelMonitor entity.
func (c *ChannelMonitorClient) Create() *ChannelMonitorCreate {
mutation := newChannelMonitorMutation(c.config, OpCreate)
return &ChannelMonitorCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of ChannelMonitor entities.
func (c *ChannelMonitorClient) CreateBulk(builders ...*ChannelMonitorCreate) *ChannelMonitorCreateBulk {
return &ChannelMonitorCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *ChannelMonitorClient) MapCreateBulk(slice any, setFunc func(*ChannelMonitorCreate, int)) *ChannelMonitorCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &ChannelMonitorCreateBulk{err: fmt.Errorf("calling to ChannelMonitorClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*ChannelMonitorCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &ChannelMonitorCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for ChannelMonitor.
func (c *ChannelMonitorClient) Update() *ChannelMonitorUpdate {
mutation := newChannelMonitorMutation(c.config, OpUpdate)
return &ChannelMonitorUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *ChannelMonitorClient) UpdateOne(_m *ChannelMonitor) *ChannelMonitorUpdateOne {
mutation := newChannelMonitorMutation(c.config, OpUpdateOne, withChannelMonitor(_m))
return &ChannelMonitorUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *ChannelMonitorClient) UpdateOneID(id int64) *ChannelMonitorUpdateOne {
mutation := newChannelMonitorMutation(c.config, OpUpdateOne, withChannelMonitorID(id))
return &ChannelMonitorUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for ChannelMonitor.
func (c *ChannelMonitorClient) Delete() *ChannelMonitorDelete {
mutation := newChannelMonitorMutation(c.config, OpDelete)
return &ChannelMonitorDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *ChannelMonitorClient) DeleteOne(_m *ChannelMonitor) *ChannelMonitorDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *ChannelMonitorClient) DeleteOneID(id int64) *ChannelMonitorDeleteOne {
builder := c.Delete().Where(channelmonitor.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &ChannelMonitorDeleteOne{builder}
}
// Query returns a query builder for ChannelMonitor.
func (c *ChannelMonitorClient) Query() *ChannelMonitorQuery {
return &ChannelMonitorQuery{
config: c.config,
ctx: &QueryContext{Type: TypeChannelMonitor},
inters: c.Interceptors(),
}
}
// Get returns a ChannelMonitor entity by its id.
func (c *ChannelMonitorClient) Get(ctx context.Context, id int64) (*ChannelMonitor, error) {
return c.Query().Where(channelmonitor.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *ChannelMonitorClient) GetX(ctx context.Context, id int64) *ChannelMonitor {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryHistory queries the history edge of a ChannelMonitor.
func (c *ChannelMonitorClient) QueryHistory(_m *ChannelMonitor) *ChannelMonitorHistoryQuery {
query := (&ChannelMonitorHistoryClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitor.Table, channelmonitor.FieldID, id),
sqlgraph.To(channelmonitorhistory.Table, channelmonitorhistory.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, channelmonitor.HistoryTable, channelmonitor.HistoryColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// QueryDailyRollups queries the daily_rollups edge of a ChannelMonitor.
func (c *ChannelMonitorClient) QueryDailyRollups(_m *ChannelMonitor) *ChannelMonitorDailyRollupQuery {
query := (&ChannelMonitorDailyRollupClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitor.Table, channelmonitor.FieldID, id),
sqlgraph.To(channelmonitordailyrollup.Table, channelmonitordailyrollup.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, channelmonitor.DailyRollupsTable, channelmonitor.DailyRollupsColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// QueryRequestTemplate queries the request_template edge of a ChannelMonitor.
func (c *ChannelMonitorClient) QueryRequestTemplate(_m *ChannelMonitor) *ChannelMonitorRequestTemplateQuery {
query := (&ChannelMonitorRequestTemplateClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitor.Table, channelmonitor.FieldID, id),
sqlgraph.To(channelmonitorrequesttemplate.Table, channelmonitorrequesttemplate.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, channelmonitor.RequestTemplateTable, channelmonitor.RequestTemplateColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *ChannelMonitorClient) Hooks() []Hook {
return c.hooks.ChannelMonitor
}
// Interceptors returns the client interceptors.
func (c *ChannelMonitorClient) Interceptors() []Interceptor {
return c.inters.ChannelMonitor
}
func (c *ChannelMonitorClient) mutate(ctx context.Context, m *ChannelMonitorMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&ChannelMonitorCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&ChannelMonitorUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&ChannelMonitorUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&ChannelMonitorDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown ChannelMonitor mutation op: %q", m.Op())
}
}
// ChannelMonitorDailyRollupClient is a client for the ChannelMonitorDailyRollup schema.
type ChannelMonitorDailyRollupClient struct {
config
}
// NewChannelMonitorDailyRollupClient returns a client for the ChannelMonitorDailyRollup from the given config.
func NewChannelMonitorDailyRollupClient(c config) *ChannelMonitorDailyRollupClient {
return &ChannelMonitorDailyRollupClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `channelmonitordailyrollup.Hooks(f(g(h())))`.
func (c *ChannelMonitorDailyRollupClient) Use(hooks ...Hook) {
c.hooks.ChannelMonitorDailyRollup = append(c.hooks.ChannelMonitorDailyRollup, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `channelmonitordailyrollup.Intercept(f(g(h())))`.
func (c *ChannelMonitorDailyRollupClient) Intercept(interceptors ...Interceptor) {
c.inters.ChannelMonitorDailyRollup = append(c.inters.ChannelMonitorDailyRollup, interceptors...)
}
// Create returns a builder for creating a ChannelMonitorDailyRollup entity.
func (c *ChannelMonitorDailyRollupClient) Create() *ChannelMonitorDailyRollupCreate {
mutation := newChannelMonitorDailyRollupMutation(c.config, OpCreate)
return &ChannelMonitorDailyRollupCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of ChannelMonitorDailyRollup entities.
func (c *ChannelMonitorDailyRollupClient) CreateBulk(builders ...*ChannelMonitorDailyRollupCreate) *ChannelMonitorDailyRollupCreateBulk {
return &ChannelMonitorDailyRollupCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *ChannelMonitorDailyRollupClient) MapCreateBulk(slice any, setFunc func(*ChannelMonitorDailyRollupCreate, int)) *ChannelMonitorDailyRollupCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &ChannelMonitorDailyRollupCreateBulk{err: fmt.Errorf("calling to ChannelMonitorDailyRollupClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*ChannelMonitorDailyRollupCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &ChannelMonitorDailyRollupCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for ChannelMonitorDailyRollup.
func (c *ChannelMonitorDailyRollupClient) Update() *ChannelMonitorDailyRollupUpdate {
mutation := newChannelMonitorDailyRollupMutation(c.config, OpUpdate)
return &ChannelMonitorDailyRollupUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *ChannelMonitorDailyRollupClient) UpdateOne(_m *ChannelMonitorDailyRollup) *ChannelMonitorDailyRollupUpdateOne {
mutation := newChannelMonitorDailyRollupMutation(c.config, OpUpdateOne, withChannelMonitorDailyRollup(_m))
return &ChannelMonitorDailyRollupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *ChannelMonitorDailyRollupClient) UpdateOneID(id int64) *ChannelMonitorDailyRollupUpdateOne {
mutation := newChannelMonitorDailyRollupMutation(c.config, OpUpdateOne, withChannelMonitorDailyRollupID(id))
return &ChannelMonitorDailyRollupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for ChannelMonitorDailyRollup.
func (c *ChannelMonitorDailyRollupClient) Delete() *ChannelMonitorDailyRollupDelete {
mutation := newChannelMonitorDailyRollupMutation(c.config, OpDelete)
return &ChannelMonitorDailyRollupDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *ChannelMonitorDailyRollupClient) DeleteOne(_m *ChannelMonitorDailyRollup) *ChannelMonitorDailyRollupDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *ChannelMonitorDailyRollupClient) DeleteOneID(id int64) *ChannelMonitorDailyRollupDeleteOne {
builder := c.Delete().Where(channelmonitordailyrollup.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &ChannelMonitorDailyRollupDeleteOne{builder}
}
// Query returns a query builder for ChannelMonitorDailyRollup.
func (c *ChannelMonitorDailyRollupClient) Query() *ChannelMonitorDailyRollupQuery {
return &ChannelMonitorDailyRollupQuery{
config: c.config,
ctx: &QueryContext{Type: TypeChannelMonitorDailyRollup},
inters: c.Interceptors(),
}
}
// Get returns a ChannelMonitorDailyRollup entity by its id.
func (c *ChannelMonitorDailyRollupClient) Get(ctx context.Context, id int64) (*ChannelMonitorDailyRollup, error) {
return c.Query().Where(channelmonitordailyrollup.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *ChannelMonitorDailyRollupClient) GetX(ctx context.Context, id int64) *ChannelMonitorDailyRollup {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryMonitor queries the monitor edge of a ChannelMonitorDailyRollup.
func (c *ChannelMonitorDailyRollupClient) QueryMonitor(_m *ChannelMonitorDailyRollup) *ChannelMonitorQuery {
query := (&ChannelMonitorClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitordailyrollup.Table, channelmonitordailyrollup.FieldID, id),
sqlgraph.To(channelmonitor.Table, channelmonitor.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, channelmonitordailyrollup.MonitorTable, channelmonitordailyrollup.MonitorColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *ChannelMonitorDailyRollupClient) Hooks() []Hook {
return c.hooks.ChannelMonitorDailyRollup
}
// Interceptors returns the client interceptors.
func (c *ChannelMonitorDailyRollupClient) Interceptors() []Interceptor {
return c.inters.ChannelMonitorDailyRollup
}
func (c *ChannelMonitorDailyRollupClient) mutate(ctx context.Context, m *ChannelMonitorDailyRollupMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&ChannelMonitorDailyRollupCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&ChannelMonitorDailyRollupUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&ChannelMonitorDailyRollupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&ChannelMonitorDailyRollupDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown ChannelMonitorDailyRollup mutation op: %q", m.Op())
}
}
// ChannelMonitorHistoryClient is a client for the ChannelMonitorHistory schema.
type ChannelMonitorHistoryClient struct {
config
}
// NewChannelMonitorHistoryClient returns a client for the ChannelMonitorHistory from the given config.
func NewChannelMonitorHistoryClient(c config) *ChannelMonitorHistoryClient {
return &ChannelMonitorHistoryClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `channelmonitorhistory.Hooks(f(g(h())))`.
func (c *ChannelMonitorHistoryClient) Use(hooks ...Hook) {
c.hooks.ChannelMonitorHistory = append(c.hooks.ChannelMonitorHistory, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `channelmonitorhistory.Intercept(f(g(h())))`.
func (c *ChannelMonitorHistoryClient) Intercept(interceptors ...Interceptor) {
c.inters.ChannelMonitorHistory = append(c.inters.ChannelMonitorHistory, interceptors...)
}
// Create returns a builder for creating a ChannelMonitorHistory entity.
func (c *ChannelMonitorHistoryClient) Create() *ChannelMonitorHistoryCreate {
mutation := newChannelMonitorHistoryMutation(c.config, OpCreate)
return &ChannelMonitorHistoryCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of ChannelMonitorHistory entities.
func (c *ChannelMonitorHistoryClient) CreateBulk(builders ...*ChannelMonitorHistoryCreate) *ChannelMonitorHistoryCreateBulk {
return &ChannelMonitorHistoryCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *ChannelMonitorHistoryClient) MapCreateBulk(slice any, setFunc func(*ChannelMonitorHistoryCreate, int)) *ChannelMonitorHistoryCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &ChannelMonitorHistoryCreateBulk{err: fmt.Errorf("calling to ChannelMonitorHistoryClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*ChannelMonitorHistoryCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &ChannelMonitorHistoryCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for ChannelMonitorHistory.
func (c *ChannelMonitorHistoryClient) Update() *ChannelMonitorHistoryUpdate {
mutation := newChannelMonitorHistoryMutation(c.config, OpUpdate)
return &ChannelMonitorHistoryUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *ChannelMonitorHistoryClient) UpdateOne(_m *ChannelMonitorHistory) *ChannelMonitorHistoryUpdateOne {
mutation := newChannelMonitorHistoryMutation(c.config, OpUpdateOne, withChannelMonitorHistory(_m))
return &ChannelMonitorHistoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *ChannelMonitorHistoryClient) UpdateOneID(id int64) *ChannelMonitorHistoryUpdateOne {
mutation := newChannelMonitorHistoryMutation(c.config, OpUpdateOne, withChannelMonitorHistoryID(id))
return &ChannelMonitorHistoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for ChannelMonitorHistory.
func (c *ChannelMonitorHistoryClient) Delete() *ChannelMonitorHistoryDelete {
mutation := newChannelMonitorHistoryMutation(c.config, OpDelete)
return &ChannelMonitorHistoryDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *ChannelMonitorHistoryClient) DeleteOne(_m *ChannelMonitorHistory) *ChannelMonitorHistoryDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *ChannelMonitorHistoryClient) DeleteOneID(id int64) *ChannelMonitorHistoryDeleteOne {
builder := c.Delete().Where(channelmonitorhistory.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &ChannelMonitorHistoryDeleteOne{builder}
}
// Query returns a query builder for ChannelMonitorHistory.
func (c *ChannelMonitorHistoryClient) Query() *ChannelMonitorHistoryQuery {
return &ChannelMonitorHistoryQuery{
config: c.config,
ctx: &QueryContext{Type: TypeChannelMonitorHistory},
inters: c.Interceptors(),
}
}
// Get returns a ChannelMonitorHistory entity by its id.
func (c *ChannelMonitorHistoryClient) Get(ctx context.Context, id int64) (*ChannelMonitorHistory, error) {
return c.Query().Where(channelmonitorhistory.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *ChannelMonitorHistoryClient) GetX(ctx context.Context, id int64) *ChannelMonitorHistory {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryMonitor queries the monitor edge of a ChannelMonitorHistory.
func (c *ChannelMonitorHistoryClient) QueryMonitor(_m *ChannelMonitorHistory) *ChannelMonitorQuery {
query := (&ChannelMonitorClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitorhistory.Table, channelmonitorhistory.FieldID, id),
sqlgraph.To(channelmonitor.Table, channelmonitor.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, channelmonitorhistory.MonitorTable, channelmonitorhistory.MonitorColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *ChannelMonitorHistoryClient) Hooks() []Hook {
return c.hooks.ChannelMonitorHistory
}
// Interceptors returns the client interceptors.
func (c *ChannelMonitorHistoryClient) Interceptors() []Interceptor {
return c.inters.ChannelMonitorHistory
}
func (c *ChannelMonitorHistoryClient) mutate(ctx context.Context, m *ChannelMonitorHistoryMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&ChannelMonitorHistoryCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&ChannelMonitorHistoryUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&ChannelMonitorHistoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&ChannelMonitorHistoryDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown ChannelMonitorHistory mutation op: %q", m.Op())
}
}
// ChannelMonitorRequestTemplateClient is a client for the ChannelMonitorRequestTemplate schema.
type ChannelMonitorRequestTemplateClient struct {
config
}
// NewChannelMonitorRequestTemplateClient returns a client for the ChannelMonitorRequestTemplate from the given config.
func NewChannelMonitorRequestTemplateClient(c config) *ChannelMonitorRequestTemplateClient {
return &ChannelMonitorRequestTemplateClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `channelmonitorrequesttemplate.Hooks(f(g(h())))`.
func (c *ChannelMonitorRequestTemplateClient) Use(hooks ...Hook) {
c.hooks.ChannelMonitorRequestTemplate = append(c.hooks.ChannelMonitorRequestTemplate, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `channelmonitorrequesttemplate.Intercept(f(g(h())))`.
func (c *ChannelMonitorRequestTemplateClient) Intercept(interceptors ...Interceptor) {
c.inters.ChannelMonitorRequestTemplate = append(c.inters.ChannelMonitorRequestTemplate, interceptors...)
}
// Create returns a builder for creating a ChannelMonitorRequestTemplate entity.
func (c *ChannelMonitorRequestTemplateClient) Create() *ChannelMonitorRequestTemplateCreate {
mutation := newChannelMonitorRequestTemplateMutation(c.config, OpCreate)
return &ChannelMonitorRequestTemplateCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of ChannelMonitorRequestTemplate entities.
func (c *ChannelMonitorRequestTemplateClient) CreateBulk(builders ...*ChannelMonitorRequestTemplateCreate) *ChannelMonitorRequestTemplateCreateBulk {
return &ChannelMonitorRequestTemplateCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *ChannelMonitorRequestTemplateClient) MapCreateBulk(slice any, setFunc func(*ChannelMonitorRequestTemplateCreate, int)) *ChannelMonitorRequestTemplateCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &ChannelMonitorRequestTemplateCreateBulk{err: fmt.Errorf("calling to ChannelMonitorRequestTemplateClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*ChannelMonitorRequestTemplateCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &ChannelMonitorRequestTemplateCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for ChannelMonitorRequestTemplate.
func (c *ChannelMonitorRequestTemplateClient) Update() *ChannelMonitorRequestTemplateUpdate {
mutation := newChannelMonitorRequestTemplateMutation(c.config, OpUpdate)
return &ChannelMonitorRequestTemplateUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *ChannelMonitorRequestTemplateClient) UpdateOne(_m *ChannelMonitorRequestTemplate) *ChannelMonitorRequestTemplateUpdateOne {
mutation := newChannelMonitorRequestTemplateMutation(c.config, OpUpdateOne, withChannelMonitorRequestTemplate(_m))
return &ChannelMonitorRequestTemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *ChannelMonitorRequestTemplateClient) UpdateOneID(id int64) *ChannelMonitorRequestTemplateUpdateOne {
mutation := newChannelMonitorRequestTemplateMutation(c.config, OpUpdateOne, withChannelMonitorRequestTemplateID(id))
return &ChannelMonitorRequestTemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for ChannelMonitorRequestTemplate.
func (c *ChannelMonitorRequestTemplateClient) Delete() *ChannelMonitorRequestTemplateDelete {
mutation := newChannelMonitorRequestTemplateMutation(c.config, OpDelete)
return &ChannelMonitorRequestTemplateDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *ChannelMonitorRequestTemplateClient) DeleteOne(_m *ChannelMonitorRequestTemplate) *ChannelMonitorRequestTemplateDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *ChannelMonitorRequestTemplateClient) DeleteOneID(id int64) *ChannelMonitorRequestTemplateDeleteOne {
builder := c.Delete().Where(channelmonitorrequesttemplate.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &ChannelMonitorRequestTemplateDeleteOne{builder}
}
// Query returns a query builder for ChannelMonitorRequestTemplate.
func (c *ChannelMonitorRequestTemplateClient) Query() *ChannelMonitorRequestTemplateQuery {
return &ChannelMonitorRequestTemplateQuery{
config: c.config,
ctx: &QueryContext{Type: TypeChannelMonitorRequestTemplate},
inters: c.Interceptors(),
}
}
// Get returns a ChannelMonitorRequestTemplate entity by its id.
func (c *ChannelMonitorRequestTemplateClient) Get(ctx context.Context, id int64) (*ChannelMonitorRequestTemplate, error) {
return c.Query().Where(channelmonitorrequesttemplate.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *ChannelMonitorRequestTemplateClient) GetX(ctx context.Context, id int64) *ChannelMonitorRequestTemplate {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryMonitors queries the monitors edge of a ChannelMonitorRequestTemplate.
func (c *ChannelMonitorRequestTemplateClient) QueryMonitors(_m *ChannelMonitorRequestTemplate) *ChannelMonitorQuery {
query := (&ChannelMonitorClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(channelmonitorrequesttemplate.Table, channelmonitorrequesttemplate.FieldID, id),
sqlgraph.To(channelmonitor.Table, channelmonitor.FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, channelmonitorrequesttemplate.MonitorsTable, channelmonitorrequesttemplate.MonitorsColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *ChannelMonitorRequestTemplateClient) Hooks() []Hook {
return c.hooks.ChannelMonitorRequestTemplate
}
// Interceptors returns the client interceptors.
func (c *ChannelMonitorRequestTemplateClient) Interceptors() []Interceptor {
return c.inters.ChannelMonitorRequestTemplate
}
func (c *ChannelMonitorRequestTemplateClient) mutate(ctx context.Context, m *ChannelMonitorRequestTemplateMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&ChannelMonitorRequestTemplateCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&ChannelMonitorRequestTemplateUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&ChannelMonitorRequestTemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&ChannelMonitorRequestTemplateDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown ChannelMonitorRequestTemplate mutation op: %q", m.Op())
}
}
// ErrorPassthroughRuleClient is a client for the ErrorPassthroughRule schema.
type ErrorPassthroughRuleClient struct {
config
@ -5355,21 +6019,23 @@ func (c *UserSubscriptionClient) mutate(ctx context.Context, m *UserSubscription
type (
hooks struct {
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, AuthIdentity,
AuthIdentityChannel, ErrorPassthroughRule, Group, IdempotencyRecord,
IdentityAdoptionDecision, PaymentAuditLog, PaymentOrder,
PaymentProviderInstance, PendingAuthSession, PromoCode, PromoCodeUsage, Proxy,
RedeemCode, SecuritySecret, Setting, SubscriptionPlan, TLSFingerprintProfile,
UsageCleanupTask, UsageLog, User, UserAllowedGroup, UserAttributeDefinition,
UserAttributeValue, UserSubscription []ent.Hook
AuthIdentityChannel, ChannelMonitor, ChannelMonitorDailyRollup,
ChannelMonitorHistory, ChannelMonitorRequestTemplate, ErrorPassthroughRule,
Group, IdempotencyRecord, IdentityAdoptionDecision, PaymentAuditLog,
PaymentOrder, PaymentProviderInstance, PendingAuthSession, PromoCode,
PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting, SubscriptionPlan,
TLSFingerprintProfile, UsageCleanupTask, UsageLog, User, UserAllowedGroup,
UserAttributeDefinition, UserAttributeValue, UserSubscription []ent.Hook
}
inters struct {
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, AuthIdentity,
AuthIdentityChannel, ErrorPassthroughRule, Group, IdempotencyRecord,
IdentityAdoptionDecision, PaymentAuditLog, PaymentOrder,
PaymentProviderInstance, PendingAuthSession, PromoCode, PromoCodeUsage, Proxy,
RedeemCode, SecuritySecret, Setting, SubscriptionPlan, TLSFingerprintProfile,
UsageCleanupTask, UsageLog, User, UserAllowedGroup, UserAttributeDefinition,
UserAttributeValue, UserSubscription []ent.Interceptor
AuthIdentityChannel, ChannelMonitor, ChannelMonitorDailyRollup,
ChannelMonitorHistory, ChannelMonitorRequestTemplate, ErrorPassthroughRule,
Group, IdempotencyRecord, IdentityAdoptionDecision, PaymentAuditLog,
PaymentOrder, PaymentProviderInstance, PendingAuthSession, PromoCode,
PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting, SubscriptionPlan,
TLSFingerprintProfile, UsageCleanupTask, UsageLog, User, UserAllowedGroup,
UserAttributeDefinition, UserAttributeValue, UserSubscription []ent.Interceptor
}
)

View File

@ -19,6 +19,10 @@ import (
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/authidentity"
"github.com/Wei-Shaw/sub2api/ent/authidentitychannel"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@ -102,36 +106,40 @@ var (
func checkColumn(t, c string) error {
initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
apikey.Table: apikey.ValidColumn,
account.Table: account.ValidColumn,
accountgroup.Table: accountgroup.ValidColumn,
announcement.Table: announcement.ValidColumn,
announcementread.Table: announcementread.ValidColumn,
authidentity.Table: authidentity.ValidColumn,
authidentitychannel.Table: authidentitychannel.ValidColumn,
errorpassthroughrule.Table: errorpassthroughrule.ValidColumn,
group.Table: group.ValidColumn,
idempotencyrecord.Table: idempotencyrecord.ValidColumn,
identityadoptiondecision.Table: identityadoptiondecision.ValidColumn,
paymentauditlog.Table: paymentauditlog.ValidColumn,
paymentorder.Table: paymentorder.ValidColumn,
paymentproviderinstance.Table: paymentproviderinstance.ValidColumn,
pendingauthsession.Table: pendingauthsession.ValidColumn,
promocode.Table: promocode.ValidColumn,
promocodeusage.Table: promocodeusage.ValidColumn,
proxy.Table: proxy.ValidColumn,
redeemcode.Table: redeemcode.ValidColumn,
securitysecret.Table: securitysecret.ValidColumn,
setting.Table: setting.ValidColumn,
subscriptionplan.Table: subscriptionplan.ValidColumn,
tlsfingerprintprofile.Table: tlsfingerprintprofile.ValidColumn,
usagecleanuptask.Table: usagecleanuptask.ValidColumn,
usagelog.Table: usagelog.ValidColumn,
user.Table: user.ValidColumn,
userallowedgroup.Table: userallowedgroup.ValidColumn,
userattributedefinition.Table: userattributedefinition.ValidColumn,
userattributevalue.Table: userattributevalue.ValidColumn,
usersubscription.Table: usersubscription.ValidColumn,
apikey.Table: apikey.ValidColumn,
account.Table: account.ValidColumn,
accountgroup.Table: accountgroup.ValidColumn,
announcement.Table: announcement.ValidColumn,
announcementread.Table: announcementread.ValidColumn,
authidentity.Table: authidentity.ValidColumn,
authidentitychannel.Table: authidentitychannel.ValidColumn,
channelmonitor.Table: channelmonitor.ValidColumn,
channelmonitordailyrollup.Table: channelmonitordailyrollup.ValidColumn,
channelmonitorhistory.Table: channelmonitorhistory.ValidColumn,
channelmonitorrequesttemplate.Table: channelmonitorrequesttemplate.ValidColumn,
errorpassthroughrule.Table: errorpassthroughrule.ValidColumn,
group.Table: group.ValidColumn,
idempotencyrecord.Table: idempotencyrecord.ValidColumn,
identityadoptiondecision.Table: identityadoptiondecision.ValidColumn,
paymentauditlog.Table: paymentauditlog.ValidColumn,
paymentorder.Table: paymentorder.ValidColumn,
paymentproviderinstance.Table: paymentproviderinstance.ValidColumn,
pendingauthsession.Table: pendingauthsession.ValidColumn,
promocode.Table: promocode.ValidColumn,
promocodeusage.Table: promocodeusage.ValidColumn,
proxy.Table: proxy.ValidColumn,
redeemcode.Table: redeemcode.ValidColumn,
securitysecret.Table: securitysecret.ValidColumn,
setting.Table: setting.ValidColumn,
subscriptionplan.Table: subscriptionplan.ValidColumn,
tlsfingerprintprofile.Table: tlsfingerprintprofile.ValidColumn,
usagecleanuptask.Table: usagecleanuptask.ValidColumn,
usagelog.Table: usagelog.ValidColumn,
user.Table: user.ValidColumn,
userallowedgroup.Table: userallowedgroup.ValidColumn,
userattributedefinition.Table: userattributedefinition.ValidColumn,
userattributevalue.Table: userattributevalue.ValidColumn,
usersubscription.Table: usersubscription.ValidColumn,
})
})
return columnCheck(t, c)

View File

@ -93,6 +93,54 @@ func (f AuthIdentityChannelFunc) Mutate(ctx context.Context, m ent.Mutation) (en
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.AuthIdentityChannelMutation", m)
}
// The ChannelMonitorFunc type is an adapter to allow the use of ordinary
// function as ChannelMonitor mutator.
type ChannelMonitorFunc func(context.Context, *ent.ChannelMonitorMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f ChannelMonitorFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.ChannelMonitorMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ChannelMonitorMutation", m)
}
// The ChannelMonitorDailyRollupFunc type is an adapter to allow the use of ordinary
// function as ChannelMonitorDailyRollup mutator.
type ChannelMonitorDailyRollupFunc func(context.Context, *ent.ChannelMonitorDailyRollupMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f ChannelMonitorDailyRollupFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.ChannelMonitorDailyRollupMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ChannelMonitorDailyRollupMutation", m)
}
// The ChannelMonitorHistoryFunc type is an adapter to allow the use of ordinary
// function as ChannelMonitorHistory mutator.
type ChannelMonitorHistoryFunc func(context.Context, *ent.ChannelMonitorHistoryMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f ChannelMonitorHistoryFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.ChannelMonitorHistoryMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ChannelMonitorHistoryMutation", m)
}
// The ChannelMonitorRequestTemplateFunc type is an adapter to allow the use of ordinary
// function as ChannelMonitorRequestTemplate mutator.
type ChannelMonitorRequestTemplateFunc func(context.Context, *ent.ChannelMonitorRequestTemplateMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f ChannelMonitorRequestTemplateFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.ChannelMonitorRequestTemplateMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ChannelMonitorRequestTemplateMutation", m)
}
// The ErrorPassthroughRuleFunc type is an adapter to allow the use of ordinary
// function as ErrorPassthroughRule mutator.
type ErrorPassthroughRuleFunc func(context.Context, *ent.ErrorPassthroughRuleMutation) (ent.Value, error)

View File

@ -15,6 +15,10 @@ import (
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/authidentity"
"github.com/Wei-Shaw/sub2api/ent/authidentitychannel"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@ -286,6 +290,114 @@ func (f TraverseAuthIdentityChannel) Traverse(ctx context.Context, q ent.Query)
return fmt.Errorf("unexpected query type %T. expect *ent.AuthIdentityChannelQuery", q)
}
// The ChannelMonitorFunc type is an adapter to allow the use of ordinary function as a Querier.
type ChannelMonitorFunc func(context.Context, *ent.ChannelMonitorQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f ChannelMonitorFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.ChannelMonitorQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorQuery", q)
}
// The TraverseChannelMonitor type is an adapter to allow the use of ordinary function as Traverser.
type TraverseChannelMonitor func(context.Context, *ent.ChannelMonitorQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseChannelMonitor) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseChannelMonitor) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.ChannelMonitorQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorQuery", q)
}
// The ChannelMonitorDailyRollupFunc type is an adapter to allow the use of ordinary function as a Querier.
type ChannelMonitorDailyRollupFunc func(context.Context, *ent.ChannelMonitorDailyRollupQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f ChannelMonitorDailyRollupFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.ChannelMonitorDailyRollupQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorDailyRollupQuery", q)
}
// The TraverseChannelMonitorDailyRollup type is an adapter to allow the use of ordinary function as Traverser.
type TraverseChannelMonitorDailyRollup func(context.Context, *ent.ChannelMonitorDailyRollupQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseChannelMonitorDailyRollup) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseChannelMonitorDailyRollup) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.ChannelMonitorDailyRollupQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorDailyRollupQuery", q)
}
// The ChannelMonitorHistoryFunc type is an adapter to allow the use of ordinary function as a Querier.
type ChannelMonitorHistoryFunc func(context.Context, *ent.ChannelMonitorHistoryQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f ChannelMonitorHistoryFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.ChannelMonitorHistoryQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorHistoryQuery", q)
}
// The TraverseChannelMonitorHistory type is an adapter to allow the use of ordinary function as Traverser.
type TraverseChannelMonitorHistory func(context.Context, *ent.ChannelMonitorHistoryQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseChannelMonitorHistory) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseChannelMonitorHistory) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.ChannelMonitorHistoryQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorHistoryQuery", q)
}
// The ChannelMonitorRequestTemplateFunc type is an adapter to allow the use of ordinary function as a Querier.
type ChannelMonitorRequestTemplateFunc func(context.Context, *ent.ChannelMonitorRequestTemplateQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f ChannelMonitorRequestTemplateFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.ChannelMonitorRequestTemplateQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorRequestTemplateQuery", q)
}
// The TraverseChannelMonitorRequestTemplate type is an adapter to allow the use of ordinary function as Traverser.
type TraverseChannelMonitorRequestTemplate func(context.Context, *ent.ChannelMonitorRequestTemplateQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseChannelMonitorRequestTemplate) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseChannelMonitorRequestTemplate) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.ChannelMonitorRequestTemplateQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorRequestTemplateQuery", q)
}
// The ErrorPassthroughRuleFunc type is an adapter to allow the use of ordinary function as a Querier.
type ErrorPassthroughRuleFunc func(context.Context, *ent.ErrorPassthroughRuleQuery) (ent.Value, error)
@ -924,6 +1036,14 @@ func NewQuery(q ent.Query) (Query, error) {
return &query[*ent.AuthIdentityQuery, predicate.AuthIdentity, authidentity.OrderOption]{typ: ent.TypeAuthIdentity, tq: q}, nil
case *ent.AuthIdentityChannelQuery:
return &query[*ent.AuthIdentityChannelQuery, predicate.AuthIdentityChannel, authidentitychannel.OrderOption]{typ: ent.TypeAuthIdentityChannel, tq: q}, nil
case *ent.ChannelMonitorQuery:
return &query[*ent.ChannelMonitorQuery, predicate.ChannelMonitor, channelmonitor.OrderOption]{typ: ent.TypeChannelMonitor, tq: q}, nil
case *ent.ChannelMonitorDailyRollupQuery:
return &query[*ent.ChannelMonitorDailyRollupQuery, predicate.ChannelMonitorDailyRollup, channelmonitordailyrollup.OrderOption]{typ: ent.TypeChannelMonitorDailyRollup, tq: q}, nil
case *ent.ChannelMonitorHistoryQuery:
return &query[*ent.ChannelMonitorHistoryQuery, predicate.ChannelMonitorHistory, channelmonitorhistory.OrderOption]{typ: ent.TypeChannelMonitorHistory, tq: q}, nil
case *ent.ChannelMonitorRequestTemplateQuery:
return &query[*ent.ChannelMonitorRequestTemplateQuery, predicate.ChannelMonitorRequestTemplate, channelmonitorrequesttemplate.OrderOption]{typ: ent.TypeChannelMonitorRequestTemplate, tq: q}, nil
case *ent.ErrorPassthroughRuleQuery:
return &query[*ent.ErrorPassthroughRuleQuery, predicate.ErrorPassthroughRule, errorpassthroughrule.OrderOption]{typ: ent.TypeErrorPassthroughRule, tq: q}, nil
case *ent.GroupQuery:

View File

@ -421,6 +421,169 @@ var (
},
},
}
// ChannelMonitorsColumns holds the columns for the "channel_monitors" table.
ChannelMonitorsColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "name", Type: field.TypeString, Size: 100},
{Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini"}},
{Name: "endpoint", Type: field.TypeString, Size: 500},
{Name: "api_key_encrypted", Type: field.TypeString},
{Name: "primary_model", Type: field.TypeString, Size: 200},
{Name: "extra_models", Type: field.TypeJSON},
{Name: "group_name", Type: field.TypeString, Nullable: true, Size: 100, Default: ""},
{Name: "enabled", Type: field.TypeBool, Default: true},
{Name: "interval_seconds", Type: field.TypeInt},
{Name: "last_checked_at", Type: field.TypeTime, Nullable: true},
{Name: "created_by", Type: field.TypeInt64},
{Name: "extra_headers", Type: field.TypeJSON},
{Name: "body_override_mode", Type: field.TypeString, Size: 10, Default: "off"},
{Name: "body_override", Type: field.TypeJSON, Nullable: true},
{Name: "template_id", Type: field.TypeInt64, Nullable: true},
}
// ChannelMonitorsTable holds the schema information for the "channel_monitors" table.
ChannelMonitorsTable = &schema.Table{
Name: "channel_monitors",
Columns: ChannelMonitorsColumns,
PrimaryKey: []*schema.Column{ChannelMonitorsColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "channel_monitors_channel_monitor_request_templates_request_template",
Columns: []*schema.Column{ChannelMonitorsColumns[17]},
RefColumns: []*schema.Column{ChannelMonitorRequestTemplatesColumns[0]},
OnDelete: schema.SetNull,
},
},
Indexes: []*schema.Index{
{
Name: "channelmonitor_enabled_last_checked_at",
Unique: false,
Columns: []*schema.Column{ChannelMonitorsColumns[10], ChannelMonitorsColumns[12]},
},
{
Name: "channelmonitor_provider",
Unique: false,
Columns: []*schema.Column{ChannelMonitorsColumns[4]},
},
{
Name: "channelmonitor_group_name",
Unique: false,
Columns: []*schema.Column{ChannelMonitorsColumns[9]},
},
{
Name: "channelmonitor_template_id",
Unique: false,
Columns: []*schema.Column{ChannelMonitorsColumns[17]},
},
},
}
// ChannelMonitorDailyRollupsColumns holds the columns for the "channel_monitor_daily_rollups" table.
ChannelMonitorDailyRollupsColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
{Name: "model", Type: field.TypeString, Size: 200},
{Name: "bucket_date", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "date"}},
{Name: "total_checks", Type: field.TypeInt, Default: 0},
{Name: "ok_count", Type: field.TypeInt, Default: 0},
{Name: "operational_count", Type: field.TypeInt, Default: 0},
{Name: "degraded_count", Type: field.TypeInt, Default: 0},
{Name: "failed_count", Type: field.TypeInt, Default: 0},
{Name: "error_count", Type: field.TypeInt, Default: 0},
{Name: "sum_latency_ms", Type: field.TypeInt64, Default: 0},
{Name: "count_latency", Type: field.TypeInt, Default: 0},
{Name: "sum_ping_latency_ms", Type: field.TypeInt64, Default: 0},
{Name: "count_ping_latency", Type: field.TypeInt, Default: 0},
{Name: "computed_at", Type: field.TypeTime},
{Name: "monitor_id", Type: field.TypeInt64},
}
// ChannelMonitorDailyRollupsTable holds the schema information for the "channel_monitor_daily_rollups" table.
ChannelMonitorDailyRollupsTable = &schema.Table{
Name: "channel_monitor_daily_rollups",
Columns: ChannelMonitorDailyRollupsColumns,
PrimaryKey: []*schema.Column{ChannelMonitorDailyRollupsColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "channel_monitor_daily_rollups_channel_monitors_daily_rollups",
Columns: []*schema.Column{ChannelMonitorDailyRollupsColumns[14]},
RefColumns: []*schema.Column{ChannelMonitorsColumns[0]},
OnDelete: schema.Cascade,
},
},
Indexes: []*schema.Index{
{
Name: "channelmonitordailyrollup_monitor_id_model_bucket_date",
Unique: true,
Columns: []*schema.Column{ChannelMonitorDailyRollupsColumns[14], ChannelMonitorDailyRollupsColumns[1], ChannelMonitorDailyRollupsColumns[2]},
},
{
Name: "channelmonitordailyrollup_bucket_date",
Unique: false,
Columns: []*schema.Column{ChannelMonitorDailyRollupsColumns[2]},
},
},
}
// ChannelMonitorHistoriesColumns holds the columns for the "channel_monitor_histories" table.
ChannelMonitorHistoriesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
{Name: "model", Type: field.TypeString, Size: 200},
{Name: "status", Type: field.TypeEnum, Enums: []string{"operational", "degraded", "failed", "error"}},
{Name: "latency_ms", Type: field.TypeInt, Nullable: true},
{Name: "ping_latency_ms", Type: field.TypeInt, Nullable: true},
{Name: "message", Type: field.TypeString, Nullable: true, Size: 500, Default: ""},
{Name: "checked_at", Type: field.TypeTime},
{Name: "monitor_id", Type: field.TypeInt64},
}
// ChannelMonitorHistoriesTable holds the schema information for the "channel_monitor_histories" table.
ChannelMonitorHistoriesTable = &schema.Table{
Name: "channel_monitor_histories",
Columns: ChannelMonitorHistoriesColumns,
PrimaryKey: []*schema.Column{ChannelMonitorHistoriesColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "channel_monitor_histories_channel_monitors_history",
Columns: []*schema.Column{ChannelMonitorHistoriesColumns[7]},
RefColumns: []*schema.Column{ChannelMonitorsColumns[0]},
OnDelete: schema.Cascade,
},
},
Indexes: []*schema.Index{
{
Name: "channelmonitorhistory_monitor_id_model_checked_at",
Unique: false,
Columns: []*schema.Column{ChannelMonitorHistoriesColumns[7], ChannelMonitorHistoriesColumns[1], ChannelMonitorHistoriesColumns[6]},
},
{
Name: "channelmonitorhistory_checked_at",
Unique: false,
Columns: []*schema.Column{ChannelMonitorHistoriesColumns[6]},
},
},
}
// ChannelMonitorRequestTemplatesColumns holds the columns for the "channel_monitor_request_templates" table.
ChannelMonitorRequestTemplatesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "name", Type: field.TypeString, Size: 100},
{Name: "provider", Type: field.TypeEnum, Enums: []string{"openai", "anthropic", "gemini"}},
{Name: "description", Type: field.TypeString, Nullable: true, Size: 500, Default: ""},
{Name: "extra_headers", Type: field.TypeJSON},
{Name: "body_override_mode", Type: field.TypeString, Size: 10, Default: "off"},
{Name: "body_override", Type: field.TypeJSON, Nullable: true},
}
// ChannelMonitorRequestTemplatesTable holds the schema information for the "channel_monitor_request_templates" table.
ChannelMonitorRequestTemplatesTable = &schema.Table{
Name: "channel_monitor_request_templates",
Columns: ChannelMonitorRequestTemplatesColumns,
PrimaryKey: []*schema.Column{ChannelMonitorRequestTemplatesColumns[0]},
Indexes: []*schema.Index{
{
Name: "channelmonitorrequesttemplate_provider_name",
Unique: true,
Columns: []*schema.Column{ChannelMonitorRequestTemplatesColumns[4], ChannelMonitorRequestTemplatesColumns[3]},
},
},
}
// ErrorPassthroughRulesColumns holds the columns for the "error_passthrough_rules" table.
ErrorPassthroughRulesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
@ -1522,6 +1685,10 @@ var (
AnnouncementReadsTable,
AuthIdentitiesTable,
AuthIdentityChannelsTable,
ChannelMonitorsTable,
ChannelMonitorDailyRollupsTable,
ChannelMonitorHistoriesTable,
ChannelMonitorRequestTemplatesTable,
ErrorPassthroughRulesTable,
GroupsTable,
IdempotencyRecordsTable,
@ -1579,6 +1746,21 @@ func init() {
AuthIdentityChannelsTable.Annotation = &entsql.Annotation{
Table: "auth_identity_channels",
}
ChannelMonitorsTable.ForeignKeys[0].RefTable = ChannelMonitorRequestTemplatesTable
ChannelMonitorsTable.Annotation = &entsql.Annotation{
Table: "channel_monitors",
}
ChannelMonitorDailyRollupsTable.ForeignKeys[0].RefTable = ChannelMonitorsTable
ChannelMonitorDailyRollupsTable.Annotation = &entsql.Annotation{
Table: "channel_monitor_daily_rollups",
}
ChannelMonitorHistoriesTable.ForeignKeys[0].RefTable = ChannelMonitorsTable
ChannelMonitorHistoriesTable.Annotation = &entsql.Annotation{
Table: "channel_monitor_histories",
}
ChannelMonitorRequestTemplatesTable.Annotation = &entsql.Annotation{
Table: "channel_monitor_request_templates",
}
ErrorPassthroughRulesTable.Annotation = &entsql.Annotation{
Table: "error_passthrough_rules",
}

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,18 @@ type AuthIdentity func(*sql.Selector)
// AuthIdentityChannel is the predicate function for authidentitychannel builders.
type AuthIdentityChannel func(*sql.Selector)
// ChannelMonitor is the predicate function for channelmonitor builders.
type ChannelMonitor func(*sql.Selector)
// ChannelMonitorDailyRollup is the predicate function for channelmonitordailyrollup builders.
type ChannelMonitorDailyRollup func(*sql.Selector)
// ChannelMonitorHistory is the predicate function for channelmonitorhistory builders.
type ChannelMonitorHistory func(*sql.Selector)
// ChannelMonitorRequestTemplate is the predicate function for channelmonitorrequesttemplate builders.
type ChannelMonitorRequestTemplate func(*sql.Selector)
// ErrorPassthroughRule is the predicate function for errorpassthroughrule builders.
type ErrorPassthroughRule func(*sql.Selector)

View File

@ -12,6 +12,10 @@ import (
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/authidentity"
"github.com/Wei-Shaw/sub2api/ent/authidentitychannel"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
@ -427,6 +431,252 @@ func init() {
authidentitychannelDescMetadata := authidentitychannelFields[6].Descriptor()
// authidentitychannel.DefaultMetadata holds the default value on creation for the metadata field.
authidentitychannel.DefaultMetadata = authidentitychannelDescMetadata.Default.(func() map[string]interface{})
channelmonitorMixin := schema.ChannelMonitor{}.Mixin()
channelmonitorMixinFields0 := channelmonitorMixin[0].Fields()
_ = channelmonitorMixinFields0
channelmonitorFields := schema.ChannelMonitor{}.Fields()
_ = channelmonitorFields
// channelmonitorDescCreatedAt is the schema descriptor for created_at field.
channelmonitorDescCreatedAt := channelmonitorMixinFields0[0].Descriptor()
// channelmonitor.DefaultCreatedAt holds the default value on creation for the created_at field.
channelmonitor.DefaultCreatedAt = channelmonitorDescCreatedAt.Default.(func() time.Time)
// channelmonitorDescUpdatedAt is the schema descriptor for updated_at field.
channelmonitorDescUpdatedAt := channelmonitorMixinFields0[1].Descriptor()
// channelmonitor.DefaultUpdatedAt holds the default value on creation for the updated_at field.
channelmonitor.DefaultUpdatedAt = channelmonitorDescUpdatedAt.Default.(func() time.Time)
// channelmonitor.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
channelmonitor.UpdateDefaultUpdatedAt = channelmonitorDescUpdatedAt.UpdateDefault.(func() time.Time)
// channelmonitorDescName is the schema descriptor for name field.
channelmonitorDescName := channelmonitorFields[0].Descriptor()
// channelmonitor.NameValidator is a validator for the "name" field. It is called by the builders before save.
channelmonitor.NameValidator = func() func(string) error {
validators := channelmonitorDescName.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(name string) error {
for _, fn := range fns {
if err := fn(name); err != nil {
return err
}
}
return nil
}
}()
// channelmonitorDescEndpoint is the schema descriptor for endpoint field.
channelmonitorDescEndpoint := channelmonitorFields[2].Descriptor()
// channelmonitor.EndpointValidator is a validator for the "endpoint" field. It is called by the builders before save.
channelmonitor.EndpointValidator = func() func(string) error {
validators := channelmonitorDescEndpoint.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(endpoint string) error {
for _, fn := range fns {
if err := fn(endpoint); err != nil {
return err
}
}
return nil
}
}()
// channelmonitorDescAPIKeyEncrypted is the schema descriptor for api_key_encrypted field.
channelmonitorDescAPIKeyEncrypted := channelmonitorFields[3].Descriptor()
// channelmonitor.APIKeyEncryptedValidator is a validator for the "api_key_encrypted" field. It is called by the builders before save.
channelmonitor.APIKeyEncryptedValidator = channelmonitorDescAPIKeyEncrypted.Validators[0].(func(string) error)
// channelmonitorDescPrimaryModel is the schema descriptor for primary_model field.
channelmonitorDescPrimaryModel := channelmonitorFields[4].Descriptor()
// channelmonitor.PrimaryModelValidator is a validator for the "primary_model" field. It is called by the builders before save.
channelmonitor.PrimaryModelValidator = func() func(string) error {
validators := channelmonitorDescPrimaryModel.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(primary_model string) error {
for _, fn := range fns {
if err := fn(primary_model); err != nil {
return err
}
}
return nil
}
}()
// channelmonitorDescExtraModels is the schema descriptor for extra_models field.
channelmonitorDescExtraModels := channelmonitorFields[5].Descriptor()
// channelmonitor.DefaultExtraModels holds the default value on creation for the extra_models field.
channelmonitor.DefaultExtraModels = channelmonitorDescExtraModels.Default.([]string)
// channelmonitorDescGroupName is the schema descriptor for group_name field.
channelmonitorDescGroupName := channelmonitorFields[6].Descriptor()
// channelmonitor.DefaultGroupName holds the default value on creation for the group_name field.
channelmonitor.DefaultGroupName = channelmonitorDescGroupName.Default.(string)
// channelmonitor.GroupNameValidator is a validator for the "group_name" field. It is called by the builders before save.
channelmonitor.GroupNameValidator = channelmonitorDescGroupName.Validators[0].(func(string) error)
// channelmonitorDescEnabled is the schema descriptor for enabled field.
channelmonitorDescEnabled := channelmonitorFields[7].Descriptor()
// channelmonitor.DefaultEnabled holds the default value on creation for the enabled field.
channelmonitor.DefaultEnabled = channelmonitorDescEnabled.Default.(bool)
// channelmonitorDescIntervalSeconds is the schema descriptor for interval_seconds field.
channelmonitorDescIntervalSeconds := channelmonitorFields[8].Descriptor()
// channelmonitor.IntervalSecondsValidator is a validator for the "interval_seconds" field. It is called by the builders before save.
channelmonitor.IntervalSecondsValidator = channelmonitorDescIntervalSeconds.Validators[0].(func(int) error)
// channelmonitorDescExtraHeaders is the schema descriptor for extra_headers field.
channelmonitorDescExtraHeaders := channelmonitorFields[12].Descriptor()
// channelmonitor.DefaultExtraHeaders holds the default value on creation for the extra_headers field.
channelmonitor.DefaultExtraHeaders = channelmonitorDescExtraHeaders.Default.(map[string]string)
// channelmonitorDescBodyOverrideMode is the schema descriptor for body_override_mode field.
channelmonitorDescBodyOverrideMode := channelmonitorFields[13].Descriptor()
// channelmonitor.DefaultBodyOverrideMode holds the default value on creation for the body_override_mode field.
channelmonitor.DefaultBodyOverrideMode = channelmonitorDescBodyOverrideMode.Default.(string)
// channelmonitor.BodyOverrideModeValidator is a validator for the "body_override_mode" field. It is called by the builders before save.
channelmonitor.BodyOverrideModeValidator = channelmonitorDescBodyOverrideMode.Validators[0].(func(string) error)
channelmonitordailyrollupFields := schema.ChannelMonitorDailyRollup{}.Fields()
_ = channelmonitordailyrollupFields
// channelmonitordailyrollupDescModel is the schema descriptor for model field.
channelmonitordailyrollupDescModel := channelmonitordailyrollupFields[1].Descriptor()
// channelmonitordailyrollup.ModelValidator is a validator for the "model" field. It is called by the builders before save.
channelmonitordailyrollup.ModelValidator = func() func(string) error {
validators := channelmonitordailyrollupDescModel.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(model string) error {
for _, fn := range fns {
if err := fn(model); err != nil {
return err
}
}
return nil
}
}()
// channelmonitordailyrollupDescTotalChecks is the schema descriptor for total_checks field.
channelmonitordailyrollupDescTotalChecks := channelmonitordailyrollupFields[3].Descriptor()
// channelmonitordailyrollup.DefaultTotalChecks holds the default value on creation for the total_checks field.
channelmonitordailyrollup.DefaultTotalChecks = channelmonitordailyrollupDescTotalChecks.Default.(int)
// channelmonitordailyrollupDescOkCount is the schema descriptor for ok_count field.
channelmonitordailyrollupDescOkCount := channelmonitordailyrollupFields[4].Descriptor()
// channelmonitordailyrollup.DefaultOkCount holds the default value on creation for the ok_count field.
channelmonitordailyrollup.DefaultOkCount = channelmonitordailyrollupDescOkCount.Default.(int)
// channelmonitordailyrollupDescOperationalCount is the schema descriptor for operational_count field.
channelmonitordailyrollupDescOperationalCount := channelmonitordailyrollupFields[5].Descriptor()
// channelmonitordailyrollup.DefaultOperationalCount holds the default value on creation for the operational_count field.
channelmonitordailyrollup.DefaultOperationalCount = channelmonitordailyrollupDescOperationalCount.Default.(int)
// channelmonitordailyrollupDescDegradedCount is the schema descriptor for degraded_count field.
channelmonitordailyrollupDescDegradedCount := channelmonitordailyrollupFields[6].Descriptor()
// channelmonitordailyrollup.DefaultDegradedCount holds the default value on creation for the degraded_count field.
channelmonitordailyrollup.DefaultDegradedCount = channelmonitordailyrollupDescDegradedCount.Default.(int)
// channelmonitordailyrollupDescFailedCount is the schema descriptor for failed_count field.
channelmonitordailyrollupDescFailedCount := channelmonitordailyrollupFields[7].Descriptor()
// channelmonitordailyrollup.DefaultFailedCount holds the default value on creation for the failed_count field.
channelmonitordailyrollup.DefaultFailedCount = channelmonitordailyrollupDescFailedCount.Default.(int)
// channelmonitordailyrollupDescErrorCount is the schema descriptor for error_count field.
channelmonitordailyrollupDescErrorCount := channelmonitordailyrollupFields[8].Descriptor()
// channelmonitordailyrollup.DefaultErrorCount holds the default value on creation for the error_count field.
channelmonitordailyrollup.DefaultErrorCount = channelmonitordailyrollupDescErrorCount.Default.(int)
// channelmonitordailyrollupDescSumLatencyMs is the schema descriptor for sum_latency_ms field.
channelmonitordailyrollupDescSumLatencyMs := channelmonitordailyrollupFields[9].Descriptor()
// channelmonitordailyrollup.DefaultSumLatencyMs holds the default value on creation for the sum_latency_ms field.
channelmonitordailyrollup.DefaultSumLatencyMs = channelmonitordailyrollupDescSumLatencyMs.Default.(int64)
// channelmonitordailyrollupDescCountLatency is the schema descriptor for count_latency field.
channelmonitordailyrollupDescCountLatency := channelmonitordailyrollupFields[10].Descriptor()
// channelmonitordailyrollup.DefaultCountLatency holds the default value on creation for the count_latency field.
channelmonitordailyrollup.DefaultCountLatency = channelmonitordailyrollupDescCountLatency.Default.(int)
// channelmonitordailyrollupDescSumPingLatencyMs is the schema descriptor for sum_ping_latency_ms field.
channelmonitordailyrollupDescSumPingLatencyMs := channelmonitordailyrollupFields[11].Descriptor()
// channelmonitordailyrollup.DefaultSumPingLatencyMs holds the default value on creation for the sum_ping_latency_ms field.
channelmonitordailyrollup.DefaultSumPingLatencyMs = channelmonitordailyrollupDescSumPingLatencyMs.Default.(int64)
// channelmonitordailyrollupDescCountPingLatency is the schema descriptor for count_ping_latency field.
channelmonitordailyrollupDescCountPingLatency := channelmonitordailyrollupFields[12].Descriptor()
// channelmonitordailyrollup.DefaultCountPingLatency holds the default value on creation for the count_ping_latency field.
channelmonitordailyrollup.DefaultCountPingLatency = channelmonitordailyrollupDescCountPingLatency.Default.(int)
// channelmonitordailyrollupDescComputedAt is the schema descriptor for computed_at field.
channelmonitordailyrollupDescComputedAt := channelmonitordailyrollupFields[13].Descriptor()
// channelmonitordailyrollup.DefaultComputedAt holds the default value on creation for the computed_at field.
channelmonitordailyrollup.DefaultComputedAt = channelmonitordailyrollupDescComputedAt.Default.(func() time.Time)
// channelmonitordailyrollup.UpdateDefaultComputedAt holds the default value on update for the computed_at field.
channelmonitordailyrollup.UpdateDefaultComputedAt = channelmonitordailyrollupDescComputedAt.UpdateDefault.(func() time.Time)
channelmonitorhistoryFields := schema.ChannelMonitorHistory{}.Fields()
_ = channelmonitorhistoryFields
// channelmonitorhistoryDescModel is the schema descriptor for model field.
channelmonitorhistoryDescModel := channelmonitorhistoryFields[1].Descriptor()
// channelmonitorhistory.ModelValidator is a validator for the "model" field. It is called by the builders before save.
channelmonitorhistory.ModelValidator = func() func(string) error {
validators := channelmonitorhistoryDescModel.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(model string) error {
for _, fn := range fns {
if err := fn(model); err != nil {
return err
}
}
return nil
}
}()
// channelmonitorhistoryDescMessage is the schema descriptor for message field.
channelmonitorhistoryDescMessage := channelmonitorhistoryFields[5].Descriptor()
// channelmonitorhistory.DefaultMessage holds the default value on creation for the message field.
channelmonitorhistory.DefaultMessage = channelmonitorhistoryDescMessage.Default.(string)
// channelmonitorhistory.MessageValidator is a validator for the "message" field. It is called by the builders before save.
channelmonitorhistory.MessageValidator = channelmonitorhistoryDescMessage.Validators[0].(func(string) error)
// channelmonitorhistoryDescCheckedAt is the schema descriptor for checked_at field.
channelmonitorhistoryDescCheckedAt := channelmonitorhistoryFields[6].Descriptor()
// channelmonitorhistory.DefaultCheckedAt holds the default value on creation for the checked_at field.
channelmonitorhistory.DefaultCheckedAt = channelmonitorhistoryDescCheckedAt.Default.(func() time.Time)
channelmonitorrequesttemplateMixin := schema.ChannelMonitorRequestTemplate{}.Mixin()
channelmonitorrequesttemplateMixinFields0 := channelmonitorrequesttemplateMixin[0].Fields()
_ = channelmonitorrequesttemplateMixinFields0
channelmonitorrequesttemplateFields := schema.ChannelMonitorRequestTemplate{}.Fields()
_ = channelmonitorrequesttemplateFields
// channelmonitorrequesttemplateDescCreatedAt is the schema descriptor for created_at field.
channelmonitorrequesttemplateDescCreatedAt := channelmonitorrequesttemplateMixinFields0[0].Descriptor()
// channelmonitorrequesttemplate.DefaultCreatedAt holds the default value on creation for the created_at field.
channelmonitorrequesttemplate.DefaultCreatedAt = channelmonitorrequesttemplateDescCreatedAt.Default.(func() time.Time)
// channelmonitorrequesttemplateDescUpdatedAt is the schema descriptor for updated_at field.
channelmonitorrequesttemplateDescUpdatedAt := channelmonitorrequesttemplateMixinFields0[1].Descriptor()
// channelmonitorrequesttemplate.DefaultUpdatedAt holds the default value on creation for the updated_at field.
channelmonitorrequesttemplate.DefaultUpdatedAt = channelmonitorrequesttemplateDescUpdatedAt.Default.(func() time.Time)
// channelmonitorrequesttemplate.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
channelmonitorrequesttemplate.UpdateDefaultUpdatedAt = channelmonitorrequesttemplateDescUpdatedAt.UpdateDefault.(func() time.Time)
// channelmonitorrequesttemplateDescName is the schema descriptor for name field.
channelmonitorrequesttemplateDescName := channelmonitorrequesttemplateFields[0].Descriptor()
// channelmonitorrequesttemplate.NameValidator is a validator for the "name" field. It is called by the builders before save.
channelmonitorrequesttemplate.NameValidator = func() func(string) error {
validators := channelmonitorrequesttemplateDescName.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(name string) error {
for _, fn := range fns {
if err := fn(name); err != nil {
return err
}
}
return nil
}
}()
// channelmonitorrequesttemplateDescDescription is the schema descriptor for description field.
channelmonitorrequesttemplateDescDescription := channelmonitorrequesttemplateFields[2].Descriptor()
// channelmonitorrequesttemplate.DefaultDescription holds the default value on creation for the description field.
channelmonitorrequesttemplate.DefaultDescription = channelmonitorrequesttemplateDescDescription.Default.(string)
// channelmonitorrequesttemplate.DescriptionValidator is a validator for the "description" field. It is called by the builders before save.
channelmonitorrequesttemplate.DescriptionValidator = channelmonitorrequesttemplateDescDescription.Validators[0].(func(string) error)
// channelmonitorrequesttemplateDescExtraHeaders is the schema descriptor for extra_headers field.
channelmonitorrequesttemplateDescExtraHeaders := channelmonitorrequesttemplateFields[3].Descriptor()
// channelmonitorrequesttemplate.DefaultExtraHeaders holds the default value on creation for the extra_headers field.
channelmonitorrequesttemplate.DefaultExtraHeaders = channelmonitorrequesttemplateDescExtraHeaders.Default.(map[string]string)
// channelmonitorrequesttemplateDescBodyOverrideMode is the schema descriptor for body_override_mode field.
channelmonitorrequesttemplateDescBodyOverrideMode := channelmonitorrequesttemplateFields[4].Descriptor()
// channelmonitorrequesttemplate.DefaultBodyOverrideMode holds the default value on creation for the body_override_mode field.
channelmonitorrequesttemplate.DefaultBodyOverrideMode = channelmonitorrequesttemplateDescBodyOverrideMode.Default.(string)
// channelmonitorrequesttemplate.BodyOverrideModeValidator is a validator for the "body_override_mode" field. It is called by the builders before save.
channelmonitorrequesttemplate.BodyOverrideModeValidator = channelmonitorrequesttemplateDescBodyOverrideMode.Validators[0].(func(string) error)
errorpassthroughruleMixin := schema.ErrorPassthroughRule{}.Mixin()
errorpassthroughruleMixinFields0 := errorpassthroughruleMixin[0].Fields()
_ = errorpassthroughruleMixinFields0

View File

@ -0,0 +1,110 @@
package schema
import (
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"entgo.io/ent"
"entgo.io/ent/dialect/entsql"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// ChannelMonitor holds the schema definition for the ChannelMonitor entity.
// 渠道监控配置:定期对指定 provider/endpoint/api_key 下的模型做心跳测试。
type ChannelMonitor struct {
ent.Schema
}
func (ChannelMonitor) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.Annotation{Table: "channel_monitors"},
}
}
func (ChannelMonitor) Mixin() []ent.Mixin {
return []ent.Mixin{
mixins.TimeMixin{},
}
}
func (ChannelMonitor) Fields() []ent.Field {
return []ent.Field{
field.String("name").
NotEmpty().
MaxLen(100),
field.Enum("provider").
Values("openai", "anthropic", "gemini"),
field.String("endpoint").
NotEmpty().
MaxLen(500).
Comment("Provider base origin, e.g. https://api.openai.com"),
field.String("api_key_encrypted").
NotEmpty().
Sensitive().
Comment("AES-256-GCM encrypted API key"),
field.String("primary_model").
NotEmpty().
MaxLen(200),
field.JSON("extra_models", []string{}).
Default([]string{}).
Comment("Additional model names to test alongside primary_model"),
field.String("group_name").
Optional().
Default("").
MaxLen(100),
field.Bool("enabled").
Default(true),
field.Int("interval_seconds").
Range(15, 3600),
field.Time("last_checked_at").
Optional().
Nillable(),
field.Int64("created_by"),
// ---- 自定义请求快照字段(来自模板 / 手动编辑) ----
// template_id: 关联的请求模板 ID仅用于 UI 分组 + 一键应用)。
// 实际运行时 checker 只读下面 3 个快照字段,**不再回查模板表**。
// 模板被删除时此字段会被 SET NULL见 Edges 的 OnDelete 注解)。
field.Int64("template_id").
Optional().
Nillable(),
// extra_headers: 自定义 HTTP 头快照(来自模板 or 用户手填)。
// 运行时 merge 进 adapter 默认 headers。
field.JSON("extra_headers", map[string]string{}).
Default(map[string]string{}),
// body_override_mode: 同 ChannelMonitorRequestTemplate.body_override_mode
field.String("body_override_mode").
Default("off").
MaxLen(10),
// body_override: 同 ChannelMonitorRequestTemplate.body_override
field.JSON("body_override", map[string]any{}).
Optional(),
}
}
func (ChannelMonitor) Edges() []ent.Edge {
return []ent.Edge{
edge.To("history", ChannelMonitorHistory.Type).
Annotations(entsql.OnDelete(entsql.Cascade)),
edge.To("daily_rollups", ChannelMonitorDailyRollup.Type).
Annotations(entsql.OnDelete(entsql.Cascade)),
// 关联请求模板:模板被删除时 template_id 自动置空,
// 监控本身保留(继续用快照字段跑)。
edge.To("request_template", ChannelMonitorRequestTemplate.Type).
Field("template_id").
Unique().
Annotations(entsql.OnDelete(entsql.SetNull)),
}
}
func (ChannelMonitor) Indexes() []ent.Index {
return []ent.Index{
index.Fields("enabled", "last_checked_at"),
index.Fields("provider"),
index.Fields("group_name"),
index.Fields("template_id"),
}
}

View File

@ -0,0 +1,66 @@
package schema
import (
"time"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/entsql"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// ChannelMonitorDailyRollup 按 (monitor_id, model, bucket_date) 维度聚合的渠道监控日统计。
// 每天的明细被收敛为一行(保留 status 分布 + 延迟和),用于 7d/15d/30d 窗口的可用率
// 加权计算avg_latency = sum_latency_ms / count_latencyavailability = ok_count / total_checks
// 超过保留期由每日维护任务分批物理删(不用软删除,理由同 channel_monitor_history
type ChannelMonitorDailyRollup struct {
ent.Schema
}
func (ChannelMonitorDailyRollup) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.Annotation{Table: "channel_monitor_daily_rollups"},
}
}
func (ChannelMonitorDailyRollup) Fields() []ent.Field {
return []ent.Field{
field.Int64("monitor_id"),
field.String("model").
NotEmpty().
MaxLen(200),
field.Time("bucket_date").
SchemaType(map[string]string{dialect.Postgres: "date"}),
field.Int("total_checks").Default(0),
field.Int("ok_count").Default(0),
field.Int("operational_count").Default(0),
field.Int("degraded_count").Default(0),
field.Int("failed_count").Default(0),
field.Int("error_count").Default(0),
field.Int64("sum_latency_ms").Default(0),
field.Int("count_latency").Default(0),
field.Int64("sum_ping_latency_ms").Default(0),
field.Int("count_ping_latency").Default(0),
field.Time("computed_at").Default(time.Now).UpdateDefault(time.Now),
}
}
func (ChannelMonitorDailyRollup) Edges() []ent.Edge {
return []ent.Edge{
edge.From("monitor", ChannelMonitor.Type).
Ref("daily_rollups").
Field("monitor_id").
Unique().
Required(),
}
}
func (ChannelMonitorDailyRollup) Indexes() []ent.Index {
return []ent.Index{
index.Fields("monitor_id", "model", "bucket_date").Unique(),
index.Fields("bucket_date"),
}
}

View File

@ -0,0 +1,66 @@
package schema
import (
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/entsql"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// ChannelMonitorHistory holds the schema definition for the ChannelMonitorHistory entity.
// 渠道监控历史:每次检测每个模型一行记录。明细只保留 1 天,超过 1 天由每日维护任务
// 先聚合到 channel_monitor_daily_rollups再分批物理删不用软删除日志类表无恢复
// 需求,软删会让行和索引只增不减,徒增磁盘和查询开销)。
type ChannelMonitorHistory struct {
ent.Schema
}
func (ChannelMonitorHistory) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.Annotation{Table: "channel_monitor_histories"},
}
}
func (ChannelMonitorHistory) Fields() []ent.Field {
return []ent.Field{
field.Int64("monitor_id"),
field.String("model").
NotEmpty().
MaxLen(200),
field.Enum("status").
Values("operational", "degraded", "failed", "error"),
field.Int("latency_ms").
Optional().
Nillable(),
field.Int("ping_latency_ms").
Optional().
Nillable(),
field.String("message").
Optional().
Default("").
MaxLen(500),
field.Time("checked_at").
Default(time.Now),
}
}
func (ChannelMonitorHistory) Edges() []ent.Edge {
return []ent.Edge{
edge.From("monitor", ChannelMonitor.Type).
Ref("history").
Field("monitor_id").
Unique().
Required(),
}
}
func (ChannelMonitorHistory) Indexes() []ent.Index {
return []ent.Index{
index.Fields("monitor_id", "model", "checked_at"),
index.Fields("checked_at"),
}
}

View File

@ -0,0 +1,80 @@
package schema
import (
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"entgo.io/ent"
"entgo.io/ent/dialect/entsql"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// ChannelMonitorRequestTemplate 请求模板:一组可复用的 headers + 可选 body 覆盖配置。
//
// 语义为快照:模板被"应用"到监控时extra_headers / body_override_mode / body_override
// 会被**拷贝**到 channel_monitors 同名字段;后续模板变动不会自动影响已应用的监控——
// 必须用户主动在模板编辑 Dialog 里点「应用到关联监控」才会覆盖快照。
// 这样模板改错不会瞬间打挂所有已经跑起来的监控。
type ChannelMonitorRequestTemplate struct {
ent.Schema
}
func (ChannelMonitorRequestTemplate) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.Annotation{Table: "channel_monitor_request_templates"},
}
}
func (ChannelMonitorRequestTemplate) Mixin() []ent.Mixin {
return []ent.Mixin{
mixins.TimeMixin{},
}
}
func (ChannelMonitorRequestTemplate) Fields() []ent.Field {
return []ent.Field{
field.String("name").
NotEmpty().
MaxLen(100),
field.Enum("provider").
Values("openai", "anthropic", "gemini"),
field.String("description").
Optional().
Default("").
MaxLen(500),
// extra_headers: 用户自定义 HTTP 头(如 User-Agent 伪装)。
// 运行时 merge 进 adapter 默认 headers用户值优先
// hop-by-hop 黑名单Host/Content-Length/...)由 checker 过滤。
field.JSON("extra_headers", map[string]string{}).
Default(map[string]string{}),
// body_override_mode: 'off' | 'merge' | 'replace'
// off - 用 adapter 默认 body忽略 body_override
// merge - adapter 默认 body 与 body_override 浅合并body_override 优先,
// model/messages/contents 等关键字段在 checker 里走黑名单跳过)
// replace - 直接用 body_override 作为完整 body此时跳过 challenge 校验,
// 改为 HTTP 2xx + 响应文本非空即视为可用
field.String("body_override_mode").
Default("off").
MaxLen(10),
// body_override: JSON 对象,根据 body_override_mode 使用。
// 用 map[string]any 以便前端传任意结构(含嵌套)。
field.JSON("body_override", map[string]any{}).
Optional(),
}
}
func (ChannelMonitorRequestTemplate) Edges() []ent.Edge {
return []ent.Edge{
edge.From("monitors", ChannelMonitor.Type).
Ref("request_template"),
}
}
func (ChannelMonitorRequestTemplate) Indexes() []ent.Index {
return []ent.Index{
// 同一 provider 内 name 唯一:允许 Anthropic + OpenAI 重名 "伪装官方客户端"。
index.Fields("provider", "name").Unique(),
}
}

View File

@ -28,6 +28,14 @@ type Tx struct {
AuthIdentity *AuthIdentityClient
// AuthIdentityChannel is the client for interacting with the AuthIdentityChannel builders.
AuthIdentityChannel *AuthIdentityChannelClient
// ChannelMonitor is the client for interacting with the ChannelMonitor builders.
ChannelMonitor *ChannelMonitorClient
// ChannelMonitorDailyRollup is the client for interacting with the ChannelMonitorDailyRollup builders.
ChannelMonitorDailyRollup *ChannelMonitorDailyRollupClient
// ChannelMonitorHistory is the client for interacting with the ChannelMonitorHistory builders.
ChannelMonitorHistory *ChannelMonitorHistoryClient
// ChannelMonitorRequestTemplate is the client for interacting with the ChannelMonitorRequestTemplate builders.
ChannelMonitorRequestTemplate *ChannelMonitorRequestTemplateClient
// ErrorPassthroughRule is the client for interacting with the ErrorPassthroughRule builders.
ErrorPassthroughRule *ErrorPassthroughRuleClient
// Group is the client for interacting with the Group builders.
@ -212,6 +220,10 @@ func (tx *Tx) init() {
tx.AnnouncementRead = NewAnnouncementReadClient(tx.config)
tx.AuthIdentity = NewAuthIdentityClient(tx.config)
tx.AuthIdentityChannel = NewAuthIdentityChannelClient(tx.config)
tx.ChannelMonitor = NewChannelMonitorClient(tx.config)
tx.ChannelMonitorDailyRollup = NewChannelMonitorDailyRollupClient(tx.config)
tx.ChannelMonitorHistory = NewChannelMonitorHistoryClient(tx.config)
tx.ChannelMonitorRequestTemplate = NewChannelMonitorRequestTemplateClient(tx.config)
tx.ErrorPassthroughRule = NewErrorPassthroughRuleClient(tx.config)
tx.Group = NewGroupClient(tx.config)
tx.IdempotencyRecord = NewIdempotencyRecordClient(tx.config)

View File

@ -183,6 +183,8 @@ github.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4=
github.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y=
github.com/imroc/req/v3 v3.57.0 h1:LMTUjNRUybUkTPn8oJDq8Kg3JRBOBTcnDhKu7mzupKI=
github.com/imroc/req/v3 v3.57.0/go.mod h1:JL62ey1nvSLq81HORNcosvlf7SxZStONNqOprg0Pz00=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=

View File

@ -158,9 +158,6 @@ func channelToResponse(ch *service.Channel) *channelResponse {
UpdatedAt: ch.UpdatedAt.Format("2006-01-02T15:04:05Z"),
}
resp.BillingModelSource = ch.BillingModelSource
if resp.BillingModelSource == "" {
resp.BillingModelSource = service.BillingModelSourceChannelMapped
}
if resp.GroupIDs == nil {
resp.GroupIDs = []int64{}
}

View File

@ -91,7 +91,7 @@ func TestChannelToResponse_EmptyDefaults(t *testing.T) {
ch := &service.Channel{
ID: 1,
Name: "ch",
BillingModelSource: "",
BillingModelSource: service.BillingModelSourceChannelMapped,
CreatedAt: now,
UpdatedAt: now,
GroupIDs: nil,
@ -105,6 +105,9 @@ func TestChannelToResponse_EmptyDefaults(t *testing.T) {
},
}
// handler 层 channelToResponse 现在是纯透传BillingModelSource 的空值兜底
// 已下放到 service 层Create/GetByID/List/Update/ListAvailable 出口统一处理),
// 因此这里构造 fixture 时直接传入归一化后的值。
resp := channelToResponse(ch)
require.Equal(t, "channel_mapped", resp.BillingModelSource)
require.NotNil(t, resp.GroupIDs)
@ -117,6 +120,19 @@ func TestChannelToResponse_EmptyDefaults(t *testing.T) {
require.Equal(t, "token", resp.ModelPricing[0].BillingMode)
}
func TestChannelToResponse_BillingModelSourcePassthrough(t *testing.T) {
// handler 不再兜底 BillingModelSource空值应原样透传由 service 层负责默认回填)。
ch := &service.Channel{
ID: 1,
Name: "ch",
BillingModelSource: "",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
resp := channelToResponse(ch)
require.Equal(t, "", resp.BillingModelSource, "handler 应纯透传,默认值由 service.normalizeBillingModelSource 负责")
}
func TestChannelToResponse_NilModels(t *testing.T) {
now := time.Now()
ch := &service.Channel{

View File

@ -0,0 +1,427 @@
package admin
import (
"strconv"
"strings"
"time"
"github.com/Wei-Shaw/sub2api/internal/handler/dto"
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
const (
// monitorMaxPageSize 列表分页上限。
monitorMaxPageSize = 100
// monitorAPIKeyMaskPrefix 脱敏时保留的明文前缀长度。
monitorAPIKeyMaskPrefix = 4
// monitorAPIKeyMaskSuffix 脱敏后追加的占位字符串。
monitorAPIKeyMaskSuffix = "***"
)
// ChannelMonitorHandler 渠道监控管理后台 handler。
type ChannelMonitorHandler struct {
monitorService *service.ChannelMonitorService
}
// NewChannelMonitorHandler 创建 handler。
func NewChannelMonitorHandler(monitorService *service.ChannelMonitorService) *ChannelMonitorHandler {
return &ChannelMonitorHandler{monitorService: monitorService}
}
// --- Request / Response ---
type channelMonitorCreateRequest struct {
Name string `json:"name" binding:"required,max=100"`
Provider string `json:"provider" binding:"required,oneof=openai anthropic gemini"`
Endpoint string `json:"endpoint" binding:"required,max=500"`
APIKey string `json:"api_key" binding:"required,max=2000"`
PrimaryModel string `json:"primary_model" binding:"required,max=200"`
ExtraModels []string `json:"extra_models"`
GroupName string `json:"group_name" binding:"max=100"`
Enabled *bool `json:"enabled"`
IntervalSeconds int `json:"interval_seconds" binding:"required,min=15,max=3600"`
TemplateID *int64 `json:"template_id"`
ExtraHeaders map[string]string `json:"extra_headers"`
BodyOverrideMode string `json:"body_override_mode" binding:"omitempty,oneof=off merge replace"`
BodyOverride map[string]any `json:"body_override"`
}
type channelMonitorUpdateRequest struct {
Name *string `json:"name" binding:"omitempty,max=100"`
Provider *string `json:"provider" binding:"omitempty,oneof=openai anthropic gemini"`
Endpoint *string `json:"endpoint" binding:"omitempty,max=500"`
APIKey *string `json:"api_key" binding:"omitempty,max=2000"`
PrimaryModel *string `json:"primary_model" binding:"omitempty,max=200"`
ExtraModels *[]string `json:"extra_models"`
GroupName *string `json:"group_name" binding:"omitempty,max=100"`
Enabled *bool `json:"enabled"`
IntervalSeconds *int `json:"interval_seconds" binding:"omitempty,min=15,max=3600"`
TemplateID *int64 `json:"template_id"`
ClearTemplate bool `json:"clear_template"` // true 时把 template_id 置空,忽略 TemplateID
ExtraHeaders *map[string]string `json:"extra_headers"`
BodyOverrideMode *string `json:"body_override_mode" binding:"omitempty,oneof=off merge replace"`
BodyOverride *map[string]any `json:"body_override"`
}
type channelMonitorResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Endpoint string `json:"endpoint"`
APIKeyMasked string `json:"api_key_masked"`
APIKeyDecryptFailed bool `json:"api_key_decrypt_failed"`
PrimaryModel string `json:"primary_model"`
ExtraModels []string `json:"extra_models"`
GroupName string `json:"group_name"`
Enabled bool `json:"enabled"`
IntervalSeconds int `json:"interval_seconds"`
LastCheckedAt *string `json:"last_checked_at"`
CreatedBy int64 `json:"created_by"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
PrimaryStatus string `json:"primary_status"`
PrimaryLatencyMs *int `json:"primary_latency_ms"`
Availability7d float64 `json:"availability_7d"`
ExtraModelsStatus []dto.ChannelMonitorExtraModelStatus `json:"extra_models_status"`
// 请求自定义快照:前端编辑 / 展示「高级设置」用
TemplateID *int64 `json:"template_id"`
ExtraHeaders map[string]string `json:"extra_headers"`
BodyOverrideMode string `json:"body_override_mode"`
BodyOverride map[string]any `json:"body_override"`
}
type channelMonitorCheckResultResponse struct {
Model string `json:"model"`
Status string `json:"status"`
LatencyMs *int `json:"latency_ms"`
PingLatencyMs *int `json:"ping_latency_ms"`
Message string `json:"message"`
CheckedAt string `json:"checked_at"`
}
type channelMonitorHistoryItemResponse struct {
ID int64 `json:"id"`
Model string `json:"model"`
Status string `json:"status"`
LatencyMs *int `json:"latency_ms"`
PingLatencyMs *int `json:"ping_latency_ms"`
Message string `json:"message"`
CheckedAt string `json:"checked_at"`
}
// maskAPIKey 对 API Key 明文做脱敏:前 4 字符 + "***",长度 ≤ 4 时只显示 "***"。
func maskAPIKey(plain string) string {
if len(plain) <= monitorAPIKeyMaskPrefix {
return monitorAPIKeyMaskSuffix
}
return plain[:monitorAPIKeyMaskPrefix] + monitorAPIKeyMaskSuffix
}
func channelMonitorToResponse(m *service.ChannelMonitor) *channelMonitorResponse {
if m == nil {
return nil
}
extras := m.ExtraModels
if extras == nil {
extras = []string{}
}
headers := m.ExtraHeaders
if headers == nil {
headers = map[string]string{}
}
resp := &channelMonitorResponse{
ID: m.ID,
Name: m.Name,
Provider: m.Provider,
Endpoint: m.Endpoint,
APIKeyMasked: maskAPIKey(m.APIKey),
APIKeyDecryptFailed: m.APIKeyDecryptFailed,
PrimaryModel: m.PrimaryModel,
ExtraModels: extras,
GroupName: m.GroupName,
Enabled: m.Enabled,
IntervalSeconds: m.IntervalSeconds,
CreatedBy: m.CreatedBy,
CreatedAt: m.CreatedAt.UTC().Format(time.RFC3339),
UpdatedAt: m.UpdatedAt.UTC().Format(time.RFC3339),
TemplateID: m.TemplateID,
ExtraHeaders: headers,
BodyOverrideMode: m.BodyOverrideMode,
BodyOverride: m.BodyOverride,
// PrimaryStatus / PrimaryLatencyMs / Availability7d 由 List handler 在批量聚合后填充。
}
if m.LastCheckedAt != nil {
s := m.LastCheckedAt.UTC().Format(time.RFC3339)
resp.LastCheckedAt = &s
}
return resp
}
func checkResultToResponse(r *service.CheckResult) channelMonitorCheckResultResponse {
return channelMonitorCheckResultResponse{
Model: r.Model,
Status: r.Status,
LatencyMs: r.LatencyMs,
PingLatencyMs: r.PingLatencyMs,
Message: r.Message,
CheckedAt: r.CheckedAt.UTC().Format(time.RFC3339),
}
}
func historyEntryToResponse(e *service.ChannelMonitorHistoryEntry) channelMonitorHistoryItemResponse {
return channelMonitorHistoryItemResponse{
ID: e.ID,
Model: e.Model,
Status: e.Status,
LatencyMs: e.LatencyMs,
PingLatencyMs: e.PingLatencyMs,
Message: e.Message,
CheckedAt: e.CheckedAt.UTC().Format(time.RFC3339),
}
}
// ParseChannelMonitorID 提取并校验路径参数 :idadmin 与 user handler 共享)。
// 校验失败时已写入 4xx 响应,调用方只需 return。
func ParseChannelMonitorID(c *gin.Context) (int64, bool) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil || id <= 0 {
response.ErrorFrom(c, infraerrors.BadRequest("INVALID_MONITOR_ID", "invalid monitor id"))
return 0, false
}
return id, true
}
// parseListEnabled 解析 enabled query 参数true/false 转为 *bool空或非法则返回 nil。
func parseListEnabled(raw string) *bool {
switch strings.ToLower(strings.TrimSpace(raw)) {
case "true", "1", "yes":
v := true
return &v
case "false", "0", "no":
v := false
return &v
default:
return nil
}
}
// --- Handlers ---
// List GET /api/v1/admin/channel-monitors
func (h *ChannelMonitorHandler) List(c *gin.Context) {
page, pageSize := response.ParsePagination(c)
if pageSize > monitorMaxPageSize {
pageSize = monitorMaxPageSize
}
params := service.ChannelMonitorListParams{
Page: page,
PageSize: pageSize,
Provider: strings.TrimSpace(c.Query("provider")),
Enabled: parseListEnabled(c.Query("enabled")),
Search: strings.TrimSpace(c.Query("search")),
}
items, total, err := h.monitorService.List(c.Request.Context(), params)
if err != nil {
response.ErrorFrom(c, err)
return
}
summaries := h.batchSummaryFor(c, items)
out := make([]*channelMonitorResponse, 0, len(items))
for _, m := range items {
out = append(out, buildListItemResponse(m, summaries[m.ID]))
}
response.Paginated(c, out, total, page, pageSize)
}
// batchSummaryFor 批量聚合 latest + 7d 可用率,避免每行 2 次 SQL消除 N+1
func (h *ChannelMonitorHandler) batchSummaryFor(c *gin.Context, items []*service.ChannelMonitor) map[int64]service.MonitorStatusSummary {
ids := make([]int64, 0, len(items))
primaryByID := make(map[int64]string, len(items))
extrasByID := make(map[int64][]string, len(items))
for _, m := range items {
ids = append(ids, m.ID)
primaryByID[m.ID] = m.PrimaryModel
extrasByID[m.ID] = m.ExtraModels
}
return h.monitorService.BatchMonitorStatusSummary(c.Request.Context(), ids, primaryByID, extrasByID)
}
// buildListItemResponse 把 monitor + summary 装成 admin list 的响应行。
func buildListItemResponse(m *service.ChannelMonitor, summary service.MonitorStatusSummary) *channelMonitorResponse {
resp := channelMonitorToResponse(m)
resp.PrimaryStatus = summary.PrimaryStatus
resp.PrimaryLatencyMs = summary.PrimaryLatencyMs
resp.Availability7d = summary.Availability7d
resp.ExtraModelsStatus = make([]dto.ChannelMonitorExtraModelStatus, 0, len(summary.ExtraModels))
for _, e := range summary.ExtraModels {
resp.ExtraModelsStatus = append(resp.ExtraModelsStatus, dto.ChannelMonitorExtraModelStatus{
Model: e.Model,
Status: e.Status,
LatencyMs: e.LatencyMs,
})
}
return resp
}
// Get GET /api/v1/admin/channel-monitors/:id
func (h *ChannelMonitorHandler) Get(c *gin.Context) {
id, ok := ParseChannelMonitorID(c)
if !ok {
return
}
m, err := h.monitorService.Get(c.Request.Context(), id)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, channelMonitorToResponse(m))
}
// Create POST /api/v1/admin/channel-monitors
func (h *ChannelMonitorHandler) Create(c *gin.Context) {
var req channelMonitorCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error()))
return
}
subject, _ := middleware2.GetAuthSubjectFromContext(c)
enabled := true
if req.Enabled != nil {
enabled = *req.Enabled
}
m, err := h.monitorService.Create(c.Request.Context(), service.ChannelMonitorCreateParams{
Name: req.Name,
Provider: req.Provider,
Endpoint: req.Endpoint,
APIKey: req.APIKey,
PrimaryModel: req.PrimaryModel,
ExtraModels: req.ExtraModels,
GroupName: req.GroupName,
Enabled: enabled,
IntervalSeconds: req.IntervalSeconds,
CreatedBy: subject.UserID,
TemplateID: req.TemplateID,
ExtraHeaders: req.ExtraHeaders,
BodyOverrideMode: req.BodyOverrideMode,
BodyOverride: req.BodyOverride,
})
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Created(c, channelMonitorToResponse(m))
}
// Update PUT /api/v1/admin/channel-monitors/:id
func (h *ChannelMonitorHandler) Update(c *gin.Context) {
id, ok := ParseChannelMonitorID(c)
if !ok {
return
}
var req channelMonitorUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error()))
return
}
m, err := h.monitorService.Update(c.Request.Context(), id, service.ChannelMonitorUpdateParams{
Name: req.Name,
Provider: req.Provider,
Endpoint: req.Endpoint,
APIKey: req.APIKey,
PrimaryModel: req.PrimaryModel,
ExtraModels: req.ExtraModels,
GroupName: req.GroupName,
Enabled: req.Enabled,
IntervalSeconds: req.IntervalSeconds,
TemplateID: req.TemplateID,
ClearTemplate: req.ClearTemplate,
ExtraHeaders: req.ExtraHeaders,
BodyOverrideMode: req.BodyOverrideMode,
BodyOverride: req.BodyOverride,
})
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, channelMonitorToResponse(m))
}
// Delete DELETE /api/v1/admin/channel-monitors/:id
func (h *ChannelMonitorHandler) Delete(c *gin.Context) {
id, ok := ParseChannelMonitorID(c)
if !ok {
return
}
if err := h.monitorService.Delete(c.Request.Context(), id); err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, nil)
}
// Run POST /api/v1/admin/channel-monitors/:id/run
func (h *ChannelMonitorHandler) Run(c *gin.Context) {
id, ok := ParseChannelMonitorID(c)
if !ok {
return
}
results, err := h.monitorService.RunCheck(c.Request.Context(), id)
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]channelMonitorCheckResultResponse, 0, len(results))
for _, r := range results {
out = append(out, checkResultToResponse(r))
}
response.Success(c, gin.H{"results": out})
}
// History GET /api/v1/admin/channel-monitors/:id/history
func (h *ChannelMonitorHandler) History(c *gin.Context) {
id, ok := ParseChannelMonitorID(c)
if !ok {
return
}
limit := parseHistoryLimit(c.Query("limit"))
model := strings.TrimSpace(c.Query("model"))
entries, err := h.monitorService.ListHistory(c.Request.Context(), id, model, limit)
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]channelMonitorHistoryItemResponse, 0, len(entries))
for _, e := range entries {
out = append(out, historyEntryToResponse(e))
}
response.Success(c, gin.H{"items": out})
}
// parseHistoryLimit 解析 history 接口的 limit query。
// 使用 service 包的统一上下限常量,避免在 handler 重复定义同名魔法值。
func parseHistoryLimit(raw string) int {
if strings.TrimSpace(raw) == "" {
return service.MonitorHistoryDefaultLimit
}
v, err := strconv.Atoi(raw)
if err != nil || v <= 0 {
return service.MonitorHistoryDefaultLimit
}
if v > service.MonitorHistoryMaxLimit {
return service.MonitorHistoryMaxLimit
}
return v
}

View File

@ -0,0 +1,234 @@
package admin
import (
"strconv"
"strings"
"time"
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// ChannelMonitorRequestTemplateHandler 请求模板管理后台 handler。
type ChannelMonitorRequestTemplateHandler struct {
templateService *service.ChannelMonitorRequestTemplateService
}
// NewChannelMonitorRequestTemplateHandler 创建 handler。
func NewChannelMonitorRequestTemplateHandler(templateService *service.ChannelMonitorRequestTemplateService) *ChannelMonitorRequestTemplateHandler {
return &ChannelMonitorRequestTemplateHandler{templateService: templateService}
}
// --- DTO ---
type channelMonitorTemplateCreateRequest struct {
Name string `json:"name" binding:"required,max=100"`
Provider string `json:"provider" binding:"required,oneof=openai anthropic gemini"`
Description string `json:"description" binding:"max=500"`
ExtraHeaders map[string]string `json:"extra_headers"`
BodyOverrideMode string `json:"body_override_mode" binding:"omitempty,oneof=off merge replace"`
BodyOverride map[string]any `json:"body_override"`
}
type channelMonitorTemplateUpdateRequest struct {
Name *string `json:"name" binding:"omitempty,max=100"`
Description *string `json:"description" binding:"omitempty,max=500"`
ExtraHeaders *map[string]string `json:"extra_headers"`
BodyOverrideMode *string `json:"body_override_mode" binding:"omitempty,oneof=off merge replace"`
BodyOverride *map[string]any `json:"body_override"`
}
type channelMonitorTemplateResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Description string `json:"description"`
ExtraHeaders map[string]string `json:"extra_headers"`
BodyOverrideMode string `json:"body_override_mode"`
BodyOverride map[string]any `json:"body_override"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
AssociatedMonitors int64 `json:"associated_monitors"`
}
func (h *ChannelMonitorRequestTemplateHandler) toResponse(c *gin.Context, t *service.ChannelMonitorRequestTemplate) *channelMonitorTemplateResponse {
if t == nil {
return nil
}
headers := t.ExtraHeaders
if headers == nil {
headers = map[string]string{}
}
count, _ := h.templateService.CountAssociatedMonitors(c.Request.Context(), t.ID)
return &channelMonitorTemplateResponse{
ID: t.ID,
Name: t.Name,
Provider: t.Provider,
Description: t.Description,
ExtraHeaders: headers,
BodyOverrideMode: t.BodyOverrideMode,
BodyOverride: t.BodyOverride,
CreatedAt: t.CreatedAt.UTC().Format(time.RFC3339),
UpdatedAt: t.UpdatedAt.UTC().Format(time.RFC3339),
AssociatedMonitors: count,
}
}
// parseTemplateID 提取并校验 :id。
func parseTemplateID(c *gin.Context) (int64, bool) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil || id <= 0 {
response.ErrorFrom(c, infraerrors.BadRequest("INVALID_TEMPLATE_ID", "invalid template id"))
return 0, false
}
return id, true
}
// --- Handlers ---
// List GET /api/v1/admin/channel-monitor-templates?provider=anthropic
func (h *ChannelMonitorRequestTemplateHandler) List(c *gin.Context) {
items, err := h.templateService.List(c.Request.Context(), service.ChannelMonitorRequestTemplateListParams{
Provider: strings.TrimSpace(c.Query("provider")),
})
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]*channelMonitorTemplateResponse, 0, len(items))
for _, t := range items {
out = append(out, h.toResponse(c, t))
}
response.Success(c, gin.H{"items": out})
}
// Get GET /api/v1/admin/channel-monitor-templates/:id
func (h *ChannelMonitorRequestTemplateHandler) Get(c *gin.Context) {
id, ok := parseTemplateID(c)
if !ok {
return
}
t, err := h.templateService.Get(c.Request.Context(), id)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, h.toResponse(c, t))
}
// Create POST /api/v1/admin/channel-monitor-templates
func (h *ChannelMonitorRequestTemplateHandler) Create(c *gin.Context) {
var req channelMonitorTemplateCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error()))
return
}
t, err := h.templateService.Create(c.Request.Context(), service.ChannelMonitorRequestTemplateCreateParams{
Name: req.Name,
Provider: req.Provider,
Description: req.Description,
ExtraHeaders: req.ExtraHeaders,
BodyOverrideMode: req.BodyOverrideMode,
BodyOverride: req.BodyOverride,
})
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Created(c, h.toResponse(c, t))
}
// Update PUT /api/v1/admin/channel-monitor-templates/:id
func (h *ChannelMonitorRequestTemplateHandler) Update(c *gin.Context) {
id, ok := parseTemplateID(c)
if !ok {
return
}
var req channelMonitorTemplateUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error()))
return
}
t, err := h.templateService.Update(c.Request.Context(), id, service.ChannelMonitorRequestTemplateUpdateParams{
Name: req.Name,
Description: req.Description,
ExtraHeaders: req.ExtraHeaders,
BodyOverrideMode: req.BodyOverrideMode,
BodyOverride: req.BodyOverride,
})
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, h.toResponse(c, t))
}
// Delete DELETE /api/v1/admin/channel-monitor-templates/:id
func (h *ChannelMonitorRequestTemplateHandler) Delete(c *gin.Context) {
id, ok := parseTemplateID(c)
if !ok {
return
}
if err := h.templateService.Delete(c.Request.Context(), id); err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, nil)
}
type channelMonitorTemplateApplyRequest struct {
// MonitorIDs 必填、非空:用户在 picker 里勾选的要被覆盖的监控 ID 列表。
// 仅当对应监控当前 template_id == :id 时才会真的被覆盖。
MonitorIDs []int64 `json:"monitor_ids" binding:"required,min=1"`
}
// Apply POST /api/v1/admin/channel-monitor-templates/:id/apply
// 把模板当前配置覆盖到 monitor_ids 列表里的关联监控picker 选中的子集)。
func (h *ChannelMonitorRequestTemplateHandler) Apply(c *gin.Context) {
id, ok := parseTemplateID(c)
if !ok {
return
}
var req channelMonitorTemplateApplyRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error()))
return
}
affected, err := h.templateService.ApplyToMonitors(c.Request.Context(), id, req.MonitorIDs)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, gin.H{"affected": affected})
}
type associatedMonitorBriefResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Enabled bool `json:"enabled"`
}
// AssociatedMonitors GET /api/v1/admin/channel-monitor-templates/:id/monitors
// 列出关联监控picker 弹窗用)。
func (h *ChannelMonitorRequestTemplateHandler) AssociatedMonitors(c *gin.Context) {
id, ok := parseTemplateID(c)
if !ok {
return
}
items, err := h.templateService.ListAssociatedMonitors(c.Request.Context(), id)
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]associatedMonitorBriefResponse, 0, len(items))
for _, m := range items {
out = append(out, associatedMonitorBriefResponse{
ID: m.ID, Name: m.Name, Provider: m.Provider, Enabled: m.Enabled,
})
}
response.Success(c, gin.H{"items": out})
}

View File

@ -236,6 +236,11 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
PaymentCancelRateLimitWindow: paymentCfg.CancelRateLimitWindow,
PaymentCancelRateLimitUnit: paymentCfg.CancelRateLimitUnit,
PaymentCancelRateLimitMode: paymentCfg.CancelRateLimitMode,
ChannelMonitorEnabled: settings.ChannelMonitorEnabled,
ChannelMonitorDefaultIntervalSeconds: settings.ChannelMonitorDefaultIntervalSeconds,
AvailableChannelsEnabled: settings.AvailableChannelsEnabled,
}
response.Success(c, systemSettingsResponseData(payload, authSourceDefaults))
}
@ -427,6 +432,13 @@ type UpdateSettingsRequest struct {
PaymentCancelRateLimitWindow *int `json:"payment_cancel_rate_limit_window"`
PaymentCancelRateLimitUnit *string `json:"payment_cancel_rate_limit_unit"`
PaymentCancelRateLimitMode *string `json:"payment_cancel_rate_limit_window_mode"`
// Channel Monitor feature switch
ChannelMonitorEnabled *bool `json:"channel_monitor_enabled"`
ChannelMonitorDefaultIntervalSeconds *int `json:"channel_monitor_default_interval_seconds"`
// Available Channels feature switch (user-facing)
AvailableChannelsEnabled *bool `json:"available_channels_enabled"`
}
// UpdateSettings 更新系统设置
@ -1222,6 +1234,24 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
return previousSettings.AccountQuotaNotifyEmails
}(),
ChannelMonitorEnabled: func() bool {
if req.ChannelMonitorEnabled != nil {
return *req.ChannelMonitorEnabled
}
return previousSettings.ChannelMonitorEnabled
}(),
ChannelMonitorDefaultIntervalSeconds: func() int {
if req.ChannelMonitorDefaultIntervalSeconds != nil {
return *req.ChannelMonitorDefaultIntervalSeconds
}
return previousSettings.ChannelMonitorDefaultIntervalSeconds
}(),
AvailableChannelsEnabled: func() bool {
if req.AvailableChannelsEnabled != nil {
return *req.AvailableChannelsEnabled
}
return previousSettings.AvailableChannelsEnabled
}(),
}
authSourceDefaults := &service.AuthSourceDefaultSettings{
@ -1453,6 +1483,11 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
PaymentCancelRateLimitWindow: updatedPaymentCfg.CancelRateLimitWindow,
PaymentCancelRateLimitUnit: updatedPaymentCfg.CancelRateLimitUnit,
PaymentCancelRateLimitMode: updatedPaymentCfg.CancelRateLimitMode,
ChannelMonitorEnabled: updatedSettings.ChannelMonitorEnabled,
ChannelMonitorDefaultIntervalSeconds: updatedSettings.ChannelMonitorDefaultIntervalSeconds,
AvailableChannelsEnabled: updatedSettings.AvailableChannelsEnabled,
}
response.Success(c, systemSettingsResponseData(payload, updatedAuthSourceDefaults))
}
@ -1809,6 +1844,15 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
if !equalNotifyEmailEntries(before.AccountQuotaNotifyEmails, after.AccountQuotaNotifyEmails) {
changed = append(changed, "account_quota_notify_emails")
}
if before.ChannelMonitorEnabled != after.ChannelMonitorEnabled {
changed = append(changed, "channel_monitor_enabled")
}
if before.ChannelMonitorDefaultIntervalSeconds != after.ChannelMonitorDefaultIntervalSeconds {
changed = append(changed, "channel_monitor_default_interval_seconds")
}
if before.AvailableChannelsEnabled != after.AvailableChannelsEnabled {
changed = append(changed, "available_channels_enabled")
}
changed = appendAuthSourceDefaultChanges(changed, beforeAuthSourceDefaults, afterAuthSourceDefaults)
return changed
}

View File

@ -0,0 +1,283 @@
package handler
import (
"sort"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// AvailableChannelHandler 处理用户侧「可用渠道」查询。
//
// 用户侧接口委托 ChannelService.ListAvailable并在返回前做三层过滤
// 1. 行过滤:只保留状态为 Active 且与当前用户可访问分组有交集的渠道;
// 2. 分组过滤:渠道的 Groups 只保留用户可访问的那些;
// 3. 平台过滤:渠道的 SupportedModels 只保留平台在用户可见 Groups 中出现过的模型,
// 防止"渠道同时挂在 antigravity / anthropic 两个平台的分组上,用户只访问
// antigravity却看到 anthropic 模型"这类跨平台信息泄漏;
// 4. 字段白名单:仅返回用户需要的字段(省略 BillingModelSource / RestrictModels
// / 内部 ID / Status 等管理字段)。
type AvailableChannelHandler struct {
channelService *service.ChannelService
apiKeyService *service.APIKeyService
settingService *service.SettingService
}
// NewAvailableChannelHandler 创建用户侧可用渠道 handler。
func NewAvailableChannelHandler(
channelService *service.ChannelService,
apiKeyService *service.APIKeyService,
settingService *service.SettingService,
) *AvailableChannelHandler {
return &AvailableChannelHandler{
channelService: channelService,
apiKeyService: apiKeyService,
settingService: settingService,
}
}
// featureEnabled 返回 available-channels 开关是否启用。默认关闭opt-in
func (h *AvailableChannelHandler) featureEnabled(c *gin.Context) bool {
if h.settingService == nil {
return false
}
return h.settingService.GetAvailableChannelsRuntime(c.Request.Context()).Enabled
}
// userAvailableGroup 用户可见的分组概要(白名单字段)。
//
// 前端据此区分专属 vs 公开分组IsExclusive、订阅 vs 标准分组SubscriptionType
// 订阅视觉加深),并用 RateMultiplier 作为默认倍率;用户专属倍率前端走
// /groups/rates和 API 密钥页面保持一致。
type userAvailableGroup struct {
ID int64 `json:"id"`
Name string `json:"name"`
Platform string `json:"platform"`
SubscriptionType string `json:"subscription_type"`
RateMultiplier float64 `json:"rate_multiplier"`
IsExclusive bool `json:"is_exclusive"`
}
// userSupportedModelPricing 用户可见的定价字段白名单。
type userSupportedModelPricing struct {
BillingMode string `json:"billing_mode"`
InputPrice *float64 `json:"input_price"`
OutputPrice *float64 `json:"output_price"`
CacheWritePrice *float64 `json:"cache_write_price"`
CacheReadPrice *float64 `json:"cache_read_price"`
ImageOutputPrice *float64 `json:"image_output_price"`
PerRequestPrice *float64 `json:"per_request_price"`
Intervals []userPricingIntervalDTO `json:"intervals"`
}
// userPricingIntervalDTO 定价区间白名单(去掉内部 ID、SortOrder 等前端不渲染的字段)。
type userPricingIntervalDTO struct {
MinTokens int `json:"min_tokens"`
MaxTokens *int `json:"max_tokens"`
TierLabel string `json:"tier_label,omitempty"`
InputPrice *float64 `json:"input_price"`
OutputPrice *float64 `json:"output_price"`
CacheWritePrice *float64 `json:"cache_write_price"`
CacheReadPrice *float64 `json:"cache_read_price"`
PerRequestPrice *float64 `json:"per_request_price"`
}
// userSupportedModel 用户可见的支持模型条目。
type userSupportedModel struct {
Name string `json:"name"`
Platform string `json:"platform"`
Pricing *userSupportedModelPricing `json:"pricing"`
}
// userChannelPlatformSection 单渠道内某个平台的子视图:用户可见的分组 + 该平台
// 支持的模型。按 platform 聚合后让前端可以把渠道名作为 row-group 一次渲染,
// 后面的平台行按 sections 顺序铺开。
type userChannelPlatformSection struct {
Platform string `json:"platform"`
Groups []userAvailableGroup `json:"groups"`
SupportedModels []userSupportedModel `json:"supported_models"`
}
// userAvailableChannel 用户可见的渠道条目(白名单字段)。
//
// 每个渠道聚合为一条记录,内嵌 platforms 子数组:每个 section 对应一个平台,
// 包含该平台的 groups 和 supported_models。
type userAvailableChannel struct {
Name string `json:"name"`
Description string `json:"description"`
Platforms []userChannelPlatformSection `json:"platforms"`
}
// List 列出当前用户可见的「可用渠道」。
// GET /api/v1/channels/available
func (h *AvailableChannelHandler) List(c *gin.Context) {
subject, ok := middleware.GetAuthSubjectFromContext(c)
if !ok {
response.Unauthorized(c, "User not authenticated")
return
}
// Feature 未启用时返回空数组(不暴露渠道信息)。检查放在认证之后,
// 保持与未开关前的 401 行为一致:未登录先 401登录后再按开关决定。
if !h.featureEnabled(c) {
response.Success(c, []userAvailableChannel{})
return
}
userGroups, err := h.apiKeyService.GetAvailableGroups(c.Request.Context(), subject.UserID)
if err != nil {
response.ErrorFrom(c, err)
return
}
allowedGroupIDs := make(map[int64]struct{}, len(userGroups))
for i := range userGroups {
allowedGroupIDs[userGroups[i].ID] = struct{}{}
}
channels, err := h.channelService.ListAvailable(c.Request.Context())
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]userAvailableChannel, 0, len(channels))
for _, ch := range channels {
if ch.Status != service.StatusActive {
continue
}
visibleGroups := filterUserVisibleGroups(ch.Groups, allowedGroupIDs)
if len(visibleGroups) == 0 {
continue
}
sections := buildPlatformSections(ch, visibleGroups)
if len(sections) == 0 {
continue
}
out = append(out, userAvailableChannel{
Name: ch.Name,
Description: ch.Description,
Platforms: sections,
})
}
response.Success(c, out)
}
// buildPlatformSections 把一个渠道按 visibleGroups 的平台集合拆成有序的 section 列表:
// 每个 section 对应一个平台,只包含该平台的 groups 和 supported_models。
// 输出按 platform 字母序稳定排序,便于前端等效比较与回归测试。
func buildPlatformSections(
ch service.AvailableChannel,
visibleGroups []userAvailableGroup,
) []userChannelPlatformSection {
groupsByPlatform := make(map[string][]userAvailableGroup, 4)
for _, g := range visibleGroups {
if g.Platform == "" {
continue
}
groupsByPlatform[g.Platform] = append(groupsByPlatform[g.Platform], g)
}
if len(groupsByPlatform) == 0 {
return nil
}
platforms := make([]string, 0, len(groupsByPlatform))
for p := range groupsByPlatform {
platforms = append(platforms, p)
}
sort.Strings(platforms)
sections := make([]userChannelPlatformSection, 0, len(platforms))
for _, platform := range platforms {
platformSet := map[string]struct{}{platform: {}}
sections = append(sections, userChannelPlatformSection{
Platform: platform,
Groups: groupsByPlatform[platform],
SupportedModels: toUserSupportedModels(ch.SupportedModels, platformSet),
})
}
return sections
}
// filterUserVisibleGroups 仅保留用户可访问的分组。
func filterUserVisibleGroups(
groups []service.AvailableGroupRef,
allowed map[int64]struct{},
) []userAvailableGroup {
visible := make([]userAvailableGroup, 0, len(groups))
for _, g := range groups {
if _, ok := allowed[g.ID]; !ok {
continue
}
visible = append(visible, userAvailableGroup{
ID: g.ID,
Name: g.Name,
Platform: g.Platform,
SubscriptionType: g.SubscriptionType,
RateMultiplier: g.RateMultiplier,
IsExclusive: g.IsExclusive,
})
}
return visible
}
// toUserSupportedModels 将 service 层支持模型转换为用户 DTO字段白名单
// 仅保留平台在 allowedPlatforms 中的条目,防止跨平台模型信息泄漏。
// allowedPlatforms 为 nil 时不做平台过滤(保留全部,供测试或明确无过滤场景使用)。
func toUserSupportedModels(
src []service.SupportedModel,
allowedPlatforms map[string]struct{},
) []userSupportedModel {
out := make([]userSupportedModel, 0, len(src))
for i := range src {
m := src[i]
if allowedPlatforms != nil {
if _, ok := allowedPlatforms[m.Platform]; !ok {
continue
}
}
out = append(out, userSupportedModel{
Name: m.Name,
Platform: m.Platform,
Pricing: toUserPricing(m.Pricing),
})
}
return out
}
// toUserPricing 将 service 层定价转换为用户 DTO入参为 nil 时返回 nil。
func toUserPricing(p *service.ChannelModelPricing) *userSupportedModelPricing {
if p == nil {
return nil
}
intervals := make([]userPricingIntervalDTO, 0, len(p.Intervals))
for _, iv := range p.Intervals {
intervals = append(intervals, userPricingIntervalDTO{
MinTokens: iv.MinTokens,
MaxTokens: iv.MaxTokens,
TierLabel: iv.TierLabel,
InputPrice: iv.InputPrice,
OutputPrice: iv.OutputPrice,
CacheWritePrice: iv.CacheWritePrice,
CacheReadPrice: iv.CacheReadPrice,
PerRequestPrice: iv.PerRequestPrice,
})
}
billingMode := string(p.BillingMode)
if billingMode == "" {
billingMode = string(service.BillingModeToken)
}
return &userSupportedModelPricing{
BillingMode: billingMode,
InputPrice: p.InputPrice,
OutputPrice: p.OutputPrice,
CacheWritePrice: p.CacheWritePrice,
CacheReadPrice: p.CacheReadPrice,
ImageOutputPrice: p.ImageOutputPrice,
PerRequestPrice: p.PerRequestPrice,
Intervals: intervals,
}
}

View File

@ -0,0 +1,157 @@
//go:build unit
package handler
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
func TestUserAvailableChannel_Unauthenticated401(t *testing.T) {
// 没有 AuthSubject 注入时handler 应返回 401 且不触达 service 依赖。
gin.SetMode(gin.TestMode)
h := &AvailableChannelHandler{} // nil services — 401 路径不会调用它们
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/api/v1/channels/available", nil)
h.List(c)
require.Equal(t, http.StatusUnauthorized, w.Code)
}
func TestFilterUserVisibleGroups_IntersectionOnly(t *testing.T) {
// 渠道挂在 {g1, g2, g3},用户只允许 {g1, g3} —— 响应必须仅含 g1/g3。
groups := []service.AvailableGroupRef{
{ID: 1, Name: "g1", Platform: "anthropic"},
{ID: 2, Name: "g2", Platform: "anthropic"},
{ID: 3, Name: "g3", Platform: "openai"},
}
allowed := map[int64]struct{}{1: {}, 3: {}}
visible := filterUserVisibleGroups(groups, allowed)
require.Len(t, visible, 2)
ids := []int64{visible[0].ID, visible[1].ID}
require.ElementsMatch(t, []int64{1, 3}, ids)
}
func TestToUserSupportedModels_FiltersByAllowedPlatforms(t *testing.T) {
// 用户可访问分组只覆盖 anthropicanthropic 平台的模型保留openai 模型被剔除。
src := []service.SupportedModel{
{Name: "claude-sonnet-4-6", Platform: "anthropic", Pricing: nil},
{Name: "gpt-4o", Platform: "openai", Pricing: nil},
}
allowed := map[string]struct{}{"anthropic": {}}
out := toUserSupportedModels(src, allowed)
require.Len(t, out, 1)
require.Equal(t, "claude-sonnet-4-6", out[0].Name)
}
func TestToUserSupportedModels_NilAllowedPlatformsKeepsAll(t *testing.T) {
// 显式传 nil allowedPlatforms 表示不做过滤。
src := []service.SupportedModel{
{Name: "a", Platform: "anthropic"},
{Name: "b", Platform: "openai"},
}
require.Len(t, toUserSupportedModels(src, nil), 2)
}
func TestUserAvailableChannel_FieldWhitelist(t *testing.T) {
// 通过序列化 userAvailableChannel 结构体验证响应形状:
// 只有 name / description / platforms不含管理端字段。
row := userAvailableChannel{
Name: "ch",
Description: "d",
Platforms: []userChannelPlatformSection{
{
Platform: "anthropic",
Groups: []userAvailableGroup{{ID: 1, Name: "g1", Platform: "anthropic"}},
SupportedModels: []userSupportedModel{},
},
},
}
raw, err := json.Marshal(row)
require.NoError(t, err)
var decoded map[string]any
require.NoError(t, json.Unmarshal(raw, &decoded))
for _, key := range []string{"id", "status", "billing_model_source", "restrict_models"} {
_, exists := decoded[key]
require.Falsef(t, exists, "user DTO must not expose %q", key)
}
for _, key := range []string{"name", "description", "platforms"} {
_, exists := decoded[key]
require.Truef(t, exists, "user DTO must expose %q", key)
}
// 验证 section 的字段platform / groups / supported_models
rawSection, err := json.Marshal(row.Platforms[0])
require.NoError(t, err)
var sectionDecoded map[string]any
require.NoError(t, json.Unmarshal(rawSection, &sectionDecoded))
for _, key := range []string{"platform", "groups", "supported_models"} {
_, exists := sectionDecoded[key]
require.Truef(t, exists, "platform section must expose %q", key)
}
// Group DTO 暴露区分专属/公开、订阅类型、默认倍率所需的字段,
// 前端据此渲染 GroupBadge 并与 API 密钥页保持一致的视觉。
rawGroup, err := json.Marshal(row.Platforms[0].Groups[0])
require.NoError(t, err)
var groupDecoded map[string]any
require.NoError(t, json.Unmarshal(rawGroup, &groupDecoded))
for _, key := range []string{"id", "name", "platform", "subscription_type", "rate_multiplier", "is_exclusive"} {
_, exists := groupDecoded[key]
require.Truef(t, exists, "group DTO must expose %q", key)
}
// pricing interval 白名单:不应暴露 id / sort_order。
pricing := toUserPricing(&service.ChannelModelPricing{
BillingMode: service.BillingModeToken,
Intervals: []service.PricingInterval{
{ID: 7, MinTokens: 0, MaxTokens: nil, SortOrder: 3},
},
})
require.NotNil(t, pricing)
require.Len(t, pricing.Intervals, 1)
rawIv, err := json.Marshal(pricing.Intervals[0])
require.NoError(t, err)
var ivDecoded map[string]any
require.NoError(t, json.Unmarshal(rawIv, &ivDecoded))
for _, key := range []string{"id", "pricing_id", "sort_order"} {
_, exists := ivDecoded[key]
require.Falsef(t, exists, "user pricing interval must not expose %q", key)
}
}
func TestBuildPlatformSections_GroupsByPlatform(t *testing.T) {
// 一个渠道横跨 anthropic / openai / 空平台:应该生成 2 个 section
// 按 platform 字母序排序,各自 groups 和 supported_models 只含同平台条目。
ch := service.AvailableChannel{
Name: "ch",
SupportedModels: []service.SupportedModel{
{Name: "claude-sonnet-4-6", Platform: "anthropic"},
{Name: "gpt-4o", Platform: "openai"},
},
}
visible := []userAvailableGroup{
{ID: 1, Name: "g-openai", Platform: "openai"},
{ID: 2, Name: "g-ant", Platform: "anthropic"},
{ID: 3, Name: "g-empty", Platform: ""},
}
sections := buildPlatformSections(ch, visible)
require.Len(t, sections, 2)
require.Equal(t, "anthropic", sections[0].Platform)
require.Equal(t, "openai", sections[1].Platform)
require.Len(t, sections[0].Groups, 1)
require.Equal(t, int64(2), sections[0].Groups[0].ID)
require.Len(t, sections[0].SupportedModels, 1)
require.Equal(t, "claude-sonnet-4-6", sections[0].SupportedModels[0].Name)
}

View File

@ -0,0 +1,176 @@
package handler
import (
"time"
"github.com/Wei-Shaw/sub2api/internal/handler/admin"
"github.com/Wei-Shaw/sub2api/internal/handler/dto"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// ChannelMonitorUserHandler 渠道监控用户只读 handler。
type ChannelMonitorUserHandler struct {
monitorService *service.ChannelMonitorService
settingService *service.SettingService
}
// NewChannelMonitorUserHandler 创建 handler。
// settingService 用于每次请求前读取功能开关;关闭时 List/GetStatus 直接返回空/404。
func NewChannelMonitorUserHandler(
monitorService *service.ChannelMonitorService,
settingService *service.SettingService,
) *ChannelMonitorUserHandler {
return &ChannelMonitorUserHandler{
monitorService: monitorService,
settingService: settingService,
}
}
// featureEnabled 返回当前渠道监控功能是否开启。
// settingService 为 nil测试场景视为启用。
func (h *ChannelMonitorUserHandler) featureEnabled(c *gin.Context) bool {
if h.settingService == nil {
return true
}
return h.settingService.GetChannelMonitorRuntime(c.Request.Context()).Enabled
}
// --- Response ---
type channelMonitorUserListItem struct {
ID int64 `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
GroupName string `json:"group_name"`
PrimaryModel string `json:"primary_model"`
PrimaryStatus string `json:"primary_status"`
PrimaryLatencyMs *int `json:"primary_latency_ms"`
PrimaryPingLatencyMs *int `json:"primary_ping_latency_ms"`
Availability7d float64 `json:"availability_7d"`
ExtraModels []dto.ChannelMonitorExtraModelStatus `json:"extra_models"`
Timeline []channelMonitorUserTimelinePoint `json:"timeline"`
}
// channelMonitorUserTimelinePoint 主模型最近一次检测的 timeline 点。
// 仅用于用户视图 list 响应admin 视图不使用。
type channelMonitorUserTimelinePoint struct {
Status string `json:"status"`
LatencyMs *int `json:"latency_ms"`
PingLatencyMs *int `json:"ping_latency_ms"`
CheckedAt string `json:"checked_at"`
}
type channelMonitorUserDetailResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
GroupName string `json:"group_name"`
Models []channelMonitorUserModelStat `json:"models"`
}
type channelMonitorUserModelStat struct {
Model string `json:"model"`
LatestStatus string `json:"latest_status"`
LatestLatencyMs *int `json:"latest_latency_ms"`
Availability7d float64 `json:"availability_7d"`
Availability15d float64 `json:"availability_15d"`
Availability30d float64 `json:"availability_30d"`
AvgLatency7dMs *int `json:"avg_latency_7d_ms"`
}
func userMonitorViewToItem(v *service.UserMonitorView) channelMonitorUserListItem {
extras := make([]dto.ChannelMonitorExtraModelStatus, 0, len(v.ExtraModels))
for _, e := range v.ExtraModels {
extras = append(extras, dto.ChannelMonitorExtraModelStatus{
Model: e.Model,
Status: e.Status,
LatencyMs: e.LatencyMs,
})
}
timeline := make([]channelMonitorUserTimelinePoint, 0, len(v.Timeline))
for _, p := range v.Timeline {
timeline = append(timeline, channelMonitorUserTimelinePoint{
Status: p.Status,
LatencyMs: p.LatencyMs,
PingLatencyMs: p.PingLatencyMs,
CheckedAt: p.CheckedAt.UTC().Format(time.RFC3339),
})
}
return channelMonitorUserListItem{
ID: v.ID,
Name: v.Name,
Provider: v.Provider,
GroupName: v.GroupName,
PrimaryModel: v.PrimaryModel,
PrimaryStatus: v.PrimaryStatus,
PrimaryLatencyMs: v.PrimaryLatencyMs,
PrimaryPingLatencyMs: v.PrimaryPingLatencyMs,
Availability7d: v.Availability7d,
ExtraModels: extras,
Timeline: timeline,
}
}
func userMonitorDetailToResponse(d *service.UserMonitorDetail) *channelMonitorUserDetailResponse {
models := make([]channelMonitorUserModelStat, 0, len(d.Models))
for _, m := range d.Models {
models = append(models, channelMonitorUserModelStat{
Model: m.Model,
LatestStatus: m.LatestStatus,
LatestLatencyMs: m.LatestLatencyMs,
Availability7d: m.Availability7d,
Availability15d: m.Availability15d,
Availability30d: m.Availability30d,
AvgLatency7dMs: m.AvgLatency7dMs,
})
}
return &channelMonitorUserDetailResponse{
ID: d.ID,
Name: d.Name,
Provider: d.Provider,
GroupName: d.GroupName,
Models: models,
}
}
// --- Handlers ---
// List GET /api/v1/channel-monitors
func (h *ChannelMonitorUserHandler) List(c *gin.Context) {
if !h.featureEnabled(c) {
response.Success(c, gin.H{"items": []channelMonitorUserListItem{}})
return
}
views, err := h.monitorService.ListUserView(c.Request.Context())
if err != nil {
response.ErrorFrom(c, err)
return
}
items := make([]channelMonitorUserListItem, 0, len(views))
for _, v := range views {
items = append(items, userMonitorViewToItem(v))
}
response.Success(c, gin.H{"items": items})
}
// GetStatus GET /api/v1/channel-monitors/:id/status
func (h *ChannelMonitorUserHandler) GetStatus(c *gin.Context) {
if !h.featureEnabled(c) {
response.ErrorFrom(c, service.ErrChannelMonitorNotFound)
return
}
// 复用 admin.ParseChannelMonitorID 保持错误码与日志一致。
id, ok := admin.ParseChannelMonitorID(c)
if !ok {
return
}
detail, err := h.monitorService.GetUserDetail(c.Request.Context(), id)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, userMonitorDetailToResponse(detail))
}

View File

@ -0,0 +1,10 @@
package dto
// ChannelMonitorExtraModelStatus 渠道监控附加模型最近一次状态。
// 同时被 admin handlerList 响应)与 user handlerList 响应)复用,
// 字段必须保持一致以保证前端拿到统一结构。
type ChannelMonitorExtraModelStatus struct {
Model string `json:"model"`
Status string `json:"status"`
LatencyMs *int `json:"latency_ms"`
}

View File

@ -0,0 +1,70 @@
package dto
import (
"reflect"
"strings"
"testing"
"github.com/Wei-Shaw/sub2api/internal/service"
)
// TestPublicSettingsInjectionPayload_SchemaDoesNotDrift guarantees the SSR
// injection struct exposes every JSON field consumed by the frontend.
//
// Why this test exists: before we extracted a named PublicSettingsInjectionPayload
// type, the inline struct was manually kept in sync with dto.PublicSettings and
// drifted — ChannelMonitorEnabled / AvailableChannelsEnabled were missing, which
// made the frontend read `undefined` on refresh and hide the "可用渠道" menu
// until the async /api/v1/settings/public round-trip finished.
//
// This test compares the two JSON-tag sets and fails if injection is missing
// any field that dto.PublicSettings exposes. Adding a new feature flag with
// only a DTO entry will fail this test until the injection struct is updated.
//
// Intentional exclusions (fields present on dto.PublicSettings that SSR does
// not need to inject) are listed in `dtoOnlyFields` below with a reason.
func TestPublicSettingsInjectionPayload_SchemaDoesNotDrift(t *testing.T) {
injection := jsonTags(reflect.TypeOf(service.PublicSettingsInjectionPayload{}))
dtoKeys := jsonTags(reflect.TypeOf(PublicSettings{}))
// Fields that legitimately live only on the DTO. Keep tiny; document each.
dtoOnlyFields := map[string]string{
// sora_client_enabled is an upstream-only field the fork does not surface.
"sora_client_enabled": "upstream-only field, not used on this fork",
// force_email_on_third_party_signup lives on the DTO but is not injected via SSR.
"force_email_on_third_party_signup": "auth-source default, not a feature flag",
}
var missing []string
for key := range dtoKeys {
if _, ok := injection[key]; ok {
continue
}
if _, allowed := dtoOnlyFields[key]; allowed {
continue
}
missing = append(missing, key)
}
if len(missing) > 0 {
t.Fatalf("service.PublicSettingsInjectionPayload is missing JSON fields present on dto.PublicSettings: %s\n"+
"add the field to PublicSettingsInjectionPayload (and GetPublicSettingsForInjection), or "+
"document the exclusion in dtoOnlyFields with a reason.", strings.Join(missing, ", "))
}
}
func jsonTags(t reflect.Type) map[string]struct{} {
out := make(map[string]struct{})
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
tag := f.Tag.Get("json")
if tag == "" || tag == "-" {
continue
}
name := strings.SplitN(tag, ",", 2)[0]
if name == "" {
continue
}
out[name] = struct{}{}
}
return out
}

View File

@ -184,6 +184,13 @@ type SystemSettings struct {
BalanceLowNotifyRechargeURL string `json:"balance_low_notify_recharge_url"`
AccountQuotaNotifyEnabled bool `json:"account_quota_notify_enabled"`
AccountQuotaNotifyEmails []NotifyEmailEntry `json:"account_quota_notify_emails"`
// Channel Monitor feature switch
ChannelMonitorEnabled bool `json:"channel_monitor_enabled"`
ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"`
// Available Channels feature switch (user-facing aggregate view)
AvailableChannelsEnabled bool `json:"available_channels_enabled"`
}
type DefaultSubscriptionSetting struct {
@ -231,6 +238,11 @@ type PublicSettings struct {
AccountQuotaNotifyEnabled bool `json:"account_quota_notify_enabled"`
BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
BalanceLowNotifyRechargeURL string `json:"balance_low_notify_recharge_url"`
ChannelMonitorEnabled bool `json:"channel_monitor_enabled"`
ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"`
AvailableChannelsEnabled bool `json:"available_channels_enabled"`
}
// OverloadCooldownSettings 529过载冷却配置 DTO

View File

@ -304,6 +304,12 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
selection, err := h.gatewayService.SelectAccountWithLoadAwareness(c.Request.Context(), apiKey.GroupID, sessionKey, reqModel, fs.FailedAccountIDs, "", int64(0)) // Gemini 不使用会话限制
if err != nil {
if len(fs.FailedAccountIDs) == 0 {
reqLog.Warn("gateway.select_account_no_available",
zap.String("model", reqModel),
zap.Int64p("group_id", apiKey.GroupID),
zap.String("platform", platform),
zap.Error(err),
)
h.handleStreamingAwareError(c, http.StatusServiceUnavailable, "api_error", "No available accounts: "+err.Error(), streamStarted)
return
}
@ -347,6 +353,11 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
accountReleaseFunc := selection.ReleaseFunc
if !selection.Acquired {
if selection.WaitPlan == nil {
reqLog.Warn("gateway.select_account_no_slot_no_wait_plan",
zap.Int64("account_id", account.ID),
zap.String("model", reqModel),
zap.String("platform", platform),
)
h.handleStreamingAwareError(c, http.StatusServiceUnavailable, "api_error", "No available accounts", streamStarted)
return
}
@ -528,6 +539,13 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
selection, err := h.gatewayService.SelectAccountWithLoadAwareness(c.Request.Context(), currentAPIKey.GroupID, sessionKey, reqModel, fs.FailedAccountIDs, parsedReq.MetadataUserID, subject.UserID)
if err != nil {
if len(fs.FailedAccountIDs) == 0 {
reqLog.Warn("gateway.select_account_no_available",
zap.String("model", reqModel),
zap.Int64p("group_id", currentAPIKey.GroupID),
zap.String("platform", platform),
zap.Bool("fallback_used", fallbackUsed),
zap.Error(err),
)
h.handleStreamingAwareError(c, http.StatusServiceUnavailable, "api_error", "No available accounts: "+err.Error(), streamStarted)
return
}
@ -571,6 +589,11 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
accountReleaseFunc := selection.ReleaseFunc
if !selection.Acquired {
if selection.WaitPlan == nil {
reqLog.Warn("gateway.select_account_no_slot_no_wait_plan",
zap.Int64("account_id", account.ID),
zap.String("model", reqModel),
zap.String("platform", platform),
)
h.handleStreamingAwareError(c, http.StatusServiceUnavailable, "api_error", "No available accounts", streamStarted)
return
}

View File

@ -6,50 +6,54 @@ import (
// AdminHandlers contains all admin-related HTTP handlers
type AdminHandlers struct {
Dashboard *admin.DashboardHandler
User *admin.UserHandler
Group *admin.GroupHandler
Account *admin.AccountHandler
Announcement *admin.AnnouncementHandler
DataManagement *admin.DataManagementHandler
Backup *admin.BackupHandler
OAuth *admin.OAuthHandler
OpenAIOAuth *admin.OpenAIOAuthHandler
GeminiOAuth *admin.GeminiOAuthHandler
AntigravityOAuth *admin.AntigravityOAuthHandler
Proxy *admin.ProxyHandler
Redeem *admin.RedeemHandler
Promo *admin.PromoHandler
Setting *admin.SettingHandler
Ops *admin.OpsHandler
System *admin.SystemHandler
Subscription *admin.SubscriptionHandler
Usage *admin.UsageHandler
UserAttribute *admin.UserAttributeHandler
ErrorPassthrough *admin.ErrorPassthroughHandler
TLSFingerprintProfile *admin.TLSFingerprintProfileHandler
APIKey *admin.AdminAPIKeyHandler
ScheduledTest *admin.ScheduledTestHandler
Channel *admin.ChannelHandler
Payment *admin.PaymentHandler
Dashboard *admin.DashboardHandler
User *admin.UserHandler
Group *admin.GroupHandler
Account *admin.AccountHandler
Announcement *admin.AnnouncementHandler
DataManagement *admin.DataManagementHandler
Backup *admin.BackupHandler
OAuth *admin.OAuthHandler
OpenAIOAuth *admin.OpenAIOAuthHandler
GeminiOAuth *admin.GeminiOAuthHandler
AntigravityOAuth *admin.AntigravityOAuthHandler
Proxy *admin.ProxyHandler
Redeem *admin.RedeemHandler
Promo *admin.PromoHandler
Setting *admin.SettingHandler
Ops *admin.OpsHandler
System *admin.SystemHandler
Subscription *admin.SubscriptionHandler
Usage *admin.UsageHandler
UserAttribute *admin.UserAttributeHandler
ErrorPassthrough *admin.ErrorPassthroughHandler
TLSFingerprintProfile *admin.TLSFingerprintProfileHandler
APIKey *admin.AdminAPIKeyHandler
ScheduledTest *admin.ScheduledTestHandler
Channel *admin.ChannelHandler
ChannelMonitor *admin.ChannelMonitorHandler
ChannelMonitorTemplate *admin.ChannelMonitorRequestTemplateHandler
Payment *admin.PaymentHandler
}
// Handlers contains all HTTP handlers
type Handlers struct {
Auth *AuthHandler
User *UserHandler
APIKey *APIKeyHandler
Usage *UsageHandler
Redeem *RedeemHandler
Subscription *SubscriptionHandler
Announcement *AnnouncementHandler
Admin *AdminHandlers
Gateway *GatewayHandler
OpenAIGateway *OpenAIGatewayHandler
Setting *SettingHandler
Totp *TotpHandler
Payment *PaymentHandler
PaymentWebhook *PaymentWebhookHandler
Auth *AuthHandler
User *UserHandler
APIKey *APIKeyHandler
Usage *UsageHandler
Redeem *RedeemHandler
Subscription *SubscriptionHandler
Announcement *AnnouncementHandler
ChannelMonitor *ChannelMonitorUserHandler
Admin *AdminHandlers
Gateway *GatewayHandler
OpenAIGateway *OpenAIGatewayHandler
Setting *SettingHandler
Totp *TotpHandler
Payment *PaymentHandler
PaymentWebhook *PaymentWebhookHandler
AvailableChannel *AvailableChannelHandler
}
// BuildInfo contains build-time information

View File

@ -2,6 +2,7 @@ package handler
import (
"context"
"errors"
"fmt"
"io"
"log/slog"
@ -114,6 +115,20 @@ func (h *PaymentWebhookHandler) handleNotify(c *gin.Context, providerKey string)
}
if err := h.paymentService.HandlePaymentNotification(c.Request.Context(), notification, resolvedProviderKey); err != nil {
// Unknown order: ack with 2xx so the provider stops retrying. This
// guards against foreign environments whose webhook endpoints are
// (mis)configured to point at us — without a 2xx, the provider will
// retry for days and spam our error logs. We still emit a WARN so the
// event is discoverable in logs.
if errors.Is(err, service.ErrOrderNotFound) {
slog.Warn("[Payment Webhook] unknown order, acking to stop retries",
"provider", resolvedProviderKey,
"outTradeNo", notification.OrderID,
"tradeNo", notification.TradeNo,
)
writeSuccessResponse(c, resolvedProviderKey)
return
}
slog.Error("[Payment Webhook] handle notification failed", "provider", resolvedProviderKey, "error", err)
c.String(http.StatusInternalServerError, "handle failed")
return

View File

@ -6,11 +6,13 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/Wei-Shaw/sub2api/internal/payment"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -91,6 +93,43 @@ func TestWriteSuccessResponse(t *testing.T) {
}
}
// TestUnknownOrderWebhookAcksWithSuccess exercises the response contract that
// handleNotify relies on when HandlePaymentNotification returns ErrOrderNotFound:
// we still need to emit the provider-specific 2xx so the provider stops
// retrying. We can't easily drive handleNotify end-to-end without mocking the
// concrete *service.PaymentService, so this test locks down the two ingredients
// the fix depends on:
// 1. errors.Is recognises the sentinel through fmt.Errorf %w wrapping (which
// is how service layer wraps it with the out_trade_no context).
// 2. writeSuccessResponse produces the provider-specific body for Stripe
// (empty 200) — matching what handleNotify calls on the ack path.
//
// If either contract breaks, the Stripe "unknown order → 500 loop" regresses.
func TestUnknownOrderWebhookAcksWithSuccess(t *testing.T) {
gin.SetMode(gin.TestMode)
// 1) Sentinel recognition through wrapping.
wrapped := fmt.Errorf("%w: out_trade_no=sub2_missing_42", service.ErrOrderNotFound)
require.True(t, errors.Is(wrapped, service.ErrOrderNotFound),
"handleNotify uses errors.Is on the wrapped service error; regression here "+
"would mean unknown-order webhooks go back to returning 500 and looping forever")
// A distinct error must NOT match — otherwise a DB failure would be silently
// swallowed as an ack.
other := errors.New("lookup order failed: connection refused")
require.False(t, errors.Is(other, service.ErrOrderNotFound))
// 2) Provider-specific success body is what handleNotify emits on the
// ack path. Asserted again here because this is the shape Stripe expects
// to consider the webhook acknowledged.
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
writeSuccessResponse(c, payment.TypeStripe)
require.Equal(t, http.StatusOK, w.Code,
"Stripe requires 2xx to stop retrying; anything else restarts the retry loop")
require.Empty(t, w.Body.String(), "Stripe expects an empty body on the ack path")
}
func TestWebhookConstants(t *testing.T) {
t.Run("maxWebhookBodySize is 1MB", func(t *testing.T) {
assert.Equal(t, int64(1<<20), int64(maxWebhookBodySize))

View File

@ -70,5 +70,10 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) {
AccountQuotaNotifyEnabled: settings.AccountQuotaNotifyEnabled,
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
BalanceLowNotifyRechargeURL: settings.BalanceLowNotifyRechargeURL,
ChannelMonitorEnabled: settings.ChannelMonitorEnabled,
ChannelMonitorDefaultIntervalSeconds: settings.ChannelMonitorDefaultIntervalSeconds,
AvailableChannelsEnabled: settings.AvailableChannelsEnabled,
})
}

View File

@ -34,35 +34,39 @@ func ProvideAdminHandlers(
apiKeyHandler *admin.AdminAPIKeyHandler,
scheduledTestHandler *admin.ScheduledTestHandler,
channelHandler *admin.ChannelHandler,
channelMonitorHandler *admin.ChannelMonitorHandler,
channelMonitorTemplateHandler *admin.ChannelMonitorRequestTemplateHandler,
paymentHandler *admin.PaymentHandler,
) *AdminHandlers {
return &AdminHandlers{
Dashboard: dashboardHandler,
User: userHandler,
Group: groupHandler,
Account: accountHandler,
Announcement: announcementHandler,
DataManagement: dataManagementHandler,
Backup: backupHandler,
OAuth: oauthHandler,
OpenAIOAuth: openaiOAuthHandler,
GeminiOAuth: geminiOAuthHandler,
AntigravityOAuth: antigravityOAuthHandler,
Proxy: proxyHandler,
Redeem: redeemHandler,
Promo: promoHandler,
Setting: settingHandler,
Ops: opsHandler,
System: systemHandler,
Subscription: subscriptionHandler,
Usage: usageHandler,
UserAttribute: userAttributeHandler,
ErrorPassthrough: errorPassthroughHandler,
TLSFingerprintProfile: tlsFingerprintProfileHandler,
APIKey: apiKeyHandler,
ScheduledTest: scheduledTestHandler,
Channel: channelHandler,
Payment: paymentHandler,
Dashboard: dashboardHandler,
User: userHandler,
Group: groupHandler,
Account: accountHandler,
Announcement: announcementHandler,
DataManagement: dataManagementHandler,
Backup: backupHandler,
OAuth: oauthHandler,
OpenAIOAuth: openaiOAuthHandler,
GeminiOAuth: geminiOAuthHandler,
AntigravityOAuth: antigravityOAuthHandler,
Proxy: proxyHandler,
Redeem: redeemHandler,
Promo: promoHandler,
Setting: settingHandler,
Ops: opsHandler,
System: systemHandler,
Subscription: subscriptionHandler,
Usage: usageHandler,
UserAttribute: userAttributeHandler,
ErrorPassthrough: errorPassthroughHandler,
TLSFingerprintProfile: tlsFingerprintProfileHandler,
APIKey: apiKeyHandler,
ScheduledTest: scheduledTestHandler,
Channel: channelHandler,
ChannelMonitor: channelMonitorHandler,
ChannelMonitorTemplate: channelMonitorTemplateHandler,
Payment: paymentHandler,
}
}
@ -85,6 +89,7 @@ func ProvideHandlers(
redeemHandler *RedeemHandler,
subscriptionHandler *SubscriptionHandler,
announcementHandler *AnnouncementHandler,
channelMonitorUserHandler *ChannelMonitorUserHandler,
adminHandlers *AdminHandlers,
gatewayHandler *GatewayHandler,
openaiGatewayHandler *OpenAIGatewayHandler,
@ -92,24 +97,27 @@ func ProvideHandlers(
totpHandler *TotpHandler,
paymentHandler *PaymentHandler,
paymentWebhookHandler *PaymentWebhookHandler,
availableChannelHandler *AvailableChannelHandler,
_ *service.IdempotencyCoordinator,
_ *service.IdempotencyCleanupService,
) *Handlers {
return &Handlers{
Auth: authHandler,
User: userHandler,
APIKey: apiKeyHandler,
Usage: usageHandler,
Redeem: redeemHandler,
Subscription: subscriptionHandler,
Announcement: announcementHandler,
Admin: adminHandlers,
Gateway: gatewayHandler,
OpenAIGateway: openaiGatewayHandler,
Setting: settingHandler,
Totp: totpHandler,
Payment: paymentHandler,
PaymentWebhook: paymentWebhookHandler,
Auth: authHandler,
User: userHandler,
APIKey: apiKeyHandler,
Usage: usageHandler,
Redeem: redeemHandler,
Subscription: subscriptionHandler,
Announcement: announcementHandler,
ChannelMonitor: channelMonitorUserHandler,
Admin: adminHandlers,
Gateway: gatewayHandler,
OpenAIGateway: openaiGatewayHandler,
Setting: settingHandler,
Totp: totpHandler,
Payment: paymentHandler,
PaymentWebhook: paymentWebhookHandler,
AvailableChannel: availableChannelHandler,
}
}
@ -123,12 +131,14 @@ var ProviderSet = wire.NewSet(
NewRedeemHandler,
NewSubscriptionHandler,
NewAnnouncementHandler,
NewChannelMonitorUserHandler,
NewGatewayHandler,
NewOpenAIGatewayHandler,
NewTotpHandler,
ProvideSettingHandler,
NewPaymentHandler,
NewPaymentWebhookHandler,
NewAvailableChannelHandler,
// Admin handlers
admin.NewDashboardHandler,
@ -156,6 +166,8 @@ var ProviderSet = wire.NewSet(
admin.NewAdminAPIKeyHandler,
admin.NewScheduledTestHandler,
admin.NewChannelHandler,
admin.NewChannelMonitorHandler,
admin.NewChannelMonitorRequestTemplateHandler,
admin.NewPaymentHandler,
// AdminHandlers and Handlers constructors

View File

@ -0,0 +1,755 @@
package repository
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
dbent "github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/lib/pq"
)
// channelMonitorRepository 实现 service.ChannelMonitorRepository。
//
// 选型说明:
// - CRUD 走 ent复用项目的事务上下文支持
// - 聚合查询latest per model / availability走原生 SQL避免 ent 在 GROUP BY 上
// 的样板代码,并保证索引能被命中
type channelMonitorRepository struct {
client *dbent.Client
db *sql.DB
}
// NewChannelMonitorRepository 创建仓储实例。
func NewChannelMonitorRepository(client *dbent.Client, db *sql.DB) service.ChannelMonitorRepository {
return &channelMonitorRepository{client: client, db: db}
}
// ---------- CRUD ----------
func (r *channelMonitorRepository) Create(ctx context.Context, m *service.ChannelMonitor) error {
client := clientFromContext(ctx, r.client)
builder := client.ChannelMonitor.Create().
SetName(m.Name).
SetProvider(channelmonitor.Provider(m.Provider)).
SetEndpoint(m.Endpoint).
SetAPIKeyEncrypted(m.APIKey). // 调用方传入的已是密文
SetPrimaryModel(m.PrimaryModel).
SetExtraModels(emptySliceIfNil(m.ExtraModels)).
SetGroupName(m.GroupName).
SetEnabled(m.Enabled).
SetIntervalSeconds(m.IntervalSeconds).
SetCreatedBy(m.CreatedBy).
SetExtraHeaders(emptyHeadersIfNilRepo(m.ExtraHeaders)).
SetBodyOverrideMode(defaultBodyModeRepo(m.BodyOverrideMode))
if m.TemplateID != nil {
builder = builder.SetTemplateID(*m.TemplateID)
}
if m.BodyOverride != nil {
builder = builder.SetBodyOverride(m.BodyOverride)
}
created, err := builder.Save(ctx)
if err != nil {
return translatePersistenceError(err, service.ErrChannelMonitorNotFound, nil)
}
m.ID = created.ID
m.CreatedAt = created.CreatedAt
m.UpdatedAt = created.UpdatedAt
return nil
}
func (r *channelMonitorRepository) GetByID(ctx context.Context, id int64) (*service.ChannelMonitor, error) {
row, err := r.client.ChannelMonitor.Query().
Where(channelmonitor.IDEQ(id)).
Only(ctx)
if err != nil {
return nil, translatePersistenceError(err, service.ErrChannelMonitorNotFound, nil)
}
return entToServiceMonitor(row), nil
}
func (r *channelMonitorRepository) Update(ctx context.Context, m *service.ChannelMonitor) error {
client := clientFromContext(ctx, r.client)
updater := client.ChannelMonitor.UpdateOneID(m.ID).
SetName(m.Name).
SetProvider(channelmonitor.Provider(m.Provider)).
SetEndpoint(m.Endpoint).
SetAPIKeyEncrypted(m.APIKey).
SetPrimaryModel(m.PrimaryModel).
SetExtraModels(emptySliceIfNil(m.ExtraModels)).
SetGroupName(m.GroupName).
SetEnabled(m.Enabled).
SetIntervalSeconds(m.IntervalSeconds).
SetExtraHeaders(emptyHeadersIfNilRepo(m.ExtraHeaders)).
SetBodyOverrideMode(defaultBodyModeRepo(m.BodyOverrideMode))
if m.TemplateID != nil {
updater = updater.SetTemplateID(*m.TemplateID)
} else {
updater = updater.ClearTemplateID()
}
if m.BodyOverride != nil {
updater = updater.SetBodyOverride(m.BodyOverride)
} else {
updater = updater.ClearBodyOverride()
}
updated, err := updater.Save(ctx)
if err != nil {
return translatePersistenceError(err, service.ErrChannelMonitorNotFound, nil)
}
m.UpdatedAt = updated.UpdatedAt
return nil
}
func (r *channelMonitorRepository) Delete(ctx context.Context, id int64) error {
client := clientFromContext(ctx, r.client)
if err := client.ChannelMonitor.DeleteOneID(id).Exec(ctx); err != nil {
return translatePersistenceError(err, service.ErrChannelMonitorNotFound, nil)
}
return nil
}
func (r *channelMonitorRepository) List(ctx context.Context, params service.ChannelMonitorListParams) ([]*service.ChannelMonitor, int64, error) {
q := r.client.ChannelMonitor.Query()
if params.Provider != "" {
q = q.Where(channelmonitor.ProviderEQ(channelmonitor.Provider(params.Provider)))
}
if params.Enabled != nil {
q = q.Where(channelmonitor.EnabledEQ(*params.Enabled))
}
if s := strings.TrimSpace(params.Search); s != "" {
q = q.Where(channelmonitor.Or(
channelmonitor.NameContainsFold(s),
channelmonitor.GroupNameContainsFold(s),
channelmonitor.PrimaryModelContainsFold(s),
))
}
total, err := q.Count(ctx)
if err != nil {
return nil, 0, fmt.Errorf("count monitors: %w", err)
}
pageSize := params.PageSize
if pageSize <= 0 {
pageSize = 20
}
page := params.Page
if page <= 0 {
page = 1
}
rows, err := q.
Order(dbent.Desc(channelmonitor.FieldID)).
Offset((page - 1) * pageSize).
Limit(pageSize).
All(ctx)
if err != nil {
return nil, 0, fmt.Errorf("list monitors: %w", err)
}
out := make([]*service.ChannelMonitor, 0, len(rows))
for _, row := range rows {
out = append(out, entToServiceMonitor(row))
}
return out, int64(total), nil
}
// ---------- 调度器辅助 ----------
func (r *channelMonitorRepository) ListEnabled(ctx context.Context) ([]*service.ChannelMonitor, error) {
rows, err := r.client.ChannelMonitor.Query().
Where(channelmonitor.EnabledEQ(true)).
All(ctx)
if err != nil {
return nil, fmt.Errorf("list enabled monitors: %w", err)
}
out := make([]*service.ChannelMonitor, 0, len(rows))
for _, row := range rows {
out = append(out, entToServiceMonitor(row))
}
return out, nil
}
func (r *channelMonitorRepository) MarkChecked(ctx context.Context, id int64, checkedAt time.Time) error {
client := clientFromContext(ctx, r.client)
if err := client.ChannelMonitor.UpdateOneID(id).
SetLastCheckedAt(checkedAt).
Exec(ctx); err != nil {
return translatePersistenceError(err, service.ErrChannelMonitorNotFound, nil)
}
return nil
}
func (r *channelMonitorRepository) InsertHistoryBatch(ctx context.Context, rows []*service.ChannelMonitorHistoryRow) error {
if len(rows) == 0 {
return nil
}
client := clientFromContext(ctx, r.client)
bulk := make([]*dbent.ChannelMonitorHistoryCreate, 0, len(rows))
for _, row := range rows {
c := client.ChannelMonitorHistory.Create().
SetMonitorID(row.MonitorID).
SetModel(row.Model).
SetStatus(channelmonitorhistory.Status(row.Status)).
SetMessage(row.Message).
SetCheckedAt(row.CheckedAt)
if row.LatencyMs != nil {
c = c.SetLatencyMs(*row.LatencyMs)
}
if row.PingLatencyMs != nil {
c = c.SetPingLatencyMs(*row.PingLatencyMs)
}
bulk = append(bulk, c)
}
if _, err := client.ChannelMonitorHistory.CreateBulk(bulk...).Save(ctx); err != nil {
return fmt.Errorf("insert history bulk: %w", err)
}
return nil
}
// DeleteHistoryBefore 物理删 checked_at < before 的明细,分批 channelMonitorPruneBatchSize 行一批,
// 避免单事务删除过多引起锁/WAL 压力。借助 (checked_at) 索引定位小批 id再按 id 删。
func (r *channelMonitorRepository) DeleteHistoryBefore(ctx context.Context, before time.Time) (int64, error) {
return deleteChannelMonitorBatched(ctx, r.db, channelMonitorPruneHistorySQL, before)
}
// ListHistory 按 checked_at 倒序返回某个监控的最近 N 条历史记录。
// model 为空时不过滤;非空时只返回该模型的记录。
func (r *channelMonitorRepository) ListHistory(ctx context.Context, monitorID int64, model string, limit int) ([]*service.ChannelMonitorHistoryEntry, error) {
q := r.client.ChannelMonitorHistory.Query().
Where(channelmonitorhistory.MonitorIDEQ(monitorID))
if strings.TrimSpace(model) != "" {
q = q.Where(channelmonitorhistory.ModelEQ(model))
}
rows, err := q.
Order(dbent.Desc(channelmonitorhistory.FieldCheckedAt)).
Limit(limit).
All(ctx)
if err != nil {
return nil, fmt.Errorf("list history: %w", err)
}
out := make([]*service.ChannelMonitorHistoryEntry, 0, len(rows))
for _, row := range rows {
entry := &service.ChannelMonitorHistoryEntry{
ID: row.ID,
Model: row.Model,
Status: string(row.Status),
LatencyMs: row.LatencyMs,
PingLatencyMs: row.PingLatencyMs,
Message: row.Message,
CheckedAt: row.CheckedAt,
}
out = append(out, entry)
}
return out, nil
}
// ---------- 用户视图聚合(原生 SQL ----------
// ListLatestPerModel 用 DISTINCT ON 取每个 (monitor_id, model) 的最近一条记录。
// 借助 (monitor_id, model, checked_at DESC) 索引可走 Index Scan。
func (r *channelMonitorRepository) ListLatestPerModel(ctx context.Context, monitorID int64) ([]*service.ChannelMonitorLatest, error) {
const q = `
SELECT DISTINCT ON (model)
model, status, latency_ms, ping_latency_ms, checked_at
FROM channel_monitor_histories
WHERE monitor_id = $1
ORDER BY model, checked_at DESC
`
rows, err := r.db.QueryContext(ctx, q, monitorID)
if err != nil {
return nil, fmt.Errorf("query latest per model: %w", err)
}
defer func() { _ = rows.Close() }()
out := make([]*service.ChannelMonitorLatest, 0)
for rows.Next() {
l := &service.ChannelMonitorLatest{}
var latency, ping sql.NullInt64
if err := rows.Scan(&l.Model, &l.Status, &latency, &ping, &l.CheckedAt); err != nil {
return nil, fmt.Errorf("scan latest row: %w", err)
}
assignNullInt(&l.LatencyMs, latency)
assignNullInt(&l.PingLatencyMs, ping)
out = append(out, l)
}
return out, rows.Err()
}
// assignNullInt 把 sql.NullInt64 解包到 *int 指针目标valid 才分配新 int
// 集中实现避免 latency / ping 两处重复 if latency.Valid { v := int(...) ... } 模板。
func assignNullInt(dst **int, n sql.NullInt64) {
if !n.Valid {
return
}
v := int(n.Int64)
*dst = &v
}
// ComputeAvailability 计算指定窗口内每个模型的可用率与平均延迟。
// "可用" = status IN (operational, degraded)。
//
// 数据来源:明细表只保留 1 天;窗口前其余天数走聚合表。
// 明细保留 30 天monitorHistoryRetentionDays窗口 <= 30 天时直接扫 histories
// 精度到秒,避免与聚合表 UNION 带来的 UTC 日切精度损失。
func (r *channelMonitorRepository) ComputeAvailability(ctx context.Context, monitorID int64, windowDays int) ([]*service.ChannelMonitorAvailability, error) {
if windowDays <= 0 {
windowDays = 7
}
const q = `
SELECT model,
COUNT(*) AS total,
COUNT(*) FILTER (WHERE status IN ('operational','degraded')) AS ok,
CASE WHEN COUNT(latency_ms) > 0
THEN SUM(latency_ms) FILTER (WHERE latency_ms IS NOT NULL)::float8 / COUNT(latency_ms)
ELSE NULL END AS avg_latency_ms
FROM channel_monitor_histories
WHERE monitor_id = $1
AND checked_at >= NOW() - ($2::int || ' days')::interval
GROUP BY model
`
rows, err := r.db.QueryContext(ctx, q, monitorID, windowDays)
if err != nil {
return nil, fmt.Errorf("query availability: %w", err)
}
defer func() { _ = rows.Close() }()
out := make([]*service.ChannelMonitorAvailability, 0)
for rows.Next() {
row, err := scanAvailabilityRow(rows, windowDays)
if err != nil {
return nil, err
}
out = append(out, row)
}
return out, rows.Err()
}
// scanAvailabilityRow 把单行 (model, total, ok, avg_latency) 扫描为 ChannelMonitorAvailability。
// 仅服务于 ComputeAvailability4 列);批量版本因为多一列 monitor_id 直接 inline 调 finalizeAvailabilityRow。
func scanAvailabilityRow(rows interface{ Scan(...any) error }, windowDays int) (*service.ChannelMonitorAvailability, error) {
row := &service.ChannelMonitorAvailability{WindowDays: windowDays}
var avgLatency sql.NullFloat64
if err := rows.Scan(&row.Model, &row.TotalChecks, &row.OperationalChecks, &avgLatency); err != nil {
return nil, fmt.Errorf("scan availability row: %w", err)
}
finalizeAvailabilityRow(row, avgLatency)
return row, nil
}
// finalizeAvailabilityRow 根据 OperationalChecks/TotalChecks 算出可用率,
// 并把 sql.NullFloat64 的平均延迟解包为 *int。两处复用避免维护漂移。
func finalizeAvailabilityRow(row *service.ChannelMonitorAvailability, avgLatency sql.NullFloat64) {
if row.TotalChecks > 0 {
row.AvailabilityPct = float64(row.OperationalChecks) * 100.0 / float64(row.TotalChecks)
}
if avgLatency.Valid {
v := int(avgLatency.Float64)
row.AvgLatencyMs = &v
}
}
// ListLatestForMonitorIDs 一次性查询多个监控的"每个 (monitor_id, model) 最近一条"记录。
// 利用 PG 的 DISTINCT ON 特性,借助 (monitor_id, model, checked_at DESC) 索引可走 Index Scan。
func (r *channelMonitorRepository) ListLatestForMonitorIDs(ctx context.Context, ids []int64) (map[int64][]*service.ChannelMonitorLatest, error) {
out := make(map[int64][]*service.ChannelMonitorLatest, len(ids))
if len(ids) == 0 {
return out, nil
}
const q = `
SELECT DISTINCT ON (monitor_id, model)
monitor_id, model, status, latency_ms, ping_latency_ms, checked_at
FROM channel_monitor_histories
WHERE monitor_id = ANY($1)
ORDER BY monitor_id, model, checked_at DESC
`
rows, err := r.db.QueryContext(ctx, q, pq.Array(ids))
if err != nil {
return nil, fmt.Errorf("query latest batch: %w", err)
}
defer func() { _ = rows.Close() }()
for rows.Next() {
var monitorID int64
l := &service.ChannelMonitorLatest{}
var latency, ping sql.NullInt64
if err := rows.Scan(&monitorID, &l.Model, &l.Status, &latency, &ping, &l.CheckedAt); err != nil {
return nil, fmt.Errorf("scan latest batch row: %w", err)
}
assignNullInt(&l.LatencyMs, latency)
assignNullInt(&l.PingLatencyMs, ping)
out[monitorID] = append(out[monitorID], l)
}
if err := rows.Err(); err != nil {
return nil, err
}
return out, nil
}
// ListRecentHistoryForMonitors 为多个 monitor 批量取各自"指定模型"最近 N 条历史(按 checked_at DESC最新在前
// primaryModels[monitorID] 指定该监控要过滤的模型名monitor 不在 primaryModels 中的记录不返回。
// 通过 CTE + unnest(两个 int8/text 数组) 构造 (monitor_id, model) 白名单,
// 再用 ROW_NUMBER() OVER (PARTITION BY monitor_id) 取各自前 N 条。
//
// 返回值map[monitorID] -> []*ChannelMonitorHistoryEntry不含 message减少网络开销
// 空 ids / 空 primaryModels 返回空 map不报错。
func (r *channelMonitorRepository) ListRecentHistoryForMonitors(
ctx context.Context,
ids []int64,
primaryModels map[int64]string,
perMonitorLimit int,
) (map[int64][]*service.ChannelMonitorHistoryEntry, error) {
out := make(map[int64][]*service.ChannelMonitorHistoryEntry, len(ids))
pairIDs, pairModels := buildMonitorModelPairs(ids, primaryModels)
if len(pairIDs) == 0 {
return out, nil
}
perMonitorLimit = clampTimelineLimit(perMonitorLimit)
const q = `
WITH targets AS (
SELECT unnest($1::bigint[]) AS monitor_id,
unnest($2::text[]) AS model
),
ranked AS (
SELECT h.monitor_id,
h.status,
h.latency_ms,
h.ping_latency_ms,
h.checked_at,
ROW_NUMBER() OVER (PARTITION BY h.monitor_id ORDER BY h.checked_at DESC) AS rn
FROM channel_monitor_histories h
JOIN targets t
ON t.monitor_id = h.monitor_id AND t.model = h.model
)
SELECT monitor_id, status, latency_ms, ping_latency_ms, checked_at
FROM ranked
WHERE rn <= $3
ORDER BY monitor_id, checked_at DESC
`
rows, err := r.db.QueryContext(ctx, q, pq.Array(pairIDs), pq.Array(pairModels), perMonitorLimit)
if err != nil {
return nil, fmt.Errorf("query recent history batch: %w", err)
}
defer func() { _ = rows.Close() }()
for rows.Next() {
var monitorID int64
entry := &service.ChannelMonitorHistoryEntry{}
var latency, ping sql.NullInt64
if err := rows.Scan(&monitorID, &entry.Status, &latency, &ping, &entry.CheckedAt); err != nil {
return nil, fmt.Errorf("scan recent history row: %w", err)
}
assignNullInt(&entry.LatencyMs, latency)
assignNullInt(&entry.PingLatencyMs, ping)
out[monitorID] = append(out[monitorID], entry)
}
if err := rows.Err(); err != nil {
return nil, err
}
return out, nil
}
// buildMonitorModelPairs 基于 ids 过滤出有效的 (monitor_id, model) 对model 为空时跳过。
// 保证两个数组长度一致且一一对应,供 unnest 展开。
func buildMonitorModelPairs(ids []int64, primaryModels map[int64]string) ([]int64, []string) {
if len(ids) == 0 || len(primaryModels) == 0 {
return nil, nil
}
pairIDs := make([]int64, 0, len(ids))
pairModels := make([]string, 0, len(ids))
for _, id := range ids {
model, ok := primaryModels[id]
if !ok || strings.TrimSpace(model) == "" {
continue
}
pairIDs = append(pairIDs, id)
pairModels = append(pairModels, model)
}
return pairIDs, pairModels
}
// timelineLimit* 批量 timeline 查询的 perMonitorLimit 夹紧范围。
// 下限 1 表示至少返回最近一条;上限 200 控制单次响应体与 SQL 内存占用ROW_NUMBER 窗口上限)。
const (
timelineLimitMin = 1
timelineLimitMax = 200
)
// clampTimelineLimit 把 perMonitorLimit 夹紧到 [timelineLimitMin, timelineLimitMax],避免非法值或超大查询。
func clampTimelineLimit(n int) int {
if n < timelineLimitMin {
return timelineLimitMin
}
if n > timelineLimitMax {
return timelineLimitMax
}
return n
}
// ComputeAvailabilityForMonitors 一次性计算多个监控在某个窗口内的每模型可用率与平均延迟。
// 明细保留 30 天,直接扫 histories窗口 <= 30 天时无需聚合)。
func (r *channelMonitorRepository) ComputeAvailabilityForMonitors(ctx context.Context, ids []int64, windowDays int) (map[int64][]*service.ChannelMonitorAvailability, error) {
out := make(map[int64][]*service.ChannelMonitorAvailability, len(ids))
if len(ids) == 0 {
return out, nil
}
if windowDays <= 0 {
windowDays = 7
}
const q = `
SELECT monitor_id,
model,
COUNT(*) AS total,
COUNT(*) FILTER (WHERE status IN ('operational','degraded')) AS ok,
CASE WHEN COUNT(latency_ms) > 0
THEN SUM(latency_ms) FILTER (WHERE latency_ms IS NOT NULL)::float8 / COUNT(latency_ms)
ELSE NULL END AS avg_latency_ms
FROM channel_monitor_histories
WHERE monitor_id = ANY($1)
AND checked_at >= NOW() - ($2::int || ' days')::interval
GROUP BY monitor_id, model
`
rows, err := r.db.QueryContext(ctx, q, pq.Array(ids), windowDays)
if err != nil {
return nil, fmt.Errorf("query availability batch: %w", err)
}
defer func() { _ = rows.Close() }()
for rows.Next() {
var monitorID int64
row := &service.ChannelMonitorAvailability{WindowDays: windowDays}
var avgLatency sql.NullFloat64
if err := rows.Scan(&monitorID, &row.Model, &row.TotalChecks, &row.OperationalChecks, &avgLatency); err != nil {
return nil, fmt.Errorf("scan availability batch row: %w", err)
}
// 批量查询多了首列 monitor_id其余字段的可用率/平均延迟换算与单 monitor 版本一致,
// 抽出 finalizeAvailabilityRow 复用,避免两处分别维护除法与 NullFloat 解包。
finalizeAvailabilityRow(row, avgLatency)
out[monitorID] = append(out[monitorID], row)
}
if err := rows.Err(); err != nil {
return nil, err
}
return out, nil
}
// ---------- 聚合维护 ----------
// UpsertDailyRollupsFor 把 targetDate 当天([targetDate, targetDate+1d))的明细
// 按 (monitor_id, model, bucket_date) 聚合写入 channel_monitor_daily_rollups。
// - 用 ON CONFLICT (monitor_id, model, bucket_date) DO UPDATE 实现幂等回填,
// 重复执行只会用最新统计覆盖;
// - $1::date 让 PG 自动把入参 truncate 到 UTC 日期,调用方不需要预处理 targetDate。
func (r *channelMonitorRepository) UpsertDailyRollupsFor(ctx context.Context, targetDate time.Time) (int64, error) {
const q = `
INSERT INTO channel_monitor_daily_rollups (
monitor_id, model, bucket_date,
total_checks, ok_count,
operational_count, degraded_count, failed_count, error_count,
sum_latency_ms, count_latency,
sum_ping_latency_ms, count_ping_latency,
computed_at
)
SELECT
monitor_id,
model,
$1::date AS bucket_date,
COUNT(*) AS total_checks,
COUNT(*) FILTER (WHERE status IN ('operational','degraded')) AS ok_count,
COUNT(*) FILTER (WHERE status = 'operational') AS operational_count,
COUNT(*) FILTER (WHERE status = 'degraded') AS degraded_count,
COUNT(*) FILTER (WHERE status = 'failed') AS failed_count,
COUNT(*) FILTER (WHERE status = 'error') AS error_count,
COALESCE(SUM(latency_ms) FILTER (WHERE latency_ms IS NOT NULL), 0) AS sum_latency_ms,
COUNT(latency_ms) AS count_latency,
COALESCE(SUM(ping_latency_ms) FILTER (WHERE ping_latency_ms IS NOT NULL), 0) AS sum_ping_latency_ms,
COUNT(ping_latency_ms) AS count_ping_latency,
NOW()
FROM channel_monitor_histories
WHERE checked_at >= $1::date
AND checked_at < ($1::date + INTERVAL '1 day')
GROUP BY monitor_id, model
ON CONFLICT (monitor_id, model, bucket_date) DO UPDATE SET
total_checks = EXCLUDED.total_checks,
ok_count = EXCLUDED.ok_count,
operational_count = EXCLUDED.operational_count,
degraded_count = EXCLUDED.degraded_count,
failed_count = EXCLUDED.failed_count,
error_count = EXCLUDED.error_count,
sum_latency_ms = EXCLUDED.sum_latency_ms,
count_latency = EXCLUDED.count_latency,
sum_ping_latency_ms = EXCLUDED.sum_ping_latency_ms,
count_ping_latency = EXCLUDED.count_ping_latency,
computed_at = NOW()
`
res, err := r.db.ExecContext(ctx, q, targetDate)
if err != nil {
return 0, fmt.Errorf("upsert daily rollups for %s: %w", targetDate.Format("2006-01-02"), err)
}
n, err := res.RowsAffected()
if err != nil {
return 0, fmt.Errorf("rows affected (upsert rollups): %w", err)
}
return n, nil
}
// DeleteRollupsBefore 物理删 bucket_date < beforeDate 的聚合行,同样分批。
func (r *channelMonitorRepository) DeleteRollupsBefore(ctx context.Context, beforeDate time.Time) (int64, error) {
return deleteChannelMonitorBatched(ctx, r.db, channelMonitorPruneRollupSQL, beforeDate)
}
// channelMonitorPruneBatchSize 单批删除上限。与 ops_cleanup_service 保持一致的 5000
// 在大表上按 id 小批删可以避免长事务和 WAL 堆积。
const channelMonitorPruneBatchSize = 5000
// channelMonitorPruneHistorySQL 分批物理删明细表过期行。
const channelMonitorPruneHistorySQL = `
WITH batch AS (
SELECT id FROM channel_monitor_histories
WHERE checked_at < $1
ORDER BY id
LIMIT $2
)
DELETE FROM channel_monitor_histories
WHERE id IN (SELECT id FROM batch)
`
// channelMonitorPruneRollupSQL 分批物理删 rollup 表过期行。bucket_date 需要 ::date 转型
// 保证与 DATE 列一致比较。
const channelMonitorPruneRollupSQL = `
WITH batch AS (
SELECT id FROM channel_monitor_daily_rollups
WHERE bucket_date < $1::date
ORDER BY id
LIMIT $2
)
DELETE FROM channel_monitor_daily_rollups
WHERE id IN (SELECT id FROM batch)
`
// deleteChannelMonitorBatched 循环执行分批 DELETE直到影响行为 0。返回累计删除行数。
// cutoff 由调用方按列类型传入(明细用 time.Time 对 TIMESTAMPTZrollup 用 time.Time SQL 侧 ::date 转型)。
func deleteChannelMonitorBatched(ctx context.Context, db *sql.DB, query string, cutoff time.Time) (int64, error) {
var total int64
for {
res, err := db.ExecContext(ctx, query, cutoff, channelMonitorPruneBatchSize)
if err != nil {
return total, fmt.Errorf("channel_monitor prune batch: %w", err)
}
affected, err := res.RowsAffected()
if err != nil {
return total, fmt.Errorf("channel_monitor prune rows affected: %w", err)
}
total += affected
if affected == 0 {
break
}
}
return total, nil
}
// LoadAggregationWatermark 读 watermark 表id=1
// watermark 表不是 ent schema只有一行直接走原生 SQL。
// - 行不存在或 last_aggregated_date IS NULL返回 (nil, nil),由调用方决定首次回填策略
func (r *channelMonitorRepository) LoadAggregationWatermark(ctx context.Context) (*time.Time, error) {
const q = `SELECT last_aggregated_date FROM channel_monitor_aggregation_watermark WHERE id = 1`
var t sql.NullTime
if err := r.db.QueryRowContext(ctx, q).Scan(&t); err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, fmt.Errorf("load aggregation watermark: %w", err)
}
if !t.Valid {
return nil, nil
}
return &t.Time, nil
}
// UpdateAggregationWatermark 更新 watermarkUPSERT 到 id=1
// $1::date 让 PG 把入参 truncate 到 UTC 日期,与 last_aggregated_date 列的 DATE 类型一致。
func (r *channelMonitorRepository) UpdateAggregationWatermark(ctx context.Context, date time.Time) error {
const q = `
INSERT INTO channel_monitor_aggregation_watermark (id, last_aggregated_date, updated_at)
VALUES (1, $1::date, NOW())
ON CONFLICT (id) DO UPDATE SET
last_aggregated_date = EXCLUDED.last_aggregated_date,
updated_at = NOW()
`
if _, err := r.db.ExecContext(ctx, q, date); err != nil {
return fmt.Errorf("update aggregation watermark: %w", err)
}
return nil
}
// ---------- helpers ----------
func entToServiceMonitor(row *dbent.ChannelMonitor) *service.ChannelMonitor {
if row == nil {
return nil
}
extras := row.ExtraModels
if extras == nil {
extras = []string{}
}
headers := row.ExtraHeaders
if headers == nil {
headers = map[string]string{}
}
out := &service.ChannelMonitor{
ID: row.ID,
Name: row.Name,
Provider: string(row.Provider),
Endpoint: row.Endpoint,
APIKey: row.APIKeyEncrypted, // 仍为密文service 层负责解密
PrimaryModel: row.PrimaryModel,
ExtraModels: extras,
GroupName: row.GroupName,
Enabled: row.Enabled,
IntervalSeconds: row.IntervalSeconds,
LastCheckedAt: row.LastCheckedAt,
CreatedBy: row.CreatedBy,
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,
ExtraHeaders: headers,
BodyOverrideMode: row.BodyOverrideMode,
BodyOverride: row.BodyOverride,
}
if row.TemplateID != nil {
id := *row.TemplateID
out.TemplateID = &id
}
return out
}
// emptyHeadersIfNilRepo 与 service.emptyHeadersIfNil 功能一致,
// repo 独立一份避免 import 循环。
func emptyHeadersIfNilRepo(h map[string]string) map[string]string {
if h == nil {
return map[string]string{}
}
return h
}
// defaultBodyModeRepo 空串归一为 off同上不循环
func defaultBodyModeRepo(mode string) string {
if mode == "" {
return "off"
}
return mode
}
func emptySliceIfNil(in []string) []string {
if in == nil {
return []string{}
}
return in
}

View File

@ -0,0 +1,195 @@
package repository
import (
"context"
"database/sql"
"fmt"
dbent "github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/ent/channelmonitor"
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
"github.com/Wei-Shaw/sub2api/internal/service"
)
// channelMonitorRequestTemplateRepository 实现 service.ChannelMonitorRequestTemplateRepository。
// 与 channelMonitorRepository 分开一个文件,职责清晰。
type channelMonitorRequestTemplateRepository struct {
client *dbent.Client
db *sql.DB
}
// NewChannelMonitorRequestTemplateRepository 创建模板仓储实例。
func NewChannelMonitorRequestTemplateRepository(client *dbent.Client, db *sql.DB) service.ChannelMonitorRequestTemplateRepository {
return &channelMonitorRequestTemplateRepository{client: client, db: db}
}
// ---------- CRUD ----------
func (r *channelMonitorRequestTemplateRepository) Create(ctx context.Context, t *service.ChannelMonitorRequestTemplate) error {
client := clientFromContext(ctx, r.client)
builder := client.ChannelMonitorRequestTemplate.Create().
SetName(t.Name).
SetProvider(channelmonitorrequesttemplate.Provider(t.Provider)).
SetDescription(t.Description).
SetExtraHeaders(emptyHeadersIfNilRepo(t.ExtraHeaders)).
SetBodyOverrideMode(defaultBodyModeRepo(t.BodyOverrideMode))
if t.BodyOverride != nil {
builder = builder.SetBodyOverride(t.BodyOverride)
}
created, err := builder.Save(ctx)
if err != nil {
return translatePersistenceError(err, service.ErrChannelMonitorTemplateNotFound, nil)
}
t.ID = created.ID
t.CreatedAt = created.CreatedAt
t.UpdatedAt = created.UpdatedAt
return nil
}
func (r *channelMonitorRequestTemplateRepository) GetByID(ctx context.Context, id int64) (*service.ChannelMonitorRequestTemplate, error) {
row, err := r.client.ChannelMonitorRequestTemplate.Query().
Where(channelmonitorrequesttemplate.IDEQ(id)).
Only(ctx)
if err != nil {
return nil, translatePersistenceError(err, service.ErrChannelMonitorTemplateNotFound, nil)
}
return entToServiceTemplate(row), nil
}
func (r *channelMonitorRequestTemplateRepository) Update(ctx context.Context, t *service.ChannelMonitorRequestTemplate) error {
client := clientFromContext(ctx, r.client)
updater := client.ChannelMonitorRequestTemplate.UpdateOneID(t.ID).
SetName(t.Name).
SetDescription(t.Description).
SetExtraHeaders(emptyHeadersIfNilRepo(t.ExtraHeaders)).
SetBodyOverrideMode(defaultBodyModeRepo(t.BodyOverrideMode))
if t.BodyOverride != nil {
updater = updater.SetBodyOverride(t.BodyOverride)
} else {
updater = updater.ClearBodyOverride()
}
updated, err := updater.Save(ctx)
if err != nil {
return translatePersistenceError(err, service.ErrChannelMonitorTemplateNotFound, nil)
}
t.UpdatedAt = updated.UpdatedAt
return nil
}
func (r *channelMonitorRequestTemplateRepository) Delete(ctx context.Context, id int64) error {
client := clientFromContext(ctx, r.client)
if err := client.ChannelMonitorRequestTemplate.DeleteOneID(id).Exec(ctx); err != nil {
return translatePersistenceError(err, service.ErrChannelMonitorTemplateNotFound, nil)
}
return nil
}
func (r *channelMonitorRequestTemplateRepository) List(ctx context.Context, params service.ChannelMonitorRequestTemplateListParams) ([]*service.ChannelMonitorRequestTemplate, error) {
q := r.client.ChannelMonitorRequestTemplate.Query()
if params.Provider != "" {
q = q.Where(channelmonitorrequesttemplate.ProviderEQ(channelmonitorrequesttemplate.Provider(params.Provider)))
}
rows, err := q.
Order(dbent.Asc(channelmonitorrequesttemplate.FieldProvider), dbent.Asc(channelmonitorrequesttemplate.FieldName)).
All(ctx)
if err != nil {
return nil, fmt.Errorf("list monitor templates: %w", err)
}
out := make([]*service.ChannelMonitorRequestTemplate, 0, len(rows))
for _, row := range rows {
out = append(out, entToServiceTemplate(row))
}
return out, nil
}
// ApplyToMonitors 把模板当前配置覆盖到 monitorIDs 列表里的关联监控。
// WHERE 双重过滤template_id = id AND id IN (monitorIDs),防止用户传了未关联本模板的 id
// 就被覆盖。走 ent UpdateMany 保留 hooks。
func (r *channelMonitorRequestTemplateRepository) ApplyToMonitors(ctx context.Context, id int64, monitorIDs []int64) (int64, error) {
if len(monitorIDs) == 0 {
return 0, nil
}
client := clientFromContext(ctx, r.client)
tpl, err := client.ChannelMonitorRequestTemplate.Query().
Where(channelmonitorrequesttemplate.IDEQ(id)).
Only(ctx)
if err != nil {
return 0, translatePersistenceError(err, service.ErrChannelMonitorTemplateNotFound, nil)
}
updater := client.ChannelMonitor.Update().
Where(
channelmonitor.TemplateIDEQ(id),
channelmonitor.IDIn(monitorIDs...),
).
SetExtraHeaders(emptyHeadersIfNilRepo(tpl.ExtraHeaders)).
SetBodyOverrideMode(defaultBodyModeRepo(tpl.BodyOverrideMode))
if tpl.BodyOverride != nil {
updater = updater.SetBodyOverride(tpl.BodyOverride)
} else {
updater = updater.ClearBodyOverride()
}
affected, err := updater.Save(ctx)
if err != nil {
return 0, fmt.Errorf("apply template to monitors: %w", err)
}
return int64(affected), nil
}
// CountAssociatedMonitors 统计关联监控数UI 展示「N 个配置」用)。
func (r *channelMonitorRequestTemplateRepository) CountAssociatedMonitors(ctx context.Context, id int64) (int64, error) {
count, err := r.client.ChannelMonitor.Query().
Where(channelmonitor.TemplateIDEQ(id)).
Count(ctx)
if err != nil {
return 0, fmt.Errorf("count monitors for template %d: %w", id, err)
}
return int64(count), nil
}
// ListAssociatedMonitors 列出模板关联的所有监控简略字段。
// ORDER BY name 稳定输出方便前端展示。
func (r *channelMonitorRequestTemplateRepository) ListAssociatedMonitors(ctx context.Context, id int64) ([]*service.AssociatedMonitorBrief, error) {
rows, err := r.client.ChannelMonitor.Query().
Where(channelmonitor.TemplateIDEQ(id)).
Order(dbent.Asc(channelmonitor.FieldName)).
All(ctx)
if err != nil {
return nil, fmt.Errorf("list associated monitors for template %d: %w", id, err)
}
out := make([]*service.AssociatedMonitorBrief, 0, len(rows))
for _, row := range rows {
out = append(out, &service.AssociatedMonitorBrief{
ID: row.ID,
Name: row.Name,
Provider: string(row.Provider),
Enabled: row.Enabled,
})
}
return out, nil
}
// ---------- helpers ----------
func entToServiceTemplate(row *dbent.ChannelMonitorRequestTemplate) *service.ChannelMonitorRequestTemplate {
if row == nil {
return nil
}
headers := row.ExtraHeaders
if headers == nil {
headers = map[string]string{}
}
return &service.ChannelMonitorRequestTemplate{
ID: row.ID,
Name: row.Name,
Provider: string(row.Provider),
Description: row.Description,
ExtraHeaders: headers,
BodyOverrideMode: row.BodyOverrideMode,
BodyOverride: row.BodyOverride,
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,
}
}

View File

@ -89,6 +89,8 @@ var ProviderSet = wire.NewSet(
NewErrorPassthroughRepository,
NewTLSFingerprintProfileRepository,
NewChannelRepository,
NewChannelMonitorRepository,
NewChannelMonitorRequestTemplateRepository,
// Cache implementations
NewGatewayCache,

View File

@ -771,6 +771,9 @@ func TestAPIContracts(t *testing.T) {
"balance_low_notify_threshold": 0,
"balance_low_notify_recharge_url": "",
"account_quota_notify_emails": [],
"channel_monitor_enabled": true,
"channel_monitor_default_interval_seconds": 60,
"available_channels_enabled": false,
"wechat_connect_enabled": false,
"wechat_connect_app_id": "",
"wechat_connect_app_secret_configured": false,
@ -943,6 +946,9 @@ func TestAPIContracts(t *testing.T) {
"balance_low_notify_threshold": 0,
"balance_low_notify_recharge_url": "",
"account_quota_notify_emails": [],
"channel_monitor_enabled": true,
"channel_monitor_default_interval_seconds": 60,
"available_channels_enabled": false,
"wechat_connect_enabled": true,
"wechat_connect_app_id": "wx-open-config",
"wechat_connect_app_secret_configured": true,

View File

@ -88,6 +88,9 @@ func RegisterAdminRoutes(
// 渠道管理
registerChannelRoutes(admin, h)
// 渠道监控
registerChannelMonitorRoutes(admin, h)
}
}
@ -567,3 +570,27 @@ func registerChannelRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
channels.DELETE("/:id", h.Admin.Channel.Delete)
}
}
func registerChannelMonitorRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
monitors := admin.Group("/channel-monitors")
{
monitors.GET("", h.Admin.ChannelMonitor.List)
monitors.POST("", h.Admin.ChannelMonitor.Create)
monitors.GET("/:id", h.Admin.ChannelMonitor.Get)
monitors.PUT("/:id", h.Admin.ChannelMonitor.Update)
monitors.DELETE("/:id", h.Admin.ChannelMonitor.Delete)
monitors.POST("/:id/run", h.Admin.ChannelMonitor.Run)
monitors.GET("/:id/history", h.Admin.ChannelMonitor.History)
}
templates := admin.Group("/channel-monitor-templates")
{
templates.GET("", h.Admin.ChannelMonitorTemplate.List)
templates.POST("", h.Admin.ChannelMonitorTemplate.Create)
templates.GET("/:id", h.Admin.ChannelMonitorTemplate.Get)
templates.PUT("/:id", h.Admin.ChannelMonitorTemplate.Update)
templates.DELETE("/:id", h.Admin.ChannelMonitorTemplate.Delete)
templates.GET("/:id/monitors", h.Admin.ChannelMonitorTemplate.AssociatedMonitors)
templates.POST("/:id/apply", h.Admin.ChannelMonitorTemplate.Apply)
}
}

View File

@ -68,6 +68,12 @@ func RegisterUserRoutes(
groups.GET("/rates", h.APIKey.GetUserGroupRates)
}
// 用户可用渠道(非管理员接口)
channels := authenticated.Group("/channels")
{
channels.GET("/available", h.AvailableChannel.List)
}
// 使用记录
usage := authenticated.Group("/usage")
{
@ -103,5 +109,12 @@ func RegisterUserRoutes(
subscriptions.GET("/progress", h.Subscription.GetProgress)
subscriptions.GET("/summary", h.Subscription.GetSummary)
}
// 渠道监控(用户只读)
monitors := authenticated.Group("/channel-monitors")
{
monitors.GET("", h.ChannelMonitor.List)
monitors.GET("/:id/status", h.ChannelMonitor.GetStatus)
}
}
}

View File

@ -111,6 +111,18 @@ func (c *Channel) IsActive() bool {
return c.Status == StatusActive
}
// normalizeBillingModelSource 若 BillingModelSource 为空则回填默认值 ChannelMapped。
// 作为 *Channel 的实体方法集中管理默认值service 层只需在 Channel 进入内存
// 缓存装填、repo 读出)时调用一次,下游读路径就无需重复兜底。
func (c *Channel) normalizeBillingModelSource() {
if c == nil {
return
}
if c.BillingModelSource == "" {
c.BillingModelSource = BillingModelSourceChannelMapped
}
}
// GetModelPricing 根据模型名查找渠道定价,未找到返回 nil。
// 精确匹配,大小写不敏感。返回值拷贝,不污染缓存。
func (c *Channel) GetModelPricing(model string) *ChannelModelPricing {
@ -345,3 +357,209 @@ type ChannelUsageFields struct {
BillingModelSource string // 计费模型来源:"requested" / "upstream" / "channel_mapped"
ModelMappingChain string // 映射链描述,如 "a→b→c"
}
// SupportedModel 渠道的一个支持模型条目(无通配符、可直接展示给用户)
type SupportedModel struct {
Name string // 用户侧模型名
Platform string // 所属平台
Pricing *ChannelModelPricing // 定价详情nil 表示未配置定价)
}
// wildcardSuffix 是模型模式中的通配符后缀标记(仅支持尾部匹配)。
const wildcardSuffix = "*"
// splitWildcardSuffix 将模型模式拆分为 (prefix, isWildcard)。
//
// "claude-opus-*" → ("claude-opus-", true)
// "claude-opus-4" → ("claude-opus-4", false)
// "*" → ("", true)
//
// 注意:返回的 prefix 保持原始大小写,由调用方按需 ToLower。
func splitWildcardSuffix(pattern string) (prefix string, isWildcard bool) {
if strings.HasSuffix(pattern, wildcardSuffix) {
return strings.TrimSuffix(pattern, wildcardSuffix), true
}
return pattern, false
}
// GetModelPricingByPlatform 在指定平台下查找精确模型的定价,未找到返回 nil。
// 与 GetModelPricing 的区别:按 Platform 隔离,避免跨平台同名模型误匹配。
func (c *Channel) GetModelPricingByPlatform(platform, model string) *ChannelModelPricing {
if c == nil {
return nil
}
modelLower := strings.ToLower(model)
for i := range c.ModelPricing {
if c.ModelPricing[i].Platform != platform {
continue
}
for _, m := range c.ModelPricing[i].Models {
if strings.ToLower(m) == modelLower {
cp := c.ModelPricing[i].Clone()
return &cp
}
}
}
return nil
}
// platformPricingIndex 是单个平台下定价信息的复合索引。
// 一次扫描即可同时支持精确查找exact 分支与有序遍历wildcard 分支),
// 避免 SupportedModels 对每个平台重复扫描定价列表。
//
// byLower 与 names/originalCase 共享同一套去重规则:以 lower-case 模型名为 key
// 首个命中保留其原始大小写。names 维持按定价行扫描顺序的稳定迭代。
type platformPricingIndex struct {
byLower map[string]*ChannelModelPricing // lowercased model name → pricing (Clone'd)
originalCase map[string]string // lowercased model name → original-case model name
names []string // priced model names in their ORIGINAL case, insertion-ordered, deduped case-insensitively (first wins)
}
// buildPricingIndex 对渠道的定价列表做一次扫描,按 platform 聚合为查找索引。
// 索引值是定价条目的 Clone 指针,调用方可安全按需返回副本而不污染缓存。
// 通配符后缀条目(如 "claude-*")不被索引(它们是模式,不是具体模型名)。
// 同一平台中以大小写不敏感方式去重,先出现者保留原始大小写。
func buildPricingIndex(pricings []ChannelModelPricing) map[string]*platformPricingIndex {
idx := make(map[string]*platformPricingIndex)
for i := range pricings {
p := pricings[i]
pidx, ok := idx[p.Platform]
if !ok {
pidx = &platformPricingIndex{
byLower: make(map[string]*ChannelModelPricing),
originalCase: make(map[string]string),
names: make([]string, 0),
}
idx[p.Platform] = pidx
}
for _, m := range p.Models {
if _, wild := splitWildcardSuffix(m); wild {
continue
}
lower := strings.ToLower(m)
if _, exists := pidx.byLower[lower]; exists {
continue // 首个命中胜出case-insensitive 去重后第一个定价 / 第一个原始大小写)
}
cp := pricings[i].Clone()
pidx.byLower[lower] = &cp
pidx.originalCase[lower] = m
pidx.names = append(pidx.names, m)
}
}
return idx
}
// SupportedModels 计算渠道的支持模型列表,结果保证不含通配符。
//
// 算法mapping pricing 并联):
//
// - Pass Amapping遍历 ModelMapping
// - 精确 src → target显示名 = src用户视角定价用 target 在同 platform 定价里查
// mapping 改写后实际计费的是 target这是用户感知的"实际花费")。
// target 为空或为通配符时退化为按 src 自查。
// - 通配符 src如 "claude-3-*"):用同 platform 定价里前缀匹配的模型作为候选展开,
// 每个候选用自身定价(通配符场景一般是 passthroughtarget 通常也是通配符)。
// - "*" 单独 mapping key 走通配符分支(前缀为空 → 全展开)。
// - Pass Bpricing-only遍历 ModelPricing 中所有非通配符模型,对未在 Pass A 添加过的
// 补齐——显示名 = 定价模型名,定价 = 自身(这是关键修复:定价存在即代表渠道支持该模型,
// 即使没配映射)。
//
// 显示名命中定价时使用**定价的原始大小写**(定价是模型身份的事实来源)。
// 按 (Platform, Name) 稳定排序,按 (Platform, lowercase(Name)) 去重,先到者胜出。
//
// 注意:定价仅在 channel.ModelPricing 内查找——全局 LiteLLM 回落由调用方
// `ChannelService.ListAvailable`)在合成展示数据时叠加。
func (c *Channel) SupportedModels() []SupportedModel {
if c == nil {
return nil
}
if len(c.ModelMapping) == 0 && len(c.ModelPricing) == 0 {
return nil
}
idx := buildPricingIndex(c.ModelPricing)
type dedupKey struct {
platform string
name string
}
seen := make(map[dedupKey]struct{})
result := make([]SupportedModel, 0)
// lookup 在 platform pricing index 中按精确名查定价,命中时返回定价大小写。
lookup := func(pidx *platformPricingIndex, name string) (display string, pricing *ChannelModelPricing) {
if pidx == nil || name == "" {
return name, nil
}
lower := strings.ToLower(name)
if p, ok := pidx.byLower[lower]; ok {
return pidx.originalCase[lower], p
}
return name, nil
}
add := func(platform, displayName string, pricing *ChannelModelPricing) {
key := dedupKey{platform: platform, name: strings.ToLower(displayName)}
if _, ok := seen[key]; ok {
return
}
seen[key] = struct{}{}
result = append(result, SupportedModel{
Name: displayName,
Platform: platform,
Pricing: pricing,
})
}
// Pass A从 mapping 展开
for platform, mapping := range c.ModelMapping {
if len(mapping) == 0 {
continue
}
pidx := idx[platform]
for src, target := range mapping {
prefix, isWild := splitWildcardSuffix(src)
if isWild {
if pidx == nil {
continue
}
prefixLower := strings.ToLower(prefix)
for _, candidate := range pidx.names {
if strings.HasPrefix(strings.ToLower(candidate), prefixLower) {
display, pricing := lookup(pidx, candidate)
add(platform, display, pricing)
}
}
continue
}
// 精确 mapping定价按 target 查target 缺失/通配则退化按 src 查
pricingKey := target
if pricingKey == "" {
pricingKey = src
}
if _, targetWild := splitWildcardSuffix(pricingKey); targetWild {
pricingKey = src
}
_, pricing := lookup(pidx, pricingKey)
// 显示名优先用 src 在定价里的原始大小写(若 src 本身是个定价模型名)
displayName, _ := lookup(pidx, src)
add(platform, displayName, pricing)
}
}
// Pass B从 pricing 补齐 mapping 未覆盖的具体模型(修复"定价存在但没配映射 → 不显示"
for platform, pidx := range idx {
for _, name := range pidx.names {
display, pricing := lookup(pidx, name)
add(platform, display, pricing)
}
}
sort.SliceStable(result, func(i, j int) bool {
if result[i].Platform != result[j].Platform {
return result[i].Platform < result[j].Platform
}
return result[i].Name < result[j].Name
})
return result
}

View File

@ -0,0 +1,149 @@
package service
import (
"context"
"fmt"
"sort"
"strings"
)
// AvailableGroupRef 渠道视图中关联分组的简要信息。
//
// 用户侧「可用渠道」页面据此展示:专属分组 vs 公开分组IsExclusive
// 订阅 vs 标准SubscriptionType、默认倍率RateMultiplier。用户专属倍率
// 不在这里暴露,前端自己通过 /groups/rates 拉取,和 API 密钥页面保持一致。
type AvailableGroupRef struct {
ID int64
Name string
Platform string
SubscriptionType string
RateMultiplier float64
IsExclusive bool
}
// AvailableChannel 可用渠道视图:用于「可用渠道」页面展示渠道基础信息 +
// 关联的分组 + 推导出的支持模型列表(无通配符)。
type AvailableChannel struct {
ID int64
Name string
Description string
Status string
BillingModelSource string
RestrictModels bool
Groups []AvailableGroupRef
SupportedModels []SupportedModel
}
// ListAvailable 返回所有渠道的可用视图:每个渠道附带关联分组信息与支持模型列表。
//
// 支持模型通过 (*Channel).SupportedModels() 计算mapping pricing 并联)。
// 对于渠道未配置定价的模型,进一步用 PricingService 的全局 LiteLLM 数据合成
// 一份展示用定价,让用户看到默认价格而非"未配置"。
//
// 关联分组信息通过 groupRepo.ListActive 查询后按 ID 映射;渠道 GroupIDs 中未在活跃列表中
// 的分组(已停用或删除)会被忽略。
//
// 前置条件s.groupRepo 必须非 nil由 wire DI 保证)。直接 nil-deref 用于 fail-fast
// 避免静默掩盖注入缺失。
func (s *ChannelService) ListAvailable(ctx context.Context) ([]AvailableChannel, error) {
channels, err := s.repo.ListAll(ctx)
if err != nil {
return nil, fmt.Errorf("list channels: %w", err)
}
groups, err := s.groupRepo.ListActive(ctx)
if err != nil {
return nil, fmt.Errorf("list active groups: %w", err)
}
groupByID := make(map[int64]AvailableGroupRef, len(groups))
for i := range groups {
g := groups[i]
groupByID[g.ID] = AvailableGroupRef{
ID: g.ID,
Name: g.Name,
Platform: g.Platform,
SubscriptionType: g.SubscriptionType,
RateMultiplier: g.RateMultiplier,
IsExclusive: g.IsExclusive,
}
}
out := make([]AvailableChannel, 0, len(channels))
for i := range channels {
ch := &channels[i]
groups := make([]AvailableGroupRef, 0, len(ch.GroupIDs))
for _, gid := range ch.GroupIDs {
if ref, ok := groupByID[gid]; ok {
groups = append(groups, ref)
}
}
sort.SliceStable(groups, func(i, j int) bool { return groups[i].Name < groups[j].Name })
ch.normalizeBillingModelSource()
supported := ch.SupportedModels()
s.fillGlobalPricingFallback(supported)
out = append(out, AvailableChannel{
ID: ch.ID,
Name: ch.Name,
Description: ch.Description,
Status: ch.Status,
BillingModelSource: ch.BillingModelSource,
RestrictModels: ch.RestrictModels,
Groups: groups,
SupportedModels: supported,
})
}
sort.SliceStable(out, func(i, j int) bool {
return strings.ToLower(out[i].Name) < strings.ToLower(out[j].Name)
})
return out, nil
}
// fillGlobalPricingFallback 对未命中渠道定价的支持模型,从全局 LiteLLM 数据合成一份
// 展示用定价(按 token 计费)。仅用于「可用渠道」展示,不影响真实计费链路。
//
// 当 s.pricingService 为 nil测试场景跳过回落。
func (s *ChannelService) fillGlobalPricingFallback(models []SupportedModel) {
if s.pricingService == nil {
return
}
for i := range models {
if models[i].Pricing != nil {
continue
}
lp := s.pricingService.GetModelPricing(models[i].Name)
if lp == nil {
continue
}
models[i].Pricing = synthesizePricingFromLiteLLM(lp)
}
}
// synthesizePricingFromLiteLLM 把 LiteLLM 的定价数据转成 ChannelModelPricing 形态,
// 仅用于展示。BillingMode 固定为 token图片场景的 OutputCostPerImageToken 也归到
// ImageOutputPrice 字段(与渠道侧"图片输出按 token 计价"语义一致)。
//
// LiteLLM 中字段 0 视为未配置,不带入展示。
func synthesizePricingFromLiteLLM(lp *LiteLLMModelPricing) *ChannelModelPricing {
if lp == nil {
return nil
}
return &ChannelModelPricing{
BillingMode: BillingModeToken,
InputPrice: nonZeroPtr(lp.InputCostPerToken),
OutputPrice: nonZeroPtr(lp.OutputCostPerToken),
CacheWritePrice: nonZeroPtr(lp.CacheCreationInputTokenCost),
CacheReadPrice: nonZeroPtr(lp.CacheReadInputTokenCost),
ImageOutputPrice: nonZeroPtr(lp.OutputCostPerImageToken),
}
}
func nonZeroPtr(v float64) *float64 {
if v == 0 {
return nil
}
return &v
}

View File

@ -0,0 +1,177 @@
//go:build unit
package service
import (
"context"
"errors"
"testing"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
"github.com/stretchr/testify/require"
)
// stubGroupRepoForAvailable 是 ListAvailable 测试用的 GroupRepository stub
// 仅实现 ListActive其他方法对本测试无关返回零值即可。
// listActiveErr 非 nil 时ListActive 返回该错误用于错误传播测试。
// listActiveCalls 记录调用次数,用于断言「失败短路时不再访问 groupRepo」等行为。
type stubGroupRepoForAvailable struct {
activeGroups []Group
listActiveErr error
listActiveCalls int
}
func (s *stubGroupRepoForAvailable) ListActive(ctx context.Context) ([]Group, error) {
s.listActiveCalls++
if s.listActiveErr != nil {
return nil, s.listActiveErr
}
return s.activeGroups, nil
}
func (s *stubGroupRepoForAvailable) Create(ctx context.Context, group *Group) error { return nil }
func (s *stubGroupRepoForAvailable) GetByID(ctx context.Context, id int64) (*Group, error) {
return nil, nil
}
func (s *stubGroupRepoForAvailable) GetByIDLite(ctx context.Context, id int64) (*Group, error) {
return nil, nil
}
func (s *stubGroupRepoForAvailable) Update(ctx context.Context, group *Group) error { return nil }
func (s *stubGroupRepoForAvailable) Delete(ctx context.Context, id int64) error { return nil }
func (s *stubGroupRepoForAvailable) DeleteCascade(ctx context.Context, id int64) ([]int64, error) {
return nil, nil
}
func (s *stubGroupRepoForAvailable) List(ctx context.Context, params pagination.PaginationParams) ([]Group, *pagination.PaginationResult, error) {
return nil, nil, nil
}
func (s *stubGroupRepoForAvailable) ListWithFilters(ctx context.Context, params pagination.PaginationParams, platform, status, search string, isExclusive *bool) ([]Group, *pagination.PaginationResult, error) {
return nil, nil, nil
}
func (s *stubGroupRepoForAvailable) ListActiveByPlatform(ctx context.Context, platform string) ([]Group, error) {
return nil, nil
}
func (s *stubGroupRepoForAvailable) ExistsByName(ctx context.Context, name string) (bool, error) {
return false, nil
}
func (s *stubGroupRepoForAvailable) GetAccountCount(ctx context.Context, groupID int64) (int64, int64, error) {
return 0, 0, nil
}
func (s *stubGroupRepoForAvailable) DeleteAccountGroupsByGroupID(ctx context.Context, groupID int64) (int64, error) {
return 0, nil
}
func (s *stubGroupRepoForAvailable) GetAccountIDsByGroupIDs(ctx context.Context, groupIDs []int64) ([]int64, error) {
return nil, nil
}
func (s *stubGroupRepoForAvailable) BindAccountsToGroup(ctx context.Context, groupID int64, accountIDs []int64) error {
return nil
}
func (s *stubGroupRepoForAvailable) UpdateSortOrders(ctx context.Context, updates []GroupSortOrderUpdate) error {
return nil
}
// newAvailableChannelService 构造一个 ChannelServicechannelRepo.ListAll 返回给定 channels
// groupRepo 由参数决定。传入空 stub 表示「活跃分组列表为空」。
func newAvailableChannelService(channels []Channel, groupRepo GroupRepository) *ChannelService {
repo := &mockChannelRepository{
listAllFn: func(ctx context.Context) ([]Channel, error) { return channels, nil },
}
return NewChannelService(repo, groupRepo, nil, nil)
}
func TestListAvailable_EmptyActiveGroups_NoGroupsAttached(t *testing.T) {
// 活跃分组列表为空时,渠道的 Groups 应为空切片,不报错。
channels := []Channel{{
ID: 1,
Name: "chA",
Status: StatusActive,
GroupIDs: []int64{10, 20},
}}
svc := newAvailableChannelService(channels, &stubGroupRepoForAvailable{})
out, err := svc.ListAvailable(context.Background())
require.NoError(t, err)
require.Len(t, out, 1)
require.Empty(t, out[0].Groups)
}
func TestListAvailable_InactiveGroupIDSilentlyDropped(t *testing.T) {
// 渠道 GroupIDs 中引用的 group 未出现在 ListActive 结果中(已停用或删除),应被静默丢弃。
channels := []Channel{{
ID: 1,
Name: "chA",
Status: StatusActive,
GroupIDs: []int64{1, 99},
}}
groupRepo := &stubGroupRepoForAvailable{
activeGroups: []Group{{ID: 1, Name: "g1", Platform: "anthropic"}},
}
svc := newAvailableChannelService(channels, groupRepo)
out, err := svc.ListAvailable(context.Background())
require.NoError(t, err)
require.Len(t, out, 1)
require.Len(t, out[0].Groups, 1)
require.Equal(t, int64(1), out[0].Groups[0].ID)
}
func TestListAvailable_SortedByName(t *testing.T) {
channels := []Channel{
{ID: 1, Name: "beta"},
{ID: 2, Name: "Alpha"},
{ID: 3, Name: "charlie"},
}
svc := newAvailableChannelService(channels, &stubGroupRepoForAvailable{})
out, err := svc.ListAvailable(context.Background())
require.NoError(t, err)
require.Len(t, out, 3)
require.Equal(t, "Alpha", out[0].Name)
require.Equal(t, "beta", out[1].Name)
require.Equal(t, "charlie", out[2].Name)
}
func TestListAvailable_ListAllErrorPropagates(t *testing.T) {
// ListAll 返回错误时 ListAvailable 应直接返回包装后的错误,且不再访问 groupRepo短路
sentinel := errors.New("list-all-boom")
repo := &mockChannelRepository{
listAllFn: func(ctx context.Context) ([]Channel, error) { return nil, sentinel },
}
groupRepo := &stubGroupRepoForAvailable{}
svc := NewChannelService(repo, groupRepo, nil, nil)
out, err := svc.ListAvailable(context.Background())
require.Nil(t, out)
require.ErrorIs(t, err, sentinel)
require.Contains(t, err.Error(), "list channels", "wrap 前缀缺失,可能 %w 被改为 %v")
require.Equal(t, 0, groupRepo.listActiveCalls, "ListAll 失败后不应再调用 groupRepo.ListActive")
}
func TestListAvailable_ListActiveErrorPropagates(t *testing.T) {
// groupRepo.ListActive 返回错误时 ListAvailable 应直接返回包装后的错误。
sentinel := errors.New("list-active-boom")
svc := newAvailableChannelService(
[]Channel{{ID: 1, Name: "chA"}},
&stubGroupRepoForAvailable{listActiveErr: sentinel},
)
out, err := svc.ListAvailable(context.Background())
require.Nil(t, out)
require.ErrorIs(t, err, sentinel)
require.Contains(t, err.Error(), "list active groups", "wrap 前缀缺失,可能 %w 被改为 %v")
}
func TestListAvailable_DefaultsEmptyBillingModelSource(t *testing.T) {
// 渠道 BillingModelSource 为空时应回填为 BillingModelSourceChannelMapped
// 显式值应原样保留(由 service 层统一处理,避免各 handler 重复默认逻辑)。
channels := []Channel{
{ID: 1, Name: "empty", BillingModelSource: ""},
{ID: 2, Name: "explicit", BillingModelSource: BillingModelSourceUpstream},
}
svc := newAvailableChannelService(channels, &stubGroupRepoForAvailable{})
out, err := svc.ListAvailable(context.Background())
require.NoError(t, err)
require.Len(t, out, 2)
// 按 Name 查找,避免依赖排序副作用。
byName := make(map[string]string, len(out))
for _, ch := range out {
byName[ch.Name] = ch.BillingModelSource
}
require.Equal(t, BillingModelSourceChannelMapped, byName["empty"])
require.Equal(t, BillingModelSourceUpstream, byName["explicit"])
}

View File

@ -0,0 +1,292 @@
package service
import (
"context"
"fmt"
"log/slog"
)
// 渠道监控聚合层:把 latest + availability 拼成 admin/user 视图所需的 summary / detail。
// 所有方法都遵守"失败仅日志,返回零值"的原则,避免 N+1 查询失败拖垮列表渲染。
// BatchMonitorStatusSummary 批量聚合多个监控的 latest + 7d 可用率admin/user list 用,消除 N+1
// 失败时返回空 map错误仅日志不影响列表渲染。
//
// 参数:
// - ids: 要聚合的 monitor ID 列表
// - primaryByID: monitor ID -> primary model用于读 7d 可用率与 latest 状态)
// - extrasByID: monitor ID -> extra models 列表(用于读 latest 状态填充 ExtraModels
func (s *ChannelMonitorService) BatchMonitorStatusSummary(
ctx context.Context,
ids []int64,
primaryByID map[int64]string,
extrasByID map[int64][]string,
) map[int64]MonitorStatusSummary {
out := make(map[int64]MonitorStatusSummary, len(ids))
if len(ids) == 0 {
return out
}
latestMap, err := s.repo.ListLatestForMonitorIDs(ctx, ids)
if err != nil {
slog.Warn("channel_monitor: batch load latest failed", "error", err)
latestMap = map[int64][]*ChannelMonitorLatest{}
}
availMap, err := s.repo.ComputeAvailabilityForMonitors(ctx, ids, monitorAvailability7Days)
if err != nil {
slog.Warn("channel_monitor: batch compute availability failed", "error", err)
availMap = map[int64][]*ChannelMonitorAvailability{}
}
for _, id := range ids {
out[id] = buildStatusSummary(
indexLatestByModel(latestMap[id]),
indexAvailabilityByModel(availMap[id]),
primaryByID[id],
extrasByID[id],
)
}
return out
}
// ListUserView 用户只读视图:列出所有 enabled 监控的概览。
// 使用批量聚合接口避免 N+1
//
// 1 次查 monitors
// 1 次批量 latest含 ping_latency_ms
// 1 次批量 7d availability
// 1 次批量 timeline主模型最近 N 条)。
func (s *ChannelMonitorService) ListUserView(ctx context.Context) ([]*UserMonitorView, error) {
monitors, err := s.repo.ListEnabled(ctx)
if err != nil {
return nil, fmt.Errorf("list enabled monitors: %w", err)
}
if len(monitors) == 0 {
return []*UserMonitorView{}, nil
}
ids, primaryByID, extrasByID := collectMonitorIndexes(monitors)
summaries := s.BatchMonitorStatusSummary(ctx, ids, primaryByID, extrasByID)
latestMap := s.batchLatest(ctx, ids)
timelineMap := s.batchTimeline(ctx, ids, primaryByID)
views := make([]*UserMonitorView, 0, len(monitors))
for _, m := range monitors {
primaryLatest := pickLatest(latestMap[m.ID], m.PrimaryModel)
views = append(views, buildUserViewFromSummary(m, summaries[m.ID], primaryLatest, timelineMap[m.ID]))
}
return views, nil
}
// collectMonitorIndexes 把 monitors 列表按 ID 展开为聚合查询所需的三个索引结构。
func collectMonitorIndexes(monitors []*ChannelMonitor) ([]int64, map[int64]string, map[int64][]string) {
ids := make([]int64, 0, len(monitors))
primaryByID := make(map[int64]string, len(monitors))
extrasByID := make(map[int64][]string, len(monitors))
for _, m := range monitors {
ids = append(ids, m.ID)
primaryByID[m.ID] = m.PrimaryModel
extrasByID[m.ID] = m.ExtraModels
}
return ids, primaryByID, extrasByID
}
// batchLatest 批量取 latest per model失败仅日志与现有 BatchMonitorStatusSummary 一致,不阻断列表渲染)。
func (s *ChannelMonitorService) batchLatest(ctx context.Context, ids []int64) map[int64][]*ChannelMonitorLatest {
latestMap, err := s.repo.ListLatestForMonitorIDs(ctx, ids)
if err != nil {
slog.Warn("channel_monitor: user view batch latest failed", "error", err)
return map[int64][]*ChannelMonitorLatest{}
}
return latestMap
}
// batchTimeline 批量取每个 monitor 主模型最近 monitorTimelineMaxPoints 条历史。
func (s *ChannelMonitorService) batchTimeline(
ctx context.Context,
ids []int64,
primaryByID map[int64]string,
) map[int64][]*ChannelMonitorHistoryEntry {
timelineMap, err := s.repo.ListRecentHistoryForMonitors(ctx, ids, primaryByID, monitorTimelineMaxPoints)
if err != nil {
slog.Warn("channel_monitor: user view batch timeline failed", "error", err)
return map[int64][]*ChannelMonitorHistoryEntry{}
}
return timelineMap
}
// pickLatest 从 latest 切片中挑出指定 model 对应项,未命中返回 nil。
func pickLatest(rows []*ChannelMonitorLatest, model string) *ChannelMonitorLatest {
if model == "" {
return nil
}
for _, r := range rows {
if r.Model == model {
return r
}
}
return nil
}
// GetUserDetail 用户只读视图:单个监控详情(每个模型 7d/15d/30d 可用率与平均延迟)。
// 不暴露 api_key。
func (s *ChannelMonitorService) GetUserDetail(ctx context.Context, id int64) (*UserMonitorDetail, error) {
m, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if !m.Enabled {
return nil, ErrChannelMonitorNotFound
}
latest, err := s.repo.ListLatestPerModel(ctx, id)
if err != nil {
return nil, fmt.Errorf("list latest per model: %w", err)
}
availMap, err := s.collectAvailabilityWindows(ctx, id)
if err != nil {
return nil, err
}
models := mergeModelDetails(m, latest, availMap)
return &UserMonitorDetail{
ID: m.ID,
Name: m.Name,
Provider: m.Provider,
GroupName: m.GroupName,
Models: models,
}, nil
}
// collectAvailabilityWindows 一次性查询 7/15/30 天三个窗口,按模型组织。
func (s *ChannelMonitorService) collectAvailabilityWindows(ctx context.Context, monitorID int64) (map[int]map[string]*ChannelMonitorAvailability, error) {
out := make(map[int]map[string]*ChannelMonitorAvailability, 3)
windows := []int{monitorAvailability7Days, monitorAvailability15Days, monitorAvailability30Days}
for _, w := range windows {
rows, err := s.repo.ComputeAvailability(ctx, monitorID, w)
if err != nil {
return nil, fmt.Errorf("compute availability %dd: %w", w, err)
}
out[w] = indexAvailabilityByModel(rows)
}
return out, nil
}
// ---------- 纯函数 helper无 IO可在 batch / 单 monitor / detail 路径复用)----------
// indexLatestByModel 把 latest 切片按 model 索引(小工具,避免在 hot path 重复写)。
func indexLatestByModel(rows []*ChannelMonitorLatest) map[string]*ChannelMonitorLatest {
m := make(map[string]*ChannelMonitorLatest, len(rows))
for _, r := range rows {
m[r.Model] = r
}
return m
}
// indexAvailabilityByModel 把 availability 切片按 model 索引。
func indexAvailabilityByModel(rows []*ChannelMonitorAvailability) map[string]*ChannelMonitorAvailability {
m := make(map[string]*ChannelMonitorAvailability, len(rows))
for _, r := range rows {
m[r.Model] = r
}
return m
}
// buildStatusSummary 由 latest + availability 字典构造 MonitorStatusSummary。
// 不做任何 IO纯组装便于在 batch 与单 monitor 路径复用。
func buildStatusSummary(
latestByModel map[string]*ChannelMonitorLatest,
availByModel map[string]*ChannelMonitorAvailability,
primary string,
extras []string,
) MonitorStatusSummary {
summary := MonitorStatusSummary{ExtraModels: make([]ExtraModelStatus, 0, len(extras))}
if primary != "" {
if l, ok := latestByModel[primary]; ok {
summary.PrimaryStatus = l.Status
summary.PrimaryLatencyMs = l.LatencyMs
}
if a, ok := availByModel[primary]; ok {
summary.Availability7d = a.AvailabilityPct
}
}
for _, model := range extras {
entry := ExtraModelStatus{Model: model}
if l, ok := latestByModel[model]; ok {
entry.Status = l.Status
entry.LatencyMs = l.LatencyMs
}
summary.ExtraModels = append(summary.ExtraModels, entry)
}
return summary
}
// buildUserViewFromSummary 用预聚合好的 MonitorStatusSummary + 主模型 latest + timeline 装填 UserMonitorView无 IO
// primaryLatest 可能为 nil该监控尚无历史timelineEntries 可能为空。
func buildUserViewFromSummary(
m *ChannelMonitor,
summary MonitorStatusSummary,
primaryLatest *ChannelMonitorLatest,
timelineEntries []*ChannelMonitorHistoryEntry,
) *UserMonitorView {
view := &UserMonitorView{
ID: m.ID,
Name: m.Name,
Provider: m.Provider,
GroupName: m.GroupName,
PrimaryModel: m.PrimaryModel,
PrimaryStatus: summary.PrimaryStatus,
PrimaryLatencyMs: summary.PrimaryLatencyMs,
Availability7d: summary.Availability7d,
ExtraModels: summary.ExtraModels,
Timeline: buildTimelinePoints(timelineEntries),
}
if primaryLatest != nil {
view.PrimaryPingLatencyMs = primaryLatest.PingLatencyMs
}
return view
}
// buildTimelinePoints 把 history entry 裁剪为 timeline 点(去除 message/ID/Model减小响应体
func buildTimelinePoints(entries []*ChannelMonitorHistoryEntry) []UserMonitorTimelinePoint {
out := make([]UserMonitorTimelinePoint, 0, len(entries))
for _, e := range entries {
out = append(out, UserMonitorTimelinePoint{
Status: e.Status,
LatencyMs: e.LatencyMs,
PingLatencyMs: e.PingLatencyMs,
CheckedAt: e.CheckedAt,
})
}
return out
}
// mergeModelDetails 合并 latest + availability 三个窗口为 ModelDetail 列表。
// 复用 indexLatestByModel避免在多处重复写 build map 逻辑。
func mergeModelDetails(
m *ChannelMonitor,
latest []*ChannelMonitorLatest,
availMap map[int]map[string]*ChannelMonitorAvailability,
) []ModelDetail {
all := append([]string{m.PrimaryModel}, m.ExtraModels...)
latestByModel := indexLatestByModel(latest)
out := make([]ModelDetail, 0, len(all))
for _, model := range all {
d := ModelDetail{Model: model}
if l, ok := latestByModel[model]; ok {
d.LatestStatus = l.Status
d.LatestLatencyMs = l.LatencyMs
}
if a, ok := availMap[monitorAvailability7Days][model]; ok {
d.Availability7d = a.AvailabilityPct
d.AvgLatency7dMs = a.AvgLatencyMs
}
if a, ok := availMap[monitorAvailability15Days][model]; ok {
d.Availability15d = a.AvailabilityPct
}
if a, ok := availMap[monitorAvailability30Days][model]; ok {
d.Availability30d = a.AvailabilityPct
}
out = append(out, d)
}
return out
}

View File

@ -0,0 +1,80 @@
package service
import (
"fmt"
"math/rand/v2"
"regexp"
"strconv"
)
// monitorChallengePromptTemplate 1:1 复刻 BingZi-233/check-cx 的 few-shot 模板。
const monitorChallengePromptTemplate = `Calculate and respond with ONLY the number, nothing else.
Q: 3 + 5 = ?
A: 8
Q: 12 - 7 = ?
A: 5
Q: %d %s %d = ?
A:`
// monitorChallengeNumberRegex 提取响应中的所有整数(含负号)。
var monitorChallengeNumberRegex = regexp.MustCompile(`-?\d+`)
// monitorChallenge 一次 challenge 的 prompt + 期望答案。
type monitorChallenge struct {
Prompt string
Expected string
}
// generateChallenge 生成一次随机算术 challenge
// - 随机两个 [monitorChallengeMin, monitorChallengeMax] 整数
// - 50% 加 / 50% 减;减法用 max - min 保证非负
// - 渲染 few-shot 模板
//
// 不强求加密随机math/rand/v2 足够分散,避免 crypto/rand 的开销。
func generateChallenge() monitorChallenge {
a := randIntInRange(monitorChallengeMin, monitorChallengeMax)
b := randIntInRange(monitorChallengeMin, monitorChallengeMax)
if rand.IntN(2) == 0 { //nolint:gosec // 仅用于生成测试问题,无安全影响
// 加法
return monitorChallenge{
Prompt: fmt.Sprintf(monitorChallengePromptTemplate, a, "+", b),
Expected: strconv.Itoa(a + b),
}
}
// 减法,保证非负
hi, lo := a, b
if lo > hi {
hi, lo = lo, hi
}
return monitorChallenge{
Prompt: fmt.Sprintf(monitorChallengePromptTemplate, hi, "-", lo),
Expected: strconv.Itoa(hi - lo),
}
}
// randIntInRange 返回 [min, max] 闭区间的随机整数。
func randIntInRange(minVal, maxVal int) int {
if maxVal <= minVal {
return minVal
}
return minVal + rand.IntN(maxVal-minVal+1) //nolint:gosec
}
// validateChallenge 在响应文本中查找 expected 整数答案,返回是否通过校验。
func validateChallenge(responseText, expected string) bool {
if responseText == "" || expected == "" {
return false
}
matches := monitorChallengeNumberRegex.FindAllString(responseText, -1)
for _, m := range matches {
if m == expected {
return true
}
}
return false
}

View File

@ -0,0 +1,443 @@
package service
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"regexp"
"strings"
"time"
"github.com/tidwall/gjson"
)
// monitorHTTPClient 共享一个 http.Client避免每次检测重建 transport。
// 自定义 Transport 在 dial 时强制再次校验 IP防止 DNS rebinding 绕过 validateEndpoint。
var monitorHTTPClient = newSSRFSafeHTTPClient(monitorRequestTimeout)
// monitorPingHTTPClient 用于 endpoint origin 的 HEAD ping超时更短。
var monitorPingHTTPClient = newSSRFSafeHTTPClient(monitorPingTimeout)
// newSSRFSafeHTTPClient 返回一个使用 safeDialContext 的 http.Client。
// 仅供监控模块对外发起请求使用——所有目标都应是公网 endpoint。
func newSSRFSafeHTTPClient(timeout time.Duration) *http.Client {
tr := &http.Transport{
DialContext: safeDialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 16,
IdleConnTimeout: monitorIdleConnTimeout,
TLSHandshakeTimeout: monitorTLSHandshakeTimeout,
ResponseHeaderTimeout: monitorResponseHeaderTimeout,
}
return &http.Client{Timeout: timeout, Transport: tr}
}
// CheckOptions 承载一次检测的自定义入参。
// 所有字段都是可选(零值即等价于"用默认行为")。
type CheckOptions struct {
// ExtraHeaders 用户自定义 HTTP 头merge 到 adapter 默认 headers用户优先
ExtraHeaders map[string]string
// BodyOverrideMode: off | merge | replace
BodyOverrideMode string
// BodyOverride 在 merge 模式下做浅合并key 命中黑名单时静默丢弃),
// 在 replace 模式下直接当作完整 body。
BodyOverride map[string]any
}
// runCheckForModel 对单个 (provider, model) 做一次完整检测。
// 不返回 error所有失败都包装进 CheckResult.Status=error/failed。
//
// opts 承载模板 / 监控快照带来的自定义配置。nil 等同于 "off + 无 extra headers"。
func runCheckForModel(ctx context.Context, provider, endpoint, apiKey, model string, opts *CheckOptions) *CheckResult {
res := &CheckResult{
Model: model,
Status: MonitorStatusError,
CheckedAt: time.Now(),
}
challenge := generateChallenge()
mode := bodyOverrideMode(opts)
start := time.Now()
respText, rawBody, statusCode, err := callProvider(ctx, provider, endpoint, apiKey, model, challenge.Prompt, opts)
latency := time.Since(start)
latencyMs := int(latency / time.Millisecond)
res.LatencyMs = &latencyMs
if err != nil {
res.Status = MonitorStatusError
res.Message = truncateMessage(sanitizeErrorMessage(err.Error()))
return res
}
if statusCode < 200 || statusCode >= 300 {
// 错误路径:用 rawBody 而非 respTextgjson textPath 抽取在错误响应里通常为空,
// 会丢掉真正的上游错误信息,例如 `{"error":{"message":"No available accounts ..."}}`)。
res.Status = MonitorStatusError
bodySnippet := truncateForErrorBody(rawBody)
res.Message = truncateMessage(sanitizeErrorMessage(fmt.Sprintf("upstream HTTP %d: %s", statusCode, bodySnippet)))
return res
}
// Replace 模式:跳过 challenge 校验(用户 body 是静态的challenge 没法嵌入)。
// 改用「HTTP 2xx + 响应文本adapter.textPath 抽取)非空」作为 operational 判定。
// 响应文本为空则降级为 failed视为上游回了 200 但没实际内容)。
if mode == MonitorBodyOverrideModeReplace {
if strings.TrimSpace(respText) == "" {
res.Status = MonitorStatusFailed
res.Message = truncateMessage("replace-mode: upstream returned 2xx with empty text")
return res
}
return finalizeOperationalOrDegraded(res, latency, latencyMs)
}
if !validateChallenge(respText, challenge.Expected) {
res.Status = MonitorStatusFailed
res.Message = truncateMessage(sanitizeErrorMessage(fmt.Sprintf("challenge mismatch (expected %s, got %q)", challenge.Expected, respText)))
return res
}
return finalizeOperationalOrDegraded(res, latency, latencyMs)
}
// finalizeOperationalOrDegraded 负责走到最后一步的 operational/degraded 判定。
// 拆出来是为了让 runCheckForModel 不超过 30 行。
func finalizeOperationalOrDegraded(res *CheckResult, latency time.Duration, latencyMs int) *CheckResult {
if latency >= monitorDegradedThreshold {
res.Status = MonitorStatusDegraded
res.Message = truncateMessage(fmt.Sprintf("slow response: %dms", latencyMs))
return res
}
res.Status = MonitorStatusOperational
return res
}
// bodyOverrideMode 归一取 opts.BodyOverrideModenil opts / 空串都视为 off。
func bodyOverrideMode(opts *CheckOptions) string {
if opts == nil || opts.BodyOverrideMode == "" {
return MonitorBodyOverrideModeOff
}
return opts.BodyOverrideMode
}
// pingEndpointOrigin 对 endpoint 的 origin (scheme://host) 发起 HEAD 请求,返回耗时。
// 失败时返回 nil不影响主状态判定
func pingEndpointOrigin(ctx context.Context, endpoint string) *int {
origin, err := extractOrigin(endpoint)
if err != nil || origin == "" {
return nil
}
req, err := http.NewRequestWithContext(ctx, http.MethodHead, origin, nil)
if err != nil {
return nil
}
start := time.Now()
resp, err := monitorPingHTTPClient.Do(req)
if err != nil {
return nil
}
defer func() { _ = resp.Body.Close() }()
_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, monitorPingDiscardMaxBytes))
ms := int(time.Since(start) / time.Millisecond)
return &ms
}
// providerAdapter 描述某个 provider 在 challenge 检测中需要的 4 件事:
// - 拼出请求路径(含 model 占位)
// - 序列化请求体
// - 构造鉴权头
// - 从响应 JSON 中按 path 提取文本gjson path
//
// 加新 provider 只需要在 providerAdapters 里增加一个条目,无需触碰 callProvider / validateProvider。
type providerAdapter struct {
buildPath func(model string) string
buildBody func(model, prompt string) ([]byte, error)
buildHeaders func(apiKey string) map[string]string
textPath string // gjson 提取响应文本的 path
}
// providerAdapters 全部已支持的 provider。键值即 MonitorProvider* 字符串。
//
//nolint:gochecknoglobals // 适配器表是只读静态数据,初始化后不变更。
var providerAdapters = map[string]providerAdapter{
MonitorProviderOpenAI: {
buildPath: func(string) string { return providerOpenAIPath },
buildBody: func(model, prompt string) ([]byte, error) {
return json.Marshal(map[string]any{
"model": model,
"messages": []map[string]string{{"role": "user", "content": prompt}},
"max_tokens": monitorChallengeMaxTokens,
"stream": false,
})
},
buildHeaders: func(apiKey string) map[string]string {
return map[string]string{"Authorization": "Bearer " + apiKey}
},
textPath: "choices.0.message.content",
},
MonitorProviderAnthropic: {
buildPath: func(string) string { return providerAnthropicPath },
buildBody: func(model, prompt string) ([]byte, error) {
return json.Marshal(map[string]any{
"model": model,
"messages": []map[string]string{{"role": "user", "content": prompt}},
"max_tokens": monitorChallengeMaxTokens,
})
},
buildHeaders: func(apiKey string) map[string]string {
return map[string]string{
"x-api-key": apiKey,
"anthropic-version": monitorAnthropicAPIVersion,
}
},
textPath: "content.0.text",
},
MonitorProviderGemini: {
// Gemini 把 model 名写在 URL path 上:/v1beta/models/{model}:generateContent
buildPath: func(model string) string { return fmt.Sprintf(providerGeminiPathTemplate, model) },
buildBody: func(_, prompt string) ([]byte, error) {
return json.Marshal(map[string]any{
"contents": []map[string]any{
{"parts": []map[string]any{{"text": prompt}}},
},
"generationConfig": map[string]any{"maxOutputTokens": monitorChallengeMaxTokens},
})
},
// 使用 x-goog-api-key header 而不是 ?key= query避免 *url.Error 把 key 回填到错误日志。
buildHeaders: func(apiKey string) map[string]string {
return map[string]string{"x-goog-api-key": apiKey}
},
textPath: "candidates.0.content.parts.0.text",
},
}
// isSupportedProvider 校验 provider 字符串是否在 adapter 表中。
// 供 validate.go 的 validateProvider 复用,避免两份 switch 漂移。
func isSupportedProvider(p string) bool {
_, ok := providerAdapters[p]
return ok
}
// callProvider 通过 providerAdapters 分发到具体实现。
// opts 承载用户的自定义 headers / body 覆盖(可为 nil
//
// 返回值:
// - extractedText: 按 textPath 抽出的成功文本,仅在 status 2xx 时有意义;非 2xx 时通常为空串
// - rawBody: 完整响应体的字符串形式(已被 monitorResponseMaxBytes 截断),用于错误路径保留上游真实回包
// - status: HTTP 状态码
// - err: 网络 / 序列化错误
func callProvider(ctx context.Context, provider, endpoint, apiKey, model, prompt string, opts *CheckOptions) (extractedText, rawBody string, status int, err error) {
adapter, ok := providerAdapters[provider]
if !ok {
return "", "", 0, fmt.Errorf("unsupported provider %q", provider)
}
body, err := buildRequestBody(adapter, provider, model, prompt, opts)
if err != nil {
return "", "", 0, err
}
headers := mergeHeaders(adapter.buildHeaders(apiKey), opts)
full := joinURL(endpoint, adapter.buildPath(model))
respBytes, status, err := postRawJSON(ctx, full, body, headers)
if err != nil {
return "", "", status, err
}
return gjson.GetBytes(respBytes, adapter.textPath).String(), string(respBytes), status, nil
}
// mergeHeaders 把用户自定义 headers 合并到 adapter 默认 headers 上。
// 用户值覆盖默认命中黑名单hop-by-hop / 由 http.Client 自管的)的 key 静默丢弃。
func mergeHeaders(base map[string]string, opts *CheckOptions) map[string]string {
if opts == nil || len(opts.ExtraHeaders) == 0 {
return base
}
out := make(map[string]string, len(base)+len(opts.ExtraHeaders))
for k, v := range base {
out[k] = v
}
for k, v := range opts.ExtraHeaders {
if IsForbiddenHeaderName(k) {
continue
}
out[k] = v
}
return out
}
// buildRequestBody 根据 body_override_mode 构造请求 body。
//
// - off: adapter 默认 body
// - merge: adapter 默认 body 与 BodyOverride 浅合并BodyOverride 中命中
// bodyMergeKeyDenyList[provider] 的 key 会被静默丢弃,避免破坏 challenge / model 路由
// - replace: 直接 marshal BodyOverride 作为完整 body
//
// 任何 mode 返回的 []byte 都已经是合法 JSON可直接送入 postRawJSON。
func buildRequestBody(adapter providerAdapter, provider, model, prompt string, opts *CheckOptions) ([]byte, error) {
mode := bodyOverrideMode(opts)
if mode == MonitorBodyOverrideModeReplace {
if opts == nil || len(opts.BodyOverride) == 0 {
return nil, fmt.Errorf("replace mode: body_override is empty")
}
body, err := json.Marshal(opts.BodyOverride)
if err != nil {
return nil, fmt.Errorf("marshal body_override (replace): %w", err)
}
return body, nil
}
defaultBody, err := adapter.buildBody(model, prompt)
if err != nil {
return nil, fmt.Errorf("marshal default body: %w", err)
}
if mode != MonitorBodyOverrideModeMerge || opts == nil || len(opts.BodyOverride) == 0 {
return defaultBody, nil
}
var defaultMap map[string]any
if err := json.Unmarshal(defaultBody, &defaultMap); err != nil {
return nil, fmt.Errorf("unmarshal default body for merge: %w", err)
}
deny := bodyMergeKeyDenyList[provider]
for k, v := range opts.BodyOverride {
if deny[k] {
continue
}
defaultMap[k] = v
}
merged, err := json.Marshal(defaultMap)
if err != nil {
return nil, fmt.Errorf("marshal merged body: %w", err)
}
return merged, nil
}
// bodyMergeKeyDenyList 在 merge 模式下,禁止用户覆盖这些 provider-specific 的关键字段。
// 思路抄 check-cx 的 EXCLUDED_METADATA_KEYS保护 challenge / model 路由不被用户误伤。
// 用户想动这些字段就用 replace 模式(已知会跳 challenge 校验)。
//
//nolint:gochecknoglobals // 静态查表,初始化后不变。
var bodyMergeKeyDenyList = map[string]map[string]bool{
MonitorProviderOpenAI: {"model": true, "messages": true, "stream": true},
MonitorProviderAnthropic: {"model": true, "messages": true},
MonitorProviderGemini: {"contents": true},
}
// postRawJSON 发送 POST + 已序列化好的 JSON 字节限制响应体大小返回响应字节、HTTP status、错误。
// adapter 自行 marshal 是为了精确控制字段顺序与类型,所以这里直接收 []byte 而不是 any。
func postRawJSON(ctx context.Context, fullURL string, payload []byte, headers map[string]string) ([]byte, int, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullURL, bytes.NewReader(payload))
if err != nil {
return nil, 0, fmt.Errorf("build request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := monitorHTTPClient.Do(req)
if err != nil {
return nil, 0, fmt.Errorf("do request: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBody, err := io.ReadAll(io.LimitReader(resp.Body, monitorResponseMaxBytes))
if err != nil {
return nil, resp.StatusCode, fmt.Errorf("read body: %w", err)
}
return respBody, resp.StatusCode, nil
}
// joinURL 把 base origin 与 path 拼成完整 URL。
// 容忍 base 末尾有/无斜杠path 必带前导斜杠。
func joinURL(base, path string) string {
base = strings.TrimRight(base, "/")
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
return base + path
}
// extractOrigin 从一个 endpoint URL 中提取 scheme://host[:port] 部分。
func extractOrigin(endpoint string) (string, error) {
u, err := url.Parse(endpoint)
if err != nil {
return "", err
}
if u.Scheme == "" || u.Host == "" {
return "", errors.New("endpoint missing scheme or host")
}
return u.Scheme + "://" + u.Host, nil
}
// monitorSensitiveQueryParamRegex 匹配 URL query 中可能泄露凭证的参数:
// key / api_key / api-key / access_token / token / authorization / x-api-key。
// 大小写不敏感,匹配 `?name=value` 或 `&name=value` 形式value 截到 & 或字符串末尾)。
var monitorSensitiveQueryParamRegex = regexp.MustCompile(`(?i)([?&](?:key|api[_-]?key|access[_-]?token|token|authorization|x-api-key)=)[^&\s"']+`)
// monitorAPIKeyPatterns 匹配常见 provider 的 API key 字面量。
// 顺序敏感sk-ant- 必须放在 sk- 之前,否则会被通用 sk- 模式先消费。
var monitorAPIKeyPatterns = []struct {
pattern *regexp.Regexp
replace string
}{
// Anthropic带前缀必须先匹配sk-ant-xxxxxxx
{regexp.MustCompile(`sk-ant-[A-Za-z0-9_-]{20,}`), "sk-ant-***REDACTED***"},
// OpenAI / Anthropic 通用 sk-: sk-xxxxxxx
{regexp.MustCompile(`sk-[A-Za-z0-9-]{20,}`), "sk-***REDACTED***"},
// Gemini / Google API Key固定前缀 + 35 位
{regexp.MustCompile(`AIza[A-Za-z0-9_-]{35}`), "AIza***REDACTED***"},
// JWT 三段式Bearer 后常出现eyJxxx.eyJxxx.signature
{regexp.MustCompile(`eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}`), "eyJ***REDACTED.JWT***"},
}
// sanitizeErrorMessage 擦除错误/响应文本中可能泄露的 API key。
// 处理两类来源:
// 1. URL query 中的 ?key= / ?api_key= 等Go *url.Error 会回填完整 URL
// 2. 上游 HTTP body 文本里直接出现的 sk-* / AIza* / JWT 等密钥碎片
//
// 注意:与 gemini_messages_compat_service.go 的 sanitizeUpstreamErrorMessage 关注点类似但参数集更广,
// 监控模块独立维护,避免互相耦合。
func sanitizeErrorMessage(msg string) string {
if msg == "" {
return msg
}
msg = monitorSensitiveQueryParamRegex.ReplaceAllString(msg, `${1}REDACTED`)
for _, p := range monitorAPIKeyPatterns {
msg = p.pattern.ReplaceAllString(msg, p.replace)
}
return msg
}
// truncateMessage 把消息按 monitorMessageMaxBytes 截断,避免 DB 列溢出与日志过长。
func truncateMessage(msg string) string {
if len(msg) <= monitorMessageMaxBytes {
return msg
}
const ellipsis = "...(truncated)"
cutoff := monitorMessageMaxBytes - len(ellipsis)
if cutoff < 0 {
cutoff = 0
}
return msg[:cutoff] + ellipsis
}
// truncateForErrorBody 把上游错误响应 body 压到 monitorErrorBodySnippetMaxBytes 以内,
// 并顺手把连续空白折成一个空格:上游 HTML 错误页常含大量缩进/换行,保留会浪费预算。
// 被 truncateMessage 做最终总截断兜底,所以这里只负责 body 自身的精简。
func truncateForErrorBody(body string) string {
body = strings.Join(strings.Fields(body), " ")
if len(body) <= monitorErrorBodySnippetMaxBytes {
return body
}
const ellipsis = "...(body truncated)"
cutoff := monitorErrorBodySnippetMaxBytes - len(ellipsis)
if cutoff < 0 {
cutoff = 0
}
return body[:cutoff] + ellipsis
}

View File

@ -0,0 +1,173 @@
//go:build unit
package service
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
// swapMonitorHTTPClient 临时替换 monitorHTTPClient 为不带 SSRF 校验的普通 client
// 让 httptest (127.0.0.1) 能连通。测试结束后恢复。
func swapMonitorHTTPClient(t *testing.T) {
t.Helper()
orig := monitorHTTPClient
monitorHTTPClient = &http.Client{Timeout: 5 * time.Second}
t.Cleanup(func() { monitorHTTPClient = orig })
}
// captureHandler 把每次收到的请求 body 和 headers 存起来,测试断言用。
type captureHandler struct {
lastBody map[string]any
lastHeaders http.Header
respondText string // 写到 Anthropic content[0].text 里(校验用)
status int
}
func (h *captureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.lastHeaders = r.Header.Clone()
defer func() { _ = r.Body.Close() }()
var parsed map[string]any
_ = json.NewDecoder(r.Body).Decode(&parsed)
h.lastBody = parsed
if h.status == 0 {
h.status = 200
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(h.status)
// 构造 Anthropic 格式的响应content[0].text = h.respondText
_ = json.NewEncoder(w).Encode(map[string]any{
"content": []map[string]any{
{"type": "text", "text": h.respondText},
},
})
}
func setupFakeAnthropic(t *testing.T, handler *captureHandler) string {
t.Helper()
swapMonitorHTTPClient(t)
srv := httptest.NewServer(handler)
t.Cleanup(srv.Close)
return srv.URL
}
func TestRunCheckForModel_OffMode_PreservesDefaultBody(t *testing.T) {
h := &captureHandler{respondText: "the answer is 42"}
endpoint := setupFakeAnthropic(t, h)
// 跑一次 off 模式opts=nil确认默认 body 行为未变
_ = runCheckForModel(context.Background(), MonitorProviderAnthropic, endpoint, "sk-fake", "claude-x", nil)
if h.lastBody["model"] != "claude-x" {
t.Errorf("default body should contain model=claude-x, got %v", h.lastBody["model"])
}
if _, ok := h.lastBody["messages"]; !ok {
t.Error("default body should contain messages")
}
if h.lastHeaders.Get("x-api-key") != "sk-fake" {
t.Errorf("expected adapter's x-api-key header, got %q", h.lastHeaders.Get("x-api-key"))
}
}
func TestRunCheckForModel_MergeMode_UserFieldsWinButDenyListProtects(t *testing.T) {
h := &captureHandler{respondText: "the answer is 42"}
endpoint := setupFakeAnthropic(t, h)
opts := &CheckOptions{
BodyOverrideMode: MonitorBodyOverrideModeMerge,
BodyOverride: map[string]any{
"system": "You are Claude Code...",
"max_tokens": float64(999), // 应该覆盖默认 50
"model": "hacked-model", // 应该被黑名单挡住,保留原 model
"messages": []any{}, // 同上,被挡
},
ExtraHeaders: map[string]string{
"User-Agent": "claude-cli/1.0",
"Content-Length": "999", // 黑名单
"x-custom": "ok",
},
}
_ = runCheckForModel(context.Background(), MonitorProviderAnthropic, endpoint, "sk-fake", "claude-x", opts)
if h.lastBody["system"] != "You are Claude Code..." {
t.Errorf("merge mode should inject system, got %v", h.lastBody["system"])
}
// max_tokens 覆盖生效
if mt, ok := h.lastBody["max_tokens"].(float64); !ok || mt != 999 {
t.Errorf("merge mode should override max_tokens to 999, got %v", h.lastBody["max_tokens"])
}
// model 在黑名单 — 应该保留默认值
if h.lastBody["model"] != "claude-x" {
t.Errorf("model should be protected by deny list, got %v", h.lastBody["model"])
}
// messages 在黑名单 — 应该保留默认值(非空)
msgs, _ := h.lastBody["messages"].([]any)
if len(msgs) == 0 {
t.Error("messages should be protected by deny list (kept default, non-empty)")
}
// header 合并
if h.lastHeaders.Get("User-Agent") != "claude-cli/1.0" {
t.Errorf("extra User-Agent should override, got %q", h.lastHeaders.Get("User-Agent"))
}
if h.lastHeaders.Get("x-custom") != "ok" {
t.Errorf("extra custom header should be present, got %q", h.lastHeaders.Get("x-custom"))
}
// Content-Length 黑名单:会被 net/http 自动重算,但不应由用户的 "999" 决定。
// 我们无法直接断言丢弃http.Client 总会填上),只断言请求成功即可。
}
func TestRunCheckForModel_ReplaceMode_FullBodyUsedAndChallengeSkipped(t *testing.T) {
// replace 模式下我们的 body 完全自定义challenge 数学题不会出现在请求里,
// 上游也不会回正确答案 — 但只要 2xx + 响应文本非空,就算 operational
h := &captureHandler{respondText: "any non-empty text"}
endpoint := setupFakeAnthropic(t, h)
userBody := map[string]any{
"model": "user-forced-model",
"messages": []any{map[string]any{"role": "user", "content": "hi"}},
"max_tokens": float64(10),
"system": "You are someone else",
}
opts := &CheckOptions{
BodyOverrideMode: MonitorBodyOverrideModeReplace,
BodyOverride: userBody,
}
res := runCheckForModel(context.Background(), MonitorProviderAnthropic, endpoint, "sk-fake", "claude-x", opts)
// 请求 body = 用户提供的原样
if h.lastBody["model"] != "user-forced-model" {
t.Errorf("replace mode should use user's model, got %v", h.lastBody["model"])
}
if h.lastBody["system"] != "You are someone else" {
t.Errorf("replace mode should use user's system, got %v", h.lastBody["system"])
}
// challenge 虽然没命中,但由于 replace 模式跳过 challenge 校验 + 响应非空 → operational
if res.Status != MonitorStatusOperational {
t.Errorf("replace mode with 2xx + non-empty text should be operational, got status=%s message=%q",
res.Status, res.Message)
}
}
func TestRunCheckForModel_ReplaceMode_EmptyResponseIsFailed(t *testing.T) {
h := &captureHandler{respondText: ""} // 上游 200 但 content[0].text 为空
endpoint := setupFakeAnthropic(t, h)
opts := &CheckOptions{
BodyOverrideMode: MonitorBodyOverrideModeReplace,
BodyOverride: map[string]any{"model": "x", "messages": []any{}},
}
res := runCheckForModel(context.Background(), MonitorProviderAnthropic, endpoint, "sk-fake", "claude-x", opts)
if res.Status != MonitorStatusFailed {
t.Errorf("replace mode with empty text should be failed, got status=%s", res.Status)
}
if !strings.Contains(res.Message, "replace-mode") {
t.Errorf("failure message should hint replace-mode, got %q", res.Message)
}
}

View File

@ -0,0 +1,142 @@
package service
import (
"time"
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
)
// ChannelMonitor 全局常量。
// 这些是 MVP 阶段的硬编码值,按需可以提到 config 中。
const (
// monitorRequestTimeout 单次模型请求总超时(含 Body 读取)。
monitorRequestTimeout = 45 * time.Second
// monitorPingTimeout HEAD 请求 endpoint origin 的超时。
monitorPingTimeout = 8 * time.Second
// monitorDegradedThreshold 主请求成功但耗时超过该阈值视为 degraded。
monitorDegradedThreshold = 6 * time.Second
// monitorHistoryRetentionDays 明细历史保留天数。
// 60s 默认间隔 * 30 天 ≈ 43200 行/monitor/model一般部署总量 <= 2M 行,
// PG 无压力;所以直接保留完整明细一个月,可用率查询可以全走原始行不依赖聚合。
// 聚合表 channel_monitor_daily_rollups 仍然保留,作为长期历史回填/降级查询的兜底。
monitorHistoryRetentionDays = 30
// monitorRollupRetentionDays 日聚合保留天数。
// 日聚合行由 RunDailyMaintenance 在超过该窗口后软删。
monitorRollupRetentionDays = 30
// monitorMaintenanceMaxDaysPerRun 单次维护任务最多聚合的天数。
// 用于限制首次上线回填30 天)+ 少量余量,避免长事务。
monitorMaintenanceMaxDaysPerRun = 35
// monitorWorkerConcurrency 调度器并发执行的监控数pond 池容量)。
monitorWorkerConcurrency = 5
// monitorStartupLoadTimeout Start 时一次性加载所有 enabled monitor 的总超时。
monitorStartupLoadTimeout = 10 * time.Second
// monitorMinIntervalSeconds / monitorMaxIntervalSeconds 用户配置的检测间隔上下限。
monitorMinIntervalSeconds = 15
monitorMaxIntervalSeconds = 3600
// monitorMessageMaxBytes message 字段最大字节数(与 schema/migration 一致)。
monitorMessageMaxBytes = 500
// monitorResponseMaxBytes 单次模型响应最大读取字节,防止 OOM。
monitorResponseMaxBytes = 64 * 1024
// monitorErrorBodySnippetMaxBytes 非 2xx 响应时保留上游 body 片段的最大字节数。
// 留 300 字节足够覆盖典型结构化错误(如 `{"error":{"message":"..."}}`
// 又给 "upstream HTTP <status>: " 前缀留出余量,避免最终被 monitorMessageMaxBytes (500) 截得太狠。
monitorErrorBodySnippetMaxBytes = 300
// monitorChallengeMin / monitorChallengeMax challenge 操作数范围。
monitorChallengeMin = 1
monitorChallengeMax = 50
// providerOpenAIPath OpenAI Chat Completions 路径。
providerOpenAIPath = "/v1/chat/completions"
// providerAnthropicPath Anthropic Messages 路径。
providerAnthropicPath = "/v1/messages"
// providerGeminiPathTemplate Gemini generateContent 路径模板(含 model 占位)。
providerGeminiPathTemplate = "/v1beta/models/%s:generateContent"
// MonitorProviderOpenAI / Anthropic / Gemini provider 字符串常量(也是 ent enum 的实际值)。
MonitorProviderOpenAI = "openai"
MonitorProviderAnthropic = "anthropic"
MonitorProviderGemini = "gemini"
// MonitorStatusOperational 等监控状态字符串常量(与 ent enum 一致)。
MonitorStatusOperational = "operational"
MonitorStatusDegraded = "degraded"
MonitorStatusFailed = "failed"
MonitorStatusError = "error"
// monitorAvailability7Days / 15 / 30 用于聚合查询窗口。
monitorAvailability7Days = 7
monitorAvailability15Days = 15
monitorAvailability30Days = 30
// MonitorHistoryDefaultLimit 历史查询默认返回条数handler 层共享)。
MonitorHistoryDefaultLimit = 100
// MonitorHistoryMaxLimit 历史查询最大返回条数handler 层共享)。
MonitorHistoryMaxLimit = 1000
// monitorTimelineMaxPoints 用户视图 timeline 每个监控最多返回的历史点数。
monitorTimelineMaxPoints = 60
// monitorEndpointResolveTimeout validateEndpoint 解析 hostname 的最长耗时。
monitorEndpointResolveTimeout = 5 * time.Second
// ---- checker / runner 行为参数(消除 magic 值)----
// monitorAnthropicAPIVersion Anthropic Messages API 版本头。
monitorAnthropicAPIVersion = "2023-06-01"
// monitorChallengeMaxTokens 单次 challenge 请求的 max_tokens足够回答个位数算术
monitorChallengeMaxTokens = 50
// monitorRunOneBuffer runOne 的总超时缓冲(除请求超时与 ping 超时外的额外裕量)。
monitorRunOneBuffer = 10 * time.Second
// monitorIdleConnTimeout HTTP transport 空闲连接关闭超时。
monitorIdleConnTimeout = 30 * time.Second
// monitorTLSHandshakeTimeout HTTP transport TLS 握手超时。
monitorTLSHandshakeTimeout = 10 * time.Second
// monitorResponseHeaderTimeout HTTP transport 等待响应头超时。
monitorResponseHeaderTimeout = 30 * time.Second
// monitorPingDiscardMaxBytes ping 时丢弃响应体的最大字节数。
monitorPingDiscardMaxBytes = 1024
// monitorDialTimeout 自定义 dialer 单次连接超时。
monitorDialTimeout = 10 * time.Second
// monitorDialKeepAlive 自定义 dialer keep-alive 间隔。
monitorDialKeepAlive = 30 * time.Second
)
// 业务错误(统一在此声明,避免散落)。
var (
ErrChannelMonitorNotFound = infraerrors.NotFound(
"CHANNEL_MONITOR_NOT_FOUND", "channel monitor not found",
)
ErrChannelMonitorInvalidProvider = infraerrors.BadRequest(
"CHANNEL_MONITOR_INVALID_PROVIDER", "provider must be one of openai/anthropic/gemini",
)
ErrChannelMonitorInvalidInterval = infraerrors.BadRequest(
"CHANNEL_MONITOR_INVALID_INTERVAL", "interval_seconds must be in [15, 3600]",
)
ErrChannelMonitorInvalidEndpoint = infraerrors.BadRequest(
"CHANNEL_MONITOR_INVALID_ENDPOINT", "endpoint must be a valid https URL",
)
ErrChannelMonitorEndpointScheme = infraerrors.BadRequest(
"CHANNEL_MONITOR_ENDPOINT_SCHEME", "endpoint must use https scheme",
)
ErrChannelMonitorEndpointPath = infraerrors.BadRequest(
"CHANNEL_MONITOR_ENDPOINT_PATH", "endpoint must be base origin only (no path/query/fragment)",
)
ErrChannelMonitorEndpointPrivate = infraerrors.BadRequest(
"CHANNEL_MONITOR_ENDPOINT_PRIVATE", "endpoint must be a public host",
)
ErrChannelMonitorEndpointUnreachable = infraerrors.BadRequest(
"CHANNEL_MONITOR_ENDPOINT_UNREACHABLE", "endpoint hostname could not be resolved",
)
ErrChannelMonitorMissingAPIKey = infraerrors.BadRequest(
"CHANNEL_MONITOR_MISSING_API_KEY", "api_key is required when creating a monitor",
)
ErrChannelMonitorMissingPrimaryModel = infraerrors.BadRequest(
"CHANNEL_MONITOR_MISSING_PRIMARY_MODEL", "primary_model is required",
)
ErrChannelMonitorAPIKeyDecryptFailed = infraerrors.InternalServer(
"CHANNEL_MONITOR_KEY_DECRYPT_FAILED", "api key decryption failed; please re-edit the monitor with a fresh key",
)
)

View File

@ -0,0 +1,291 @@
package service
import (
"context"
"log/slog"
"sync"
"time"
"github.com/alitto/pond/v2"
)
// MonitorScheduler 调度器接口,供 ChannelMonitorService 在 CRUD 时回调,
// 用 setter 注入避免 service ↔ runner 的 wire 依赖环。
type MonitorScheduler interface {
// Schedule 为指定监控创建(或重置)独立定时任务。
// 当 m.Enabled=false 时等同于 Unschedule(m.ID)。
Schedule(m *ChannelMonitor)
// Unschedule 取消指定监控的定时任务(若存在)。
Unschedule(id int64)
}
// monitorRunnerSvc 抽出 runner 实际依赖的两个 service 方法:
// - 启动时加载 enabled monitor
// - 每次 ticker 触发执行检测
//
// 用接口而非 *ChannelMonitorService 是为了让 runner 单元测试可注入轻量 stub
// 避免依赖完整的 repo + encryptor 链路。生产实现 *ChannelMonitorService 自然满足。
type monitorRunnerSvc interface {
ListEnabledMonitors(ctx context.Context) ([]*ChannelMonitor, error)
RunCheck(ctx context.Context, id int64) ([]*CheckResult, error)
}
// ChannelMonitorRunner 渠道监控调度器。
//
// 设计:
// - 每个 enabled monitor 对应一个独立 goroutine + ticker按各自 IntervalSeconds
// - Start 时一次性加载所有 enabled monitor 并为每个建立任务
// - Service 在 Create/Update/Delete 后通过 MonitorScheduler 接口回调,
// 即时重建/取消对应任务(无需轮询 DB
// - 实际 HTTP 检测交给 pond 池(容量 monitorWorkerConcurrency
// 防止突发并发拖垮上游
//
// 历史清理与日聚合维护由 OpsCleanupService 的 cron 触发
// ChannelMonitorService.RunDailyMaintenance复用 leader lock + heartbeat
// 不在 runner 职责内。
type ChannelMonitorRunner struct {
svc monitorRunnerSvc
settingService *SettingService
pool pond.Pool
parentCtx context.Context
parentCancel context.CancelFunc
mu sync.Mutex
tasks map[int64]*scheduledMonitor
wg sync.WaitGroup
started bool
stopped bool
// inFlight 跟踪正在执行的 monitor.ID。fire 调度前会检查避免重复提交,
// 防止单次检测耗时 > interval 时同一 monitor 被并发执行。
inFlight map[int64]struct{}
inFlightMu sync.Mutex
}
// scheduledMonitor 单个监控的运行时上下文。
type scheduledMonitor struct {
id int64
name string
interval time.Duration
cancel context.CancelFunc
}
// NewChannelMonitorRunner 构造调度器。Start 在 wire 中调用一次。
// settingService 用于在每次 fire 前读取功能开关;传 nil 时视为总是启用(兼容测试)。
//
// pool 在构造时即建好:避免 Start 在 mu 内赋值、fire/Stop 在 mu 外读取的竞态隐患,
// 且 pond.NewPool 创建本身近似零开销,提前建池不会浪费资源。
func NewChannelMonitorRunner(svc *ChannelMonitorService, settingService *SettingService) *ChannelMonitorRunner {
return newChannelMonitorRunner(svc, settingService)
}
// newChannelMonitorRunner 内部构造,接受最小化接口,便于单元测试注入 stub。
func newChannelMonitorRunner(svc monitorRunnerSvc, settingService *SettingService) *ChannelMonitorRunner {
ctx, cancel := context.WithCancel(context.Background())
return &ChannelMonitorRunner{
svc: svc,
settingService: settingService,
pool: pond.NewPool(monitorWorkerConcurrency),
parentCtx: ctx,
parentCancel: cancel,
tasks: make(map[int64]*scheduledMonitor),
inFlight: make(map[int64]struct{}),
}
}
// Start 加载所有 enabled monitor 并为每个建立独立定时任务。
// 调用方需保证只调一次wire ProvideChannelMonitorRunner 内只调一次)。
func (r *ChannelMonitorRunner) Start() {
if r == nil || r.svc == nil {
return
}
r.mu.Lock()
if r.started || r.stopped {
r.mu.Unlock()
return
}
r.started = true
r.mu.Unlock()
ctx, cancel := context.WithTimeout(context.Background(), monitorStartupLoadTimeout)
defer cancel()
enabled, err := r.svc.ListEnabledMonitors(ctx)
if err != nil {
slog.Error("channel_monitor: load enabled monitors failed at startup", "error", err)
return
}
for _, m := range enabled {
r.Schedule(m)
}
slog.Info("channel_monitor: runner started", "scheduled_tasks", len(enabled))
}
// Schedule 为指定监控创建(或重置)独立定时任务。
// - m.Enabled=false → 等同于 Unschedule(m.ID)
// - 已存在的任务会先被取消再重建(适用于 IntervalSeconds 变更场景)
// - 新任务立即触发首次检测,之后按 IntervalSeconds 周期触发
func (r *ChannelMonitorRunner) Schedule(m *ChannelMonitor) {
if r == nil || m == nil {
return
}
if !m.Enabled {
r.Unschedule(m.ID)
return
}
interval := time.Duration(m.IntervalSeconds) * time.Second
if interval <= 0 {
// Create/Update 已通过 validateInterval 校验区间,正常路径不可能到这里。
// 真触发说明数据库中存在违反约束的数据或校验链路有 bug记 Error 暴露问题。
slog.Error("channel_monitor: skip schedule for invalid interval",
"monitor_id", m.ID, "interval_seconds", m.IntervalSeconds)
return
}
r.mu.Lock()
if r.stopped {
r.mu.Unlock()
return
}
if !r.started {
// Start 之前调用 Schedule 通常意味着 wire 顺序错乱:
// 当前 wire 顺序是 SetScheduler → StartCRUD 钩子最早也只能在请求到达时触发,
// 此时 Start 早已完成。出现此分支时把 monitor 信息打出来便于排查,
// 不入队、不缓存——交给运维通过重启或修复 wire 解决。
r.mu.Unlock()
slog.Warn("channel_monitor: schedule before runner started, skip",
"monitor_id", m.ID, "name", m.Name)
return
}
if existing, ok := r.tasks[m.ID]; ok {
existing.cancel()
}
ctx, cancel := context.WithCancel(r.parentCtx)
task := &scheduledMonitor{
id: m.ID,
name: m.Name,
interval: interval,
cancel: cancel,
}
r.tasks[m.ID] = task
r.wg.Add(1)
r.mu.Unlock()
go r.runScheduled(ctx, task)
}
// Unschedule 取消指定监控的定时任务(若存在)。
// 已经在执行中的检测会通过 ctx 取消信号传递。
func (r *ChannelMonitorRunner) Unschedule(id int64) {
if r == nil {
return
}
r.mu.Lock()
task, ok := r.tasks[id]
if ok {
delete(r.tasks, id)
}
r.mu.Unlock()
if ok {
task.cancel()
}
}
// Stop 优雅停止:取消所有任务、关闭池。
func (r *ChannelMonitorRunner) Stop() {
if r == nil {
return
}
r.mu.Lock()
if r.stopped {
r.mu.Unlock()
return
}
r.stopped = true
r.parentCancel()
r.tasks = nil
r.mu.Unlock()
r.wg.Wait()
r.pool.StopAndWait()
}
// runScheduled 单个监控的循环:立即触发首次(满足"新建/启用即跑"
// 之后按 interval 周期触发ctx 取消即退出。
func (r *ChannelMonitorRunner) runScheduled(ctx context.Context, task *scheduledMonitor) {
defer r.wg.Done()
r.fire(ctx, task)
ticker := time.NewTicker(task.interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
r.fire(ctx, task)
}
}
}
// fire 提交一次检测到 worker 池。功能开关关闭时跳过本次(不取消任务,
// 重新启用时立即恢复);池满或重复在飞时也跳过。
func (r *ChannelMonitorRunner) fire(ctx context.Context, task *scheduledMonitor) {
if r.settingService != nil && !r.settingService.GetChannelMonitorRuntime(ctx).Enabled {
return
}
if !r.tryAcquireInFlight(task.id) {
slog.Debug("channel_monitor: skip already in-flight",
"monitor_id", task.id, "name", task.name)
return
}
if _, ok := r.pool.TrySubmit(func() {
r.runOne(task.id, task.name)
}); !ok {
// 池满:丢弃本次检测,但必须释放已占用的 inFlight 槽,否则该 monitor 会被永久卡住。
r.releaseInFlight(task.id)
slog.Warn("channel_monitor: worker pool full, skip submission",
"monitor_id", task.id, "name", task.name)
}
}
// tryAcquireInFlight 原子地占用 monitor 的 in-flight 槽。
// 已被占用返回 false调用方应跳过本次提交
func (r *ChannelMonitorRunner) tryAcquireInFlight(id int64) bool {
r.inFlightMu.Lock()
defer r.inFlightMu.Unlock()
if _, exists := r.inFlight[id]; exists {
return false
}
r.inFlight[id] = struct{}{}
return true
}
// releaseInFlight 释放 in-flight 槽。runOne 完成(含 panic recover后必须调用。
func (r *ChannelMonitorRunner) releaseInFlight(id int64) {
r.inFlightMu.Lock()
delete(r.inFlight, id)
r.inFlightMu.Unlock()
}
// runOne 执行单个监控的检测。所有错误只记日志,不熔断。
// 任务结束时(含 panic recover必须释放 in-flight 槽。
func (r *ChannelMonitorRunner) runOne(id int64, name string) {
ctx, cancel := context.WithTimeout(context.Background(), monitorRequestTimeout+monitorPingTimeout+monitorRunOneBuffer)
defer cancel()
defer r.releaseInFlight(id)
defer func() {
if rec := recover(); rec != nil {
slog.Error("channel_monitor: runner panic",
"monitor_id", id, "name", name, "panic", rec)
}
}()
if _, err := r.svc.RunCheck(ctx, id); err != nil {
slog.Warn("channel_monitor: run check failed",
"monitor_id", id, "name", name, "error", err)
}
}

View File

@ -0,0 +1,277 @@
//go:build unit
package service
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
)
// stubMonitorSvc 实现 monitorRunnerSvc用于隔离 runner 与真实 service/repo。
type stubMonitorSvc struct {
enabled []*ChannelMonitor
runCount atomic.Int64
runCalled chan int64 // 每次 RunCheck 触发时 push 一次(缓冲足够大避免阻塞)
runErr error
listErr error
runHoldFor time.Duration // RunCheck 内额外阻塞的时长,用来测试 Stop 等待行为
}
func (s *stubMonitorSvc) ListEnabledMonitors(_ context.Context) ([]*ChannelMonitor, error) {
if s.listErr != nil {
return nil, s.listErr
}
return s.enabled, nil
}
func (s *stubMonitorSvc) RunCheck(ctx context.Context, id int64) ([]*CheckResult, error) {
s.runCount.Add(1)
if s.runCalled != nil {
select {
case s.runCalled <- id:
default:
}
}
if s.runHoldFor > 0 {
select {
case <-time.After(s.runHoldFor):
case <-ctx.Done():
}
}
return nil, s.runErr
}
func newRunnerForTest(svc monitorRunnerSvc) *ChannelMonitorRunner {
return newChannelMonitorRunner(svc, nil)
}
// 等待 condition 在 timeout 内变 true否则 t.Fatalf。轮询 5ms 一次。
func waitFor(t *testing.T, timeout time.Duration, msg string, cond func() bool) {
t.Helper()
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if cond() {
return
}
time.Sleep(5 * time.Millisecond)
}
if !cond() {
t.Fatalf("waitFor timed out: %s", msg)
}
}
func runnerTaskCount(r *ChannelMonitorRunner) int {
r.mu.Lock()
defer r.mu.Unlock()
return len(r.tasks)
}
func runnerTaskPtr(r *ChannelMonitorRunner, id int64) *scheduledMonitor {
r.mu.Lock()
defer r.mu.Unlock()
return r.tasks[id]
}
// TestSchedule_AddsTaskAndFiresOnce 验证 Schedule 后立即触发一次首检测,并把任务记入 tasks 表。
func TestSchedule_AddsTaskAndFiresOnce(t *testing.T) {
svc := &stubMonitorSvc{runCalled: make(chan int64, 4)}
r := newRunnerForTest(svc)
r.Start() // svc.enabled 为空Start 立即完成
r.Schedule(&ChannelMonitor{ID: 1, Name: "m1", Enabled: true, IntervalSeconds: 60})
if got := runnerTaskCount(r); got != 1 {
t.Fatalf("expected 1 scheduled task, got %d", got)
}
select {
case id := <-svc.runCalled:
if id != 1 {
t.Fatalf("expected first fire for id=1, got %d", id)
}
case <-time.After(2 * time.Second):
t.Fatal("expected immediate first fire within 2s")
}
r.Stop()
}
// TestSchedule_ReplaceCancelsOldTask 验证对同一 id 二次 Schedule 会替换旧 task 实例。
// (旧 goroutine 通过 ctx 取消退出;这里以 task 指针不同 + Stop 不超时作为证据。)
func TestSchedule_ReplaceCancelsOldTask(t *testing.T) {
svc := &stubMonitorSvc{runCalled: make(chan int64, 8)}
r := newRunnerForTest(svc)
r.Start()
m := &ChannelMonitor{ID: 7, Name: "m7", Enabled: true, IntervalSeconds: 60}
r.Schedule(m)
first := runnerTaskPtr(r, 7)
if first == nil {
t.Fatal("first schedule did not register task")
}
r.Schedule(m)
second := runnerTaskPtr(r, 7)
if second == nil {
t.Fatal("second schedule did not register task")
}
if first == second {
t.Fatal("re-Schedule should create a new scheduledMonitor instance")
}
stoppedWithin(t, r, 3*time.Second)
}
// TestUnschedule_RemovesTask 验证 Unschedule 删除 task 并使对应 goroutine 退出。
func TestUnschedule_RemovesTask(t *testing.T) {
svc := &stubMonitorSvc{runCalled: make(chan int64, 4)}
r := newRunnerForTest(svc)
r.Start()
r.Schedule(&ChannelMonitor{ID: 3, Enabled: true, IntervalSeconds: 60})
waitFor(t, time.Second, "task registered", func() bool { return runnerTaskCount(r) == 1 })
r.Unschedule(3)
if got := runnerTaskCount(r); got != 0 {
t.Fatalf("expected tasks empty after Unschedule, got %d", got)
}
stoppedWithin(t, r, 3*time.Second)
}
// TestSchedule_DisabledRedirectsToUnschedule 验证 Enabled=false 等同于 Unschedule。
func TestSchedule_DisabledRedirectsToUnschedule(t *testing.T) {
svc := &stubMonitorSvc{runCalled: make(chan int64, 4)}
r := newRunnerForTest(svc)
r.Start()
r.Schedule(&ChannelMonitor{ID: 9, Enabled: true, IntervalSeconds: 60})
waitFor(t, time.Second, "task registered", func() bool { return runnerTaskCount(r) == 1 })
r.Schedule(&ChannelMonitor{ID: 9, Enabled: false, IntervalSeconds: 60})
if got := runnerTaskCount(r); got != 0 {
t.Fatalf("expected tasks empty after disabled re-Schedule, got %d", got)
}
stoppedWithin(t, r, 3*time.Second)
}
// TestSchedule_InvalidIntervalSkipped 验证 IntervalSeconds<=0 不会注册任务(防御性检查)。
func TestSchedule_InvalidIntervalSkipped(t *testing.T) {
svc := &stubMonitorSvc{}
r := newRunnerForTest(svc)
r.Start()
r.Schedule(&ChannelMonitor{ID: 1, Enabled: true, IntervalSeconds: 0})
if got := runnerTaskCount(r); got != 0 {
t.Fatalf("expected no task for invalid interval, got %d", got)
}
r.Stop()
}
// TestSchedule_BeforeStartIsNoOp 验证 Start 之前调用 Schedule 不会注册任务。
func TestSchedule_BeforeStartIsNoOp(t *testing.T) {
svc := &stubMonitorSvc{}
r := newRunnerForTest(svc)
// 故意不调用 Start
r.Schedule(&ChannelMonitor{ID: 1, Enabled: true, IntervalSeconds: 60})
if got := runnerTaskCount(r); got != 0 {
t.Fatalf("expected no task before Start, got %d", got)
}
r.Stop()
}
// TestStart_LoadsAllEnabledMonitors 验证 Start 会为 ListEnabledMonitors 返回的每条记录建立任务。
func TestStart_LoadsAllEnabledMonitors(t *testing.T) {
svc := &stubMonitorSvc{
enabled: []*ChannelMonitor{
{ID: 1, Enabled: true, IntervalSeconds: 60},
{ID: 2, Enabled: true, IntervalSeconds: 60},
{ID: 3, Enabled: true, IntervalSeconds: 60},
},
}
r := newRunnerForTest(svc)
r.Start()
waitFor(t, 2*time.Second, "all 3 tasks scheduled", func() bool { return runnerTaskCount(r) == 3 })
stoppedWithin(t, r, 3*time.Second)
}
// TestStop_DrainsAllGoroutines 验证 Stop 会等待所有调度 goroutine 退出(无游离)。
func TestStop_DrainsAllGoroutines(t *testing.T) {
svc := &stubMonitorSvc{}
r := newRunnerForTest(svc)
r.Start()
for id := int64(1); id <= 5; id++ {
r.Schedule(&ChannelMonitor{ID: id, Enabled: true, IntervalSeconds: 60})
}
waitFor(t, 2*time.Second, "5 tasks scheduled", func() bool { return runnerTaskCount(r) == 5 })
stoppedWithin(t, r, 3*time.Second)
}
// TestStop_WaitsForInFlightCheck 验证 Stop 会等待正在执行的 RunCheck 退出pool.StopAndWait
func TestStop_WaitsForInFlightCheck(t *testing.T) {
svc := &stubMonitorSvc{
runCalled: make(chan int64, 1),
runHoldFor: 200 * time.Millisecond,
}
r := newRunnerForTest(svc)
r.Start()
r.Schedule(&ChannelMonitor{ID: 1, Enabled: true, IntervalSeconds: 60})
select {
case <-svc.runCalled:
case <-time.After(2 * time.Second):
t.Fatal("first fire never happened")
}
start := time.Now()
stoppedWithin(t, r, 3*time.Second)
elapsed := time.Since(start)
// Stop 必须等待 in-flight check 跑完runHoldFor=200ms耗时下界约 100ms。
if elapsed < 100*time.Millisecond {
t.Fatalf("Stop returned too fast (%v); did not wait for in-flight check", elapsed)
}
}
// TestInFlight_PoolFullReleasesSlot 直接驱动 fire 路径,模拟 pool.TrySubmit 失败时 inFlight 必须释放。
// 用一个小型 stub pool 替换 r.pool 不便pond.Pool 是接口但 mock 麻烦),
// 改为:占满 inFlight 后直接 fire验证不会在 inFlight 空槽时永久卡住。
func TestInFlight_AcquireReleaseSymmetric(t *testing.T) {
svc := &stubMonitorSvc{}
r := newRunnerForTest(svc)
if !r.tryAcquireInFlight(42) {
t.Fatal("first acquire should succeed")
}
if r.tryAcquireInFlight(42) {
t.Fatal("second acquire (no release) must fail")
}
r.releaseInFlight(42)
if !r.tryAcquireInFlight(42) {
t.Fatal("acquire after release should succeed")
}
r.releaseInFlight(42)
}
// stoppedWithin 在 timeout 内并行调用 Stop超时则 Fatal。验证 Stop 不会阻塞。
func stoppedWithin(t *testing.T, r *ChannelMonitorRunner, timeout time.Duration) {
t.Helper()
done := make(chan struct{})
var once sync.Once
go func() {
r.Stop()
once.Do(func() { close(done) })
}()
select {
case <-done:
case <-time.After(timeout):
t.Fatalf("Stop did not return within %s — leaked goroutine?", timeout)
}
}

View File

@ -0,0 +1,539 @@
package service
import (
"context"
"fmt"
"log/slog"
"strings"
"sync"
"time"
"golang.org/x/sync/errgroup"
)
// ChannelMonitorRepository 渠道监控数据访问接口。
// 入参/返回的指针类型均使用 service 包的 ChannelMonitor 模型,
// repository 实现负责与 ent 模型互转,并保持 api_key_encrypted 字段为密文。
type ChannelMonitorRepository interface {
// CRUD
Create(ctx context.Context, m *ChannelMonitor) error
GetByID(ctx context.Context, id int64) (*ChannelMonitor, error)
Update(ctx context.Context, m *ChannelMonitor) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, params ChannelMonitorListParams) ([]*ChannelMonitor, int64, error)
// 调度器辅助
ListEnabled(ctx context.Context) ([]*ChannelMonitor, error)
MarkChecked(ctx context.Context, id int64, checkedAt time.Time) error
InsertHistoryBatch(ctx context.Context, rows []*ChannelMonitorHistoryRow) error
DeleteHistoryBefore(ctx context.Context, before time.Time) (int64, error)
// 历史记录
ListHistory(ctx context.Context, monitorID int64, model string, limit int) ([]*ChannelMonitorHistoryEntry, error)
// 用户视图聚合
ListLatestPerModel(ctx context.Context, monitorID int64) ([]*ChannelMonitorLatest, error)
ComputeAvailability(ctx context.Context, monitorID int64, windowDays int) ([]*ChannelMonitorAvailability, error)
// 批量聚合admin/user list 用,避免 N+1
ListLatestForMonitorIDs(ctx context.Context, ids []int64) (map[int64][]*ChannelMonitorLatest, error)
ComputeAvailabilityForMonitors(ctx context.Context, ids []int64, windowDays int) (map[int64][]*ChannelMonitorAvailability, error)
// ListRecentHistoryForMonitors 批量取多个 monitor 各自主模型primaryModels[monitorID])最近 perMonitorLimit 条历史。
// 返回的 entry 已按 checked_at DESC 排序(最新在前),不含 message 字段。
ListRecentHistoryForMonitors(ctx context.Context, ids []int64, primaryModels map[int64]string, perMonitorLimit int) (map[int64][]*ChannelMonitorHistoryEntry, error)
// ---------- 聚合维护OpsCleanupService 调用) ----------
// UpsertDailyRollupsFor 把 targetDate 当天的明细按 (monitor_id, model, bucket_date)
// 聚合到 channel_monitor_daily_rollups。targetDate 会被截断到日期;
// 用 ON CONFLICT DO UPDATE 实现幂等回填,返回 upsert 影响的行数。
UpsertDailyRollupsFor(ctx context.Context, targetDate time.Time) (int64, error)
// DeleteRollupsBefore 软删 bucket_date < beforeDate 的聚合行,返回删除行数。
DeleteRollupsBefore(ctx context.Context, beforeDate time.Time) (int64, error)
// LoadAggregationWatermark 读 watermarkid=1
// 返回 nil 表示从未聚合过watermark 表本身预期已存在单行migration 110 写入)。
LoadAggregationWatermark(ctx context.Context) (*time.Time, error)
// UpdateAggregationWatermark 写 watermarkUPSERT 到 id=1
UpdateAggregationWatermark(ctx context.Context, date time.Time) error
}
// ChannelMonitorService 渠道监控管理服务。
type ChannelMonitorService struct {
repo ChannelMonitorRepository
encryptor SecretEncryptor
// scheduler 由 wire 通过 SetScheduler 注入CRUD 后调用对应钩子即时同步任务。
// 测试或未注入场景下保持 nil所有钩子调用变为 no-op。
scheduler MonitorScheduler
}
// NewChannelMonitorService 创建渠道监控服务实例。
func NewChannelMonitorService(repo ChannelMonitorRepository, encryptor SecretEncryptor) *ChannelMonitorService {
return &ChannelMonitorService{repo: repo, encryptor: encryptor}
}
// ---------- CRUD ----------
// List 列表查询(支持 provider/enabled/search 过滤 + 分页)。
// 返回的 ChannelMonitor.APIKey 已解密为明文handler 层负责脱敏。
func (s *ChannelMonitorService) List(ctx context.Context, params ChannelMonitorListParams) ([]*ChannelMonitor, int64, error) {
if params.Page < 1 {
params.Page = 1
}
if params.PageSize < 1 || params.PageSize > 200 {
params.PageSize = 20
}
items, total, err := s.repo.List(ctx, params)
if err != nil {
return nil, 0, fmt.Errorf("list channel monitors: %w", err)
}
for _, it := range items {
s.decryptInPlace(it)
}
return items, total, nil
}
// Get 查询单个监控(解密 API Key
func (s *ChannelMonitorService) Get(ctx context.Context, id int64) (*ChannelMonitor, error) {
m, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
s.decryptInPlace(m)
return m, nil
}
// Create 创建监控(内部加密 api_key
func (s *ChannelMonitorService) Create(ctx context.Context, p ChannelMonitorCreateParams) (*ChannelMonitor, error) {
if err := validateCreateParams(p); err != nil {
return nil, err
}
if err := validateBodyModeParams(p.BodyOverrideMode, p.BodyOverride); err != nil {
return nil, err
}
if err := validateExtraHeaders(p.ExtraHeaders); err != nil {
return nil, err
}
encrypted, err := s.encryptor.Encrypt(p.APIKey)
if err != nil {
return nil, fmt.Errorf("encrypt api key: %w", err)
}
m := &ChannelMonitor{
Name: strings.TrimSpace(p.Name),
Provider: p.Provider,
Endpoint: normalizeEndpoint(p.Endpoint),
APIKey: encrypted, // 注意:传入 repository 时该字段为密文
PrimaryModel: strings.TrimSpace(p.PrimaryModel),
ExtraModels: normalizeModels(p.ExtraModels),
GroupName: strings.TrimSpace(p.GroupName),
Enabled: p.Enabled,
IntervalSeconds: p.IntervalSeconds,
CreatedBy: p.CreatedBy,
TemplateID: p.TemplateID,
ExtraHeaders: emptyHeadersIfNil(p.ExtraHeaders),
BodyOverrideMode: defaultBodyMode(p.BodyOverrideMode),
BodyOverride: p.BodyOverride,
}
if err := s.repo.Create(ctx, m); err != nil {
return nil, fmt.Errorf("create channel monitor: %w", err)
}
// 不再调 s.Get 重走解密链:已知刚加密的明文,直接构造响应。
// 这样可避免 SecretEncryptor 解密失败时 APIKey 被静默清空的问题(见 Fix 4
m.APIKey = strings.TrimSpace(p.APIKey)
if s.scheduler != nil {
s.scheduler.Schedule(m)
}
return m, nil
}
// validateCreateParams 把 Create 入参的所有校验聚拢为一个函数,避免 Create 主体超过 30 行。
func validateCreateParams(p ChannelMonitorCreateParams) error {
if err := validateProvider(p.Provider); err != nil {
return err
}
if err := validateInterval(p.IntervalSeconds); err != nil {
return err
}
if err := validateEndpoint(p.Endpoint); err != nil {
return err
}
if strings.TrimSpace(p.APIKey) == "" {
return ErrChannelMonitorMissingAPIKey
}
if strings.TrimSpace(p.PrimaryModel) == "" {
return ErrChannelMonitorMissingPrimaryModel
}
return nil
}
// Update 更新监控。APIKey 字段nil 或空字符串 = 不修改;非空 = 加密后覆盖。
func (s *ChannelMonitorService) Update(ctx context.Context, id int64, p ChannelMonitorUpdateParams) (*ChannelMonitor, error) {
existing, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if err := applyMonitorUpdate(existing, p); err != nil {
return nil, err
}
newPlainAPIKey, apiKeyUpdated, err := s.applyAPIKeyUpdate(existing, p.APIKey)
if err != nil {
return nil, err
}
if err := s.repo.Update(ctx, existing); err != nil {
return nil, fmt.Errorf("update channel monitor: %w", err)
}
// 不再调 s.Get 重走解密链:避免二次解密带来的"密文被静默清空"风险(与 Create 一致)。
if apiKeyUpdated {
existing.APIKey = newPlainAPIKey
} else {
s.decryptInPlace(existing)
}
if s.scheduler != nil {
// Schedule 内部根据 Enabled 自动选择 Unschedule 或重建任务,
// IntervalSeconds 变化也会被自然吸收(旧 task 取消 + 新 task 用新 interval
s.scheduler.Schedule(existing)
}
return existing, nil
}
// applyAPIKeyUpdate 处理 Update 中的 APIKey 字段:
// - 入参 raw 为 nil 或空白:不修改 existing.APIKey仍为密文返回 updated=false
// - 非空:加密后写入 existing.APIKey同时把明文返回给调用方
// 供写库成功后塞回 existing 避免把密文吐回客户端
func (s *ChannelMonitorService) applyAPIKeyUpdate(existing *ChannelMonitor, raw *string) (plain string, updated bool, err error) {
if raw == nil || strings.TrimSpace(*raw) == "" {
return "", false, nil
}
plain = strings.TrimSpace(*raw)
encrypted, encErr := s.encryptor.Encrypt(plain)
if encErr != nil {
return "", false, fmt.Errorf("encrypt api key: %w", encErr)
}
existing.APIKey = encrypted
return plain, true, nil
}
// Delete 删除监控(历史通过外键 CASCADE 自动清理)。
func (s *ChannelMonitorService) Delete(ctx context.Context, id int64) error {
if err := s.repo.Delete(ctx, id); err != nil {
return fmt.Errorf("delete channel monitor: %w", err)
}
if s.scheduler != nil {
s.scheduler.Unschedule(id)
}
return nil
}
// ListHistory 列出某个监控最近的检测历史。
// model 为空表示返回所有模型limit <= 0 时使用默认值,超过上限会被截断。
func (s *ChannelMonitorService) ListHistory(ctx context.Context, id int64, model string, limit int) ([]*ChannelMonitorHistoryEntry, error) {
if _, err := s.repo.GetByID(ctx, id); err != nil {
return nil, err
}
if limit <= 0 {
limit = MonitorHistoryDefaultLimit
}
if limit > MonitorHistoryMaxLimit {
limit = MonitorHistoryMaxLimit
}
entries, err := s.repo.ListHistory(ctx, id, strings.TrimSpace(model), limit)
if err != nil {
return nil, fmt.Errorf("list history: %w", err)
}
return entries, nil
}
// ---------- 业务 ----------
// RunCheck 同步触发对一个监控的检测:并发跑 primary + extra 模型,
// 写历史记录并更新 last_checked_at。返回每个模型的检测结果。
func (s *ChannelMonitorService) RunCheck(ctx context.Context, id int64) ([]*CheckResult, error) {
m, err := s.Get(ctx, id) // 已解密 APIKey
if err != nil {
return nil, err
}
if m.APIKeyDecryptFailed {
return nil, ErrChannelMonitorAPIKeyDecryptFailed
}
results := s.runChecksConcurrent(ctx, m)
s.persistCheckResults(ctx, m, results)
return results, nil
}
// persistCheckResults 写入本次检测的历史记录并更新 last_checked_at。
// 任一写库失败都只记日志,不影响调用方拿到 results与 MVP 期望一致:宁可漏记历史也要先返回结果)。
func (s *ChannelMonitorService) persistCheckResults(ctx context.Context, m *ChannelMonitor, results []*CheckResult) {
rows := make([]*ChannelMonitorHistoryRow, 0, len(results))
for _, r := range results {
rows = append(rows, &ChannelMonitorHistoryRow{
MonitorID: m.ID,
Model: r.Model,
Status: r.Status,
LatencyMs: r.LatencyMs,
PingLatencyMs: r.PingLatencyMs,
Message: r.Message,
CheckedAt: r.CheckedAt,
})
}
if err := s.repo.InsertHistoryBatch(ctx, rows); err != nil {
slog.Error("channel_monitor: insert history failed",
"monitor_id", m.ID, "name", m.Name, "error", err)
}
if err := s.repo.MarkChecked(ctx, m.ID, time.Now()); err != nil {
slog.Error("channel_monitor: mark checked failed",
"monitor_id", m.ID, "error", err)
}
}
// runChecksConcurrent 对 primary + extra 模型并发执行检测。
// errgroup 仅用于等待,不传播错误(每个 model 失败都已打包进 CheckResult
func (s *ChannelMonitorService) runChecksConcurrent(ctx context.Context, m *ChannelMonitor) []*CheckResult {
models := append([]string{m.PrimaryModel}, m.ExtraModels...)
results := make([]*CheckResult, len(models))
// ping 共享一次,所有模型记录同一个 ping 延迟。
pingMs := pingEndpointOrigin(ctx, m.Endpoint)
// 所有模型共用同一份 CheckOptions来自监控的快照字段
opts := &CheckOptions{
ExtraHeaders: m.ExtraHeaders,
BodyOverrideMode: m.BodyOverrideMode,
BodyOverride: m.BodyOverride,
}
var eg errgroup.Group
var mu sync.Mutex
for i, model := range models {
i, model := i, model
eg.Go(func() error {
r := runCheckForModel(ctx, m.Provider, m.Endpoint, m.APIKey, model, opts)
r.PingLatencyMs = pingMs
mu.Lock()
results[i] = r
mu.Unlock()
return nil
})
}
_ = eg.Wait()
return results
}
// ---------- 调度器协作 ----------
// SetScheduler 由 wire 在 runner 构造后注入,用于在 CRUD 时即时同步任务表。
// 通过 setter 注入避免 service ↔ runner 的依赖环。
func (s *ChannelMonitorService) SetScheduler(sched MonitorScheduler) {
s.scheduler = sched
}
// ListEnabledMonitors 返回所有 enabled=true 的监控(解密后),供 runner 启动时建立任务表。
func (s *ChannelMonitorService) ListEnabledMonitors(ctx context.Context) ([]*ChannelMonitor, error) {
all, err := s.repo.ListEnabled(ctx)
if err != nil {
return nil, err
}
for _, m := range all {
s.decryptInPlace(m)
}
return all, nil
}
// cleanupOldHistory 删除 monitorHistoryRetentionDays 天之前的明细历史记录。
// 由 RunDailyMaintenance 调用SoftDeleteMixin 自动把 DELETE 改为 UPDATE deleted_at。
func (s *ChannelMonitorService) cleanupOldHistory(ctx context.Context) error {
before := time.Now().UTC().AddDate(0, 0, -monitorHistoryRetentionDays)
deleted, err := s.repo.DeleteHistoryBefore(ctx, before)
if err != nil {
return fmt.Errorf("delete history before %s: %w", before.Format(time.RFC3339), err)
}
if deleted > 0 {
slog.Info("channel_monitor: history cleanup",
"deleted_rows", deleted, "before", before.Format(time.RFC3339))
}
return nil
}
// RunDailyMaintenance 每日维护任务:聚合昨天之前未聚合的明细,软删过期明细和聚合。
// 由 OpsCleanupService 的 cron 调度触发(共享 schedule 和 leader lock
//
// 幂等性:
// - watermark 保证已聚合的日期不会重复处理;
// - UpsertDailyRollupsFor 内部使用 ON CONFLICT DO UPDATE同一日重复跑结果一致。
//
// 每一步失败都只记 slog.Warn整体函数始终返回 nil 让后续步骤能继续跑
// (与 OpsCleanupService.runCleanupOnce 风格一致)。
func (s *ChannelMonitorService) RunDailyMaintenance(ctx context.Context) error {
now := time.Now().UTC()
today := now.Truncate(24 * time.Hour)
if err := s.runDailyAggregation(ctx, today); err != nil {
slog.Warn("channel_monitor: maintenance step failed",
"step", "aggregate", "error", err)
}
if err := s.cleanupOldHistory(ctx); err != nil {
slog.Warn("channel_monitor: maintenance step failed",
"step", "prune_history", "error", err)
}
if err := s.cleanupOldRollups(ctx, today); err != nil {
slog.Warn("channel_monitor: maintenance step failed",
"step", "prune_rollups", "error", err)
}
return nil
}
// runDailyAggregation 从 watermark+1 聚合到昨天UTC
// 首次跑watermark nil从 today-monitorRollupRetentionDays 开始回填。
// 每次最多聚合 monitorMaintenanceMaxDaysPerRun 天,避免长事务。
func (s *ChannelMonitorService) runDailyAggregation(ctx context.Context, today time.Time) error {
watermark, err := s.repo.LoadAggregationWatermark(ctx)
if err != nil {
return fmt.Errorf("load watermark: %w", err)
}
start := s.resolveAggregationStart(watermark, today)
if !start.Before(today) {
return nil // 没有需要聚合的日期
}
iterations := 0
for d := start; d.Before(today); d = d.Add(24 * time.Hour) {
if iterations >= monitorMaintenanceMaxDaysPerRun {
slog.Info("channel_monitor: maintenance aggregation capped",
"max_days", monitorMaintenanceMaxDaysPerRun,
"next_resume", d.Format("2006-01-02"))
break
}
affected, upErr := s.repo.UpsertDailyRollupsFor(ctx, d)
if upErr != nil {
return fmt.Errorf("upsert rollups for %s: %w", d.Format("2006-01-02"), upErr)
}
if err := s.repo.UpdateAggregationWatermark(ctx, d); err != nil {
return fmt.Errorf("update watermark to %s: %w", d.Format("2006-01-02"), err)
}
slog.Info("channel_monitor: rollups upserted",
"date", d.Format("2006-01-02"), "affected_rows", affected)
iterations++
}
return nil
}
// resolveAggregationStart 计算本次聚合起点:
// - watermark == niltoday - monitorRollupRetentionDays首次回填最多 30 天)
// - watermark != nil*watermark + 1 day
func (s *ChannelMonitorService) resolveAggregationStart(watermark *time.Time, today time.Time) time.Time {
if watermark == nil {
return today.AddDate(0, 0, -monitorRollupRetentionDays)
}
return watermark.UTC().Truncate(24 * time.Hour).Add(24 * time.Hour)
}
// cleanupOldRollups 软删 bucket_date < today - monitorRollupRetentionDays 的日聚合行。
func (s *ChannelMonitorService) cleanupOldRollups(ctx context.Context, today time.Time) error {
cutoff := today.AddDate(0, 0, -monitorRollupRetentionDays)
deleted, err := s.repo.DeleteRollupsBefore(ctx, cutoff)
if err != nil {
return fmt.Errorf("delete rollups before %s: %w", cutoff.Format("2006-01-02"), err)
}
if deleted > 0 {
slog.Info("channel_monitor: rollups cleanup",
"deleted_rows", deleted, "before", cutoff.Format("2006-01-02"))
}
return nil
}
// ---------- helpers ----------
// decryptInPlace 把 ChannelMonitor.APIKey 从密文解密为明文。
// 解密失败时把字段清空 + 设置 APIKeyDecryptFailed=true不返回错误避免阻断列表渲染
// runner / RunCheck 必须读取该标志位并拒绝执行检测。
func (s *ChannelMonitorService) decryptInPlace(m *ChannelMonitor) {
if m == nil || m.APIKey == "" {
return
}
plain, err := s.encryptor.Decrypt(m.APIKey)
if err != nil {
slog.Warn("channel_monitor: decrypt api key failed",
"monitor_id", m.ID, "error", err)
m.APIKey = ""
m.APIKeyDecryptFailed = true
return
}
m.APIKey = plain
}
// applyMonitorUpdate 把 update params 中非 nil 的字段应用到 existing 上。
// APIKey 字段在调用方单独处理(涉及加密)。
//
// 行数稍超过 30这是逐字段平铺的 dispatcher每个 if 都是 1-3 行的"非 nil 则覆盖"模式,
// 拆分反而会增加跳转噪音、影响可读性,故保留为单函数。
func applyMonitorUpdate(existing *ChannelMonitor, p ChannelMonitorUpdateParams) error {
if p.Name != nil {
existing.Name = strings.TrimSpace(*p.Name)
}
if p.Provider != nil {
if err := validateProvider(*p.Provider); err != nil {
return err
}
existing.Provider = *p.Provider
}
if p.Endpoint != nil {
if err := validateEndpoint(*p.Endpoint); err != nil {
return err
}
existing.Endpoint = normalizeEndpoint(*p.Endpoint)
}
if p.PrimaryModel != nil {
existing.PrimaryModel = strings.TrimSpace(*p.PrimaryModel)
}
if p.ExtraModels != nil {
existing.ExtraModels = normalizeModels(*p.ExtraModels)
}
if p.GroupName != nil {
existing.GroupName = strings.TrimSpace(*p.GroupName)
}
if p.Enabled != nil {
existing.Enabled = *p.Enabled
}
if p.IntervalSeconds != nil {
if err := validateInterval(*p.IntervalSeconds); err != nil {
return err
}
existing.IntervalSeconds = *p.IntervalSeconds
}
return applyMonitorAdvancedUpdate(existing, p)
}
// applyMonitorAdvancedUpdate 处理自定义请求快照相关字段,从 applyMonitorUpdate 拆出避免过长。
func applyMonitorAdvancedUpdate(existing *ChannelMonitor, p ChannelMonitorUpdateParams) error {
if p.ClearTemplate {
existing.TemplateID = nil
} else if p.TemplateID != nil {
id := *p.TemplateID
existing.TemplateID = &id
}
if p.ExtraHeaders != nil {
if err := validateExtraHeaders(*p.ExtraHeaders); err != nil {
return err
}
existing.ExtraHeaders = emptyHeadersIfNil(*p.ExtraHeaders)
}
// BodyOverrideMode / BodyOverride 联合校验,和模板一致。
newMode := existing.BodyOverrideMode
newBody := existing.BodyOverride
if p.BodyOverrideMode != nil {
newMode = *p.BodyOverrideMode
}
if p.BodyOverride != nil {
newBody = *p.BodyOverride
}
if p.BodyOverrideMode != nil || p.BodyOverride != nil {
if err := validateBodyModeParams(newMode, newBody); err != nil {
return err
}
existing.BodyOverrideMode = defaultBodyMode(newMode)
existing.BodyOverride = newBody
}
return nil
}

View File

@ -0,0 +1,152 @@
package service
import (
"context"
"net"
"strings"
)
// SSRF 防护 helper
// - validateEndpoint 在 admin 提交时阻止 http/loopback/私网/云元数据 URL
// - safeDialContext 在 socket 层再次校验真实 IP防止 DNS rebinding
//
// 已知 cloud metadata hostname 拒绝列表(小写比较)。
var monitorBlockedHostnames = map[string]struct{}{
"localhost": {},
"localhost.localdomain": {},
"metadata": {},
"metadata.google.internal": {},
"metadata.goog": {},
"instance-data": {},
"instance-data.ec2.internal": {},
}
// CIDR 列表:包含所有需要拒绝的 IPv4/IPv6 段。
// 解析时只 panic 一次(启动时确认),生产路径只做 Contains。
var monitorBlockedCIDRs = mustParseCIDRs([]string{
"127.0.0.0/8", // IPv4 loopback
"10.0.0.0/8", // RFC1918
"172.16.0.0/12", // RFC1918
"192.168.0.0/16", // RFC1918
"169.254.0.0/16", // link-local含云元数据 169.254.169.254
"100.64.0.0/10", // CGNAT
"0.0.0.0/8", // "this network"
"::1/128", // IPv6 loopback
"fc00::/7", // IPv6 ULA
"fe80::/10", // IPv6 link-local
"::/128", // IPv6 unspecified
})
// monitorDialer 共享 Dialer与 net/http 默认值对齐。
var monitorDialer = &net.Dialer{
Timeout: monitorDialTimeout,
KeepAlive: monitorDialKeepAlive,
}
// mustParseCIDRs 在包初始化时解析 CIDR 字符串,失败 panic。
func mustParseCIDRs(cidrs []string) []*net.IPNet {
out := make([]*net.IPNet, 0, len(cidrs))
for _, c := range cidrs {
_, n, err := net.ParseCIDR(c)
if err != nil {
panic("channel_monitor_ssrf: invalid CIDR " + c + ": " + err.Error())
}
out = append(out, n)
}
return out
}
// isBlockedHostname 判断 hostname 是否命中黑名单。
func isBlockedHostname(hostname string) bool {
if hostname == "" {
return true
}
_, blocked := monitorBlockedHostnames[strings.ToLower(hostname)]
return blocked
}
// isPrivateIP 判断 IP 是否落在禁止段loopback/RFC1918/link-local/ULA 等)。
func isPrivateIP(ip net.IP) bool {
if ip == nil {
return true
}
if ip.IsUnspecified() || ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast() {
return true
}
for _, n := range monitorBlockedCIDRs {
if n.Contains(ip) {
return true
}
}
return false
}
// isPrivateOrLoopbackHost 解析 hostname 的所有 A/AAAA 记录,
// 任一 IP 落在私网/loopback 段即认为不安全。
//
// hostname 是 IP 字面量时也走同一路径。
func isPrivateOrLoopbackHost(ctx context.Context, hostname string) (bool, error) {
if isBlockedHostname(hostname) {
return true, nil
}
// IP 字面量直接判断。
if ip := net.ParseIP(hostname); ip != nil {
return isPrivateIP(ip), nil
}
resolver := net.DefaultResolver
addrs, err := resolver.LookupIPAddr(ctx, hostname)
if err != nil {
return false, err
}
if len(addrs) == 0 {
return true, nil
}
for _, a := range addrs {
if isPrivateIP(a.IP) {
return true, nil
}
}
return false, nil
}
// safeDialContext 在真实 dial 前再次校验目标 IP防止 DNS rebinding。
// 解析 hostname 后逐个 IP 尝试连接,命中私网即拒绝(即便 validateEndpoint 时返回的是公网 IP
func safeDialContext(ctx context.Context, network, address string) (net.Conn, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
// 字面量 IP 走快速路径。
if ip := net.ParseIP(host); ip != nil {
if isPrivateIP(ip) {
return nil, &net.AddrError{Err: "blocked by SSRF policy", Addr: address}
}
return monitorDialer.DialContext(ctx, network, address)
}
if isBlockedHostname(host) {
return nil, &net.AddrError{Err: "blocked by SSRF policy", Addr: address}
}
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
if len(addrs) == 0 {
return nil, &net.AddrError{Err: "no addresses for host", Addr: host}
}
var lastErr error
for _, a := range addrs {
if isPrivateIP(a.IP) {
lastErr = &net.AddrError{Err: "blocked by SSRF policy", Addr: a.IP.String()}
continue
}
conn, err := monitorDialer.DialContext(ctx, network, net.JoinHostPort(a.IP.String(), port))
if err == nil {
return conn, nil
}
lastErr = err
}
if lastErr == nil {
lastErr = &net.AddrError{Err: "no usable addresses", Addr: host}
}
return nil, lastErr
}

View File

@ -0,0 +1,251 @@
package service
import (
"context"
"fmt"
"regexp"
"strings"
)
// ChannelMonitorRequestTemplateRepository 模板数据访问接口。
type ChannelMonitorRequestTemplateRepository interface {
Create(ctx context.Context, t *ChannelMonitorRequestTemplate) error
GetByID(ctx context.Context, id int64) (*ChannelMonitorRequestTemplate, error)
Update(ctx context.Context, t *ChannelMonitorRequestTemplate) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, params ChannelMonitorRequestTemplateListParams) ([]*ChannelMonitorRequestTemplate, error)
// ApplyToMonitors 把模板当前的 extra_headers / body_override_mode / body_override
// 批量覆盖到指定 monitorIDs 的监控上(同时还要求这些监控当前 template_id = id
// 防止误覆盖未关联的监控。monitorIDs 必须非空;空列表直接返回 0 不写库。
// 返回被覆盖的监控数量。
ApplyToMonitors(ctx context.Context, id int64, monitorIDs []int64) (int64, error)
// CountAssociatedMonitors 统计 template_id = id 的监控数(用于 UI 展示「应用到 N 个配置」)。
CountAssociatedMonitors(ctx context.Context, id int64) (int64, error)
// ListAssociatedMonitors 列出所有 template_id = id 的监控简略信息id/name/provider/enabled
// 给 apply picker UI 用,避免前端再做一次 list+filter。
ListAssociatedMonitors(ctx context.Context, id int64) ([]*AssociatedMonitorBrief, error)
}
// AssociatedMonitorBrief 模板关联监控的简略信息picker / 列表展示用)。
type AssociatedMonitorBrief struct {
ID int64
Name string
Provider string
Enabled bool
}
// ChannelMonitorRequestTemplateService 模板管理 service。
type ChannelMonitorRequestTemplateService struct {
repo ChannelMonitorRequestTemplateRepository
}
// NewChannelMonitorRequestTemplateService 创建模板 service。
func NewChannelMonitorRequestTemplateService(repo ChannelMonitorRequestTemplateRepository) *ChannelMonitorRequestTemplateService {
return &ChannelMonitorRequestTemplateService{repo: repo}
}
// ---------- CRUD ----------
// List 按 provider 过滤(空串 = 全部),不分页(模板量级小)。
func (s *ChannelMonitorRequestTemplateService) List(ctx context.Context, params ChannelMonitorRequestTemplateListParams) ([]*ChannelMonitorRequestTemplate, error) {
if params.Provider != "" {
if err := validateProvider(params.Provider); err != nil {
return nil, err
}
}
return s.repo.List(ctx, params)
}
// Get 返回单个模板。
func (s *ChannelMonitorRequestTemplateService) Get(ctx context.Context, id int64) (*ChannelMonitorRequestTemplate, error) {
return s.repo.GetByID(ctx, id)
}
// Create 创建模板(会校验 headers 黑名单和 body 模式匹配)。
func (s *ChannelMonitorRequestTemplateService) Create(ctx context.Context, p ChannelMonitorRequestTemplateCreateParams) (*ChannelMonitorRequestTemplate, error) {
if err := validateTemplateCreateParams(p); err != nil {
return nil, err
}
t := &ChannelMonitorRequestTemplate{
Name: strings.TrimSpace(p.Name),
Provider: p.Provider,
Description: strings.TrimSpace(p.Description),
ExtraHeaders: emptyHeadersIfNil(p.ExtraHeaders),
BodyOverrideMode: defaultBodyMode(p.BodyOverrideMode),
BodyOverride: p.BodyOverride,
}
if err := s.repo.Create(ctx, t); err != nil {
return nil, fmt.Errorf("create template: %w", err)
}
return t, nil
}
// Update 更新模板provider 不可改)。
func (s *ChannelMonitorRequestTemplateService) Update(ctx context.Context, id int64, p ChannelMonitorRequestTemplateUpdateParams) (*ChannelMonitorRequestTemplate, error) {
existing, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if err := applyTemplateUpdate(existing, p); err != nil {
return nil, err
}
if err := s.repo.Update(ctx, existing); err != nil {
return nil, fmt.Errorf("update template: %w", err)
}
return existing, nil
}
// Delete 删除模板。关联监控的 template_id 会被 SET NULL监控保留快照继续跑。
func (s *ChannelMonitorRequestTemplateService) Delete(ctx context.Context, id int64) error {
if err := s.repo.Delete(ctx, id); err != nil {
return fmt.Errorf("delete template: %w", err)
}
return nil
}
// ApplyToMonitors 把模板当前配置应用到 monitorIDs 列表里的关联监控。
// monitorIDs 必须非空且每个 id 都必须当前 template_id = id不满足条件的会被 SQL WHERE 过滤掉。
// 返回实际被覆盖的监控数。
func (s *ChannelMonitorRequestTemplateService) ApplyToMonitors(ctx context.Context, id int64, monitorIDs []int64) (int64, error) {
if _, err := s.repo.GetByID(ctx, id); err != nil {
return 0, err
}
if len(monitorIDs) == 0 {
return 0, ErrChannelMonitorTemplateApplyEmpty
}
affected, err := s.repo.ApplyToMonitors(ctx, id, monitorIDs)
if err != nil {
return 0, fmt.Errorf("apply template to monitors: %w", err)
}
return affected, nil
}
// CountAssociatedMonitors 返回关联监控数。
func (s *ChannelMonitorRequestTemplateService) CountAssociatedMonitors(ctx context.Context, id int64) (int64, error) {
return s.repo.CountAssociatedMonitors(ctx, id)
}
// ListAssociatedMonitors 返回模板关联的所有监控简略信息。
// 给前端 apply picker 用handler 直接吐 JSON 不再做 join。
func (s *ChannelMonitorRequestTemplateService) ListAssociatedMonitors(ctx context.Context, id int64) ([]*AssociatedMonitorBrief, error) {
if _, err := s.repo.GetByID(ctx, id); err != nil {
return nil, err
}
return s.repo.ListAssociatedMonitors(ctx, id)
}
// ---------- 校验 & 工具 ----------
// validateTemplateCreateParams 聚合 create 入参校验,避免函数超过 30 行。
func validateTemplateCreateParams(p ChannelMonitorRequestTemplateCreateParams) error {
if strings.TrimSpace(p.Name) == "" {
return ErrChannelMonitorTemplateMissingName
}
if err := validateProvider(p.Provider); err != nil {
return ErrChannelMonitorTemplateInvalidProvider
}
if err := validateBodyModeParams(p.BodyOverrideMode, p.BodyOverride); err != nil {
return err
}
if err := validateExtraHeaders(p.ExtraHeaders); err != nil {
return err
}
return nil
}
// applyTemplateUpdate 把 update params 中非 nil 字段应用到 existing 上。
func applyTemplateUpdate(existing *ChannelMonitorRequestTemplate, p ChannelMonitorRequestTemplateUpdateParams) error {
if p.Name != nil {
name := strings.TrimSpace(*p.Name)
if name == "" {
return ErrChannelMonitorTemplateMissingName
}
existing.Name = name
}
if p.Description != nil {
existing.Description = strings.TrimSpace(*p.Description)
}
if p.ExtraHeaders != nil {
if err := validateExtraHeaders(*p.ExtraHeaders); err != nil {
return err
}
existing.ExtraHeaders = emptyHeadersIfNil(*p.ExtraHeaders)
}
// BodyOverrideMode / BodyOverride 联合校验:任一变化都用「更新后的值」做校验。
newMode := existing.BodyOverrideMode
newBody := existing.BodyOverride
if p.BodyOverrideMode != nil {
newMode = *p.BodyOverrideMode
}
if p.BodyOverride != nil {
newBody = *p.BodyOverride
}
if err := validateBodyModeParams(newMode, newBody); err != nil {
return err
}
existing.BodyOverrideMode = defaultBodyMode(newMode)
existing.BodyOverride = newBody
return nil
}
// validateBodyModeParams 校验 body_override_mode 合法,且 merge/replace 模式下 body_override 非空。
func validateBodyModeParams(mode string, body map[string]any) error {
switch mode {
case "", MonitorBodyOverrideModeOff:
return nil
case MonitorBodyOverrideModeMerge, MonitorBodyOverrideModeReplace:
if len(body) == 0 {
return ErrChannelMonitorTemplateBodyRequired
}
return nil
default:
return ErrChannelMonitorTemplateInvalidBodyMode
}
}
// headerNameRegex 合法 header 名RFC 7230 tokenASCII 可见字符减特殊符号)。
var headerNameRegex = regexp.MustCompile(`^[A-Za-z0-9!#$%&'*+\-.^_` + "`" + `|~]+$`)
// forbiddenHeaderNames hop-by-hop + HTTP 客户端自管的 header禁止用户覆盖
// 否则会让 Go http.Client 行为异常(双重 Content-Length、连接复用错乱等
var forbiddenHeaderNames = map[string]bool{
"host": true,
"content-length": true,
"content-encoding": true,
"transfer-encoding": true,
"connection": true,
}
// IsForbiddenHeaderName 对外暴露checker 运行时也会再过滤一次做兜底。
func IsForbiddenHeaderName(name string) bool {
return forbiddenHeaderNames[strings.ToLower(strings.TrimSpace(name))]
}
// validateExtraHeaders 校验 header 名字格式 + 黑名单。保存时就拒绝非法 header早失败。
func validateExtraHeaders(h map[string]string) error {
for k := range h {
if !headerNameRegex.MatchString(k) {
return ErrChannelMonitorTemplateHeaderInvalidName
}
if IsForbiddenHeaderName(k) {
return ErrChannelMonitorTemplateHeaderForbidden
}
}
return nil
}
// emptyHeadersIfNil 把 nil map 归一成空 maprepo 层写库时 JSONB 需要非 nil
func emptyHeadersIfNil(h map[string]string) map[string]string {
if h == nil {
return map[string]string{}
}
return h
}
// defaultBodyMode 空串归一为 off。
func defaultBodyMode(mode string) string {
if mode == "" {
return MonitorBodyOverrideModeOff
}
return mode
}

View File

@ -0,0 +1,77 @@
package service
import (
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
"time"
)
// ChannelMonitorRequestTemplate 请求模板service 层模型)。
// 作用:把一组可复用的 headers + 可选 body 覆盖配置抽出来管理,
// 被监控「应用」时以快照方式拷贝到监控本身的同名字段。
type ChannelMonitorRequestTemplate struct {
ID int64
Name string
Provider string
Description string
ExtraHeaders map[string]string
BodyOverrideMode string
BodyOverride map[string]any
CreatedAt time.Time
UpdatedAt time.Time
}
// ChannelMonitorRequestTemplateListParams 列表过滤。
type ChannelMonitorRequestTemplateListParams struct {
Provider string // 空 = 全部;非空则按 provider 过滤
}
// ChannelMonitorRequestTemplateCreateParams 创建参数。
type ChannelMonitorRequestTemplateCreateParams struct {
Name string
Provider string
Description string
ExtraHeaders map[string]string
BodyOverrideMode string
BodyOverride map[string]any
}
// ChannelMonitorRequestTemplateUpdateParams 更新参数(指针字段 = 不修改)。
// 注意 Provider 不可修改:改 provider 会让已关联监控的 body 黑名单语义错乱。
type ChannelMonitorRequestTemplateUpdateParams struct {
Name *string
Description *string
ExtraHeaders *map[string]string
BodyOverrideMode *string
BodyOverride *map[string]any
}
// 模板相关错误(命名与现有 ErrChannelMonitor* 风格保持一致)。
var (
ErrChannelMonitorTemplateNotFound = infraerrors.NotFound(
"CHANNEL_MONITOR_TEMPLATE_NOT_FOUND", "channel monitor request template not found",
)
ErrChannelMonitorTemplateInvalidProvider = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_INVALID_PROVIDER", "template provider must be one of openai/anthropic/gemini",
)
ErrChannelMonitorTemplateMissingName = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_MISSING_NAME", "template name is required",
)
ErrChannelMonitorTemplateInvalidBodyMode = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_INVALID_BODY_MODE", "body_override_mode must be one of off/merge/replace",
)
ErrChannelMonitorTemplateBodyRequired = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_BODY_REQUIRED", "body_override is required when body_override_mode is merge or replace",
)
ErrChannelMonitorTemplateHeaderForbidden = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_HEADER_FORBIDDEN", "header name is forbidden (hop-by-hop or computed by HTTP client)",
)
ErrChannelMonitorTemplateHeaderInvalidName = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_HEADER_INVALID_NAME", "header name contains invalid characters",
)
ErrChannelMonitorTemplateProviderMismatch = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_PROVIDER_MISMATCH", "monitor provider does not match template provider",
)
ErrChannelMonitorTemplateApplyEmpty = infraerrors.BadRequest(
"CHANNEL_MONITOR_TEMPLATE_APPLY_EMPTY", "monitor_ids must be a non-empty array",
)
)

View File

@ -0,0 +1,203 @@
package service
import "time"
// MonitorBodyOverrideMode 自定义请求体处理模式。
//
// - off 使用 adapter 默认 body忽略 BodyOverride
// - merge adapter 默认 body 与 BodyOverride 浅合并(用户优先;
// model/messages/contents 等关键字段在 checker 黑名单内会被静默丢弃)
// - replace 完全用 BodyOverride 作为 body跳过 challenge 校验,
// 改成 HTTP 2xx + 响应非空即视为可用(用户负责构造 body
const (
MonitorBodyOverrideModeOff = "off"
MonitorBodyOverrideModeMerge = "merge"
MonitorBodyOverrideModeReplace = "replace"
)
// ChannelMonitor 渠道监控配置service 层模型,不直接暴露 ent 类型)。
type ChannelMonitor struct {
ID int64
Name string
Provider string
Endpoint string
APIKey string // 解密后的明文 API Key仅在 service 内部使用handler 层不应直接序列化返回)
PrimaryModel string
ExtraModels []string
GroupName string
Enabled bool
IntervalSeconds int
LastCheckedAt *time.Time
CreatedBy int64
CreatedAt time.Time
UpdatedAt time.Time
// 请求自定义快照(来自模板拷贝 or 用户手填,运行时直接读取)
TemplateID *int64 // 仅用于 UI 分组 + 一键应用,运行时不用
ExtraHeaders map[string]string // 与 adapter 默认 headers 合并,用户优先
BodyOverrideMode string // off / merge / replace
BodyOverride map[string]any // 仅 mode != off 时使用
// APIKeyDecryptFailed 表示 APIKey 字段无法解密(密钥不一致或损坏)。
// 此时 APIKey 为空字符串runner / RunCheck 必须跳过该监控并提示重填。
APIKeyDecryptFailed bool
}
// ChannelMonitorListParams 列表查询过滤参数。
type ChannelMonitorListParams struct {
Page int
PageSize int
Provider string
Enabled *bool
Search string
}
// ChannelMonitorCreateParams 创建参数。
type ChannelMonitorCreateParams struct {
Name string
Provider string
Endpoint string
APIKey string
PrimaryModel string
ExtraModels []string
GroupName string
Enabled bool
IntervalSeconds int
CreatedBy int64
TemplateID *int64
ExtraHeaders map[string]string
BodyOverrideMode string
BodyOverride map[string]any
}
// ChannelMonitorUpdateParams 更新参数(指针字段表示"未提供则不更新")。
type ChannelMonitorUpdateParams struct {
Name *string
Provider *string
Endpoint *string
APIKey *string // 空字符串表示不修改;非空字符串覆盖
PrimaryModel *string
ExtraModels *[]string
GroupName *string
Enabled *bool
IntervalSeconds *int
// 自定义快照字段:指针为 nil 表示不更新,非 nil 覆盖
// TemplateID *(*int64):用 ** 表达三态nil=不更新;&nil=清空;&&id=设为 id。
// 简化处理:用 ClearTemplate 显式标志 + TemplateID普通指针
TemplateID *int64
ClearTemplate bool // true 时无视 TemplateID把监控的 template_id 置空
ExtraHeaders *map[string]string
BodyOverrideMode *string
BodyOverride *map[string]any
}
// CheckResult 单个模型一次检测的结果。
type CheckResult struct {
Model string
Status string // operational / degraded / failed / error
LatencyMs *int
PingLatencyMs *int
Message string
CheckedAt time.Time
}
// UserMonitorView 用户只读视图:监控概览(含主模型最近状态 + 7d 可用率 + 附加模型最近状态)。
type UserMonitorView struct {
ID int64
Name string
Provider string
GroupName string
PrimaryModel string
PrimaryStatus string
PrimaryLatencyMs *int
PrimaryPingLatencyMs *int // 主模型最近一次 ping 延迟
Availability7d float64 // 0-100
ExtraModels []ExtraModelStatus
Timeline []UserMonitorTimelinePoint // 主模型最近 N 个历史点(按 checked_at DESC最新在前
}
// UserMonitorTimelinePoint 用户视图 timeline 单点数据(去除 message 以减小响应体)。
type UserMonitorTimelinePoint struct {
Status string `json:"status"`
LatencyMs *int `json:"latency_ms"`
PingLatencyMs *int `json:"ping_latency_ms"`
CheckedAt time.Time `json:"checked_at"`
}
// ExtraModelStatus 附加模型最近一次状态。
type ExtraModelStatus struct {
Model string
Status string
LatencyMs *int
}
// UserMonitorDetail 用户只读视图:监控详情(含全部模型 7d/15d/30d 可用率与平均延迟)。
type UserMonitorDetail struct {
ID int64
Name string
Provider string
GroupName string
Models []ModelDetail
}
// ModelDetail 单个模型的可用率/延迟统计。
type ModelDetail struct {
Model string
LatestStatus string
LatestLatencyMs *int
Availability7d float64 // 0-100
Availability15d float64
Availability30d float64
AvgLatency7dMs *int
}
// ChannelMonitorHistoryRow 历史记录入库行service 层向 repository 提交的数据)。
type ChannelMonitorHistoryRow struct {
MonitorID int64
Model string
Status string
LatencyMs *int
PingLatencyMs *int
Message string
CheckedAt time.Time
}
// ChannelMonitorHistoryEntry 历史记录查询返回行(含 ent 主键 ID
type ChannelMonitorHistoryEntry struct {
ID int64
Model string
Status string
LatencyMs *int
PingLatencyMs *int
Message string
CheckedAt time.Time
}
// ChannelMonitorLatest 最近一次检测的简明信息(用于 UserMonitorView 聚合)。
type ChannelMonitorLatest struct {
Model string
Status string
LatencyMs *int
PingLatencyMs *int
CheckedAt time.Time
}
// ChannelMonitorAvailability 单个模型在某窗口内的可用率与平均延迟(用于 UserMonitorDetail 聚合)。
type ChannelMonitorAvailability struct {
Model string
WindowDays int
TotalChecks int
OperationalChecks int // operational + degraded 视为可用
AvailabilityPct float64
AvgLatencyMs *int
}
// MonitorStatusSummary 监控状态聚合admin list 用,单次 repo 查询消除前端 N+1
// PrimaryStatus / PrimaryLatencyMs 描述主模型最近状态Availability7d 是主模型 7 天可用率;
// ExtraModels 描述附加模型最近状态(用于 hover 展示)。
type MonitorStatusSummary struct {
PrimaryStatus string // 空字符串表示无历史
PrimaryLatencyMs *int
Availability7d float64 // 0-100无历史时为 0
ExtraModels []ExtraModelStatus
}

View File

@ -0,0 +1,99 @@
package service
import (
"context"
"net/url"
"strings"
)
// 渠道监控参数校验与归一化辅助函数。
// 校验失败一律返回 channel_monitor_const.go 中预定义的 Err* 错误,错误信息不含具体 IP/hostname避免泄露内网拓扑。
// validateProvider 校验 provider 字符串。
// 唯一来源于 providerAdapters新增 provider 只需要在 channel_monitor_checker.go 注册 adapter。
func validateProvider(p string) error {
if !isSupportedProvider(p) {
return ErrChannelMonitorInvalidProvider
}
return nil
}
// validateInterval 校验 interval_seconds 范围。
func validateInterval(sec int) error {
if sec < monitorMinIntervalSeconds || sec > monitorMaxIntervalSeconds {
return ErrChannelMonitorInvalidInterval
}
return nil
}
// validateEndpoint 校验 endpoint
// - scheme 强制 https拒绝 http避免明文凭证 + 部分 SSRF 利用面)
// - 必须为 origin无 path/query/fragment防止用户填 https://api.openai.com/v1
// 导致 joinURL 拼出 /v1/v1/chat/completions
// - hostname 不能是 localhost/metadata 等已知元数据 hostname
// - 解析所有 IP任一落在 loopback/RFC1918/link-local/ULA 段即拒绝(防 SSRF
//
// 错误信息不暴露具体 IP / hostname避免泄露内网拓扑。
func validateEndpoint(ep string) error {
ep = strings.TrimSpace(ep)
if ep == "" {
return ErrChannelMonitorInvalidEndpoint
}
u, err := url.Parse(ep)
if err != nil {
return ErrChannelMonitorInvalidEndpoint
}
if u.Scheme != "https" {
return ErrChannelMonitorEndpointScheme
}
if u.Host == "" {
return ErrChannelMonitorInvalidEndpoint
}
if u.Path != "" && u.Path != "/" {
return ErrChannelMonitorEndpointPath
}
if u.RawQuery != "" || u.Fragment != "" {
return ErrChannelMonitorEndpointPath
}
hostname := u.Hostname()
ctx, cancel := context.WithTimeout(context.Background(), monitorEndpointResolveTimeout)
defer cancel()
blocked, err := isPrivateOrLoopbackHost(ctx, hostname)
if err != nil {
return ErrChannelMonitorEndpointUnreachable
}
if blocked {
return ErrChannelMonitorEndpointPrivate
}
return nil
}
// normalizeEndpoint 去除前后空白与末尾 `/`,保证存储统一为 origin。
// validateEndpoint 已确保格式合法(仅 origin这里只做最终归一化。
func normalizeEndpoint(ep string) string {
ep = strings.TrimSpace(ep)
ep = strings.TrimRight(ep, "/")
return ep
}
// normalizeModels 去除空白、重复模型名。保留输入顺序map 的迭代顺序无关)。
func normalizeModels(in []string) []string {
if len(in) == 0 {
return []string{}
}
seen := make(map[string]struct{}, len(in))
out := make([]string, 0, len(in))
for _, m := range in {
m = strings.TrimSpace(m)
if m == "" {
continue
}
if _, ok := seen[m]; ok {
continue
}
seen[m] = struct{}{}
out = append(out, m)
}
return out
}

View File

@ -141,17 +141,23 @@ const (
// ChannelService 渠道管理服务
type ChannelService struct {
repo ChannelRepository
groupRepo GroupRepository
authCacheInvalidator APIKeyAuthCacheInvalidator
pricingService *PricingService // 用于「可用渠道」展示时回落到全局定价;可为 nil测试场景
cache atomic.Value // *channelCache
cacheSF singleflight.Group
}
// NewChannelService 创建渠道服务实例
func NewChannelService(repo ChannelRepository, authCacheInvalidator APIKeyAuthCacheInvalidator) *ChannelService {
// NewChannelService 创建渠道服务实例。
// pricingService 仅供 ListAvailable 在渠道未配置定价时回落到全局 LiteLLM 数据;
// 计费热路径走独立的 ModelPricingResolver与此参数无关。可传 nil。
func NewChannelService(repo ChannelRepository, groupRepo GroupRepository, authCacheInvalidator APIKeyAuthCacheInvalidator, pricingService *PricingService) *ChannelService {
s := &ChannelService{
repo: repo,
groupRepo: groupRepo,
authCacheInvalidator: authCacheInvalidator,
pricingService: pricingService,
}
return s
}
@ -299,6 +305,9 @@ func (s *ChannelService) fetchChannelData(ctx context.Context) ([]Channel, map[i
}
// populateChannelCache 将渠道列表和分组平台映射填充到缓存快照中。
// 装填时对每个 Channel 统一归一化 BillingModelSource让缓存命中的所有下游
// gateway routing / billing / 未来任何 cache-backed 读路径)都拿到已归一化的实体,
// 避免"每个出口各自记得 normalize"反模式。
func populateChannelCache(channels []Channel, groupPlatforms map[int64]string) *channelCache {
cache := newEmptyChannelCache()
cache.groupPlatform = groupPlatforms
@ -306,6 +315,7 @@ func populateChannelCache(channels []Channel, groupPlatforms map[int64]string) *
cache.loadedAt = time.Now()
for i := range channels {
channels[i].normalizeBillingModelSource()
ch := &channels[i]
cache.byID[ch.ID] = ch
for _, gid := range ch.GroupIDs {
@ -516,14 +526,13 @@ func (s *ChannelService) ResolveChannelMappingAndRestrict(ctx context.Context, g
// resolveMapping 基于已查找的渠道信息解析模型映射。
// antigravity 分组依次尝试所有匹配平台,确保跨平台同名映射各自独立。
func resolveMapping(lk *channelLookup, groupID int64, model string) ChannelMappingResult {
// lk.channel 来自已装填的缓存BillingModelSource 已在 populateChannelCache 阶段归一化,
// 这里无需重复兜底。
result := ChannelMappingResult{
MappedModel: model,
ChannelID: lk.channel.ID,
BillingModelSource: lk.channel.BillingModelSource,
}
if result.BillingModelSource == "" {
result.BillingModelSource = BillingModelSourceChannelMapped
}
modelLower := strings.ToLower(model)
if mapped := lookupMappingAcrossPlatforms(lk.cache, groupID, lk.platform, modelLower); mapped != "" {
@ -684,9 +693,7 @@ func (s *ChannelService) Create(ctx context.Context, input *CreateChannelInput)
ApplyPricingToAccountStats: input.ApplyPricingToAccountStats,
AccountStatsPricingRules: input.AccountStatsPricingRules,
}
if channel.BillingModelSource == "" {
channel.BillingModelSource = BillingModelSourceChannelMapped
}
channel.normalizeBillingModelSource()
if err := validateChannelConfig(channel.ModelPricing, channel.ModelMapping); err != nil {
return nil, err
@ -702,12 +709,23 @@ func (s *ChannelService) Create(ctx context.Context, input *CreateChannelInput)
}
s.invalidateCache()
return s.repo.GetByID(ctx, channel.ID)
created, err := s.repo.GetByID(ctx, channel.ID)
if err != nil {
return nil, err
}
created.normalizeBillingModelSource()
return created, nil
}
// GetByID 获取渠道详情
// GetByID 获取渠道详情。返回前统一把空 BillingModelSource 回填为 ChannelMapped
// 让所有 handler 无需重复处理历史空值。
func (s *ChannelService) GetByID(ctx context.Context, id int64) (*Channel, error) {
return s.repo.GetByID(ctx, id)
ch, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
ch.normalizeBillingModelSource()
return ch, nil
}
// Update 更新渠道
@ -739,7 +757,12 @@ func (s *ChannelService) Update(ctx context.Context, id int64, input *UpdateChan
s.invalidateCache()
s.invalidateAuthCacheForGroups(ctx, oldGroupIDs, channel.GroupIDs)
return s.repo.GetByID(ctx, id)
updated, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
updated.normalizeBillingModelSource()
return updated, nil
}
// applyUpdateInput 将更新请求的字段应用到渠道实体上。
@ -857,7 +880,14 @@ func (s *ChannelService) Delete(ctx context.Context, id int64) error {
// List 获取渠道列表
func (s *ChannelService) List(ctx context.Context, params pagination.PaginationParams, status, search string) ([]Channel, *pagination.PaginationResult, error) {
return s.repo.List(ctx, params, status, search)
channels, res, err := s.repo.List(ctx, params, status, search)
if err != nil {
return nil, nil, err
}
for i := range channels {
channels[i].normalizeBillingModelSource()
}
return channels, res, nil
}
// modelEntry 表示一个模型模式条目(用于冲突检测)
@ -884,12 +914,7 @@ func conflictsBetween(a, b modelEntry) bool {
// toModelEntry 将模型名转换为 modelEntry
func toModelEntry(pattern string) modelEntry {
lower := strings.ToLower(pattern)
isWild := strings.HasSuffix(lower, "*")
prefix := lower
if isWild {
prefix = strings.TrimSuffix(lower, "*")
}
prefix, isWild := splitWildcardSuffix(strings.ToLower(pattern))
return modelEntry{pattern: pattern, prefix: prefix, wildcard: isWild}
}

View File

@ -189,11 +189,11 @@ func (m *mockChannelAuthCacheInvalidator) InvalidateAuthCacheByGroupID(_ context
// ---------------------------------------------------------------------------
func newTestChannelService(repo *mockChannelRepository) *ChannelService {
return NewChannelService(repo, nil)
return NewChannelService(repo, nil, nil, nil)
}
func newTestChannelServiceWithAuth(repo *mockChannelRepository, auth *mockChannelAuthCacheInvalidator) *ChannelService {
return NewChannelService(repo, auth)
return NewChannelService(repo, nil, auth, nil)
}
// makeStandardRepo returns a repo that serves one active channel with anthropic pricing

View File

@ -433,3 +433,296 @@ func TestValidateIntervals_UnboundedNotLast(t *testing.T) {
require.Contains(t, err.Error(), "unbounded")
require.Contains(t, err.Error(), "last")
}
func TestSupportedModels_ExactKeysAndPricing(t *testing.T) {
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 10, Platform: "anthropic", Models: []string{"claude-sonnet-4-6"}, InputPrice: testPtrFloat64(3e-6)},
{ID: 11, Platform: "anthropic", Models: []string{"claude-opus-4-6"}, InputPrice: testPtrFloat64(1.5e-5)},
},
ModelMapping: map[string]map[string]string{
"anthropic": {
"claude-sonnet-4-6": "claude-sonnet-4-6",
"claude-opus-4-6": "claude-opus-4-6",
},
},
}
got := ch.SupportedModels()
require.Len(t, got, 2)
require.Equal(t, "anthropic", got[0].Platform)
require.Equal(t, "claude-opus-4-6", got[0].Name)
require.NotNil(t, got[0].Pricing)
require.Equal(t, int64(11), got[0].Pricing.ID)
require.Equal(t, "claude-sonnet-4-6", got[1].Name)
require.Equal(t, int64(10), got[1].Pricing.ID)
}
func TestSupportedModels_WildcardExpandedFromPricing(t *testing.T) {
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "anthropic", Models: []string{"claude-sonnet-4-6", "claude-sonnet-4-5"}},
{ID: 2, Platform: "anthropic", Models: []string{"claude-opus-4-6"}},
},
ModelMapping: map[string]map[string]string{
"anthropic": {
"claude-sonnet-*": "claude-sonnet-4-6",
},
},
}
got := ch.SupportedModels()
names := make([]string, 0, len(got))
for _, m := range got {
names = append(names, m.Name)
}
require.ElementsMatch(t, []string{"claude-sonnet-4-5", "claude-sonnet-4-6", "claude-opus-4-6"}, names)
for _, m := range got {
require.NotContains(t, m.Name, "*")
}
}
func TestSupportedModels_MissingPricingKeepsNilPricing(t *testing.T) {
ch := &Channel{
ModelMapping: map[string]map[string]string{
"anthropic": {"claude-sonnet-4-6": "claude-sonnet-4-6"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 1)
require.Equal(t, "claude-sonnet-4-6", got[0].Name)
require.Nil(t, got[0].Pricing)
}
func TestSupportedModels_DedupAndSort(t *testing.T) {
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "anthropic", Models: []string{"claude-sonnet-4-6", "claude-sonnet-4-5"}},
{ID: 2, Platform: "openai", Models: []string{"gpt-4o"}},
},
ModelMapping: map[string]map[string]string{
"anthropic": {
"claude-sonnet-4-6": "upstream-a",
"claude-sonnet-*": "upstream-a",
},
"openai": {"gpt-4o": "gpt-4o"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 3)
require.Equal(t, "anthropic", got[0].Platform)
require.Equal(t, "claude-sonnet-4-5", got[0].Name)
require.Equal(t, "anthropic", got[1].Platform)
require.Equal(t, "claude-sonnet-4-6", got[1].Name)
require.Equal(t, "openai", got[2].Platform)
require.Equal(t, "gpt-4o", got[2].Name)
}
func TestSupportedModels_NilChannelAndEmpty(t *testing.T) {
var nilCh *Channel
require.Nil(t, nilCh.SupportedModels())
empty := &Channel{}
require.Nil(t, empty.SupportedModels())
}
func TestGetModelPricingByPlatform(t *testing.T) {
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "anthropic", Models: []string{"claude-sonnet-4-6"}, InputPrice: testPtrFloat64(3e-6)},
{ID: 2, Platform: "openai", Models: []string{"claude-sonnet-4-6"}, InputPrice: testPtrFloat64(1e-6)},
},
}
ant := ch.GetModelPricingByPlatform("anthropic", "claude-sonnet-4-6")
require.NotNil(t, ant)
require.Equal(t, int64(1), ant.ID)
oa := ch.GetModelPricingByPlatform("openai", "claude-sonnet-4-6")
require.NotNil(t, oa)
require.Equal(t, int64(2), oa.ID)
require.Nil(t, ch.GetModelPricingByPlatform("gemini", "claude-sonnet-4-6"))
}
func TestSupportedModels_WildcardOnlyPricingRowsSkipped(t *testing.T) {
// 定价中含通配符条目pattern不应被当作具体模型名展开。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "anthropic", Models: []string{"claude-sonnet-*", "claude-sonnet-4-6"}},
},
ModelMapping: map[string]map[string]string{
"anthropic": {"claude-sonnet-*": "claude-sonnet-4-6"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 1)
require.Equal(t, "claude-sonnet-4-6", got[0].Name)
for _, m := range got {
require.NotContains(t, m.Name, "*")
}
}
func TestSupportedModels_WildcardPrefixMatchesNothing(t *testing.T) {
// 通配符模式无任何对应定价模型时,该平台 mapping 路不产出;
// 但其他平台的 pricing-only 模型仍会通过 Pass B 出现。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "openai", Models: []string{"gpt-4o"}},
},
ModelMapping: map[string]map[string]string{
"anthropic": {"gpt-foo-*": "gpt-foo-1"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 1)
require.Equal(t, "openai", got[0].Platform)
require.Equal(t, "gpt-4o", got[0].Name)
}
func TestSupportedModels_CrossPlatformPricingDoesNotBleed(t *testing.T) {
// anthropic 的通配符不应把 openai 定价行拉到 anthropic 平台下;
// openai 的 pricing-only 模型则正常通过 Pass B 暴露在 openai 平台下。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "openai", Models: []string{"claude-sonnet-4-6"}},
},
ModelMapping: map[string]map[string]string{
"anthropic": {"claude-sonnet-*": "x"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 1)
require.Equal(t, "openai", got[0].Platform, "不能把 openai 定价标记为 anthropic 模型")
require.Equal(t, "claude-sonnet-4-6", got[0].Name)
}
func TestSupportedModels_CaseInsensitiveDedup(t *testing.T) {
// 两行定价用不同大小写定义了同一模型,结果应去重为 1 条;首次出现的原始大小写保留。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "openai", Models: []string{"GPT-4o"}},
{ID: 2, Platform: "openai", Models: []string{"gpt-4o"}},
},
ModelMapping: map[string]map[string]string{
"openai": {"gpt-*": "x"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 1)
require.Equal(t, "GPT-4o", got[0].Name)
}
func TestSupportedModels_EmptyPlatformMapping(t *testing.T) {
// ModelMapping 平台 key 存在但 value 为空 mapmapping 路跳过该平台,
// 但 pricing 路仍会把该平台的定价模型补齐关键修复azcc 这种"只配定价不配映射"渠道)。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "anthropic", Models: []string{"claude-sonnet-4-6"}},
},
ModelMapping: map[string]map[string]string{
"anthropic": {},
},
}
got := ch.SupportedModels()
require.Len(t, got, 1)
require.Equal(t, "anthropic", got[0].Platform)
require.Equal(t, "claude-sonnet-4-6", got[0].Name)
require.NotNil(t, got[0].Pricing)
}
func TestSupportedModels_ExactKeyUsesPricedCaseWhenAvailable(t *testing.T) {
// mapping key uses uppercase, pricing uses lowercase — pricing's case should win.
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "openai", Models: []string{"gpt-4o"}},
},
ModelMapping: map[string]map[string]string{
"openai": {"GPT-4o": "gpt-4o"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 1)
require.Equal(t, "gpt-4o", got[0].Name) // pricing's case wins
}
func TestSupportedModels_AsteriskOnlyMappingExpandsAllPriced(t *testing.T) {
// 映射 key 为单独的 "*":前缀为空 → 命中该平台所有定价模型(透传场景)。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "openai", Models: []string{"gpt-4o", "gpt-4o-mini"}},
},
ModelMapping: map[string]map[string]string{
"openai": {"*": "gpt-4o"},
},
}
got := ch.SupportedModels()
require.Len(t, got, 2)
names := []string{got[0].Name, got[1].Name}
require.ElementsMatch(t, []string{"gpt-4o", "gpt-4o-mini"}, names)
}
func TestSupportedModels_PricingOnlyNoMapping(t *testing.T) {
// 渠道完全没配 mapping只配了定价 —— 应该把所有定价模型作为支持模型返回。
// 这是修复前的核心 bug 场景(前端显示"未配置模型")。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "anthropic", Models: []string{"claude-opus-4-6"}, InputPrice: testPtrFloat64(1.5e-5)},
{ID: 2, Platform: "anthropic", Models: []string{"claude-haiku-4-5"}, InputPrice: testPtrFloat64(3e-7)},
},
}
got := ch.SupportedModels()
require.Len(t, got, 2)
require.Equal(t, "claude-haiku-4-5", got[0].Name)
require.NotNil(t, got[0].Pricing)
require.Equal(t, int64(2), got[0].Pricing.ID)
require.Equal(t, "claude-opus-4-6", got[1].Name)
require.Equal(t, int64(1), got[1].Pricing.ID)
}
func TestSupportedModels_ExactMappingUsesTargetPricing(t *testing.T) {
// 精确 mapping `src → target`:定价应按 target 查(实际计费的是 target
// 而不是按 src 自查。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 100, Platform: "anthropic", Models: []string{"req-model"}, InputPrice: testPtrFloat64(3e-6)},
{ID: 200, Platform: "anthropic", Models: []string{"served-model"}, InputPrice: testPtrFloat64(1.5e-5)},
},
ModelMapping: map[string]map[string]string{
"anthropic": {
"req-model": "served-model",
},
},
}
got := ch.SupportedModels()
require.Len(t, got, 2)
require.Equal(t, "req-model", got[0].Name)
require.NotNil(t, got[0].Pricing)
require.Equal(t, int64(200), got[0].Pricing.ID, "req-model 显示但定价是 served-model 的mapping target")
require.Equal(t, "served-model", got[1].Name)
require.Equal(t, int64(200), got[1].Pricing.ID)
}
func TestSupportedModels_ExactMappingTargetMissingFromPricing(t *testing.T) {
// `src → target` 但 target 不在渠道定价里 —— 结果中 src 的 Pricing 为 nil
// (等待 ListAvailable 阶段的全局 LiteLLM 回落填充)。
ch := &Channel{
ModelPricing: []ChannelModelPricing{
{ID: 1, Platform: "anthropic", Models: []string{"some-priced-model"}, InputPrice: testPtrFloat64(1.5e-5)},
},
ModelMapping: map[string]map[string]string{
"anthropic": {
"missing-src": "missing-target",
},
},
}
got := ch.SupportedModels()
require.Len(t, got, 2)
require.Equal(t, "missing-src", got[0].Name)
require.Nil(t, got[0].Pricing, "target 在渠道定价中缺失时不虚假填充,留给 ListAvailable 走 LiteLLM 回落")
require.Equal(t, "some-priced-model", got[1].Name)
require.NotNil(t, got[1].Pricing)
}

View File

@ -243,6 +243,23 @@ const (
// SettingKeyOpsRuntimeLogConfig stores JSON config for runtime log settings.
SettingKeyOpsRuntimeLogConfig = "ops_runtime_log_config"
// =========================
// Channel Monitor (渠道监控)
// =========================
// SettingKeyChannelMonitorEnabled is a DB-backed soft switch for the channel monitor feature.
// When false: runner skips scheduling and user-facing endpoints return an empty list.
SettingKeyChannelMonitorEnabled = "channel_monitor_enabled"
// SettingKeyChannelMonitorDefaultIntervalSeconds controls the default interval (seconds)
// pre-filled when creating a new channel monitor from the admin UI. Range: [15, 3600].
SettingKeyChannelMonitorDefaultIntervalSeconds = "channel_monitor_default_interval_seconds"
// SettingKeyAvailableChannelsEnabled is a DB-backed soft switch for the "Available Channels"
// user-facing aggregate view. When false: user endpoint returns an empty list and the
// sidebar entry is hidden. Defaults to false (opt-in feature).
SettingKeyAvailableChannelsEnabled = "available_channels_enabled"
// =========================
// Overload Cooldown (529)
// =========================

View File

@ -184,7 +184,7 @@ func newResolverWithChannel(t *testing.T, pricing []ChannelModelPricing) *ModelP
return map[int64]string{groupID: "anthropic"}, nil
},
}
cs := NewChannelService(repo, nil)
cs := NewChannelService(repo, nil, nil, nil)
bs := newTestBillingServiceForResolver()
return NewModelPricingResolver(cs, bs)
}
@ -517,7 +517,7 @@ func TestResolve_WithChannelOverride_CacheError(t *testing.T) {
return nil, errors.New("database unavailable")
},
}
cs := NewChannelService(repo, nil)
cs := NewChannelService(repo, nil, nil, nil)
bs := newTestBillingServiceForResolver()
r := NewModelPricingResolver(cs, bs)

View File

@ -36,11 +36,15 @@ return 0
// - Scheduling: 5-field cron spec (minute hour dom month dow).
// - Multi-instance: best-effort Redis leader lock so only one node runs cleanup.
// - Safety: deletes in batches to avoid long transactions.
//
// 附带:在 runCleanupOnce 末尾调用 ChannelMonitorService.RunDailyMaintenance
// 统一共享 cron schedule + leader lock + heartbeat避免再引一套调度。
type OpsCleanupService struct {
opsRepo OpsRepository
db *sql.DB
redisClient *redis.Client
cfg *config.Config
opsRepo OpsRepository
db *sql.DB
redisClient *redis.Client
cfg *config.Config
channelMonitorSvc *ChannelMonitorService
instanceID string
@ -57,13 +61,15 @@ func NewOpsCleanupService(
db *sql.DB,
redisClient *redis.Client,
cfg *config.Config,
channelMonitorSvc *ChannelMonitorService,
) *OpsCleanupService {
return &OpsCleanupService{
opsRepo: opsRepo,
db: db,
redisClient: redisClient,
cfg: cfg,
instanceID: uuid.NewString(),
opsRepo: opsRepo,
db: db,
redisClient: redisClient,
cfg: cfg,
channelMonitorSvc: channelMonitorSvc,
instanceID: uuid.NewString(),
}
}
@ -248,6 +254,15 @@ func (s *OpsCleanupService) runCleanupOnce(ctx context.Context) (opsCleanupDelet
out.dailyPreagg = n
}
// Channel monitor 每日维护(聚合昨日明细 + 软删过期明细/聚合)。
// 失败只记日志,不影响 ops 清理的成功状态(与 ops 各步骤风格一致);
// 维护本身已经把每步错误打到 slogheartbeat result 不再分项记录。
if s.channelMonitorSvc != nil {
if err := s.channelMonitorSvc.RunDailyMaintenance(ctx); err != nil {
logger.LegacyPrintf("service.ops_cleanup", "[OpsCleanup] channel monitor maintenance failed: %v", err)
}
}
return out, nil
}

View File

@ -2,6 +2,7 @@ package service
import (
"context"
"errors"
"fmt"
"log/slog"
"math"
@ -16,6 +17,14 @@ import (
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
)
// ErrOrderNotFound is returned by HandlePaymentNotification when the webhook
// references an out_trade_no that does not exist in our DB. Callers (webhook
// handlers) should treat this as a terminal, non-retryable condition and still
// respond with a 2xx success to the provider — otherwise the provider will keep
// retrying forever (e.g. when a foreign environment's webhook endpoint is
// misconfigured to point at us, or when our orders table has been wiped).
var ErrOrderNotFound = errors.New("payment order not found")
// --- Payment Notification & Fulfillment ---
func (s *PaymentService) HandlePaymentNotification(ctx context.Context, n *payment.PaymentNotification, pk string) error {
@ -30,7 +39,10 @@ func (s *PaymentService) HandlePaymentNotification(ctx context.Context, n *payme
if oid, ok := parseLegacyPaymentOrderID(n.OrderID, err); ok {
return s.confirmPayment(ctx, oid, n.TradeNo, n.Amount, pk, n.Metadata)
}
return fmt.Errorf("order not found for out_trade_no: %s", n.OrderID)
if dbent.IsNotFound(err) {
return fmt.Errorf("%w: out_trade_no=%s", ErrOrderNotFound, n.OrderID)
}
return fmt.Errorf("lookup order failed for out_trade_no %s: %w", n.OrderID, err)
}
return s.confirmPayment(ctx, order.ID, n.TradeNo, n.Amount, pk, n.Metadata)
}

View File

@ -0,0 +1,106 @@
//go:build unit
package service
import (
"context"
"database/sql"
"errors"
"testing"
"entgo.io/ent/dialect"
entsql "entgo.io/ent/dialect/sql"
_ "modernc.org/sqlite"
dbent "github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/ent/enttest"
"github.com/Wei-Shaw/sub2api/internal/payment"
"github.com/stretchr/testify/require"
)
// newOrderNotFoundTestClient wires an in-memory sqlite-backed ent.Client so
// tests can exercise HandlePaymentNotification's real DB lookup path without
// standing up a service stack.
func newOrderNotFoundTestClient(t *testing.T) *dbent.Client {
t.Helper()
db, err := sql.Open("sqlite", "file:payment_order_not_found?mode=memory&cache=shared&_fk=1")
require.NoError(t, err)
t.Cleanup(func() { _ = db.Close() })
_, err = db.Exec("PRAGMA foreign_keys = ON")
require.NoError(t, err)
drv := entsql.OpenDB(dialect.SQLite, db)
client := enttest.NewClient(t, enttest.WithOptions(dbent.Driver(drv)))
t.Cleanup(func() { _ = client.Close() })
return client
}
// TestHandlePaymentNotification_UnknownOrder_ReturnsSentinel exercises the
// happy-path of the webhook 404 fix: when the notification references an
// out_trade_no that does not exist in our DB, HandlePaymentNotification must
// return an error that errors.Is(err, ErrOrderNotFound) recognizes. The
// webhook handler relies on that contract to ack with a 2xx so the provider
// stops retrying.
func TestHandlePaymentNotification_UnknownOrder_ReturnsSentinel(t *testing.T) {
ctx := context.Background()
client := newOrderNotFoundTestClient(t)
svc := &PaymentService{
entClient: client,
providersLoaded: true,
}
notification := &payment.PaymentNotification{
OrderID: "sub2_does_not_exist_12345",
TradeNo: "stripe_evt_test_xyz",
Status: payment.NotificationStatusSuccess,
Amount: 1000,
}
err := svc.HandlePaymentNotification(ctx, notification, payment.TypeStripe)
require.Error(t, err, "unknown out_trade_no should surface an error")
require.ErrorIs(t, err, ErrOrderNotFound,
"webhook handler relies on errors.Is(err, ErrOrderNotFound) to downgrade to 200")
// Sanity: the wrapped error message should still include the out_trade_no
// for operator diagnostics.
require.Contains(t, err.Error(), notification.OrderID)
}
// TestHandlePaymentNotification_NonSuccessStatus_Skips documents the
// short-circuit that precedes the DB lookup: when the notification is not a
// success event (e.g. Stripe non-payment events that reach us via the webhook
// route), we return nil without touching the DB and the handler responds 200.
func TestHandlePaymentNotification_NonSuccessStatus_Skips(t *testing.T) {
ctx := context.Background()
client := newOrderNotFoundTestClient(t)
svc := &PaymentService{
entClient: client,
providersLoaded: true,
}
notification := &payment.PaymentNotification{
OrderID: "sub2_does_not_exist_12345",
Status: "failed", // any value other than NotificationStatusSuccess
}
err := svc.HandlePaymentNotification(ctx, notification, payment.TypeStripe)
require.NoError(t, err,
"non-success notifications must short-circuit before the DB lookup")
}
// TestErrOrderNotFound_DistinctFromOtherErrors guards against an accidental
// collapse where a generic wrapped error would start matching ErrOrderNotFound
// (which would silently mask real DB failures).
func TestErrOrderNotFound_DistinctFromOtherErrors(t *testing.T) {
genericErr := errors.New("some other failure")
require.False(t, errors.Is(genericErr, ErrOrderNotFound))
require.False(t, errors.Is(ErrOrderNotFound, genericErr))
wrappedLookupErr := errors.New("lookup order failed for out_trade_no sub2_42: connection refused")
require.False(t, errors.Is(wrappedLookupErr, ErrOrderNotFound),
"DB connection failures must not masquerade as order-not-found")
}

View File

@ -450,6 +450,9 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
SettingKeyBalanceLowNotifyThreshold,
SettingKeyBalanceLowNotifyRechargeURL,
SettingKeyAccountQuotaNotifyEnabled,
SettingKeyChannelMonitorEnabled,
SettingKeyChannelMonitorDefaultIntervalSeconds,
SettingKeyAvailableChannelsEnabled,
}
settings, err := s.settingRepo.GetMultiple(ctx, keys)
@ -532,9 +535,88 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
AccountQuotaNotifyEnabled: settings[SettingKeyAccountQuotaNotifyEnabled] == "true",
BalanceLowNotifyThreshold: balanceLowNotifyThreshold,
BalanceLowNotifyRechargeURL: settings[SettingKeyBalanceLowNotifyRechargeURL],
ChannelMonitorEnabled: !isFalseSettingValue(settings[SettingKeyChannelMonitorEnabled]),
ChannelMonitorDefaultIntervalSeconds: parseChannelMonitorInterval(settings[SettingKeyChannelMonitorDefaultIntervalSeconds]),
AvailableChannelsEnabled: settings[SettingKeyAvailableChannelsEnabled] == "true",
}, nil
}
// channelMonitorIntervalMin / channelMonitorIntervalMax bound the default interval
// (mirrors the monitor-level constraint but lives here so setting_service stays decoupled).
const (
channelMonitorIntervalMin = 15
channelMonitorIntervalMax = 3600
channelMonitorIntervalFallback = 60
)
// parseChannelMonitorInterval parses the stored string and clamps to [15, 3600].
// Empty / invalid input falls back to channelMonitorIntervalFallback.
func parseChannelMonitorInterval(raw string) int {
v, err := strconv.Atoi(strings.TrimSpace(raw))
if err != nil {
return channelMonitorIntervalFallback
}
return clampChannelMonitorInterval(v)
}
// clampChannelMonitorInterval clamps v to the allowed range. 0 means "not provided".
func clampChannelMonitorInterval(v int) int {
if v <= 0 {
return 0
}
if v < channelMonitorIntervalMin {
return channelMonitorIntervalMin
}
if v > channelMonitorIntervalMax {
return channelMonitorIntervalMax
}
return v
}
// ChannelMonitorRuntime is the lightweight view of the channel monitor feature
// consumed by the runner and user-facing handlers.
type ChannelMonitorRuntime struct {
Enabled bool
DefaultIntervalSeconds int
}
// GetChannelMonitorRuntime reads the channel monitor feature flags directly from
// the settings store. Fail-open: on error returns Enabled=true with the default interval.
func (s *SettingService) GetChannelMonitorRuntime(ctx context.Context) ChannelMonitorRuntime {
vals, err := s.settingRepo.GetMultiple(ctx, []string{
SettingKeyChannelMonitorEnabled,
SettingKeyChannelMonitorDefaultIntervalSeconds,
})
if err != nil {
return ChannelMonitorRuntime{Enabled: true, DefaultIntervalSeconds: channelMonitorIntervalFallback}
}
return ChannelMonitorRuntime{
Enabled: !isFalseSettingValue(vals[SettingKeyChannelMonitorEnabled]),
DefaultIntervalSeconds: parseChannelMonitorInterval(vals[SettingKeyChannelMonitorDefaultIntervalSeconds]),
}
}
// AvailableChannelsRuntime is the lightweight view of the available-channels feature
// switch consumed by the user-facing handler.
type AvailableChannelsRuntime struct {
Enabled bool
}
// GetAvailableChannelsRuntime reads the available-channels feature switch directly
// from the settings store. Fail-closed: on error returns Enabled=false, matching
// the opt-in default (unknown ↔ disabled).
func (s *SettingService) GetAvailableChannelsRuntime(ctx context.Context) AvailableChannelsRuntime {
vals, err := s.settingRepo.GetMultiple(ctx, []string{SettingKeyAvailableChannelsEnabled})
if err != nil {
return AvailableChannelsRuntime{Enabled: false}
}
return AvailableChannelsRuntime{
Enabled: vals[SettingKeyAvailableChannelsEnabled] == "true",
}
}
// SetOnUpdateCallback sets a callback function to be called when settings are updated
// This is used for cache invalidation (e.g., HTML cache in frontend server)
func (s *SettingService) SetOnUpdateCallback(callback func()) {
@ -546,54 +628,75 @@ func (s *SettingService) SetVersion(version string) {
s.version = version
}
// GetPublicSettingsForInjection returns public settings in a format suitable for HTML injection
// This implements the web.PublicSettingsProvider interface
// PublicSettingsInjectionPayload is the JSON shape embedded into HTML as
// `window.__APP_CONFIG__` so the frontend can hydrate feature flags & site
// config before the first XHR finishes.
//
// INVARIANT: every `json` tag here MUST also exist on handler/dto.PublicSettings.
// If you forget a feature-flag field here, the frontend's
// `cachedPublicSettings.xxx_enabled` will be `undefined` on refresh until the
// async `/api/v1/settings/public` call returns — which causes opt-in menus
// (strict `=== true`) to flicker off/on. See
// frontend/src/utils/featureFlags.ts for the matching registry.
//
// A unit test diffs this struct's JSON keys against dto.PublicSettings to catch
// drift automatically (see setting_service_injection_test.go).
type PublicSettingsInjectionPayload struct {
RegistrationEnabled bool `json:"registration_enabled"`
EmailVerifyEnabled bool `json:"email_verify_enabled"`
RegistrationEmailSuffixWhitelist []string `json:"registration_email_suffix_whitelist"`
PromoCodeEnabled bool `json:"promo_code_enabled"`
PasswordResetEnabled bool `json:"password_reset_enabled"`
InvitationCodeEnabled bool `json:"invitation_code_enabled"`
TotpEnabled bool `json:"totp_enabled"`
TurnstileEnabled bool `json:"turnstile_enabled"`
TurnstileSiteKey string `json:"turnstile_site_key"`
SiteName string `json:"site_name"`
SiteLogo string `json:"site_logo"`
SiteSubtitle string `json:"site_subtitle"`
APIBaseURL string `json:"api_base_url"`
ContactInfo string `json:"contact_info"`
DocURL string `json:"doc_url"`
HomeContent string `json:"home_content"`
HideCcsImportButton bool `json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL string `json:"purchase_subscription_url"`
TableDefaultPageSize int `json:"table_default_page_size"`
TablePageSizeOptions []int `json:"table_page_size_options"`
CustomMenuItems json.RawMessage `json:"custom_menu_items"`
CustomEndpoints json.RawMessage `json:"custom_endpoints"`
LinuxDoOAuthEnabled bool `json:"linuxdo_oauth_enabled"`
WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"`
WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"`
WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"`
WeChatOAuthMobileEnabled bool `json:"wechat_oauth_mobile_enabled"`
OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"`
OIDCOAuthProviderName string `json:"oidc_oauth_provider_name"`
BackendModeEnabled bool `json:"backend_mode_enabled"`
PaymentEnabled bool `json:"payment_enabled"`
Version string `json:"version"`
BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
AccountQuotaNotifyEnabled bool `json:"account_quota_notify_enabled"`
BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
BalanceLowNotifyRechargeURL string `json:"balance_low_notify_recharge_url"`
// Feature flags — MUST match the opt-in/opt-out registry in
// frontend/src/utils/featureFlags.ts. Missing a field here is the bug
// that hid the "可用渠道" menu on page refresh.
ChannelMonitorEnabled bool `json:"channel_monitor_enabled"`
ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"`
AvailableChannelsEnabled bool `json:"available_channels_enabled"`
}
// GetPublicSettingsForInjection returns public settings in a format suitable for HTML injection.
// This implements the web.PublicSettingsProvider interface.
func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any, error) {
settings, err := s.GetPublicSettings(ctx)
if err != nil {
return nil, err
}
// Return a struct that matches the frontend's expected format
return &struct {
RegistrationEnabled bool `json:"registration_enabled"`
EmailVerifyEnabled bool `json:"email_verify_enabled"`
RegistrationEmailSuffixWhitelist []string `json:"registration_email_suffix_whitelist"`
PromoCodeEnabled bool `json:"promo_code_enabled"`
PasswordResetEnabled bool `json:"password_reset_enabled"`
InvitationCodeEnabled bool `json:"invitation_code_enabled"`
TotpEnabled bool `json:"totp_enabled"`
TurnstileEnabled bool `json:"turnstile_enabled"`
TurnstileSiteKey string `json:"turnstile_site_key,omitempty"`
SiteName string `json:"site_name"`
SiteLogo string `json:"site_logo,omitempty"`
SiteSubtitle string `json:"site_subtitle,omitempty"`
APIBaseURL string `json:"api_base_url,omitempty"`
ContactInfo string `json:"contact_info,omitempty"`
DocURL string `json:"doc_url,omitempty"`
HomeContent string `json:"home_content,omitempty"`
HideCcsImportButton bool `json:"hide_ccs_import_button"`
PurchaseSubscriptionEnabled bool `json:"purchase_subscription_enabled"`
PurchaseSubscriptionURL string `json:"purchase_subscription_url,omitempty"`
TableDefaultPageSize int `json:"table_default_page_size"`
TablePageSizeOptions []int `json:"table_page_size_options"`
CustomMenuItems json.RawMessage `json:"custom_menu_items"`
CustomEndpoints json.RawMessage `json:"custom_endpoints"`
LinuxDoOAuthEnabled bool `json:"linuxdo_oauth_enabled"`
WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"`
WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"`
WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"`
WeChatOAuthMobileEnabled bool `json:"wechat_oauth_mobile_enabled"`
BackendModeEnabled bool `json:"backend_mode_enabled"`
PaymentEnabled bool `json:"payment_enabled"`
OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"`
OIDCOAuthProviderName string `json:"oidc_oauth_provider_name"`
Version string `json:"version,omitempty"`
BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
AccountQuotaNotifyEnabled bool `json:"account_quota_notify_enabled"`
BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
BalanceLowNotifyRechargeURL string `json:"balance_low_notify_recharge_url"`
}{
return &PublicSettingsInjectionPayload{
RegistrationEnabled: settings.RegistrationEnabled,
EmailVerifyEnabled: settings.EmailVerifyEnabled,
RegistrationEmailSuffixWhitelist: settings.RegistrationEmailSuffixWhitelist,
@ -622,15 +725,19 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any
WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled,
WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled,
WeChatOAuthMobileEnabled: settings.WeChatOAuthMobileEnabled,
BackendModeEnabled: settings.BackendModeEnabled,
PaymentEnabled: settings.PaymentEnabled,
OIDCOAuthEnabled: settings.OIDCOAuthEnabled,
OIDCOAuthProviderName: settings.OIDCOAuthProviderName,
BackendModeEnabled: settings.BackendModeEnabled,
PaymentEnabled: settings.PaymentEnabled,
Version: s.version,
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
AccountQuotaNotifyEnabled: settings.AccountQuotaNotifyEnabled,
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
BalanceLowNotifyRechargeURL: settings.BalanceLowNotifyRechargeURL,
ChannelMonitorEnabled: settings.ChannelMonitorEnabled,
ChannelMonitorDefaultIntervalSeconds: settings.ChannelMonitorDefaultIntervalSeconds,
AvailableChannelsEnabled: settings.AvailableChannelsEnabled,
}, nil
}
@ -1086,6 +1193,15 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
updates[SettingKeyOpsMetricsIntervalSeconds] = strconv.Itoa(settings.OpsMetricsIntervalSeconds)
}
// Channel monitor feature switch
updates[SettingKeyChannelMonitorEnabled] = strconv.FormatBool(settings.ChannelMonitorEnabled)
if v := clampChannelMonitorInterval(settings.ChannelMonitorDefaultIntervalSeconds); v > 0 {
updates[SettingKeyChannelMonitorDefaultIntervalSeconds] = strconv.Itoa(v)
}
// Available channels feature switch
updates[SettingKeyAvailableChannelsEnabled] = strconv.FormatBool(settings.AvailableChannelsEnabled)
// Claude Code version check
updates[SettingKeyMinClaudeCodeVersion] = settings.MinClaudeCodeVersion
updates[SettingKeyMaxClaudeCodeVersion] = settings.MaxClaudeCodeVersion
@ -1644,6 +1760,13 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
SettingKeyOpsQueryModeDefault: "auto",
SettingKeyOpsMetricsIntervalSeconds: "60",
// Channel monitor defaults (enabled, 60s)
SettingKeyChannelMonitorEnabled: "true",
SettingKeyChannelMonitorDefaultIntervalSeconds: "60",
// Available channels feature (default disabled; opt-in)
SettingKeyAvailableChannelsEnabled: "false",
// Claude Code version check (default: empty = disabled)
SettingKeyMinClaudeCodeVersion: "",
SettingKeyMaxClaudeCodeVersion: "",
@ -1950,6 +2073,15 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
}
}
// Channel monitor feature (default: enabled, 60s)
result.ChannelMonitorEnabled = !isFalseSettingValue(settings[SettingKeyChannelMonitorEnabled])
result.ChannelMonitorDefaultIntervalSeconds = parseChannelMonitorInterval(
settings[SettingKeyChannelMonitorDefaultIntervalSeconds],
)
// Available channels feature (default: disabled; strict true)
result.AvailableChannelsEnabled = settings[SettingKeyAvailableChannelsEnabled] == "true"
// Claude Code version check
result.MinClaudeCodeVersion = settings[SettingKeyMinClaudeCodeVersion]
result.MaxClaudeCodeVersion = settings[SettingKeyMaxClaudeCodeVersion]

View File

@ -126,6 +126,13 @@ type SystemSettings struct {
OpsQueryModeDefault string
OpsMetricsIntervalSeconds int
// Channel Monitor feature
ChannelMonitorEnabled bool `json:"channel_monitor_enabled"`
ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"`
// Available Channels feature (user-facing aggregate view)
AvailableChannelsEnabled bool `json:"available_channels_enabled"`
// Claude Code version check
MinClaudeCodeVersion string
MaxClaudeCodeVersion string
@ -210,6 +217,13 @@ type PublicSettings struct {
AccountQuotaNotifyEnabled bool
BalanceLowNotifyThreshold float64
BalanceLowNotifyRechargeURL string
// Channel Monitor feature
ChannelMonitorEnabled bool `json:"channel_monitor_enabled"`
ChannelMonitorDefaultIntervalSeconds int `json:"channel_monitor_default_interval_seconds"`
// Available Channels feature (user-facing aggregate view)
AvailableChannelsEnabled bool `json:"available_channels_enabled"`
}
type WeChatConnectOAuthConfig struct {

View File

@ -269,13 +269,16 @@ func ProvideOpsAlertEvaluatorService(
}
// ProvideOpsCleanupService creates and starts OpsCleanupService (cron scheduled).
// channelMonitorSvc 让维护任务(聚合 + 历史/聚合软删)跟随 ops 清理 cron 一起跑,
// 共享 leader lock + heartbeat。
func ProvideOpsCleanupService(
opsRepo OpsRepository,
db *sql.DB,
redisClient *redis.Client,
cfg *config.Config,
channelMonitorSvc *ChannelMonitorService,
) *OpsCleanupService {
svc := NewOpsCleanupService(opsRepo, db, redisClient, cfg)
svc := NewOpsCleanupService(opsRepo, db, redisClient, cfg, channelMonitorSvc)
svc.Start()
return svc
}
@ -487,6 +490,9 @@ var ProviderSet = wire.NewSet(
NewPaymentService,
ProvidePaymentOrderExpiryService,
ProvideBalanceNotifyService,
ProvideChannelMonitorService,
ProvideChannelMonitorRunner,
NewChannelMonitorRequestTemplateService,
)
// ProvidePaymentConfigService wraps NewPaymentConfigService to accept the named
@ -506,3 +512,23 @@ func ProvidePaymentOrderExpiryService(paymentSvc *PaymentService) *PaymentOrderE
svc.Start()
return svc
}
// ProvideChannelMonitorService 创建渠道监控服务CRUD + RunCheck + 用户视图聚合)。
// 加密器复用 wire 中已注入的 SecretEncryptorAES-256-GCM
func ProvideChannelMonitorService(
repo ChannelMonitorRepository,
encryptor SecretEncryptor,
) *ChannelMonitorService {
return NewChannelMonitorService(repo, encryptor)
}
// ProvideChannelMonitorRunner 创建并启动渠道监控调度器。
// 通过 SetScheduler 注入回 service 后再 Start确保启动时加载所有 enabled monitor
// 后续 CRUD 也能即时同步任务表。Runner.Stop 由 cleanup function 调用。
// settingService 用于 runner 每次 fire 读取功能开关。
func ProvideChannelMonitorRunner(svc *ChannelMonitorService, settingService *SettingService) *ChannelMonitorRunner {
r := NewChannelMonitorRunner(svc, settingService)
svc.SetScheduler(r)
r.Start()
return r
}

View File

@ -0,0 +1,58 @@
-- Migration: 125_add_channel_monitors
-- 渠道监控 MVP周期性对外部 provider/endpoint/api_key 做模型心跳测试。
--
-- 表结构说明:
-- - channel_monitors 渠道配置表(一行 = 一个监控对象)
-- - channel_monitor_histories 检测历史明细表(一次检测一个模型 = 一行)
--
-- 设计要点:
-- - api_key_encrypted 列存放 AES-256-GCM 密文base64由 service 层加密。
-- - extra_models 用 JSONB 存储字符串数组,便于扩展(后续可加权重等元数据)。
-- - history 表通过 ON DELETE CASCADE 自动清理已删除监控的历史。
-- - (enabled, last_checked_at) 索引服务于调度器扫描“到期需要检测”的监控。
-- - histories 上 (monitor_id, model, checked_at DESC) 服务用户视图聚合查询;
-- 单独的 (checked_at) 索引服务定期清理 30 天前数据的 DELETE。
CREATE TABLE IF NOT EXISTS channel_monitors (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
provider VARCHAR(20) NOT NULL, -- openai / anthropic / gemini
endpoint VARCHAR(500) NOT NULL, -- base origin
api_key_encrypted TEXT NOT NULL, -- AES-256-GCM (base64)
primary_model VARCHAR(200) NOT NULL,
extra_models JSONB NOT NULL DEFAULT '[]'::jsonb,
group_name VARCHAR(100) NOT NULL DEFAULT '',
enabled BOOLEAN NOT NULL DEFAULT TRUE,
interval_seconds INT NOT NULL,
last_checked_at TIMESTAMPTZ,
created_by BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT channel_monitors_provider_check CHECK (provider IN ('openai', 'anthropic', 'gemini')),
CONSTRAINT channel_monitors_interval_check CHECK (interval_seconds BETWEEN 15 AND 3600)
);
CREATE INDEX IF NOT EXISTS idx_channel_monitors_enabled_last_checked
ON channel_monitors (enabled, last_checked_at);
CREATE INDEX IF NOT EXISTS idx_channel_monitors_provider
ON channel_monitors (provider);
CREATE INDEX IF NOT EXISTS idx_channel_monitors_group_name
ON channel_monitors (group_name);
CREATE TABLE IF NOT EXISTS channel_monitor_histories (
id BIGSERIAL PRIMARY KEY,
monitor_id BIGINT NOT NULL REFERENCES channel_monitors(id) ON DELETE CASCADE,
model VARCHAR(200) NOT NULL,
status VARCHAR(20) NOT NULL,
latency_ms INT,
ping_latency_ms INT,
message VARCHAR(500) NOT NULL DEFAULT '',
checked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT channel_monitor_histories_status_check
CHECK (status IN ('operational', 'degraded', 'failed', 'error'))
);
CREATE INDEX IF NOT EXISTS idx_channel_monitor_histories_monitor_model_checked
ON channel_monitor_histories (monitor_id, model, checked_at DESC);
CREATE INDEX IF NOT EXISTS idx_channel_monitor_histories_checked_at
ON channel_monitor_histories (checked_at);

View File

@ -0,0 +1,60 @@
-- Migration: 126_add_channel_monitor_aggregation
-- 渠道监控日聚合:把 channel_monitor_histories 的明细按天聚合,明细只保留 1 天,
-- 聚合保留 30 天。明细和聚合表都用软删除deleted_at由 ops cleanup 任务每天
-- 凌晨随运维监控清理一起跑(共享 cron
--
-- 设计要点:
-- - channel_monitor_histories 加 deleted_at 软删除字段SoftDeleteMixin 全局
-- Hook 会把 DELETE 自动改写成 UPDATE deleted_at = NOW())。
-- - channel_monitor_daily_rollups 按 (monitor_id, model, bucket_date) 唯一,
-- 用 ON CONFLICT DO UPDATE 实现幂等回填,状态分布和延迟分子分母都保留,
-- 方便后续按窗口任意求加权可用率和均值。
-- - watermark 表只有一行id=1记录最近一次聚合到达的日期避免重启后重复
-- 扫全表。
-- - rollup 上 (bucket_date) 索引服务清理任务的 DELETE WHERE bucket_date < cutoff。
-- 1) 给历史明细表加软删除字段
ALTER TABLE channel_monitor_histories
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
CREATE INDEX IF NOT EXISTS idx_channel_monitor_histories_deleted_at
ON channel_monitor_histories (deleted_at);
-- 2) 创建日聚合表
CREATE TABLE IF NOT EXISTS channel_monitor_daily_rollups (
id BIGSERIAL PRIMARY KEY,
monitor_id BIGINT NOT NULL REFERENCES channel_monitors(id) ON DELETE CASCADE,
model VARCHAR(200) NOT NULL,
bucket_date DATE NOT NULL,
total_checks INT NOT NULL DEFAULT 0,
ok_count INT NOT NULL DEFAULT 0,
operational_count INT NOT NULL DEFAULT 0,
degraded_count INT NOT NULL DEFAULT 0,
failed_count INT NOT NULL DEFAULT 0,
error_count INT NOT NULL DEFAULT 0,
sum_latency_ms BIGINT NOT NULL DEFAULT 0,
count_latency INT NOT NULL DEFAULT 0,
sum_ping_latency_ms BIGINT NOT NULL DEFAULT 0,
count_ping_latency INT NOT NULL DEFAULT 0,
computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_monitor_daily_rollups_unique
ON channel_monitor_daily_rollups (monitor_id, model, bucket_date);
CREATE INDEX IF NOT EXISTS idx_channel_monitor_daily_rollups_bucket
ON channel_monitor_daily_rollups (bucket_date);
CREATE INDEX IF NOT EXISTS idx_channel_monitor_daily_rollups_deleted_at
ON channel_monitor_daily_rollups (deleted_at);
-- 3) 创建 watermark 表单行id=1
CREATE TABLE IF NOT EXISTS channel_monitor_aggregation_watermark (
id INT PRIMARY KEY DEFAULT 1,
last_aggregated_date DATE,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT channel_monitor_aggregation_watermark_singleton CHECK (id = 1)
);
INSERT INTO channel_monitor_aggregation_watermark (id, last_aggregated_date, updated_at)
VALUES (1, NULL, NOW())
ON CONFLICT (id) DO NOTHING;

View File

@ -0,0 +1,16 @@
-- Migration: 127_drop_channel_monitor_deleted_at
-- 纠正 110 引入的 SoftDeleteMixin日志/聚合表无恢复需求,软删会让行和索引只增不减,
-- 徒增磁盘和查询开销。改回分批物理删(由 OpsCleanupService 每天凌晨统一调度,
-- deleteOldRowsByID 模板batch=5000
--
-- 110 尚未跑过聚合/清理(首次 maintenance 在次日 02:00所以此处不担心业务数据。
-- 直接 DROP 列 + 索引;对应的 Go 侧 ent schema 已移除 SoftDeleteMixin、repo 的
-- raw SQL 已移除 deleted_at IS NULL 过滤。
DROP INDEX IF EXISTS idx_channel_monitor_histories_deleted_at;
ALTER TABLE channel_monitor_histories
DROP COLUMN IF EXISTS deleted_at;
DROP INDEX IF EXISTS idx_channel_monitor_daily_rollups_deleted_at;
ALTER TABLE channel_monitor_daily_rollups
DROP COLUMN IF EXISTS deleted_at;

View File

@ -0,0 +1,70 @@
-- Migration: 128_add_channel_monitor_request_templates
-- 加请求模板表 + 给 channel_monitors 加 4 个快照字段template_id 关联引用 + extra_headers /
-- body_override_mode / body_override 三个真正运行时使用的快照)。
--
-- 设计要点:
-- 1) 模板与监控之间是「应用即拷贝」的快照语义,运行时 checker 不再回查模板表。
-- 模板 UPDATE 不会自动影响监控;只有用户主动「应用到关联监控」才会刷新快照。
-- 2) ON DELETE SET NULL模板删除不级联清理监控监控保留快照继续工作。
-- 3) extra_headers / body_override 都是 JSONBbody_override_mode 用 varchar不是 enum
-- 便于将来加新模式无需 ALTER TYPE。
-- 4) 同一 provider 内模板 name 唯一(允许 Anthropic + OpenAI 重名 "伪装官方客户端")。
CREATE TABLE IF NOT EXISTS channel_monitor_request_templates (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
provider VARCHAR(20) NOT NULL,
description VARCHAR(500) NOT NULL DEFAULT '',
extra_headers JSONB NOT NULL DEFAULT '{}'::jsonb,
body_override_mode VARCHAR(10) NOT NULL DEFAULT 'off',
body_override JSONB NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT channel_monitor_request_templates_provider_check
CHECK (provider IN ('openai', 'anthropic', 'gemini')),
CONSTRAINT channel_monitor_request_templates_body_mode_check
CHECK (body_override_mode IN ('off', 'merge', 'replace'))
);
CREATE UNIQUE INDEX IF NOT EXISTS channel_monitor_request_templates_provider_name
ON channel_monitor_request_templates (provider, name);
-- channel_monitors 加 4 列ADD COLUMN IF NOT EXISTS 需要 PG 9.6+,生产使用 PG 16
ALTER TABLE channel_monitors
ADD COLUMN IF NOT EXISTS template_id BIGINT NULL;
ALTER TABLE channel_monitors
ADD COLUMN IF NOT EXISTS extra_headers JSONB NOT NULL DEFAULT '{}'::jsonb;
ALTER TABLE channel_monitors
ADD COLUMN IF NOT EXISTS body_override_mode VARCHAR(10) NOT NULL DEFAULT 'off';
ALTER TABLE channel_monitors
ADD COLUMN IF NOT EXISTS body_override JSONB NULL;
-- 约束 + 外键DO 块里 IF NOT EXISTS 判断,保证幂等)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'channel_monitors_body_mode_check'
AND table_name = 'channel_monitors'
) THEN
ALTER TABLE channel_monitors
ADD CONSTRAINT channel_monitors_body_mode_check
CHECK (body_override_mode IN ('off', 'merge', 'replace'));
END IF;
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'channel_monitors_template_id_fkey'
AND table_name = 'channel_monitors'
) THEN
ALTER TABLE channel_monitors
ADD CONSTRAINT channel_monitors_template_id_fkey
FOREIGN KEY (template_id)
REFERENCES channel_monitor_request_templates (id)
ON DELETE SET NULL;
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_channel_monitors_template_id
ON channel_monitors (template_id)
WHERE template_id IS NOT NULL;

View File

@ -0,0 +1,38 @@
-- Migration: 129_seed_claude_code_template
-- 内置「Claude Code 伪装」请求模板,覆盖 Anthropic 上游对官方 CLI 客户端的所有验证项:
-- 1) User-Agent / X-App / anthropic-beta / anthropic-version 等头
-- 2) system 数组首项与官方 system prompt 字面一致Dice >= 0.5
-- 3) metadata.user_id 满足 ParseMetadataUserID — 这里用 legacy 格式user_<64hex>_account_<uuid>_session_<36char>
-- 避免新版 JSON 字符串内嵌 JSON 在编辑器里出现一长串 \" 转义,便于用户阅读。
--
-- ON CONFLICT DO NOTHING已部署环境手动建过模板跑此 migration 不会重复 / 覆盖。
-- 用户可自行编辑后续覆盖此 seedCC 升大版时再起一条 migration 提供新模板,不动用户的旧模板。
INSERT INTO channel_monitor_request_templates (
name, provider, description, extra_headers, body_override_mode, body_override
)
VALUES (
'Claude Code 伪装',
'anthropic',
'完整模拟 Claude Code 2.1.114 客户端UA + anthropic-beta + system + metadata.user_id 全部对齐,绕过 Anthropic 上游 ''Claude Code only'' 限制(如 Max 套餐)。',
'{
"User-Agent": "claude-cli/2.1.114 (external, sdk-cli)",
"X-App": "cli",
"anthropic-version": "2023-06-01",
"anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advisor-tool-2026-03-01",
"anthropic-dangerous-direct-browser-access": "true"
}'::jsonb,
'merge',
'{
"system": [
{
"type": "text",
"text": "You are Claude Code, Anthropic''s official CLI for Claude."
}
],
"metadata": {
"user_id": "user_0000000000000000000000000000000000000000000000000000000000000000_account_00000000-0000-0000-0000-000000000000_session_00000000-0000-0000-0000-000000000000"
}
}'::jsonb
)
ON CONFLICT (provider, name) DO NOTHING;

Some files were not shown because too many files have changed in this diff Show More