diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go index 034c70ecde..5e1c29b7e6 100644 --- a/backend/cmd/server/wire_gen.go +++ b/backend/cmd/server/wire_gen.go @@ -129,7 +129,9 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { antigravityQuotaFetcher := service.NewAntigravityQuotaFetcher(proxyRepository) usageCache := service.NewUsageCache() identityCache := repository.NewIdentityCache(redisClient) - accountUsageService := service.NewAccountUsageService(accountRepository, usageLogRepository, claudeUsageFetcher, geminiQuotaService, antigravityQuotaFetcher, usageCache, identityCache) + scriptEngine := service.NewScriptEngine() + usageScriptRepository := repository.NewUsageScriptRepository(client) + accountUsageService := service.NewAccountUsageService(accountRepository, usageLogRepository, claudeUsageFetcher, geminiQuotaService, antigravityQuotaFetcher, usageCache, identityCache, scriptEngine, usageScriptRepository) geminiTokenProvider := service.NewGeminiTokenProvider(accountRepository, geminiTokenCache, geminiOAuthService) gatewayCache := repository.NewGatewayCache(redisClient) schedulerOutboxRepository := repository.NewSchedulerOutboxRepository(db) @@ -199,7 +201,8 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { scheduledTestResultRepository := repository.NewScheduledTestResultRepository(db) scheduledTestService := service.ProvideScheduledTestService(scheduledTestPlanRepository, scheduledTestResultRepository) scheduledTestHandler := admin.NewScheduledTestHandler(scheduledTestService) - adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, dataManagementHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, adminAPIKeyHandler, scheduledTestHandler) + usageScriptHandler := admin.NewUsageScriptHandler(usageScriptRepository) + adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, adminAnnouncementHandler, dataManagementHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, promoHandler, settingHandler, opsHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler, errorPassthroughHandler, adminAPIKeyHandler, scheduledTestHandler, usageScriptHandler) usageRecordWorkerPool := service.NewUsageRecordWorkerPool(configConfig) userMsgQueueCache := repository.NewUserMsgQueueCache(redisClient) userMessageQueueService := service.ProvideUserMessageQueueService(userMsgQueueCache, rpmCache, configConfig) diff --git a/backend/ent/client.go b/backend/ent/client.go index 7ebbaa3224..2fe12c9785 100644 --- a/backend/ent/client.go +++ b/backend/ent/client.go @@ -31,6 +31,7 @@ import ( "github.com/Wei-Shaw/sub2api/ent/setting" "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask" "github.com/Wei-Shaw/sub2api/ent/usagelog" + "github.com/Wei-Shaw/sub2api/ent/usagescript" "github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userattributedefinition" @@ -77,6 +78,8 @@ type Client struct { UsageCleanupTask *UsageCleanupTaskClient // UsageLog is the client for interacting with the UsageLog builders. UsageLog *UsageLogClient + // UsageScript is the client for interacting with the UsageScript builders. + UsageScript *UsageScriptClient // User is the client for interacting with the User builders. User *UserClient // UserAllowedGroup is the client for interacting with the UserAllowedGroup builders. @@ -114,6 +117,7 @@ func (c *Client) init() { c.Setting = NewSettingClient(c.config) c.UsageCleanupTask = NewUsageCleanupTaskClient(c.config) c.UsageLog = NewUsageLogClient(c.config) + c.UsageScript = NewUsageScriptClient(c.config) c.User = NewUserClient(c.config) c.UserAllowedGroup = NewUserAllowedGroupClient(c.config) c.UserAttributeDefinition = NewUserAttributeDefinitionClient(c.config) @@ -227,6 +231,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Setting: NewSettingClient(cfg), UsageCleanupTask: NewUsageCleanupTaskClient(cfg), UsageLog: NewUsageLogClient(cfg), + UsageScript: NewUsageScriptClient(cfg), User: NewUserClient(cfg), UserAllowedGroup: NewUserAllowedGroupClient(cfg), UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg), @@ -267,6 +272,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Setting: NewSettingClient(cfg), UsageCleanupTask: NewUsageCleanupTaskClient(cfg), UsageLog: NewUsageLogClient(cfg), + UsageScript: NewUsageScriptClient(cfg), User: NewUserClient(cfg), UserAllowedGroup: NewUserAllowedGroupClient(cfg), UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg), @@ -304,7 +310,7 @@ func (c *Client) Use(hooks ...Hook) { c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead, c.ErrorPassthroughRule, c.Group, c.IdempotencyRecord, c.PromoCode, c.PromoCodeUsage, c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting, - c.UsageCleanupTask, c.UsageLog, c.User, c.UserAllowedGroup, + c.UsageCleanupTask, c.UsageLog, c.UsageScript, c.User, c.UserAllowedGroup, c.UserAttributeDefinition, c.UserAttributeValue, c.UserSubscription, } { n.Use(hooks...) @@ -318,7 +324,7 @@ func (c *Client) Intercept(interceptors ...Interceptor) { c.APIKey, c.Account, c.AccountGroup, c.Announcement, c.AnnouncementRead, c.ErrorPassthroughRule, c.Group, c.IdempotencyRecord, c.PromoCode, c.PromoCodeUsage, c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting, - c.UsageCleanupTask, c.UsageLog, c.User, c.UserAllowedGroup, + c.UsageCleanupTask, c.UsageLog, c.UsageScript, c.User, c.UserAllowedGroup, c.UserAttributeDefinition, c.UserAttributeValue, c.UserSubscription, } { n.Intercept(interceptors...) @@ -360,6 +366,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.UsageCleanupTask.mutate(ctx, m) case *UsageLogMutation: return c.UsageLog.mutate(ctx, m) + case *UsageScriptMutation: + return c.UsageScript.mutate(ctx, m) case *UserMutation: return c.User.mutate(ctx, m) case *UserAllowedGroupMutation: @@ -2958,6 +2966,141 @@ func (c *UsageLogClient) mutate(ctx context.Context, m *UsageLogMutation) (Value } } +// UsageScriptClient is a client for the UsageScript schema. +type UsageScriptClient struct { + config +} + +// NewUsageScriptClient returns a client for the UsageScript from the given config. +func NewUsageScriptClient(c config) *UsageScriptClient { + return &UsageScriptClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `usagescript.Hooks(f(g(h())))`. +func (c *UsageScriptClient) Use(hooks ...Hook) { + c.hooks.UsageScript = append(c.hooks.UsageScript, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `usagescript.Intercept(f(g(h())))`. +func (c *UsageScriptClient) Intercept(interceptors ...Interceptor) { + c.inters.UsageScript = append(c.inters.UsageScript, interceptors...) +} + +// Create returns a builder for creating a UsageScript entity. +func (c *UsageScriptClient) Create() *UsageScriptCreate { + mutation := newUsageScriptMutation(c.config, OpCreate) + return &UsageScriptCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of UsageScript entities. +func (c *UsageScriptClient) CreateBulk(builders ...*UsageScriptCreate) *UsageScriptCreateBulk { + return &UsageScriptCreateBulk{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 *UsageScriptClient) MapCreateBulk(slice any, setFunc func(*UsageScriptCreate, int)) *UsageScriptCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &UsageScriptCreateBulk{err: fmt.Errorf("calling to UsageScriptClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*UsageScriptCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &UsageScriptCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for UsageScript. +func (c *UsageScriptClient) Update() *UsageScriptUpdate { + mutation := newUsageScriptMutation(c.config, OpUpdate) + return &UsageScriptUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *UsageScriptClient) UpdateOne(_m *UsageScript) *UsageScriptUpdateOne { + mutation := newUsageScriptMutation(c.config, OpUpdateOne, withUsageScript(_m)) + return &UsageScriptUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *UsageScriptClient) UpdateOneID(id int64) *UsageScriptUpdateOne { + mutation := newUsageScriptMutation(c.config, OpUpdateOne, withUsageScriptID(id)) + return &UsageScriptUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for UsageScript. +func (c *UsageScriptClient) Delete() *UsageScriptDelete { + mutation := newUsageScriptMutation(c.config, OpDelete) + return &UsageScriptDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *UsageScriptClient) DeleteOne(_m *UsageScript) *UsageScriptDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *UsageScriptClient) DeleteOneID(id int64) *UsageScriptDeleteOne { + builder := c.Delete().Where(usagescript.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &UsageScriptDeleteOne{builder} +} + +// Query returns a query builder for UsageScript. +func (c *UsageScriptClient) Query() *UsageScriptQuery { + return &UsageScriptQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeUsageScript}, + inters: c.Interceptors(), + } +} + +// Get returns a UsageScript entity by its id. +func (c *UsageScriptClient) Get(ctx context.Context, id int64) (*UsageScript, error) { + return c.Query().Where(usagescript.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *UsageScriptClient) GetX(ctx context.Context, id int64) *UsageScript { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *UsageScriptClient) Hooks() []Hook { + hooks := c.hooks.UsageScript + return append(hooks[:len(hooks):len(hooks)], usagescript.Hooks[:]...) +} + +// Interceptors returns the client interceptors. +func (c *UsageScriptClient) Interceptors() []Interceptor { + inters := c.inters.UsageScript + return append(inters[:len(inters):len(inters)], usagescript.Interceptors[:]...) +} + +func (c *UsageScriptClient) mutate(ctx context.Context, m *UsageScriptMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&UsageScriptCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&UsageScriptUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&UsageScriptUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&UsageScriptDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown UsageScript mutation op: %q", m.Op()) + } +} + // UserClient is a client for the User schema. type UserClient struct { config @@ -3889,16 +4032,16 @@ type ( hooks struct { APIKey, Account, AccountGroup, Announcement, AnnouncementRead, ErrorPassthroughRule, Group, IdempotencyRecord, PromoCode, PromoCodeUsage, - Proxy, RedeemCode, SecuritySecret, Setting, UsageCleanupTask, UsageLog, User, - UserAllowedGroup, UserAttributeDefinition, UserAttributeValue, - UserSubscription []ent.Hook + Proxy, RedeemCode, SecuritySecret, Setting, UsageCleanupTask, UsageLog, + UsageScript, User, UserAllowedGroup, UserAttributeDefinition, + UserAttributeValue, UserSubscription []ent.Hook } inters struct { APIKey, Account, AccountGroup, Announcement, AnnouncementRead, ErrorPassthroughRule, Group, IdempotencyRecord, PromoCode, PromoCodeUsage, - Proxy, RedeemCode, SecuritySecret, Setting, UsageCleanupTask, UsageLog, User, - UserAllowedGroup, UserAttributeDefinition, UserAttributeValue, - UserSubscription []ent.Interceptor + Proxy, RedeemCode, SecuritySecret, Setting, UsageCleanupTask, UsageLog, + UsageScript, User, UserAllowedGroup, UserAttributeDefinition, + UserAttributeValue, UserSubscription []ent.Interceptor } ) diff --git a/backend/ent/ent.go b/backend/ent/ent.go index 5197e4d849..00fd2d4cc8 100644 --- a/backend/ent/ent.go +++ b/backend/ent/ent.go @@ -28,6 +28,7 @@ import ( "github.com/Wei-Shaw/sub2api/ent/setting" "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask" "github.com/Wei-Shaw/sub2api/ent/usagelog" + "github.com/Wei-Shaw/sub2api/ent/usagescript" "github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userattributedefinition" @@ -109,6 +110,7 @@ func checkColumn(t, c string) error { setting.Table: setting.ValidColumn, usagecleanuptask.Table: usagecleanuptask.ValidColumn, usagelog.Table: usagelog.ValidColumn, + usagescript.Table: usagescript.ValidColumn, user.Table: user.ValidColumn, userallowedgroup.Table: userallowedgroup.ValidColumn, userattributedefinition.Table: userattributedefinition.ValidColumn, diff --git a/backend/ent/hook/hook.go b/backend/ent/hook/hook.go index 49d7f3c556..8c458b0ae0 100644 --- a/backend/ent/hook/hook.go +++ b/backend/ent/hook/hook.go @@ -201,6 +201,18 @@ func (f UsageLogFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, er return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UsageLogMutation", m) } +// The UsageScriptFunc type is an adapter to allow the use of ordinary +// function as UsageScript mutator. +type UsageScriptFunc func(context.Context, *ent.UsageScriptMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f UsageScriptFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.UsageScriptMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UsageScriptMutation", m) +} + // The UserFunc type is an adapter to allow the use of ordinary // function as User mutator. type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error) diff --git a/backend/ent/intercept/intercept.go b/backend/ent/intercept/intercept.go index e77464026d..1ca56e9303 100644 --- a/backend/ent/intercept/intercept.go +++ b/backend/ent/intercept/intercept.go @@ -25,6 +25,7 @@ import ( "github.com/Wei-Shaw/sub2api/ent/setting" "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask" "github.com/Wei-Shaw/sub2api/ent/usagelog" + "github.com/Wei-Shaw/sub2api/ent/usagescript" "github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userattributedefinition" @@ -520,6 +521,33 @@ func (f TraverseUsageLog) Traverse(ctx context.Context, q ent.Query) error { return fmt.Errorf("unexpected query type %T. expect *ent.UsageLogQuery", q) } +// The UsageScriptFunc type is an adapter to allow the use of ordinary function as a Querier. +type UsageScriptFunc func(context.Context, *ent.UsageScriptQuery) (ent.Value, error) + +// Query calls f(ctx, q). +func (f UsageScriptFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) { + if q, ok := q.(*ent.UsageScriptQuery); ok { + return f(ctx, q) + } + return nil, fmt.Errorf("unexpected query type %T. expect *ent.UsageScriptQuery", q) +} + +// The TraverseUsageScript type is an adapter to allow the use of ordinary function as Traverser. +type TraverseUsageScript func(context.Context, *ent.UsageScriptQuery) error + +// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline. +func (f TraverseUsageScript) Intercept(next ent.Querier) ent.Querier { + return next +} + +// Traverse calls f(ctx, q). +func (f TraverseUsageScript) Traverse(ctx context.Context, q ent.Query) error { + if q, ok := q.(*ent.UsageScriptQuery); ok { + return f(ctx, q) + } + return fmt.Errorf("unexpected query type %T. expect *ent.UsageScriptQuery", q) +} + // The UserFunc type is an adapter to allow the use of ordinary function as a Querier. type UserFunc func(context.Context, *ent.UserQuery) (ent.Value, error) @@ -690,6 +718,8 @@ func NewQuery(q ent.Query) (Query, error) { return &query[*ent.UsageCleanupTaskQuery, predicate.UsageCleanupTask, usagecleanuptask.OrderOption]{typ: ent.TypeUsageCleanupTask, tq: q}, nil case *ent.UsageLogQuery: return &query[*ent.UsageLogQuery, predicate.UsageLog, usagelog.OrderOption]{typ: ent.TypeUsageLog, tq: q}, nil + case *ent.UsageScriptQuery: + return &query[*ent.UsageScriptQuery, predicate.UsageScript, usagescript.OrderOption]{typ: ent.TypeUsageScript, tq: q}, nil case *ent.UserQuery: return &query[*ent.UserQuery, predicate.User, user.OrderOption]{typ: ent.TypeUser, tq: q}, nil case *ent.UserAllowedGroupQuery: diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go index ff1c1b8865..f8a39af0a1 100644 --- a/backend/ent/migrate/schema.go +++ b/backend/ent/migrate/schema.go @@ -842,6 +842,30 @@ var ( }, }, } + // UsageScriptsColumns holds the columns for the "usage_scripts" table. + UsageScriptsColumns = []*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: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}}, + {Name: "base_url_host", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}}, + {Name: "account_type", Type: field.TypeString, Size: 20}, + {Name: "script", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}}, + {Name: "enabled", Type: field.TypeBool, Default: true}, + } + // UsageScriptsTable holds the schema information for the "usage_scripts" table. + UsageScriptsTable = &schema.Table{ + Name: "usage_scripts", + Columns: UsageScriptsColumns, + PrimaryKey: []*schema.Column{UsageScriptsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "usagescript_base_url_host_account_type", + Unique: true, + Columns: []*schema.Column{UsageScriptsColumns[4], UsageScriptsColumns[5]}, + }, + }, + } // UsersColumns holds the columns for the "users" table. UsersColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt64, Increment: true}, @@ -1106,6 +1130,7 @@ var ( SettingsTable, UsageCleanupTasksTable, UsageLogsTable, + UsageScriptsTable, UsersTable, UserAllowedGroupsTable, UserAttributeDefinitionsTable, @@ -1179,6 +1204,9 @@ func init() { UsageLogsTable.Annotation = &entsql.Annotation{ Table: "usage_logs", } + UsageScriptsTable.Annotation = &entsql.Annotation{ + Table: "usage_scripts", + } UsersTable.Annotation = &entsql.Annotation{ Table: "users", } diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go index 652adcac70..c30c04ea13 100644 --- a/backend/ent/mutation.go +++ b/backend/ent/mutation.go @@ -29,6 +29,7 @@ import ( "github.com/Wei-Shaw/sub2api/ent/setting" "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask" "github.com/Wei-Shaw/sub2api/ent/usagelog" + "github.com/Wei-Shaw/sub2api/ent/usagescript" "github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userattributedefinition" @@ -62,6 +63,7 @@ const ( TypeSetting = "Setting" TypeUsageCleanupTask = "UsageCleanupTask" TypeUsageLog = "UsageLog" + TypeUsageScript = "UsageScript" TypeUser = "User" TypeUserAllowedGroup = "UserAllowedGroup" TypeUserAttributeDefinition = "UserAttributeDefinition" @@ -21243,6 +21245,678 @@ func (m *UsageLogMutation) ResetEdge(name string) error { return fmt.Errorf("unknown UsageLog edge %s", name) } +// UsageScriptMutation represents an operation that mutates the UsageScript nodes in the graph. +type UsageScriptMutation struct { + config + op Op + typ string + id *int64 + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + base_url_host *string + account_type *string + script *string + enabled *bool + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*UsageScript, error) + predicates []predicate.UsageScript +} + +var _ ent.Mutation = (*UsageScriptMutation)(nil) + +// usagescriptOption allows management of the mutation configuration using functional options. +type usagescriptOption func(*UsageScriptMutation) + +// newUsageScriptMutation creates new mutation for the UsageScript entity. +func newUsageScriptMutation(c config, op Op, opts ...usagescriptOption) *UsageScriptMutation { + m := &UsageScriptMutation{ + config: c, + op: op, + typ: TypeUsageScript, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withUsageScriptID sets the ID field of the mutation. +func withUsageScriptID(id int64) usagescriptOption { + return func(m *UsageScriptMutation) { + var ( + err error + once sync.Once + value *UsageScript + ) + m.oldValue = func(ctx context.Context) (*UsageScript, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().UsageScript.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withUsageScript sets the old UsageScript of the mutation. +func withUsageScript(node *UsageScript) usagescriptOption { + return func(m *UsageScriptMutation) { + m.oldValue = func(context.Context) (*UsageScript, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m UsageScriptMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m UsageScriptMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *UsageScriptMutation) ID() (id int64, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *UsageScriptMutation) IDs(ctx context.Context) ([]int64, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int64{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().UsageScript.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedAt sets the "created_at" field. +func (m *UsageScriptMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *UsageScriptMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the UsageScript entity. +// If the UsageScript object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UsageScriptMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *UsageScriptMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *UsageScriptMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *UsageScriptMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the UsageScript entity. +// If the UsageScript object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UsageScriptMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *UsageScriptMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetDeletedAt sets the "deleted_at" field. +func (m *UsageScriptMutation) SetDeletedAt(t time.Time) { + m.deleted_at = &t +} + +// DeletedAt returns the value of the "deleted_at" field in the mutation. +func (m *UsageScriptMutation) DeletedAt() (r time.Time, exists bool) { + v := m.deleted_at + if v == nil { + return + } + return *v, true +} + +// OldDeletedAt returns the old "deleted_at" field's value of the UsageScript entity. +// If the UsageScript object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UsageScriptMutation) OldDeletedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDeletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err) + } + return oldValue.DeletedAt, nil +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (m *UsageScriptMutation) ClearDeletedAt() { + m.deleted_at = nil + m.clearedFields[usagescript.FieldDeletedAt] = struct{}{} +} + +// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation. +func (m *UsageScriptMutation) DeletedAtCleared() bool { + _, ok := m.clearedFields[usagescript.FieldDeletedAt] + return ok +} + +// ResetDeletedAt resets all changes to the "deleted_at" field. +func (m *UsageScriptMutation) ResetDeletedAt() { + m.deleted_at = nil + delete(m.clearedFields, usagescript.FieldDeletedAt) +} + +// SetBaseURLHost sets the "base_url_host" field. +func (m *UsageScriptMutation) SetBaseURLHost(s string) { + m.base_url_host = &s +} + +// BaseURLHost returns the value of the "base_url_host" field in the mutation. +func (m *UsageScriptMutation) BaseURLHost() (r string, exists bool) { + v := m.base_url_host + if v == nil { + return + } + return *v, true +} + +// OldBaseURLHost returns the old "base_url_host" field's value of the UsageScript entity. +// If the UsageScript object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UsageScriptMutation) OldBaseURLHost(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBaseURLHost is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBaseURLHost requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBaseURLHost: %w", err) + } + return oldValue.BaseURLHost, nil +} + +// ResetBaseURLHost resets all changes to the "base_url_host" field. +func (m *UsageScriptMutation) ResetBaseURLHost() { + m.base_url_host = nil +} + +// SetAccountType sets the "account_type" field. +func (m *UsageScriptMutation) SetAccountType(s string) { + m.account_type = &s +} + +// AccountType returns the value of the "account_type" field in the mutation. +func (m *UsageScriptMutation) AccountType() (r string, exists bool) { + v := m.account_type + if v == nil { + return + } + return *v, true +} + +// OldAccountType returns the old "account_type" field's value of the UsageScript entity. +// If the UsageScript object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UsageScriptMutation) OldAccountType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAccountType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAccountType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAccountType: %w", err) + } + return oldValue.AccountType, nil +} + +// ResetAccountType resets all changes to the "account_type" field. +func (m *UsageScriptMutation) ResetAccountType() { + m.account_type = nil +} + +// SetScript sets the "script" field. +func (m *UsageScriptMutation) SetScript(s string) { + m.script = &s +} + +// Script returns the value of the "script" field in the mutation. +func (m *UsageScriptMutation) Script() (r string, exists bool) { + v := m.script + if v == nil { + return + } + return *v, true +} + +// OldScript returns the old "script" field's value of the UsageScript entity. +// If the UsageScript object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UsageScriptMutation) OldScript(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScript is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScript requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScript: %w", err) + } + return oldValue.Script, nil +} + +// ResetScript resets all changes to the "script" field. +func (m *UsageScriptMutation) ResetScript() { + m.script = nil +} + +// SetEnabled sets the "enabled" field. +func (m *UsageScriptMutation) SetEnabled(b bool) { + m.enabled = &b +} + +// Enabled returns the value of the "enabled" field in the mutation. +func (m *UsageScriptMutation) Enabled() (r bool, exists bool) { + v := m.enabled + if v == nil { + return + } + return *v, true +} + +// OldEnabled returns the old "enabled" field's value of the UsageScript entity. +// If the UsageScript object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UsageScriptMutation) OldEnabled(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEnabled is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEnabled requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEnabled: %w", err) + } + return oldValue.Enabled, nil +} + +// ResetEnabled resets all changes to the "enabled" field. +func (m *UsageScriptMutation) ResetEnabled() { + m.enabled = nil +} + +// Where appends a list predicates to the UsageScriptMutation builder. +func (m *UsageScriptMutation) Where(ps ...predicate.UsageScript) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the UsageScriptMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *UsageScriptMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.UsageScript, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *UsageScriptMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *UsageScriptMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (UsageScript). +func (m *UsageScriptMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *UsageScriptMutation) Fields() []string { + fields := make([]string, 0, 7) + if m.created_at != nil { + fields = append(fields, usagescript.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, usagescript.FieldUpdatedAt) + } + if m.deleted_at != nil { + fields = append(fields, usagescript.FieldDeletedAt) + } + if m.base_url_host != nil { + fields = append(fields, usagescript.FieldBaseURLHost) + } + if m.account_type != nil { + fields = append(fields, usagescript.FieldAccountType) + } + if m.script != nil { + fields = append(fields, usagescript.FieldScript) + } + if m.enabled != nil { + fields = append(fields, usagescript.FieldEnabled) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *UsageScriptMutation) Field(name string) (ent.Value, bool) { + switch name { + case usagescript.FieldCreatedAt: + return m.CreatedAt() + case usagescript.FieldUpdatedAt: + return m.UpdatedAt() + case usagescript.FieldDeletedAt: + return m.DeletedAt() + case usagescript.FieldBaseURLHost: + return m.BaseURLHost() + case usagescript.FieldAccountType: + return m.AccountType() + case usagescript.FieldScript: + return m.Script() + case usagescript.FieldEnabled: + return m.Enabled() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *UsageScriptMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case usagescript.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case usagescript.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case usagescript.FieldDeletedAt: + return m.OldDeletedAt(ctx) + case usagescript.FieldBaseURLHost: + return m.OldBaseURLHost(ctx) + case usagescript.FieldAccountType: + return m.OldAccountType(ctx) + case usagescript.FieldScript: + return m.OldScript(ctx) + case usagescript.FieldEnabled: + return m.OldEnabled(ctx) + } + return nil, fmt.Errorf("unknown UsageScript field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *UsageScriptMutation) SetField(name string, value ent.Value) error { + switch name { + case usagescript.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case usagescript.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case usagescript.FieldDeletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDeletedAt(v) + return nil + case usagescript.FieldBaseURLHost: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBaseURLHost(v) + return nil + case usagescript.FieldAccountType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAccountType(v) + return nil + case usagescript.FieldScript: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScript(v) + return nil + case usagescript.FieldEnabled: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEnabled(v) + return nil + } + return fmt.Errorf("unknown UsageScript field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *UsageScriptMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *UsageScriptMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *UsageScriptMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown UsageScript numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *UsageScriptMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(usagescript.FieldDeletedAt) { + fields = append(fields, usagescript.FieldDeletedAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *UsageScriptMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *UsageScriptMutation) ClearField(name string) error { + switch name { + case usagescript.FieldDeletedAt: + m.ClearDeletedAt() + return nil + } + return fmt.Errorf("unknown UsageScript nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *UsageScriptMutation) ResetField(name string) error { + switch name { + case usagescript.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case usagescript.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case usagescript.FieldDeletedAt: + m.ResetDeletedAt() + return nil + case usagescript.FieldBaseURLHost: + m.ResetBaseURLHost() + return nil + case usagescript.FieldAccountType: + m.ResetAccountType() + return nil + case usagescript.FieldScript: + m.ResetScript() + return nil + case usagescript.FieldEnabled: + m.ResetEnabled() + return nil + } + return fmt.Errorf("unknown UsageScript field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *UsageScriptMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *UsageScriptMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *UsageScriptMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *UsageScriptMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *UsageScriptMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *UsageScriptMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *UsageScriptMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown UsageScript unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *UsageScriptMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown UsageScript edge %s", name) +} + // UserMutation represents an operation that mutates the User nodes in the graph. type UserMutation struct { config diff --git a/backend/ent/predicate/predicate.go b/backend/ent/predicate/predicate.go index 89d933fcdc..389eed9b5e 100644 --- a/backend/ent/predicate/predicate.go +++ b/backend/ent/predicate/predicate.go @@ -54,6 +54,9 @@ type UsageCleanupTask func(*sql.Selector) // UsageLog is the predicate function for usagelog builders. type UsageLog func(*sql.Selector) +// UsageScript is the predicate function for usagescript builders. +type UsageScript func(*sql.Selector) + // User is the predicate function for user builders. type User func(*sql.Selector) diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go index b8facf362b..68c45b7704 100644 --- a/backend/ent/runtime/runtime.go +++ b/backend/ent/runtime/runtime.go @@ -22,6 +22,7 @@ import ( "github.com/Wei-Shaw/sub2api/ent/setting" "github.com/Wei-Shaw/sub2api/ent/usagecleanuptask" "github.com/Wei-Shaw/sub2api/ent/usagelog" + "github.com/Wei-Shaw/sub2api/ent/usagescript" "github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userattributedefinition" @@ -909,6 +910,51 @@ func init() { usagelogDescCreatedAt := usagelogFields[31].Descriptor() // usagelog.DefaultCreatedAt holds the default value on creation for the created_at field. usagelog.DefaultCreatedAt = usagelogDescCreatedAt.Default.(func() time.Time) + usagescriptMixin := schema.UsageScript{}.Mixin() + usagescriptMixinHooks1 := usagescriptMixin[1].Hooks() + usagescript.Hooks[0] = usagescriptMixinHooks1[0] + usagescriptMixinInters1 := usagescriptMixin[1].Interceptors() + usagescript.Interceptors[0] = usagescriptMixinInters1[0] + usagescriptMixinFields0 := usagescriptMixin[0].Fields() + _ = usagescriptMixinFields0 + usagescriptFields := schema.UsageScript{}.Fields() + _ = usagescriptFields + // usagescriptDescCreatedAt is the schema descriptor for created_at field. + usagescriptDescCreatedAt := usagescriptMixinFields0[0].Descriptor() + // usagescript.DefaultCreatedAt holds the default value on creation for the created_at field. + usagescript.DefaultCreatedAt = usagescriptDescCreatedAt.Default.(func() time.Time) + // usagescriptDescUpdatedAt is the schema descriptor for updated_at field. + usagescriptDescUpdatedAt := usagescriptMixinFields0[1].Descriptor() + // usagescript.DefaultUpdatedAt holds the default value on creation for the updated_at field. + usagescript.DefaultUpdatedAt = usagescriptDescUpdatedAt.Default.(func() time.Time) + // usagescript.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + usagescript.UpdateDefaultUpdatedAt = usagescriptDescUpdatedAt.UpdateDefault.(func() time.Time) + // usagescriptDescBaseURLHost is the schema descriptor for base_url_host field. + usagescriptDescBaseURLHost := usagescriptFields[0].Descriptor() + // usagescript.BaseURLHostValidator is a validator for the "base_url_host" field. It is called by the builders before save. + usagescript.BaseURLHostValidator = usagescriptDescBaseURLHost.Validators[0].(func(string) error) + // usagescriptDescAccountType is the schema descriptor for account_type field. + usagescriptDescAccountType := usagescriptFields[1].Descriptor() + // usagescript.AccountTypeValidator is a validator for the "account_type" field. It is called by the builders before save. + usagescript.AccountTypeValidator = func() func(string) error { + validators := usagescriptDescAccountType.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(account_type string) error { + for _, fn := range fns { + if err := fn(account_type); err != nil { + return err + } + } + return nil + } + }() + // usagescriptDescEnabled is the schema descriptor for enabled field. + usagescriptDescEnabled := usagescriptFields[3].Descriptor() + // usagescript.DefaultEnabled holds the default value on creation for the enabled field. + usagescript.DefaultEnabled = usagescriptDescEnabled.Default.(bool) userMixin := schema.User{}.Mixin() userMixinHooks1 := userMixin[1].Hooks() user.Hooks[0] = userMixinHooks1[0] diff --git a/backend/ent/schema/usage_script.go b/backend/ent/schema/usage_script.go new file mode 100644 index 0000000000..3cbce20e50 --- /dev/null +++ b/backend/ent/schema/usage_script.go @@ -0,0 +1,55 @@ +package schema + +import ( + "github.com/Wei-Shaw/sub2api/ent/schema/mixins" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" +) + +// UsageScript holds the schema definition for the UsageScript entity. +type UsageScript struct { + ent.Schema +} + +func (UsageScript) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "usage_scripts"}, + } +} + +func (UsageScript) Mixin() []ent.Mixin { + return []ent.Mixin{ + mixins.TimeMixin{}, + mixins.SoftDeleteMixin{}, + } +} + +func (UsageScript) Fields() []ent.Field { + return []ent.Field{ + field.String("base_url_host"). + NotEmpty(). + SchemaType(map[string]string{ + dialect.Postgres: "text", + }), + field.String("account_type"). + NotEmpty(). + MaxLen(20), + field.String("script"). + SchemaType(map[string]string{ + dialect.Postgres: "text", + }), + field.Bool("enabled"). + Default(true), + } +} + +func (UsageScript) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("base_url_host", "account_type").Unique(), + } +} diff --git a/backend/ent/tx.go b/backend/ent/tx.go index cd3b2296c7..e52a262763 100644 --- a/backend/ent/tx.go +++ b/backend/ent/tx.go @@ -46,6 +46,8 @@ type Tx struct { UsageCleanupTask *UsageCleanupTaskClient // UsageLog is the client for interacting with the UsageLog builders. UsageLog *UsageLogClient + // UsageScript is the client for interacting with the UsageScript builders. + UsageScript *UsageScriptClient // User is the client for interacting with the User builders. User *UserClient // UserAllowedGroup is the client for interacting with the UserAllowedGroup builders. @@ -203,6 +205,7 @@ func (tx *Tx) init() { tx.Setting = NewSettingClient(tx.config) tx.UsageCleanupTask = NewUsageCleanupTaskClient(tx.config) tx.UsageLog = NewUsageLogClient(tx.config) + tx.UsageScript = NewUsageScriptClient(tx.config) tx.User = NewUserClient(tx.config) tx.UserAllowedGroup = NewUserAllowedGroupClient(tx.config) tx.UserAttributeDefinition = NewUserAttributeDefinitionClient(tx.config) diff --git a/backend/ent/usagescript.go b/backend/ent/usagescript.go new file mode 100644 index 0000000000..b4e58e09e7 --- /dev/null +++ b/backend/ent/usagescript.go @@ -0,0 +1,177 @@ +// 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/usagescript" +) + +// UsageScript is the model entity for the UsageScript schema. +type UsageScript 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"` + // DeletedAt holds the value of the "deleted_at" field. + DeletedAt *time.Time `json:"deleted_at,omitempty"` + // BaseURLHost holds the value of the "base_url_host" field. + BaseURLHost string `json:"base_url_host,omitempty"` + // AccountType holds the value of the "account_type" field. + AccountType string `json:"account_type,omitempty"` + // Script holds the value of the "script" field. + Script string `json:"script,omitempty"` + // Enabled holds the value of the "enabled" field. + Enabled bool `json:"enabled,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*UsageScript) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case usagescript.FieldEnabled: + values[i] = new(sql.NullBool) + case usagescript.FieldID: + values[i] = new(sql.NullInt64) + case usagescript.FieldBaseURLHost, usagescript.FieldAccountType, usagescript.FieldScript: + values[i] = new(sql.NullString) + case usagescript.FieldCreatedAt, usagescript.FieldUpdatedAt, usagescript.FieldDeletedAt: + 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 UsageScript fields. +func (_m *UsageScript) 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 usagescript.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 usagescript.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 usagescript.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 usagescript.FieldDeletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field deleted_at", values[i]) + } else if value.Valid { + _m.DeletedAt = new(time.Time) + *_m.DeletedAt = value.Time + } + case usagescript.FieldBaseURLHost: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field base_url_host", values[i]) + } else if value.Valid { + _m.BaseURLHost = value.String + } + case usagescript.FieldAccountType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field account_type", values[i]) + } else if value.Valid { + _m.AccountType = value.String + } + case usagescript.FieldScript: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field script", values[i]) + } else if value.Valid { + _m.Script = value.String + } + case usagescript.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 + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the UsageScript. +// This includes values selected through modifiers, order, etc. +func (_m *UsageScript) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this UsageScript. +// Note that you need to call UsageScript.Unwrap() before calling this method if this UsageScript +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *UsageScript) Update() *UsageScriptUpdateOne { + return NewUsageScriptClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the UsageScript 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 *UsageScript) Unwrap() *UsageScript { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: UsageScript is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *UsageScript) String() string { + var builder strings.Builder + builder.WriteString("UsageScript(") + 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(", ") + if v := _m.DeletedAt; v != nil { + builder.WriteString("deleted_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("base_url_host=") + builder.WriteString(_m.BaseURLHost) + builder.WriteString(", ") + builder.WriteString("account_type=") + builder.WriteString(_m.AccountType) + builder.WriteString(", ") + builder.WriteString("script=") + builder.WriteString(_m.Script) + builder.WriteString(", ") + builder.WriteString("enabled=") + builder.WriteString(fmt.Sprintf("%v", _m.Enabled)) + builder.WriteByte(')') + return builder.String() +} + +// UsageScripts is a parsable slice of UsageScript. +type UsageScripts []*UsageScript diff --git a/backend/ent/usagescript/usagescript.go b/backend/ent/usagescript/usagescript.go new file mode 100644 index 0000000000..68b72dc73a --- /dev/null +++ b/backend/ent/usagescript/usagescript.go @@ -0,0 +1,120 @@ +// Code generated by ent, DO NOT EDIT. + +package usagescript + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the usagescript type in the database. + Label = "usage_script" + // 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" + // FieldDeletedAt holds the string denoting the deleted_at field in the database. + FieldDeletedAt = "deleted_at" + // FieldBaseURLHost holds the string denoting the base_url_host field in the database. + FieldBaseURLHost = "base_url_host" + // FieldAccountType holds the string denoting the account_type field in the database. + FieldAccountType = "account_type" + // FieldScript holds the string denoting the script field in the database. + FieldScript = "script" + // FieldEnabled holds the string denoting the enabled field in the database. + FieldEnabled = "enabled" + // Table holds the table name of the usagescript in the database. + Table = "usage_scripts" +) + +// Columns holds all SQL columns for usagescript fields. +var Columns = []string{ + FieldID, + FieldCreatedAt, + FieldUpdatedAt, + FieldDeletedAt, + FieldBaseURLHost, + FieldAccountType, + FieldScript, + FieldEnabled, +} + +// 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 +} + +// Note that the variables below are initialized by the runtime +// package on the initialization of the application. Therefore, +// it should be imported in the main as follows: +// +// import _ "github.com/Wei-Shaw/sub2api/ent/runtime" +var ( + Hooks [1]ent.Hook + Interceptors [1]ent.Interceptor + // 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 + // BaseURLHostValidator is a validator for the "base_url_host" field. It is called by the builders before save. + BaseURLHostValidator func(string) error + // AccountTypeValidator is a validator for the "account_type" field. It is called by the builders before save. + AccountTypeValidator func(string) error + // DefaultEnabled holds the default value on creation for the "enabled" field. + DefaultEnabled bool +) + +// OrderOption defines the ordering options for the UsageScript 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() +} + +// ByDeletedAt orders the results by the deleted_at field. +func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDeletedAt, opts...).ToFunc() +} + +// ByBaseURLHost orders the results by the base_url_host field. +func ByBaseURLHost(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBaseURLHost, opts...).ToFunc() +} + +// ByAccountType orders the results by the account_type field. +func ByAccountType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAccountType, opts...).ToFunc() +} + +// ByScript orders the results by the script field. +func ByScript(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScript, opts...).ToFunc() +} + +// ByEnabled orders the results by the enabled field. +func ByEnabled(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEnabled, opts...).ToFunc() +} diff --git a/backend/ent/usagescript/where.go b/backend/ent/usagescript/where.go new file mode 100644 index 0000000000..d654c05299 --- /dev/null +++ b/backend/ent/usagescript/where.go @@ -0,0 +1,440 @@ +// Code generated by ent, DO NOT EDIT. + +package usagescript + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/Wei-Shaw/sub2api/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int64) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int64) predicate.UsageScript { + return predicate.UsageScript(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.UsageScript { + return predicate.UsageScript(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.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ. +func DeletedAt(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldDeletedAt, v)) +} + +// BaseURLHost applies equality check predicate on the "base_url_host" field. It's identical to BaseURLHostEQ. +func BaseURLHost(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldBaseURLHost, v)) +} + +// AccountType applies equality check predicate on the "account_type" field. It's identical to AccountTypeEQ. +func AccountType(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldAccountType, v)) +} + +// Script applies equality check predicate on the "script" field. It's identical to ScriptEQ. +func Script(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldScript, v)) +} + +// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ. +func Enabled(v bool) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldEnabled, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// DeletedAtEQ applies the EQ predicate on the "deleted_at" field. +func DeletedAtEQ(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldDeletedAt, v)) +} + +// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field. +func DeletedAtNEQ(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldDeletedAt, v)) +} + +// DeletedAtIn applies the In predicate on the "deleted_at" field. +func DeletedAtIn(vs ...time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldIn(FieldDeletedAt, vs...)) +} + +// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field. +func DeletedAtNotIn(vs ...time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotIn(FieldDeletedAt, vs...)) +} + +// DeletedAtGT applies the GT predicate on the "deleted_at" field. +func DeletedAtGT(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGT(FieldDeletedAt, v)) +} + +// DeletedAtGTE applies the GTE predicate on the "deleted_at" field. +func DeletedAtGTE(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGTE(FieldDeletedAt, v)) +} + +// DeletedAtLT applies the LT predicate on the "deleted_at" field. +func DeletedAtLT(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLT(FieldDeletedAt, v)) +} + +// DeletedAtLTE applies the LTE predicate on the "deleted_at" field. +func DeletedAtLTE(v time.Time) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLTE(FieldDeletedAt, v)) +} + +// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field. +func DeletedAtIsNil() predicate.UsageScript { + return predicate.UsageScript(sql.FieldIsNull(FieldDeletedAt)) +} + +// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field. +func DeletedAtNotNil() predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotNull(FieldDeletedAt)) +} + +// BaseURLHostEQ applies the EQ predicate on the "base_url_host" field. +func BaseURLHostEQ(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldBaseURLHost, v)) +} + +// BaseURLHostNEQ applies the NEQ predicate on the "base_url_host" field. +func BaseURLHostNEQ(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldBaseURLHost, v)) +} + +// BaseURLHostIn applies the In predicate on the "base_url_host" field. +func BaseURLHostIn(vs ...string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldIn(FieldBaseURLHost, vs...)) +} + +// BaseURLHostNotIn applies the NotIn predicate on the "base_url_host" field. +func BaseURLHostNotIn(vs ...string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotIn(FieldBaseURLHost, vs...)) +} + +// BaseURLHostGT applies the GT predicate on the "base_url_host" field. +func BaseURLHostGT(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGT(FieldBaseURLHost, v)) +} + +// BaseURLHostGTE applies the GTE predicate on the "base_url_host" field. +func BaseURLHostGTE(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGTE(FieldBaseURLHost, v)) +} + +// BaseURLHostLT applies the LT predicate on the "base_url_host" field. +func BaseURLHostLT(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLT(FieldBaseURLHost, v)) +} + +// BaseURLHostLTE applies the LTE predicate on the "base_url_host" field. +func BaseURLHostLTE(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLTE(FieldBaseURLHost, v)) +} + +// BaseURLHostContains applies the Contains predicate on the "base_url_host" field. +func BaseURLHostContains(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldContains(FieldBaseURLHost, v)) +} + +// BaseURLHostHasPrefix applies the HasPrefix predicate on the "base_url_host" field. +func BaseURLHostHasPrefix(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldHasPrefix(FieldBaseURLHost, v)) +} + +// BaseURLHostHasSuffix applies the HasSuffix predicate on the "base_url_host" field. +func BaseURLHostHasSuffix(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldHasSuffix(FieldBaseURLHost, v)) +} + +// BaseURLHostEqualFold applies the EqualFold predicate on the "base_url_host" field. +func BaseURLHostEqualFold(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEqualFold(FieldBaseURLHost, v)) +} + +// BaseURLHostContainsFold applies the ContainsFold predicate on the "base_url_host" field. +func BaseURLHostContainsFold(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldContainsFold(FieldBaseURLHost, v)) +} + +// AccountTypeEQ applies the EQ predicate on the "account_type" field. +func AccountTypeEQ(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldAccountType, v)) +} + +// AccountTypeNEQ applies the NEQ predicate on the "account_type" field. +func AccountTypeNEQ(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldAccountType, v)) +} + +// AccountTypeIn applies the In predicate on the "account_type" field. +func AccountTypeIn(vs ...string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldIn(FieldAccountType, vs...)) +} + +// AccountTypeNotIn applies the NotIn predicate on the "account_type" field. +func AccountTypeNotIn(vs ...string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotIn(FieldAccountType, vs...)) +} + +// AccountTypeGT applies the GT predicate on the "account_type" field. +func AccountTypeGT(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGT(FieldAccountType, v)) +} + +// AccountTypeGTE applies the GTE predicate on the "account_type" field. +func AccountTypeGTE(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGTE(FieldAccountType, v)) +} + +// AccountTypeLT applies the LT predicate on the "account_type" field. +func AccountTypeLT(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLT(FieldAccountType, v)) +} + +// AccountTypeLTE applies the LTE predicate on the "account_type" field. +func AccountTypeLTE(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLTE(FieldAccountType, v)) +} + +// AccountTypeContains applies the Contains predicate on the "account_type" field. +func AccountTypeContains(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldContains(FieldAccountType, v)) +} + +// AccountTypeHasPrefix applies the HasPrefix predicate on the "account_type" field. +func AccountTypeHasPrefix(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldHasPrefix(FieldAccountType, v)) +} + +// AccountTypeHasSuffix applies the HasSuffix predicate on the "account_type" field. +func AccountTypeHasSuffix(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldHasSuffix(FieldAccountType, v)) +} + +// AccountTypeEqualFold applies the EqualFold predicate on the "account_type" field. +func AccountTypeEqualFold(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEqualFold(FieldAccountType, v)) +} + +// AccountTypeContainsFold applies the ContainsFold predicate on the "account_type" field. +func AccountTypeContainsFold(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldContainsFold(FieldAccountType, v)) +} + +// ScriptEQ applies the EQ predicate on the "script" field. +func ScriptEQ(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldScript, v)) +} + +// ScriptNEQ applies the NEQ predicate on the "script" field. +func ScriptNEQ(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldScript, v)) +} + +// ScriptIn applies the In predicate on the "script" field. +func ScriptIn(vs ...string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldIn(FieldScript, vs...)) +} + +// ScriptNotIn applies the NotIn predicate on the "script" field. +func ScriptNotIn(vs ...string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNotIn(FieldScript, vs...)) +} + +// ScriptGT applies the GT predicate on the "script" field. +func ScriptGT(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGT(FieldScript, v)) +} + +// ScriptGTE applies the GTE predicate on the "script" field. +func ScriptGTE(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldGTE(FieldScript, v)) +} + +// ScriptLT applies the LT predicate on the "script" field. +func ScriptLT(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLT(FieldScript, v)) +} + +// ScriptLTE applies the LTE predicate on the "script" field. +func ScriptLTE(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldLTE(FieldScript, v)) +} + +// ScriptContains applies the Contains predicate on the "script" field. +func ScriptContains(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldContains(FieldScript, v)) +} + +// ScriptHasPrefix applies the HasPrefix predicate on the "script" field. +func ScriptHasPrefix(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldHasPrefix(FieldScript, v)) +} + +// ScriptHasSuffix applies the HasSuffix predicate on the "script" field. +func ScriptHasSuffix(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldHasSuffix(FieldScript, v)) +} + +// ScriptEqualFold applies the EqualFold predicate on the "script" field. +func ScriptEqualFold(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEqualFold(FieldScript, v)) +} + +// ScriptContainsFold applies the ContainsFold predicate on the "script" field. +func ScriptContainsFold(v string) predicate.UsageScript { + return predicate.UsageScript(sql.FieldContainsFold(FieldScript, v)) +} + +// EnabledEQ applies the EQ predicate on the "enabled" field. +func EnabledEQ(v bool) predicate.UsageScript { + return predicate.UsageScript(sql.FieldEQ(FieldEnabled, v)) +} + +// EnabledNEQ applies the NEQ predicate on the "enabled" field. +func EnabledNEQ(v bool) predicate.UsageScript { + return predicate.UsageScript(sql.FieldNEQ(FieldEnabled, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.UsageScript) predicate.UsageScript { + return predicate.UsageScript(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.UsageScript) predicate.UsageScript { + return predicate.UsageScript(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.UsageScript) predicate.UsageScript { + return predicate.UsageScript(sql.NotPredicates(p)) +} diff --git a/backend/ent/usagescript_create.go b/backend/ent/usagescript_create.go new file mode 100644 index 0000000000..4a93a7d17d --- /dev/null +++ b/backend/ent/usagescript_create.go @@ -0,0 +1,831 @@ +// 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/usagescript" +) + +// UsageScriptCreate is the builder for creating a UsageScript entity. +type UsageScriptCreate struct { + config + mutation *UsageScriptMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetCreatedAt sets the "created_at" field. +func (_c *UsageScriptCreate) SetCreatedAt(v time.Time) *UsageScriptCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *UsageScriptCreate) SetNillableCreatedAt(v *time.Time) *UsageScriptCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *UsageScriptCreate) SetUpdatedAt(v time.Time) *UsageScriptCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *UsageScriptCreate) SetNillableUpdatedAt(v *time.Time) *UsageScriptCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetDeletedAt sets the "deleted_at" field. +func (_c *UsageScriptCreate) SetDeletedAt(v time.Time) *UsageScriptCreate { + _c.mutation.SetDeletedAt(v) + return _c +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (_c *UsageScriptCreate) SetNillableDeletedAt(v *time.Time) *UsageScriptCreate { + if v != nil { + _c.SetDeletedAt(*v) + } + return _c +} + +// SetBaseURLHost sets the "base_url_host" field. +func (_c *UsageScriptCreate) SetBaseURLHost(v string) *UsageScriptCreate { + _c.mutation.SetBaseURLHost(v) + return _c +} + +// SetAccountType sets the "account_type" field. +func (_c *UsageScriptCreate) SetAccountType(v string) *UsageScriptCreate { + _c.mutation.SetAccountType(v) + return _c +} + +// SetScript sets the "script" field. +func (_c *UsageScriptCreate) SetScript(v string) *UsageScriptCreate { + _c.mutation.SetScript(v) + return _c +} + +// SetEnabled sets the "enabled" field. +func (_c *UsageScriptCreate) SetEnabled(v bool) *UsageScriptCreate { + _c.mutation.SetEnabled(v) + return _c +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_c *UsageScriptCreate) SetNillableEnabled(v *bool) *UsageScriptCreate { + if v != nil { + _c.SetEnabled(*v) + } + return _c +} + +// Mutation returns the UsageScriptMutation object of the builder. +func (_c *UsageScriptCreate) Mutation() *UsageScriptMutation { + return _c.mutation +} + +// Save creates the UsageScript in the database. +func (_c *UsageScriptCreate) Save(ctx context.Context) (*UsageScript, error) { + if err := _c.defaults(); err != nil { + return nil, err + } + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *UsageScriptCreate) SaveX(ctx context.Context) *UsageScript { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *UsageScriptCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *UsageScriptCreate) 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 *UsageScriptCreate) defaults() error { + if _, ok := _c.mutation.CreatedAt(); !ok { + if usagescript.DefaultCreatedAt == nil { + return fmt.Errorf("ent: uninitialized usagescript.DefaultCreatedAt (forgotten import ent/runtime?)") + } + v := usagescript.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + if usagescript.DefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized usagescript.DefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := usagescript.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.Enabled(); !ok { + v := usagescript.DefaultEnabled + _c.mutation.SetEnabled(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (_c *UsageScriptCreate) check() error { + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "UsageScript.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "UsageScript.updated_at"`)} + } + if _, ok := _c.mutation.BaseURLHost(); !ok { + return &ValidationError{Name: "base_url_host", err: errors.New(`ent: missing required field "UsageScript.base_url_host"`)} + } + if v, ok := _c.mutation.BaseURLHost(); ok { + if err := usagescript.BaseURLHostValidator(v); err != nil { + return &ValidationError{Name: "base_url_host", err: fmt.Errorf(`ent: validator failed for field "UsageScript.base_url_host": %w`, err)} + } + } + if _, ok := _c.mutation.AccountType(); !ok { + return &ValidationError{Name: "account_type", err: errors.New(`ent: missing required field "UsageScript.account_type"`)} + } + if v, ok := _c.mutation.AccountType(); ok { + if err := usagescript.AccountTypeValidator(v); err != nil { + return &ValidationError{Name: "account_type", err: fmt.Errorf(`ent: validator failed for field "UsageScript.account_type": %w`, err)} + } + } + if _, ok := _c.mutation.Script(); !ok { + return &ValidationError{Name: "script", err: errors.New(`ent: missing required field "UsageScript.script"`)} + } + if _, ok := _c.mutation.Enabled(); !ok { + return &ValidationError{Name: "enabled", err: errors.New(`ent: missing required field "UsageScript.enabled"`)} + } + return nil +} + +func (_c *UsageScriptCreate) sqlSave(ctx context.Context) (*UsageScript, 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 *UsageScriptCreate) createSpec() (*UsageScript, *sqlgraph.CreateSpec) { + var ( + _node = &UsageScript{config: _c.config} + _spec = sqlgraph.NewCreateSpec(usagescript.Table, sqlgraph.NewFieldSpec(usagescript.FieldID, field.TypeInt64)) + ) + _spec.OnConflict = _c.conflict + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(usagescript.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(usagescript.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := _c.mutation.DeletedAt(); ok { + _spec.SetField(usagescript.FieldDeletedAt, field.TypeTime, value) + _node.DeletedAt = &value + } + if value, ok := _c.mutation.BaseURLHost(); ok { + _spec.SetField(usagescript.FieldBaseURLHost, field.TypeString, value) + _node.BaseURLHost = value + } + if value, ok := _c.mutation.AccountType(); ok { + _spec.SetField(usagescript.FieldAccountType, field.TypeString, value) + _node.AccountType = value + } + if value, ok := _c.mutation.Script(); ok { + _spec.SetField(usagescript.FieldScript, field.TypeString, value) + _node.Script = value + } + if value, ok := _c.mutation.Enabled(); ok { + _spec.SetField(usagescript.FieldEnabled, field.TypeBool, value) + _node.Enabled = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.UsageScript.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.UsageScriptUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (_c *UsageScriptCreate) OnConflict(opts ...sql.ConflictOption) *UsageScriptUpsertOne { + _c.conflict = opts + return &UsageScriptUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.UsageScript.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *UsageScriptCreate) OnConflictColumns(columns ...string) *UsageScriptUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &UsageScriptUpsertOne{ + create: _c, + } +} + +type ( + // UsageScriptUpsertOne is the builder for "upsert"-ing + // one UsageScript node. + UsageScriptUpsertOne struct { + create *UsageScriptCreate + } + + // UsageScriptUpsert is the "OnConflict" setter. + UsageScriptUpsert struct { + *sql.UpdateSet + } +) + +// SetUpdatedAt sets the "updated_at" field. +func (u *UsageScriptUpsert) SetUpdatedAt(v time.Time) *UsageScriptUpsert { + u.Set(usagescript.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *UsageScriptUpsert) UpdateUpdatedAt() *UsageScriptUpsert { + u.SetExcluded(usagescript.FieldUpdatedAt) + return u +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *UsageScriptUpsert) SetDeletedAt(v time.Time) *UsageScriptUpsert { + u.Set(usagescript.FieldDeletedAt, v) + return u +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *UsageScriptUpsert) UpdateDeletedAt() *UsageScriptUpsert { + u.SetExcluded(usagescript.FieldDeletedAt) + return u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *UsageScriptUpsert) ClearDeletedAt() *UsageScriptUpsert { + u.SetNull(usagescript.FieldDeletedAt) + return u +} + +// SetBaseURLHost sets the "base_url_host" field. +func (u *UsageScriptUpsert) SetBaseURLHost(v string) *UsageScriptUpsert { + u.Set(usagescript.FieldBaseURLHost, v) + return u +} + +// UpdateBaseURLHost sets the "base_url_host" field to the value that was provided on create. +func (u *UsageScriptUpsert) UpdateBaseURLHost() *UsageScriptUpsert { + u.SetExcluded(usagescript.FieldBaseURLHost) + return u +} + +// SetAccountType sets the "account_type" field. +func (u *UsageScriptUpsert) SetAccountType(v string) *UsageScriptUpsert { + u.Set(usagescript.FieldAccountType, v) + return u +} + +// UpdateAccountType sets the "account_type" field to the value that was provided on create. +func (u *UsageScriptUpsert) UpdateAccountType() *UsageScriptUpsert { + u.SetExcluded(usagescript.FieldAccountType) + return u +} + +// SetScript sets the "script" field. +func (u *UsageScriptUpsert) SetScript(v string) *UsageScriptUpsert { + u.Set(usagescript.FieldScript, v) + return u +} + +// UpdateScript sets the "script" field to the value that was provided on create. +func (u *UsageScriptUpsert) UpdateScript() *UsageScriptUpsert { + u.SetExcluded(usagescript.FieldScript) + return u +} + +// SetEnabled sets the "enabled" field. +func (u *UsageScriptUpsert) SetEnabled(v bool) *UsageScriptUpsert { + u.Set(usagescript.FieldEnabled, v) + return u +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *UsageScriptUpsert) UpdateEnabled() *UsageScriptUpsert { + u.SetExcluded(usagescript.FieldEnabled) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create. +// Using this option is equivalent to using: +// +// client.UsageScript.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *UsageScriptUpsertOne) UpdateNewValues() *UsageScriptUpsertOne { + 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(usagescript.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.UsageScript.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UsageScriptUpsertOne) Ignore() *UsageScriptUpsertOne { + 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 *UsageScriptUpsertOne) DoNothing() *UsageScriptUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UsageScriptCreate.OnConflict +// documentation for more info. +func (u *UsageScriptUpsertOne) Update(set func(*UsageScriptUpsert)) *UsageScriptUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UsageScriptUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *UsageScriptUpsertOne) SetUpdatedAt(v time.Time) *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *UsageScriptUpsertOne) UpdateUpdatedAt() *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *UsageScriptUpsertOne) SetDeletedAt(v time.Time) *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *UsageScriptUpsertOne) UpdateDeletedAt() *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *UsageScriptUpsertOne) ClearDeletedAt() *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.ClearDeletedAt() + }) +} + +// SetBaseURLHost sets the "base_url_host" field. +func (u *UsageScriptUpsertOne) SetBaseURLHost(v string) *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.SetBaseURLHost(v) + }) +} + +// UpdateBaseURLHost sets the "base_url_host" field to the value that was provided on create. +func (u *UsageScriptUpsertOne) UpdateBaseURLHost() *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateBaseURLHost() + }) +} + +// SetAccountType sets the "account_type" field. +func (u *UsageScriptUpsertOne) SetAccountType(v string) *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.SetAccountType(v) + }) +} + +// UpdateAccountType sets the "account_type" field to the value that was provided on create. +func (u *UsageScriptUpsertOne) UpdateAccountType() *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateAccountType() + }) +} + +// SetScript sets the "script" field. +func (u *UsageScriptUpsertOne) SetScript(v string) *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.SetScript(v) + }) +} + +// UpdateScript sets the "script" field to the value that was provided on create. +func (u *UsageScriptUpsertOne) UpdateScript() *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateScript() + }) +} + +// SetEnabled sets the "enabled" field. +func (u *UsageScriptUpsertOne) SetEnabled(v bool) *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.SetEnabled(v) + }) +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *UsageScriptUpsertOne) UpdateEnabled() *UsageScriptUpsertOne { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateEnabled() + }) +} + +// Exec executes the query. +func (u *UsageScriptUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UsageScriptCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UsageScriptUpsertOne) 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 *UsageScriptUpsertOne) 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 *UsageScriptUpsertOne) IDX(ctx context.Context) int64 { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// UsageScriptCreateBulk is the builder for creating many UsageScript entities in bulk. +type UsageScriptCreateBulk struct { + config + err error + builders []*UsageScriptCreate + conflict []sql.ConflictOption +} + +// Save creates the UsageScript entities in the database. +func (_c *UsageScriptCreateBulk) Save(ctx context.Context) ([]*UsageScript, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*UsageScript, 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.(*UsageScriptMutation) + 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 *UsageScriptCreateBulk) SaveX(ctx context.Context) []*UsageScript { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *UsageScriptCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *UsageScriptCreateBulk) 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.UsageScript.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.UsageScriptUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (_c *UsageScriptCreateBulk) OnConflict(opts ...sql.ConflictOption) *UsageScriptUpsertBulk { + _c.conflict = opts + return &UsageScriptUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.UsageScript.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *UsageScriptCreateBulk) OnConflictColumns(columns ...string) *UsageScriptUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &UsageScriptUpsertBulk{ + create: _c, + } +} + +// UsageScriptUpsertBulk is the builder for "upsert"-ing +// a bulk of UsageScript nodes. +type UsageScriptUpsertBulk struct { + create *UsageScriptCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.UsageScript.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *UsageScriptUpsertBulk) UpdateNewValues() *UsageScriptUpsertBulk { + 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(usagescript.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.UsageScript.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UsageScriptUpsertBulk) Ignore() *UsageScriptUpsertBulk { + 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 *UsageScriptUpsertBulk) DoNothing() *UsageScriptUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UsageScriptCreateBulk.OnConflict +// documentation for more info. +func (u *UsageScriptUpsertBulk) Update(set func(*UsageScriptUpsert)) *UsageScriptUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UsageScriptUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *UsageScriptUpsertBulk) SetUpdatedAt(v time.Time) *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *UsageScriptUpsertBulk) UpdateUpdatedAt() *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *UsageScriptUpsertBulk) SetDeletedAt(v time.Time) *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *UsageScriptUpsertBulk) UpdateDeletedAt() *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *UsageScriptUpsertBulk) ClearDeletedAt() *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.ClearDeletedAt() + }) +} + +// SetBaseURLHost sets the "base_url_host" field. +func (u *UsageScriptUpsertBulk) SetBaseURLHost(v string) *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.SetBaseURLHost(v) + }) +} + +// UpdateBaseURLHost sets the "base_url_host" field to the value that was provided on create. +func (u *UsageScriptUpsertBulk) UpdateBaseURLHost() *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateBaseURLHost() + }) +} + +// SetAccountType sets the "account_type" field. +func (u *UsageScriptUpsertBulk) SetAccountType(v string) *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.SetAccountType(v) + }) +} + +// UpdateAccountType sets the "account_type" field to the value that was provided on create. +func (u *UsageScriptUpsertBulk) UpdateAccountType() *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateAccountType() + }) +} + +// SetScript sets the "script" field. +func (u *UsageScriptUpsertBulk) SetScript(v string) *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.SetScript(v) + }) +} + +// UpdateScript sets the "script" field to the value that was provided on create. +func (u *UsageScriptUpsertBulk) UpdateScript() *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateScript() + }) +} + +// SetEnabled sets the "enabled" field. +func (u *UsageScriptUpsertBulk) SetEnabled(v bool) *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.SetEnabled(v) + }) +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *UsageScriptUpsertBulk) UpdateEnabled() *UsageScriptUpsertBulk { + return u.Update(func(s *UsageScriptUpsert) { + s.UpdateEnabled() + }) +} + +// Exec executes the query. +func (u *UsageScriptUpsertBulk) 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 UsageScriptCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UsageScriptCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UsageScriptUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/usagescript_delete.go b/backend/ent/usagescript_delete.go new file mode 100644 index 0000000000..43799907ba --- /dev/null +++ b/backend/ent/usagescript_delete.go @@ -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/predicate" + "github.com/Wei-Shaw/sub2api/ent/usagescript" +) + +// UsageScriptDelete is the builder for deleting a UsageScript entity. +type UsageScriptDelete struct { + config + hooks []Hook + mutation *UsageScriptMutation +} + +// Where appends a list predicates to the UsageScriptDelete builder. +func (_d *UsageScriptDelete) Where(ps ...predicate.UsageScript) *UsageScriptDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *UsageScriptDelete) 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 *UsageScriptDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *UsageScriptDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(usagescript.Table, sqlgraph.NewFieldSpec(usagescript.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 +} + +// UsageScriptDeleteOne is the builder for deleting a single UsageScript entity. +type UsageScriptDeleteOne struct { + _d *UsageScriptDelete +} + +// Where appends a list predicates to the UsageScriptDelete builder. +func (_d *UsageScriptDeleteOne) Where(ps ...predicate.UsageScript) *UsageScriptDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *UsageScriptDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{usagescript.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *UsageScriptDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/usagescript_query.go b/backend/ent/usagescript_query.go new file mode 100644 index 0000000000..23ae258706 --- /dev/null +++ b/backend/ent/usagescript_query.go @@ -0,0 +1,564 @@ +// 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/predicate" + "github.com/Wei-Shaw/sub2api/ent/usagescript" +) + +// UsageScriptQuery is the builder for querying UsageScript entities. +type UsageScriptQuery struct { + config + ctx *QueryContext + order []usagescript.OrderOption + inters []Interceptor + predicates []predicate.UsageScript + 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 UsageScriptQuery builder. +func (_q *UsageScriptQuery) Where(ps ...predicate.UsageScript) *UsageScriptQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *UsageScriptQuery) Limit(limit int) *UsageScriptQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *UsageScriptQuery) Offset(offset int) *UsageScriptQuery { + _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 *UsageScriptQuery) Unique(unique bool) *UsageScriptQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *UsageScriptQuery) Order(o ...usagescript.OrderOption) *UsageScriptQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first UsageScript entity from the query. +// Returns a *NotFoundError when no UsageScript was found. +func (_q *UsageScriptQuery) First(ctx context.Context) (*UsageScript, 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{usagescript.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *UsageScriptQuery) FirstX(ctx context.Context) *UsageScript { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first UsageScript ID from the query. +// Returns a *NotFoundError when no UsageScript ID was found. +func (_q *UsageScriptQuery) 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{usagescript.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *UsageScriptQuery) FirstIDX(ctx context.Context) int64 { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single UsageScript entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one UsageScript entity is found. +// Returns a *NotFoundError when no UsageScript entities are found. +func (_q *UsageScriptQuery) Only(ctx context.Context) (*UsageScript, 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{usagescript.Label} + default: + return nil, &NotSingularError{usagescript.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *UsageScriptQuery) OnlyX(ctx context.Context) *UsageScript { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only UsageScript ID in the query. +// Returns a *NotSingularError when more than one UsageScript ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *UsageScriptQuery) 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{usagescript.Label} + default: + err = &NotSingularError{usagescript.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *UsageScriptQuery) 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 UsageScripts. +func (_q *UsageScriptQuery) All(ctx context.Context) ([]*UsageScript, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*UsageScript, *UsageScriptQuery]() + return withInterceptors[[]*UsageScript](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *UsageScriptQuery) AllX(ctx context.Context) []*UsageScript { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of UsageScript IDs. +func (_q *UsageScriptQuery) 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(usagescript.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *UsageScriptQuery) 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 *UsageScriptQuery) 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[*UsageScriptQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *UsageScriptQuery) 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 *UsageScriptQuery) 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 *UsageScriptQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the UsageScriptQuery 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 *UsageScriptQuery) Clone() *UsageScriptQuery { + if _q == nil { + return nil + } + return &UsageScriptQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]usagescript.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.UsageScript{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// 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.UsageScript.Query(). +// GroupBy(usagescript.FieldCreatedAt). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *UsageScriptQuery) GroupBy(field string, fields ...string) *UsageScriptGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &UsageScriptGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = usagescript.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.UsageScript.Query(). +// Select(usagescript.FieldCreatedAt). +// Scan(ctx, &v) +func (_q *UsageScriptQuery) Select(fields ...string) *UsageScriptSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &UsageScriptSelect{UsageScriptQuery: _q} + sbuild.label = usagescript.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a UsageScriptSelect configured with the given aggregations. +func (_q *UsageScriptQuery) Aggregate(fns ...AggregateFunc) *UsageScriptSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *UsageScriptQuery) 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 !usagescript.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 *UsageScriptQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UsageScript, error) { + var ( + nodes = []*UsageScript{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*UsageScript).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &UsageScript{config: _q.config} + nodes = append(nodes, node) + 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 + } + return nodes, nil +} + +func (_q *UsageScriptQuery) 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 *UsageScriptQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(usagescript.Table, usagescript.Columns, sqlgraph.NewFieldSpec(usagescript.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, usagescript.FieldID) + for i := range fields { + if fields[i] != usagescript.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 *UsageScriptQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(usagescript.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = usagescript.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 *UsageScriptQuery) ForUpdate(opts ...sql.LockOption) *UsageScriptQuery { + 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 *UsageScriptQuery) ForShare(opts ...sql.LockOption) *UsageScriptQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// UsageScriptGroupBy is the group-by builder for UsageScript entities. +type UsageScriptGroupBy struct { + selector + build *UsageScriptQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *UsageScriptGroupBy) Aggregate(fns ...AggregateFunc) *UsageScriptGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *UsageScriptGroupBy) 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[*UsageScriptQuery, *UsageScriptGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *UsageScriptGroupBy) sqlScan(ctx context.Context, root *UsageScriptQuery, 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) +} + +// UsageScriptSelect is the builder for selecting fields of UsageScript entities. +type UsageScriptSelect struct { + *UsageScriptQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *UsageScriptSelect) Aggregate(fns ...AggregateFunc) *UsageScriptSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *UsageScriptSelect) 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[*UsageScriptQuery, *UsageScriptSelect](ctx, _s.UsageScriptQuery, _s, _s.inters, v) +} + +func (_s *UsageScriptSelect) sqlScan(ctx context.Context, root *UsageScriptQuery, 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) +} diff --git a/backend/ent/usagescript_update.go b/backend/ent/usagescript_update.go new file mode 100644 index 0000000000..b4f55c604c --- /dev/null +++ b/backend/ent/usagescript_update.go @@ -0,0 +1,448 @@ +// 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/predicate" + "github.com/Wei-Shaw/sub2api/ent/usagescript" +) + +// UsageScriptUpdate is the builder for updating UsageScript entities. +type UsageScriptUpdate struct { + config + hooks []Hook + mutation *UsageScriptMutation +} + +// Where appends a list predicates to the UsageScriptUpdate builder. +func (_u *UsageScriptUpdate) Where(ps ...predicate.UsageScript) *UsageScriptUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *UsageScriptUpdate) SetUpdatedAt(v time.Time) *UsageScriptUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// SetDeletedAt sets the "deleted_at" field. +func (_u *UsageScriptUpdate) SetDeletedAt(v time.Time) *UsageScriptUpdate { + _u.mutation.SetDeletedAt(v) + return _u +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (_u *UsageScriptUpdate) SetNillableDeletedAt(v *time.Time) *UsageScriptUpdate { + if v != nil { + _u.SetDeletedAt(*v) + } + return _u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (_u *UsageScriptUpdate) ClearDeletedAt() *UsageScriptUpdate { + _u.mutation.ClearDeletedAt() + return _u +} + +// SetBaseURLHost sets the "base_url_host" field. +func (_u *UsageScriptUpdate) SetBaseURLHost(v string) *UsageScriptUpdate { + _u.mutation.SetBaseURLHost(v) + return _u +} + +// SetNillableBaseURLHost sets the "base_url_host" field if the given value is not nil. +func (_u *UsageScriptUpdate) SetNillableBaseURLHost(v *string) *UsageScriptUpdate { + if v != nil { + _u.SetBaseURLHost(*v) + } + return _u +} + +// SetAccountType sets the "account_type" field. +func (_u *UsageScriptUpdate) SetAccountType(v string) *UsageScriptUpdate { + _u.mutation.SetAccountType(v) + return _u +} + +// SetNillableAccountType sets the "account_type" field if the given value is not nil. +func (_u *UsageScriptUpdate) SetNillableAccountType(v *string) *UsageScriptUpdate { + if v != nil { + _u.SetAccountType(*v) + } + return _u +} + +// SetScript sets the "script" field. +func (_u *UsageScriptUpdate) SetScript(v string) *UsageScriptUpdate { + _u.mutation.SetScript(v) + return _u +} + +// SetNillableScript sets the "script" field if the given value is not nil. +func (_u *UsageScriptUpdate) SetNillableScript(v *string) *UsageScriptUpdate { + if v != nil { + _u.SetScript(*v) + } + return _u +} + +// SetEnabled sets the "enabled" field. +func (_u *UsageScriptUpdate) SetEnabled(v bool) *UsageScriptUpdate { + _u.mutation.SetEnabled(v) + return _u +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_u *UsageScriptUpdate) SetNillableEnabled(v *bool) *UsageScriptUpdate { + if v != nil { + _u.SetEnabled(*v) + } + return _u +} + +// Mutation returns the UsageScriptMutation object of the builder. +func (_u *UsageScriptUpdate) Mutation() *UsageScriptMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *UsageScriptUpdate) Save(ctx context.Context) (int, error) { + if err := _u.defaults(); err != nil { + return 0, err + } + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *UsageScriptUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *UsageScriptUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *UsageScriptUpdate) 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 *UsageScriptUpdate) defaults() error { + if _, ok := _u.mutation.UpdatedAt(); !ok { + if usagescript.UpdateDefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized usagescript.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := usagescript.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (_u *UsageScriptUpdate) check() error { + if v, ok := _u.mutation.BaseURLHost(); ok { + if err := usagescript.BaseURLHostValidator(v); err != nil { + return &ValidationError{Name: "base_url_host", err: fmt.Errorf(`ent: validator failed for field "UsageScript.base_url_host": %w`, err)} + } + } + if v, ok := _u.mutation.AccountType(); ok { + if err := usagescript.AccountTypeValidator(v); err != nil { + return &ValidationError{Name: "account_type", err: fmt.Errorf(`ent: validator failed for field "UsageScript.account_type": %w`, err)} + } + } + return nil +} + +func (_u *UsageScriptUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(usagescript.Table, usagescript.Columns, sqlgraph.NewFieldSpec(usagescript.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(usagescript.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := _u.mutation.DeletedAt(); ok { + _spec.SetField(usagescript.FieldDeletedAt, field.TypeTime, value) + } + if _u.mutation.DeletedAtCleared() { + _spec.ClearField(usagescript.FieldDeletedAt, field.TypeTime) + } + if value, ok := _u.mutation.BaseURLHost(); ok { + _spec.SetField(usagescript.FieldBaseURLHost, field.TypeString, value) + } + if value, ok := _u.mutation.AccountType(); ok { + _spec.SetField(usagescript.FieldAccountType, field.TypeString, value) + } + if value, ok := _u.mutation.Script(); ok { + _spec.SetField(usagescript.FieldScript, field.TypeString, value) + } + if value, ok := _u.mutation.Enabled(); ok { + _spec.SetField(usagescript.FieldEnabled, field.TypeBool, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{usagescript.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// UsageScriptUpdateOne is the builder for updating a single UsageScript entity. +type UsageScriptUpdateOne struct { + config + fields []string + hooks []Hook + mutation *UsageScriptMutation +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *UsageScriptUpdateOne) SetUpdatedAt(v time.Time) *UsageScriptUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// SetDeletedAt sets the "deleted_at" field. +func (_u *UsageScriptUpdateOne) SetDeletedAt(v time.Time) *UsageScriptUpdateOne { + _u.mutation.SetDeletedAt(v) + return _u +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (_u *UsageScriptUpdateOne) SetNillableDeletedAt(v *time.Time) *UsageScriptUpdateOne { + if v != nil { + _u.SetDeletedAt(*v) + } + return _u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (_u *UsageScriptUpdateOne) ClearDeletedAt() *UsageScriptUpdateOne { + _u.mutation.ClearDeletedAt() + return _u +} + +// SetBaseURLHost sets the "base_url_host" field. +func (_u *UsageScriptUpdateOne) SetBaseURLHost(v string) *UsageScriptUpdateOne { + _u.mutation.SetBaseURLHost(v) + return _u +} + +// SetNillableBaseURLHost sets the "base_url_host" field if the given value is not nil. +func (_u *UsageScriptUpdateOne) SetNillableBaseURLHost(v *string) *UsageScriptUpdateOne { + if v != nil { + _u.SetBaseURLHost(*v) + } + return _u +} + +// SetAccountType sets the "account_type" field. +func (_u *UsageScriptUpdateOne) SetAccountType(v string) *UsageScriptUpdateOne { + _u.mutation.SetAccountType(v) + return _u +} + +// SetNillableAccountType sets the "account_type" field if the given value is not nil. +func (_u *UsageScriptUpdateOne) SetNillableAccountType(v *string) *UsageScriptUpdateOne { + if v != nil { + _u.SetAccountType(*v) + } + return _u +} + +// SetScript sets the "script" field. +func (_u *UsageScriptUpdateOne) SetScript(v string) *UsageScriptUpdateOne { + _u.mutation.SetScript(v) + return _u +} + +// SetNillableScript sets the "script" field if the given value is not nil. +func (_u *UsageScriptUpdateOne) SetNillableScript(v *string) *UsageScriptUpdateOne { + if v != nil { + _u.SetScript(*v) + } + return _u +} + +// SetEnabled sets the "enabled" field. +func (_u *UsageScriptUpdateOne) SetEnabled(v bool) *UsageScriptUpdateOne { + _u.mutation.SetEnabled(v) + return _u +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_u *UsageScriptUpdateOne) SetNillableEnabled(v *bool) *UsageScriptUpdateOne { + if v != nil { + _u.SetEnabled(*v) + } + return _u +} + +// Mutation returns the UsageScriptMutation object of the builder. +func (_u *UsageScriptUpdateOne) Mutation() *UsageScriptMutation { + return _u.mutation +} + +// Where appends a list predicates to the UsageScriptUpdate builder. +func (_u *UsageScriptUpdateOne) Where(ps ...predicate.UsageScript) *UsageScriptUpdateOne { + _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 *UsageScriptUpdateOne) Select(field string, fields ...string) *UsageScriptUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated UsageScript entity. +func (_u *UsageScriptUpdateOne) Save(ctx context.Context) (*UsageScript, error) { + if err := _u.defaults(); err != nil { + return nil, err + } + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *UsageScriptUpdateOne) SaveX(ctx context.Context) *UsageScript { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *UsageScriptUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *UsageScriptUpdateOne) 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 *UsageScriptUpdateOne) defaults() error { + if _, ok := _u.mutation.UpdatedAt(); !ok { + if usagescript.UpdateDefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized usagescript.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := usagescript.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (_u *UsageScriptUpdateOne) check() error { + if v, ok := _u.mutation.BaseURLHost(); ok { + if err := usagescript.BaseURLHostValidator(v); err != nil { + return &ValidationError{Name: "base_url_host", err: fmt.Errorf(`ent: validator failed for field "UsageScript.base_url_host": %w`, err)} + } + } + if v, ok := _u.mutation.AccountType(); ok { + if err := usagescript.AccountTypeValidator(v); err != nil { + return &ValidationError{Name: "account_type", err: fmt.Errorf(`ent: validator failed for field "UsageScript.account_type": %w`, err)} + } + } + return nil +} + +func (_u *UsageScriptUpdateOne) sqlSave(ctx context.Context) (_node *UsageScript, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(usagescript.Table, usagescript.Columns, sqlgraph.NewFieldSpec(usagescript.FieldID, field.TypeInt64)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UsageScript.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, usagescript.FieldID) + for _, f := range fields { + if !usagescript.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != usagescript.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(usagescript.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := _u.mutation.DeletedAt(); ok { + _spec.SetField(usagescript.FieldDeletedAt, field.TypeTime, value) + } + if _u.mutation.DeletedAtCleared() { + _spec.ClearField(usagescript.FieldDeletedAt, field.TypeTime) + } + if value, ok := _u.mutation.BaseURLHost(); ok { + _spec.SetField(usagescript.FieldBaseURLHost, field.TypeString, value) + } + if value, ok := _u.mutation.AccountType(); ok { + _spec.SetField(usagescript.FieldAccountType, field.TypeString, value) + } + if value, ok := _u.mutation.Script(); ok { + _spec.SetField(usagescript.FieldScript, field.TypeString, value) + } + if value, ok := _u.mutation.Enabled(); ok { + _spec.SetField(usagescript.FieldEnabled, field.TypeBool, value) + } + _node = &UsageScript{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{usagescript.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/backend/go.mod b/backend/go.mod index 0363740135..7159261fb9 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -34,6 +34,7 @@ require ( github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/zeromicro/go-zero v1.9.4 + go.starlark.net v0.0.0-20260210143700-b62fd896b91b go.uber.org/zap v1.24.0 golang.org/x/crypto v0.48.0 golang.org/x/net v0.49.0 diff --git a/backend/go.sum b/backend/go.sum index 993a1d5487..ed5bc03781 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -94,10 +94,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= -github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= -github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U= -github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -234,8 +230,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= @@ -269,8 +263,6 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -322,8 +314,6 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= @@ -398,6 +388,8 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.starlark.net v0.0.0-20260210143700-b62fd896b91b h1:mDO9/2PuBcapqFbhiCmFcEQZvlQnk3ILEZR+a8NL1z4= +go.starlark.net v0.0.0-20260210143700-b62fd896b91b/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= diff --git a/backend/internal/handler/admin/usage_script_handler.go b/backend/internal/handler/admin/usage_script_handler.go new file mode 100644 index 0000000000..8a28e055bc --- /dev/null +++ b/backend/internal/handler/admin/usage_script_handler.go @@ -0,0 +1,123 @@ +package admin + +import ( + "strconv" + + "github.com/Wei-Shaw/sub2api/internal/pkg/response" + "github.com/Wei-Shaw/sub2api/internal/service" + "github.com/gin-gonic/gin" +) + +// UsageScriptHandler 用量脚本管理 HTTP 处理器 +type UsageScriptHandler struct { + repo service.UsageScriptRepository +} + +// NewUsageScriptHandler 创建用量脚本处理器 +func NewUsageScriptHandler(repo service.UsageScriptRepository) *UsageScriptHandler { + return &UsageScriptHandler{repo: repo} +} + +// CreateUsageScriptRequest 创建用量脚本请求 +type CreateUsageScriptRequest struct { + BaseURLHost string `json:"base_url_host" binding:"required"` + AccountType string `json:"account_type" binding:"required"` + Script string `json:"script" binding:"required"` + Enabled *bool `json:"enabled"` +} + +// UpdateUsageScriptRequest 更新用量脚本请求 +type UpdateUsageScriptRequest struct { + BaseURLHost string `json:"base_url_host"` + AccountType string `json:"account_type"` + Script string `json:"script"` + Enabled *bool `json:"enabled"` +} + +// List 获取所有用量脚本 +// GET /api/v1/admin/usage-scripts +func (h *UsageScriptHandler) List(c *gin.Context) { + scripts, err := h.repo.List(c.Request.Context()) + if err != nil { + response.ErrorFrom(c, err) + return + } + response.Success(c, scripts) +} + +// Create 创建用量脚本 +// POST /api/v1/admin/usage-scripts +func (h *UsageScriptHandler) Create(c *gin.Context) { + var req CreateUsageScriptRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, err.Error()) + return + } + + script := &service.UsageScript{ + BaseURLHost: req.BaseURLHost, + AccountType: req.AccountType, + Script: req.Script, + Enabled: true, + } + if req.Enabled != nil { + script.Enabled = *req.Enabled + } + + created, err := h.repo.Create(c.Request.Context(), script) + if err != nil { + response.ErrorFrom(c, err) + return + } + response.Success(c, created) +} + +// Update 更新用量脚本 +// PUT /api/v1/admin/usage-scripts/:id +func (h *UsageScriptHandler) Update(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + response.BadRequest(c, "Invalid script ID") + return + } + + var req UpdateUsageScriptRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, err.Error()) + return + } + + script := &service.UsageScript{ + BaseURLHost: req.BaseURLHost, + AccountType: req.AccountType, + Script: req.Script, + } + if req.Enabled != nil { + script.Enabled = *req.Enabled + } else { + script.Enabled = true + } + + updated, err := h.repo.Update(c.Request.Context(), id, script) + if err != nil { + response.ErrorFrom(c, err) + return + } + response.Success(c, updated) +} + +// Delete 删除用量脚本 +// DELETE /api/v1/admin/usage-scripts/:id +func (h *UsageScriptHandler) Delete(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + response.BadRequest(c, "Invalid script ID") + return + } + + if err := h.repo.Delete(c.Request.Context(), id); err != nil { + response.ErrorFrom(c, err) + return + } + response.Success(c, gin.H{"message": "Usage script deleted successfully"}) +} diff --git a/backend/internal/handler/handler.go b/backend/internal/handler/handler.go index 3f1d73ca31..8f89feba7e 100644 --- a/backend/internal/handler/handler.go +++ b/backend/internal/handler/handler.go @@ -28,6 +28,7 @@ type AdminHandlers struct { ErrorPassthrough *admin.ErrorPassthroughHandler APIKey *admin.AdminAPIKeyHandler ScheduledTest *admin.ScheduledTestHandler + UsageScript *admin.UsageScriptHandler } // Handlers contains all HTTP handlers diff --git a/backend/internal/handler/wire.go b/backend/internal/handler/wire.go index d1e12e0366..e4e18617a2 100644 --- a/backend/internal/handler/wire.go +++ b/backend/internal/handler/wire.go @@ -31,6 +31,7 @@ func ProvideAdminHandlers( errorPassthroughHandler *admin.ErrorPassthroughHandler, apiKeyHandler *admin.AdminAPIKeyHandler, scheduledTestHandler *admin.ScheduledTestHandler, + usageScriptHandler *admin.UsageScriptHandler, ) *AdminHandlers { return &AdminHandlers{ Dashboard: dashboardHandler, @@ -55,6 +56,7 @@ func ProvideAdminHandlers( ErrorPassthrough: errorPassthroughHandler, APIKey: apiKeyHandler, ScheduledTest: scheduledTestHandler, + UsageScript: usageScriptHandler, } } @@ -144,6 +146,7 @@ var ProviderSet = wire.NewSet( admin.NewErrorPassthroughHandler, admin.NewAdminAPIKeyHandler, admin.NewScheduledTestHandler, + admin.NewUsageScriptHandler, // AdminHandlers and Handlers constructors ProvideAdminHandlers, diff --git a/backend/internal/repository/usage_script_repo.go b/backend/internal/repository/usage_script_repo.go new file mode 100644 index 0000000000..7eeaffdd4b --- /dev/null +++ b/backend/internal/repository/usage_script_repo.go @@ -0,0 +1,90 @@ +package repository + +import ( + "context" + + "github.com/Wei-Shaw/sub2api/ent" + "github.com/Wei-Shaw/sub2api/ent/usagescript" + "github.com/Wei-Shaw/sub2api/internal/service" +) + +type usageScriptRepository struct { + client *ent.Client +} + +func NewUsageScriptRepository(client *ent.Client) service.UsageScriptRepository { + return &usageScriptRepository{client: client} +} + +func (r *usageScriptRepository) FindByHostAndType(ctx context.Context, baseURLHost string, accountType string) (*service.UsageScript, error) { + m, err := r.client.UsageScript.Query(). + Where( + usagescript.BaseURLHostEQ(baseURLHost), + usagescript.AccountTypeEQ(accountType), + usagescript.EnabledEQ(true), + ). + Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return nil, nil // no script configured for this host+type + } + return nil, err + } + return toServiceUsageScript(m), nil +} + +func (r *usageScriptRepository) List(ctx context.Context) ([]*service.UsageScript, error) { + items, err := r.client.UsageScript.Query(). + Order(ent.Desc(usagescript.FieldCreatedAt)). + All(ctx) + if err != nil { + return nil, err + } + result := make([]*service.UsageScript, len(items)) + for i, m := range items { + result[i] = toServiceUsageScript(m) + } + return result, nil +} + +func (r *usageScriptRepository) Create(ctx context.Context, script *service.UsageScript) (*service.UsageScript, error) { + m, err := r.client.UsageScript.Create(). + SetBaseURLHost(script.BaseURLHost). + SetAccountType(script.AccountType). + SetScript(script.Script). + SetEnabled(script.Enabled). + Save(ctx) + if err != nil { + return nil, err + } + return toServiceUsageScript(m), nil +} + +func (r *usageScriptRepository) Update(ctx context.Context, id int64, script *service.UsageScript) (*service.UsageScript, error) { + m, err := r.client.UsageScript.UpdateOneID(id). + SetBaseURLHost(script.BaseURLHost). + SetAccountType(script.AccountType). + SetScript(script.Script). + SetEnabled(script.Enabled). + Save(ctx) + if err != nil { + return nil, err + } + return toServiceUsageScript(m), nil +} + +func (r *usageScriptRepository) Delete(ctx context.Context, id int64) error { + return r.client.UsageScript.DeleteOneID(id).Exec(ctx) +} + +func toServiceUsageScript(m *ent.UsageScript) *service.UsageScript { + return &service.UsageScript{ + ID: m.ID, + BaseURLHost: m.BaseURLHost, + AccountType: m.AccountType, + Script: m.Script, + Enabled: m.Enabled, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} diff --git a/backend/internal/repository/wire.go b/backend/internal/repository/wire.go index 5fe7a98e65..6d0caf171f 100644 --- a/backend/internal/repository/wire.go +++ b/backend/internal/repository/wire.go @@ -72,6 +72,7 @@ var ProviderSet = wire.NewSet( NewUserAttributeValueRepository, NewUserGroupRateRepository, NewErrorPassthroughRepository, + NewUsageScriptRepository, // Cache implementations NewGatewayCache, diff --git a/backend/internal/server/routes/admin.go b/backend/internal/server/routes/admin.go index a69f159584..200ca239d6 100644 --- a/backend/internal/server/routes/admin.go +++ b/backend/internal/server/routes/admin.go @@ -76,6 +76,9 @@ func RegisterAdminRoutes( // 错误透传规则管理 registerErrorPassthroughRoutes(admin, h) + // 用量脚本管理 + registerUsageScriptRoutes(admin, h) + // API Key 管理 registerAdminAPIKeyRoutes(admin, h) @@ -513,3 +516,13 @@ func registerErrorPassthroughRoutes(admin *gin.RouterGroup, h *handler.Handlers) rules.DELETE("/:id", h.Admin.ErrorPassthrough.Delete) } } + +func registerUsageScriptRoutes(admin *gin.RouterGroup, h *handler.Handlers) { + scripts := admin.Group("/usage-scripts") + { + scripts.GET("", h.Admin.UsageScript.List) + scripts.POST("", h.Admin.UsageScript.Create) + scripts.PUT("/:id", h.Admin.UsageScript.Update) + scripts.DELETE("/:id", h.Admin.UsageScript.Delete) + } +} diff --git a/backend/internal/service/account_usage_service.go b/backend/internal/service/account_usage_service.go index 7c00111876..23b60c8eb9 100644 --- a/backend/internal/service/account_usage_service.go +++ b/backend/internal/service/account_usage_service.go @@ -6,8 +6,10 @@ import ( "encoding/json" "fmt" "log" + "log/slog" "math/rand/v2" "net/http" + "net/url" "strings" "sync" "time" @@ -163,6 +165,9 @@ type UsageInfo struct { // Antigravity 多模型配额 AntigravityQuota map[string]*AntigravityModelQuota `json:"antigravity_quota,omitempty"` + + // 脚本引擎采集的通用用量窗口 + ScriptWindows []ScriptUsageWindow `json:"script_windows,omitempty"` } // ClaudeUsageResponse Anthropic API返回的usage结构 @@ -206,6 +211,8 @@ type AccountUsageService struct { antigravityQuotaFetcher *AntigravityQuotaFetcher cache *UsageCache identityCache IdentityCache + scriptEngine *ScriptEngine + usageScriptRepo UsageScriptRepository } // NewAccountUsageService 创建AccountUsageService实例 @@ -217,6 +224,8 @@ func NewAccountUsageService( antigravityQuotaFetcher *AntigravityQuotaFetcher, cache *UsageCache, identityCache IdentityCache, + scriptEngine *ScriptEngine, + usageScriptRepo UsageScriptRepository, ) *AccountUsageService { return &AccountUsageService{ accountRepo: accountRepo, @@ -226,6 +235,8 @@ func NewAccountUsageService( antigravityQuotaFetcher: antigravityQuotaFetcher, cache: cache, identityCache: identityCache, + scriptEngine: scriptEngine, + usageScriptRepo: usageScriptRepo, } } @@ -255,6 +266,17 @@ func (s *AccountUsageService) GetUsage(ctx context.Context, accountID int64) (*U return usage, err } + // 非官方上游(中转站):通过 Starlark 脚本采集用量 + if baseURL := account.GetBaseURL(); baseURL != "" { + if usage, err := s.getScriptUsage(ctx, account, baseURL); usage != nil || err != nil { + return usage, err + } + // 无匹配脚本:自定义 base_url 的账号不走平台原生逻辑,返回空用量 + if account.GetCredential("base_url") != "" { + return &UsageInfo{}, nil + } + } + // Antigravity 平台:使用 AntigravityQuotaFetcher 获取额度 if account.Platform == PlatformAntigravity { usage, err := s.getAntigravityUsage(ctx, account) @@ -1073,3 +1095,102 @@ func buildGeminiUsageProgress(used, limit int64, resetAt time.Time, tokens int64 func (s *AccountUsageService) GetAccountWindowStats(ctx context.Context, accountID int64, startTime time.Time) (*usagestats.AccountStats, error) { return s.usageLogRepo.GetAccountWindowStats(ctx, accountID, startTime) } + +// UsageScriptRepository 用量脚本仓储接口 +type UsageScriptRepository interface { + FindByHostAndType(ctx context.Context, baseURLHost string, accountType string) (*UsageScript, error) + List(ctx context.Context) ([]*UsageScript, error) + Create(ctx context.Context, script *UsageScript) (*UsageScript, error) + Update(ctx context.Context, id int64, script *UsageScript) (*UsageScript, error) + Delete(ctx context.Context, id int64) error +} + +// extractBaseURLHost 从 base_url 提取域名级标识(scheme://host) +func extractBaseURLHost(baseURL string) string { + u, err := url.Parse(baseURL) + if err != nil || u.Host == "" { + return baseURL + } + return u.Scheme + "://" + u.Host +} + +// getScriptUsage 尝试通过脚本引擎获取非官方上游的用量 +// 返回 (nil, nil) 表示无匹配脚本,调用方应继续走平台原生逻辑 +func (s *AccountUsageService) getScriptUsage(ctx context.Context, account *Account, baseURL string) (*UsageInfo, error) { + if s.usageScriptRepo == nil || s.scriptEngine == nil { + return nil, nil + } + + host := extractBaseURLHost(baseURL) + script, err := s.usageScriptRepo.FindByHostAndType(ctx, host, account.Type) + if err != nil { + slog.Warn("usage_script_lookup_failed", "account_id", account.ID, "host", host, "error", err) + return nil, nil // 查找失败不阻塞,降级到平台原生逻辑 + } + if script == nil { + return nil, nil // 无匹配脚本 + } + + // 使用 singleflight 防止并发执行同一账号的脚本 + cacheKey := fmt.Sprintf("script:%d", account.ID) + result, flightErr, _ := s.cache.apiFlight.Do(cacheKey, func() (any, error) { + return s.scriptEngine.Execute(ctx, script.Script, account) + }) + if flightErr != nil { + slog.Warn("usage_script_exec_failed", "account_id", account.ID, "host", host, "error", flightErr) + return nil, nil // 执行失败不阻塞 + } + + scriptResult, ok := result.(*ScriptUsageResult) + if !ok || scriptResult == nil { + return nil, nil + } + + if scriptResult.Error != "" { + slog.Warn("usage_script_returned_error", "account_id", account.ID, "host", host, "error", scriptResult.Error) + } + + now := time.Now() + return &UsageInfo{ + UpdatedAt: &now, + ScriptWindows: scriptResult.Windows, + }, nil +} + +// CheckScriptUsageForScheduling 检查脚本用量并在超限时标记账号为临时不可调度 +func (s *AccountUsageService) CheckScriptUsageForScheduling(ctx context.Context, account *Account) { + baseURL := account.GetBaseURL() + if baseURL == "" { + return + } + if s.usageScriptRepo == nil || s.scriptEngine == nil { + return + } + + host := extractBaseURLHost(baseURL) + script, err := s.usageScriptRepo.FindByHostAndType(ctx, host, account.Type) + if err != nil || script == nil { + return + } + + result, err := s.scriptEngine.Execute(ctx, script.Script, account) + if err != nil || result == nil || result.Error != "" { + return + } + + for _, w := range result.Windows { + if w.Utilization >= 1.0 { + var until time.Time + if w.ResetsAt != nil { + until = time.Unix(*w.ResetsAt, 0) + } else { + until = time.Now().Add(5 * time.Minute) // 无重置时间则默认 5 分钟 + } + reason := fmt.Sprintf("usage_script: %s/%s %.0f%%", host, w.Name, w.Utilization*100) + if setErr := s.accountRepo.SetTempUnschedulable(ctx, account.ID, until, reason); setErr != nil { + slog.Warn("script_usage_set_temp_unsched_failed", "account_id", account.ID, "error", setErr) + } + return // 一个窗口超限即标记,不重复 + } + } +} diff --git a/backend/internal/service/script_engine.go b/backend/internal/service/script_engine.go new file mode 100644 index 0000000000..d8995628c0 --- /dev/null +++ b/backend/internal/service/script_engine.go @@ -0,0 +1,403 @@ +package service + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "sync" + "time" + + httppool "github.com/Wei-Shaw/sub2api/internal/pkg/httpclient" + "go.starlark.net/starlark" + "go.starlark.net/starlarkstruct" + "go.starlark.net/syntax" +) + +const ( + scriptExecTimeout = 10 * time.Second + scriptHTTPTimeout = 5 * time.Second + scriptMaxResponseBody = 1 << 20 // 1MB +) + +// ScriptEngine Starlark 脚本引擎 +type ScriptEngine struct { + programs sync.Map // scriptHash -> *starlark.Program (compiled cache) +} + +func NewScriptEngine() *ScriptEngine { + return &ScriptEngine{} +} + +// Execute 执行用量脚本,返回结构化结果 +func (e *ScriptEngine) Execute(ctx context.Context, scriptContent string, account *Account) (*ScriptUsageResult, error) { + // 1. Get or compile the program + program, err := e.getOrCompile(scriptContent) + if err != nil { + return nil, fmt.Errorf("compile script: %w", err) + } + + // 2. Create execution context with timeout + execCtx, cancel := context.WithTimeout(ctx, scriptExecTimeout) + defer cancel() + + // 3. Build the context dict to inject into the script + ctxDict := e.buildContextDict(account) + + // 4. Build builtins (http_get, http_post, json_parse) + builtins := e.buildBuiltins(execCtx) + + // 5. Create thread with cancellation + thread := &starlark.Thread{ + Name: "usage-script", + } + thread.SetLocal("context", execCtx) + + // Set up cancellation: when context is done, cancel the thread + go func() { + <-execCtx.Done() + thread.Cancel("script execution timeout") + }() + + // 6. Create predeclared with builtins + ctx + predeclared := starlark.StringDict{ + "ctx": ctxDict, + "http_get": starlark.NewBuiltin("http_get", builtins.httpGet), + "http_post": starlark.NewBuiltin("http_post", builtins.httpPost), + "json_parse": starlark.NewBuiltin("json_parse", builtins.jsonParse), + "time_now": starlark.NewBuiltin("time_now", builtinTimeNow), + } + + // 7. Execute the program + globals, err := program.Init(thread, predeclared) + if err != nil { + return &ScriptUsageResult{Error: fmt.Sprintf("init: %v", err)}, nil + } + + // 8. Call fetch_usage(ctx) + fetchUsage, ok := globals["fetch_usage"] + if !ok { + return nil, fmt.Errorf("script missing fetch_usage function") + } + + callable, ok := fetchUsage.(starlark.Callable) + if !ok { + return nil, fmt.Errorf("fetch_usage is not callable") + } + + resultVal, err := starlark.Call(thread, callable, starlark.Tuple{ctxDict}, nil) + if err != nil { + return &ScriptUsageResult{Error: fmt.Sprintf("exec: %v", err)}, nil + } + + // 9. Parse the result + return e.parseResult(resultVal) +} + +func (e *ScriptEngine) getOrCompile(scriptContent string) (*starlark.Program, error) { + hash := sha256Hash(scriptContent) + + if cached, ok := e.programs.Load(hash); ok { + prog, _ := cached.(*starlark.Program) + return prog, nil + } + + // Compile (thread-safe: FileOptions is stateless) + _, program, err := starlark.SourceProgramOptions(&syntax.FileOptions{}, "usage_script.star", scriptContent, func(name string) bool { + // Only allow predeclared names + return name == "ctx" || name == "http_get" || name == "http_post" || name == "json_parse" || name == "time_now" + }) + if err != nil { + return nil, err + } + + e.programs.Store(hash, program) + return program, nil +} + +func (e *ScriptEngine) buildContextDict(account *Account) *starlarkstruct.Struct { + baseURL := account.GetBaseURL() + + // Convert credentials to starlark dict + credDict := starlark.NewDict(len(account.Credentials)) + for k, v := range account.Credentials { + _ = credDict.SetKey(starlark.String(k), goToStarlark(v)) + } + + // Convert extra to starlark dict + extraDict := starlark.NewDict(len(account.Extra)) + for k, v := range account.Extra { + _ = extraDict.SetKey(starlark.String(k), goToStarlark(v)) + } + + return starlarkstruct.FromStringDict(starlarkstruct.Default, starlark.StringDict{ + "base_url": starlark.String(baseURL), + "credentials": credDict, + "extra": extraDict, + "platform": starlark.String(account.Platform), + "type": starlark.String(account.Type), + }) +} + +// scriptBuiltins holds builtin functions that need execCtx +type scriptBuiltins struct { + ctx context.Context +} + +func (e *ScriptEngine) buildBuiltins(ctx context.Context) *scriptBuiltins { + return &scriptBuiltins{ctx: ctx} +} + +// httpGet implements http_get(url, headers={}) -> (status_code, body, response_headers) +func (b *scriptBuiltins) httpGet(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var url string + headers := &starlark.Dict{} + if err := starlark.UnpackArgs("http_get", args, kwargs, "url", &url, "headers?", &headers); err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(b.ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("http_get: create request: %w", err) + } + + applyStarlarkHeaders(req, headers) + + client, err := httppool.GetClient(httppool.Options{ + Timeout: scriptHTTPTimeout, + }) + if err != nil { + return nil, fmt.Errorf("http_get: get client: %w", err) + } + + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("http_get: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(io.LimitReader(resp.Body, scriptMaxResponseBody)) + if err != nil { + return nil, fmt.Errorf("http_get: read body: %w", err) + } + + return starlark.Tuple{starlark.MakeInt(resp.StatusCode), starlark.String(body), responseHeadersToStarlark(resp)}, nil +} + +// httpPost implements http_post(url, headers={}, body="") -> (status_code, body, response_headers) +func (b *scriptBuiltins) httpPost(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var url string + var bodyStr string + headers := &starlark.Dict{} + if err := starlark.UnpackArgs("http_post", args, kwargs, "url", &url, "headers?", &headers, "body?", &bodyStr); err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(b.ctx, http.MethodPost, url, strings.NewReader(bodyStr)) + if err != nil { + return nil, fmt.Errorf("http_post: create request: %w", err) + } + + applyStarlarkHeaders(req, headers) + + client, err := httppool.GetClient(httppool.Options{ + Timeout: scriptHTTPTimeout, + }) + if err != nil { + return nil, fmt.Errorf("http_post: get client: %w", err) + } + + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("http_post: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(io.LimitReader(resp.Body, scriptMaxResponseBody)) + if err != nil { + return nil, fmt.Errorf("http_post: read body: %w", err) + } + + return starlark.Tuple{starlark.MakeInt(resp.StatusCode), starlark.String(body), responseHeadersToStarlark(resp)}, nil +} + +// jsonParse implements json_parse(str) -> dict/list +func (b *scriptBuiltins) jsonParse(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var s string + if err := starlark.UnpackPositionalArgs("json_parse", args, kwargs, 1, &s); err != nil { + return nil, err + } + + var data any + if err := json.Unmarshal([]byte(s), &data); err != nil { + return nil, fmt.Errorf("json_parse: %w", err) + } + + return goToStarlark(data), nil +} + +// builtinTimeNow implements time_now() -> int (current unix timestamp in seconds) +func builtinTimeNow(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + if err := starlark.UnpackPositionalArgs("time_now", args, kwargs, 0); err != nil { + return nil, err + } + return starlark.MakeInt64(time.Now().Unix()), nil +} + +// parseResult converts the starlark return value to ScriptUsageResult +func (e *ScriptEngine) parseResult(val starlark.Value) (*ScriptUsageResult, error) { + // Expect a dict with "windows" key + dict, ok := val.(*starlark.Dict) + if !ok { + return nil, fmt.Errorf("fetch_usage must return a dict, got %s", val.Type()) + } + + windowsVal, found, err := dict.Get(starlark.String("windows")) + if err != nil { + return nil, err + } + if !found { + return nil, fmt.Errorf("fetch_usage result missing 'windows' key") + } + + windowsList, ok := windowsVal.(*starlark.List) + if !ok { + return nil, fmt.Errorf("'windows' must be a list, got %s", windowsVal.Type()) + } + + result := &ScriptUsageResult{ + Windows: make([]ScriptUsageWindow, 0, windowsList.Len()), + } + + for i := 0; i < windowsList.Len(); i++ { + wDict, ok := windowsList.Index(i).(*starlark.Dict) + if !ok { + continue + } + window := ScriptUsageWindow{} + + if v, found, _ := wDict.Get(starlark.String("name")); found { + if s, ok := v.(starlark.String); ok { + window.Name = string(s) + } + } + if v, found, _ := wDict.Get(starlark.String("utilization")); found { + if f, ok := starlark.AsFloat(v); ok { + window.Utilization = f + } + } + if v, found, _ := wDict.Get(starlark.String("resets_at")); found { + if intVal, ok := v.(starlark.Int); ok { + if i64, ok := intVal.Int64(); ok { + window.ResetsAt = &i64 + } + } + } + if v, found, _ := wDict.Get(starlark.String("used")); found { + if f, ok := starlark.AsFloat(v); ok { + window.Used = &f + } + } + if v, found, _ := wDict.Get(starlark.String("limit")); found { + if f, ok := starlark.AsFloat(v); ok { + window.Limit = &f + } + } + if v, found, _ := wDict.Get(starlark.String("unit")); found { + if s, ok := v.(starlark.String); ok { + window.Unit = string(s) + } + } + + result.Windows = append(result.Windows, window) + } + + // Check for error field + if v, found, _ := dict.Get(starlark.String("error")); found { + if s, ok := v.(starlark.String); ok { + result.Error = string(s) + } + } + + return result, nil +} + +// Helper functions + +func sha256Hash(s string) string { + h := sha256.Sum256([]byte(s)) + return hex.EncodeToString(h[:]) +} + +// responseHeadersToStarlark converts HTTP response headers to a starlark dict. +// Multi-value headers are joined with "; " (e.g. multiple Set-Cookie values). +func responseHeadersToStarlark(resp *http.Response) *starlark.Dict { + d := starlark.NewDict(len(resp.Header)) + for k, vals := range resp.Header { + _ = d.SetKey(starlark.String(k), starlark.String(strings.Join(vals, "; "))) + } + return d +} + +func applyStarlarkHeaders(req *http.Request, headers *starlark.Dict) { + if headers == nil { + return + } + for _, item := range headers.Items() { + if k, ok := item[0].(starlark.String); ok { + if v, ok := item[1].(starlark.String); ok { + req.Header.Set(string(k), string(v)) + } + } + } +} + +// goToStarlark converts a Go value to a Starlark value +func goToStarlark(v any) starlark.Value { + switch v := v.(type) { + case nil: + return starlark.None + case bool: + return starlark.Bool(v) + case string: + return starlark.String(v) + case float64: + // Check if it's an integer value + if v == float64(int64(v)) { + return starlark.MakeInt64(int64(v)) + } + return starlark.Float(v) + case float32: + return starlark.Float(float64(v)) + case int: + return starlark.MakeInt(v) + case int64: + return starlark.MakeInt64(v) + case json.Number: + if i, err := v.Int64(); err == nil { + return starlark.MakeInt64(i) + } + if f, err := v.Float64(); err == nil { + return starlark.Float(f) + } + return starlark.String(v.String()) + case map[string]any: + d := starlark.NewDict(len(v)) + for k, val := range v { + _ = d.SetKey(starlark.String(k), goToStarlark(val)) + } + return d + case []any: + elems := make([]starlark.Value, len(v)) + for i, val := range v { + elems[i] = goToStarlark(val) + } + return starlark.NewList(elems) + default: + return starlark.String(fmt.Sprintf("%v", v)) + } +} diff --git a/backend/internal/service/script_usage_types.go b/backend/internal/service/script_usage_types.go new file mode 100644 index 0000000000..752a9b1e70 --- /dev/null +++ b/backend/internal/service/script_usage_types.go @@ -0,0 +1,17 @@ +package service + +// ScriptUsageWindow 脚本返回的单个用量窗口 +type ScriptUsageWindow struct { + Name string `json:"name"` + Utilization float64 `json:"utilization"` // 0.0~1.0+ + ResetsAt *int64 `json:"resets_at"` // unix timestamp, optional + Used *float64 `json:"used"` + Limit *float64 `json:"limit"` + Unit string `json:"unit"` // tokens/requests/credits +} + +// ScriptUsageResult 脚本执行结果 +type ScriptUsageResult struct { + Windows []ScriptUsageWindow `json:"windows"` + Error string `json:"error,omitempty"` +} diff --git a/backend/internal/service/usage_script.go b/backend/internal/service/usage_script.go new file mode 100644 index 0000000000..b9d5adb840 --- /dev/null +++ b/backend/internal/service/usage_script.go @@ -0,0 +1,14 @@ +package service + +import "time" + +// UsageScript 用量脚本领域模型 +type UsageScript struct { + ID int64 `json:"id"` + BaseURLHost string `json:"base_url_host"` + AccountType string `json:"account_type"` + Script string `json:"script"` + Enabled bool `json:"enabled"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/backend/internal/service/wire.go b/backend/internal/service/wire.go index 7457b77e0d..1cce6b6853 100644 --- a/backend/internal/service/wire.go +++ b/backend/internal/service/wire.go @@ -396,6 +396,7 @@ var ProviderSet = wire.NewSet( ProvideUsageCleanupService, ProvideDeferredService, NewAntigravityQuotaFetcher, + NewScriptEngine, NewUserAttributeService, NewUsageCache, NewTotpService, diff --git a/backend/migrations/072_add_usage_scripts.sql b/backend/migrations/072_add_usage_scripts.sql new file mode 100644 index 0000000000..512c22b161 --- /dev/null +++ b/backend/migrations/072_add_usage_scripts.sql @@ -0,0 +1,13 @@ +-- +goose Up +CREATE TABLE usage_scripts ( + id BIGSERIAL PRIMARY KEY, + base_url_host TEXT NOT NULL, + account_type VARCHAR(20) NOT NULL, + script TEXT NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +CREATE UNIQUE INDEX idx_usage_scripts_host_type ON usage_scripts (base_url_host, account_type) WHERE deleted_at IS NULL; diff --git a/frontend/src/api/admin/usageScripts.ts b/frontend/src/api/admin/usageScripts.ts new file mode 100644 index 0000000000..90119f22a9 --- /dev/null +++ b/frontend/src/api/admin/usageScripts.ts @@ -0,0 +1,64 @@ +/** + * Admin Usage Scripts API endpoints + * Handles Starlark usage script management for administrators + */ + +import { apiClient } from '../client' + +export interface UsageScript { + id: number + base_url_host: string + account_type: string + script: string + enabled: boolean + created_at: string + updated_at: string +} + +export interface CreateUsageScriptRequest { + base_url_host: string + account_type: string + script: string + enabled?: boolean +} + +export interface UpdateUsageScriptRequest { + base_url_host: string + account_type: string + script: string + enabled?: boolean +} + +export async function list(): Promise { + const { data } = await apiClient.get('/admin/usage-scripts') + return data +} + +export async function create(req: CreateUsageScriptRequest): Promise { + const { data } = await apiClient.post('/admin/usage-scripts', req) + return data +} + +export async function update(id: number, req: UpdateUsageScriptRequest): Promise { + const { data } = await apiClient.put(`/admin/usage-scripts/${id}`, req) + return data +} + +export async function deleteScript(id: number): Promise<{ message: string }> { + const { data } = await apiClient.delete<{ message: string }>(`/admin/usage-scripts/${id}`) + return data +} + +export async function toggleEnabled(id: number, enabled: boolean): Promise { + return update(id, { enabled } as UpdateUsageScriptRequest) +} + +export const usageScriptsAPI = { + list, + create, + update, + delete: deleteScript, + toggleEnabled +} + +export default usageScriptsAPI diff --git a/frontend/src/components/account/AccountUsageCell.vue b/frontend/src/components/account/AccountUsageCell.vue index f5cab57084..1179afac13 100644 --- a/frontend/src/components/account/AccountUsageCell.vue +++ b/frontend/src/components/account/AccountUsageCell.vue @@ -323,6 +323,20 @@ + + +