From c2d28f95c53f1e3c852c961b8e9b9e3e1cc90019 Mon Sep 17 00:00:00 2001 From: wintbit Date: Tue, 17 Jun 2025 02:22:13 +0800 Subject: [PATCH] feat: port user-group mapping to M2M basic DB definition and api migrate. --- application/migrator/user.go | 2 +- ent/client.go | 221 ++++- ent/ent.go | 2 + ent/group.go | 24 +- ent/group/group.go | 44 +- ent/group/where.go | 25 +- ent/group_create.go | 40 +- ent/group_query.go | 141 ++- ent/group_update.go | 211 ++++- ent/hook/hook.go | 12 + ent/intercept/intercept.go | 30 + ent/internal/schema.go | 2 +- ent/migrate/schema.go | 36 +- ent/mutation.go | 943 +++++++++++++++++-- ent/mutationhelper.go | 6 + ent/predicate/predicate.go | 3 + ent/runtime/runtime.go | 7 + ent/schema/group.go | 3 +- ent/schema/user.go | 7 +- ent/schema/user_group.go | 40 + ent/tx.go | 3 + ent/user.go | 68 +- ent/user/user.go | 73 +- ent/user/where.go | 60 +- ent/user_create.go | 113 +-- ent/user_query.go | 170 +++- ent/user_update.go | 355 +++++-- ent/usergroup.go | 206 ++++ ent/usergroup/usergroup.go | 123 +++ ent/usergroup/where.go | 237 +++++ ent/usergroup_create.go | 713 ++++++++++++++ ent/usergroup_delete.go | 88 ++ ent/usergroup_query.go | 680 +++++++++++++ ent/usergroup_update.go | 508 ++++++++++ inventory/group.go | 4 + inventory/user.go | 25 +- middleware/auth.go | 16 +- pkg/filemanager/fs/dbfs/dbfs.go | 28 +- pkg/filemanager/fs/dbfs/manage.go | 21 +- pkg/filemanager/fs/dbfs/my_navigator.go | 5 +- pkg/filemanager/fs/dbfs/props.go | 5 +- pkg/filemanager/fs/dbfs/share_navigator.go | 5 +- pkg/filemanager/manager/entity.go | 16 +- pkg/filemanager/workflows/archive.go | 9 +- pkg/filemanager/workflows/extract.go | 15 +- pkg/filemanager/workflows/remote_download.go | 2 +- pkg/webdav/webdav.go | 8 +- service/admin/user.go | 10 +- service/explorer/file.go | 20 +- service/explorer/workflows.go | 25 +- service/setting/webdav.go | 9 +- service/share/manage.go | 10 +- service/user/response.go | 28 +- 53 files changed, 4973 insertions(+), 484 deletions(-) create mode 100644 ent/schema/user_group.go create mode 100644 ent/usergroup.go create mode 100644 ent/usergroup/usergroup.go create mode 100644 ent/usergroup/where.go create mode 100644 ent/usergroup_create.go create mode 100644 ent/usergroup_delete.go create mode 100644 ent/usergroup_query.go create mode 100644 ent/usergroup_update.go diff --git a/application/migrator/user.go b/application/migrator/user.go index 273d8f09..64a66454 100644 --- a/application/migrator/user.go +++ b/application/migrator/user.go @@ -71,7 +71,7 @@ func (m *Migrator) migrateUser() error { SetNick(u.Nick). SetStatus(userStatus). SetStorage(int64(u.Storage)). - SetGroupID(int(u.GroupID)). + AddGroupIDs(int(u.GroupID)). SetSettings(setting). SetPassword(u.Password) diff --git a/ent/client.go b/ent/client.go index 7a157b5d..581d0561 100644 --- a/ent/client.go +++ b/ent/client.go @@ -28,6 +28,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" stdsql "database/sql" ) @@ -63,6 +64,8 @@ type Client struct { Task *TaskClient // User is the client for interacting with the User builders. User *UserClient + // UserGroup is the client for interacting with the UserGroup builders. + UserGroup *UserGroupClient } // NewClient creates a new client configured with the given options. @@ -87,6 +90,7 @@ func (c *Client) init() { c.StoragePolicy = NewStoragePolicyClient(c.config) c.Task = NewTaskClient(c.config) c.User = NewUserClient(c.config) + c.UserGroup = NewUserGroupClient(c.config) } type ( @@ -192,6 +196,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { StoragePolicy: NewStoragePolicyClient(cfg), Task: NewTaskClient(cfg), User: NewUserClient(cfg), + UserGroup: NewUserGroupClient(cfg), }, nil } @@ -224,6 +229,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) StoragePolicy: NewStoragePolicyClient(cfg), Task: NewTaskClient(cfg), User: NewUserClient(cfg), + UserGroup: NewUserGroupClient(cfg), }, nil } @@ -254,7 +260,7 @@ func (c *Client) Close() error { func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ c.DavAccount, c.DirectLink, c.Entity, c.File, c.Group, c.Metadata, c.Node, - c.Passkey, c.Setting, c.Share, c.StoragePolicy, c.Task, c.User, + c.Passkey, c.Setting, c.Share, c.StoragePolicy, c.Task, c.User, c.UserGroup, } { n.Use(hooks...) } @@ -265,7 +271,7 @@ func (c *Client) Use(hooks ...Hook) { func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ c.DavAccount, c.DirectLink, c.Entity, c.File, c.Group, c.Metadata, c.Node, - c.Passkey, c.Setting, c.Share, c.StoragePolicy, c.Task, c.User, + c.Passkey, c.Setting, c.Share, c.StoragePolicy, c.Task, c.User, c.UserGroup, } { n.Intercept(interceptors...) } @@ -300,6 +306,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.Task.mutate(ctx, m) case *UserMutation: return c.User.mutate(ctx, m) + case *UserGroupMutation: + return c.UserGroup.mutate(ctx, m) default: return nil, fmt.Errorf("ent: unknown mutation type %T", m) } @@ -1169,7 +1177,7 @@ func (c *GroupClient) QueryUsers(gr *Group) *UserQuery { step := sqlgraph.NewStep( sqlgraph.From(group.Table, group.FieldID, id), sqlgraph.To(user.Table, user.FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, group.UsersTable, group.UsersColumn), + sqlgraph.Edge(sqlgraph.M2M, false, group.UsersTable, group.UsersPrimaryKey...), ) fromV = sqlgraph.Neighbors(gr.driver.Dialect(), step) return fromV, nil @@ -1193,6 +1201,22 @@ func (c *GroupClient) QueryStoragePolicies(gr *Group) *StoragePolicyQuery { return query } +// QueryUserGroup queries the user_group edge of a Group. +func (c *GroupClient) QueryUserGroup(gr *Group) *UserGroupQuery { + query := (&UserGroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := gr.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(usergroup.Table, usergroup.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, group.UserGroupTable, group.UserGroupColumn), + ) + fromV = sqlgraph.Neighbors(gr.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *GroupClient) Hooks() []Hook { hooks := c.hooks.Group @@ -2433,15 +2457,15 @@ func (c *UserClient) GetX(ctx context.Context, id int) *User { return obj } -// QueryGroup queries the group edge of a User. -func (c *UserClient) QueryGroup(u *User) *GroupQuery { +// QueryGroups queries the groups edge of a User. +func (c *UserClient) QueryGroups(u *User) *GroupQuery { query := (&GroupClient{config: c.config}).Query() query.path = func(context.Context) (fromV *sql.Selector, _ error) { id := u.ID step := sqlgraph.NewStep( sqlgraph.From(user.Table, user.FieldID, id), sqlgraph.To(group.Table, group.FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, user.GroupTable, user.GroupColumn), + sqlgraph.Edge(sqlgraph.M2M, true, user.GroupsTable, user.GroupsPrimaryKey...), ) fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) return fromV, nil @@ -2545,6 +2569,22 @@ func (c *UserClient) QueryEntities(u *User) *EntityQuery { return query } +// QueryUserGroup queries the user_group edge of a User. +func (c *UserClient) QueryUserGroup(u *User) *UserGroupQuery { + query := (&UserGroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(usergroup.Table, usergroup.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, user.UserGroupTable, user.UserGroupColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *UserClient) Hooks() []Hook { hooks := c.hooks.User @@ -2572,15 +2612,180 @@ func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error) } } +// UserGroupClient is a client for the UserGroup schema. +type UserGroupClient struct { + config +} + +// NewUserGroupClient returns a client for the UserGroup from the given config. +func NewUserGroupClient(c config) *UserGroupClient { + return &UserGroupClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `usergroup.Hooks(f(g(h())))`. +func (c *UserGroupClient) Use(hooks ...Hook) { + c.hooks.UserGroup = append(c.hooks.UserGroup, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `usergroup.Intercept(f(g(h())))`. +func (c *UserGroupClient) Intercept(interceptors ...Interceptor) { + c.inters.UserGroup = append(c.inters.UserGroup, interceptors...) +} + +// Create returns a builder for creating a UserGroup entity. +func (c *UserGroupClient) Create() *UserGroupCreate { + mutation := newUserGroupMutation(c.config, OpCreate) + return &UserGroupCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of UserGroup entities. +func (c *UserGroupClient) CreateBulk(builders ...*UserGroupCreate) *UserGroupCreateBulk { + return &UserGroupCreateBulk{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 *UserGroupClient) MapCreateBulk(slice any, setFunc func(*UserGroupCreate, int)) *UserGroupCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &UserGroupCreateBulk{err: fmt.Errorf("calling to UserGroupClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*UserGroupCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &UserGroupCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for UserGroup. +func (c *UserGroupClient) Update() *UserGroupUpdate { + mutation := newUserGroupMutation(c.config, OpUpdate) + return &UserGroupUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *UserGroupClient) UpdateOne(ug *UserGroup) *UserGroupUpdateOne { + mutation := newUserGroupMutation(c.config, OpUpdateOne, withUserGroup(ug)) + return &UserGroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *UserGroupClient) UpdateOneID(id int) *UserGroupUpdateOne { + mutation := newUserGroupMutation(c.config, OpUpdateOne, withUserGroupID(id)) + return &UserGroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for UserGroup. +func (c *UserGroupClient) Delete() *UserGroupDelete { + mutation := newUserGroupMutation(c.config, OpDelete) + return &UserGroupDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *UserGroupClient) DeleteOne(ug *UserGroup) *UserGroupDeleteOne { + return c.DeleteOneID(ug.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *UserGroupClient) DeleteOneID(id int) *UserGroupDeleteOne { + builder := c.Delete().Where(usergroup.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &UserGroupDeleteOne{builder} +} + +// Query returns a query builder for UserGroup. +func (c *UserGroupClient) Query() *UserGroupQuery { + return &UserGroupQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeUserGroup}, + inters: c.Interceptors(), + } +} + +// Get returns a UserGroup entity by its id. +func (c *UserGroupClient) Get(ctx context.Context, id int) (*UserGroup, error) { + return c.Query().Where(usergroup.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *UserGroupClient) GetX(ctx context.Context, id int) *UserGroup { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryUser queries the user edge of a UserGroup. +func (c *UserGroupClient) QueryUser(ug *UserGroup) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := ug.ID + step := sqlgraph.NewStep( + sqlgraph.From(usergroup.Table, usergroup.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, usergroup.UserTable, usergroup.UserColumn), + ) + fromV = sqlgraph.Neighbors(ug.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryGroup queries the group edge of a UserGroup. +func (c *UserGroupClient) QueryGroup(ug *UserGroup) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := ug.ID + step := sqlgraph.NewStep( + sqlgraph.From(usergroup.Table, usergroup.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, usergroup.GroupTable, usergroup.GroupColumn), + ) + fromV = sqlgraph.Neighbors(ug.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *UserGroupClient) Hooks() []Hook { + return c.hooks.UserGroup +} + +// Interceptors returns the client interceptors. +func (c *UserGroupClient) Interceptors() []Interceptor { + return c.inters.UserGroup +} + +func (c *UserGroupClient) mutate(ctx context.Context, m *UserGroupMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&UserGroupCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&UserGroupUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&UserGroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&UserGroupDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown UserGroup mutation op: %q", m.Op()) + } +} + // hooks and interceptors per client, for fast access. type ( hooks struct { DavAccount, DirectLink, Entity, File, Group, Metadata, Node, Passkey, Setting, - Share, StoragePolicy, Task, User []ent.Hook + Share, StoragePolicy, Task, User, UserGroup []ent.Hook } inters struct { DavAccount, DirectLink, Entity, File, Group, Metadata, Node, Passkey, Setting, - Share, StoragePolicy, Task, User []ent.Interceptor + Share, StoragePolicy, Task, User, UserGroup []ent.Interceptor } ) diff --git a/ent/ent.go b/ent/ent.go index 4026fe67..0484298e 100644 --- a/ent/ent.go +++ b/ent/ent.go @@ -25,6 +25,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" ) // ent aliases to avoid import conflicts in user's code. @@ -98,6 +99,7 @@ func checkColumn(table, column string) error { storagepolicy.Table: storagepolicy.ValidColumn, task.Table: task.ValidColumn, user.Table: user.ValidColumn, + usergroup.Table: usergroup.ValidColumn, }) }) return columnCheck(table, column) diff --git a/ent/group.go b/ent/group.go index 80882cb5..822c20fb 100644 --- a/ent/group.go +++ b/ent/group.go @@ -51,9 +51,11 @@ type GroupEdges struct { Users []*User `json:"users,omitempty"` // StoragePolicies holds the value of the storage_policies edge. StoragePolicies *StoragePolicy `json:"storage_policies,omitempty"` + // UserGroup holds the value of the user_group edge. + UserGroup []*UserGroup `json:"user_group,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [2]bool + loadedTypes [3]bool } // UsersOrErr returns the Users value or an error if the edge @@ -78,6 +80,15 @@ func (e GroupEdges) StoragePoliciesOrErr() (*StoragePolicy, error) { return nil, &NotLoadedError{edge: "storage_policies"} } +// UserGroupOrErr returns the UserGroup value or an error if the edge +// was not loaded in eager-loading. +func (e GroupEdges) UserGroupOrErr() ([]*UserGroup, error) { + if e.loadedTypes[2] { + return e.UserGroup, nil + } + return nil, &NotLoadedError{edge: "user_group"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -194,6 +205,11 @@ func (gr *Group) QueryStoragePolicies() *StoragePolicyQuery { return NewGroupClient(gr.config).QueryStoragePolicies(gr) } +// QueryUserGroup queries the "user_group" edge of the Group entity. +func (gr *Group) QueryUserGroup() *UserGroupQuery { + return NewGroupClient(gr.config).QueryUserGroup(gr) +} + // Update returns a builder for updating this Group. // Note that you need to call Group.Unwrap() before calling this method if this Group // was returned from a transaction, and the transaction was committed or rolled back. @@ -261,5 +277,11 @@ func (e *Group) SetStoragePolicies(v *StoragePolicy) { e.Edges.loadedTypes[1] = true } +// SetUserGroup manually set the edge as loaded state. +func (e *Group) SetUserGroup(v []*UserGroup) { + e.Edges.UserGroup = v + e.Edges.loadedTypes[2] = true +} + // Groups is a parsable slice of Group. type Groups []*Group diff --git a/ent/group/group.go b/ent/group/group.go index 7ceee545..23c48426 100644 --- a/ent/group/group.go +++ b/ent/group/group.go @@ -38,15 +38,15 @@ const ( EdgeUsers = "users" // EdgeStoragePolicies holds the string denoting the storage_policies edge name in mutations. EdgeStoragePolicies = "storage_policies" + // EdgeUserGroup holds the string denoting the user_group edge name in mutations. + EdgeUserGroup = "user_group" // Table holds the table name of the group in the database. Table = "groups" - // UsersTable is the table that holds the users relation/edge. - UsersTable = "users" + // UsersTable is the table that holds the users relation/edge. The primary key declared below. + UsersTable = "user_groups" // UsersInverseTable is the table name for the User entity. // It exists in this package in order to avoid circular dependency with the "user" package. UsersInverseTable = "users" - // UsersColumn is the table column denoting the users relation/edge. - UsersColumn = "group_users" // StoragePoliciesTable is the table that holds the storage_policies relation/edge. StoragePoliciesTable = "groups" // StoragePoliciesInverseTable is the table name for the StoragePolicy entity. @@ -54,6 +54,13 @@ const ( StoragePoliciesInverseTable = "storage_policies" // StoragePoliciesColumn is the table column denoting the storage_policies relation/edge. StoragePoliciesColumn = "storage_policy_id" + // UserGroupTable is the table that holds the user_group relation/edge. + UserGroupTable = "user_groups" + // UserGroupInverseTable is the table name for the UserGroup entity. + // It exists in this package in order to avoid circular dependency with the "usergroup" package. + UserGroupInverseTable = "user_groups" + // UserGroupColumn is the table column denoting the user_group relation/edge. + UserGroupColumn = "group_id" ) // Columns holds all SQL columns for group fields. @@ -70,6 +77,12 @@ var Columns = []string{ FieldStoragePolicyID, } +var ( + // UsersPrimaryKey and UsersColumn2 are the table columns denoting the + // primary key for the users relation (M2M). + UsersPrimaryKey = []string{"group_id", "user_id"} +) + // ValidColumn reports if the column name is valid (part of the table columns). func ValidColumn(column string) bool { for i := range Columns { @@ -161,11 +174,25 @@ func ByStoragePoliciesField(field string, opts ...sql.OrderTermOption) OrderOpti sqlgraph.OrderByNeighborTerms(s, newStoragePoliciesStep(), sql.OrderByField(field, opts...)) } } + +// ByUserGroupCount orders the results by user_group count. +func ByUserGroupCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newUserGroupStep(), opts...) + } +} + +// ByUserGroup orders the results by user_group terms. +func ByUserGroup(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newUserGroupStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newUsersStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), sqlgraph.To(UsersInverseTable, FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, UsersTable, UsersColumn), + sqlgraph.Edge(sqlgraph.M2M, false, UsersTable, UsersPrimaryKey...), ) } func newStoragePoliciesStep() *sqlgraph.Step { @@ -175,3 +202,10 @@ func newStoragePoliciesStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, StoragePoliciesTable, StoragePoliciesColumn), ) } +func newUserGroupStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserGroupInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, UserGroupTable, UserGroupColumn), + ) +} diff --git a/ent/group/where.go b/ent/group/where.go index 4c414ff5..d2a19d3b 100644 --- a/ent/group/where.go +++ b/ent/group/where.go @@ -476,7 +476,7 @@ func HasUsers() predicate.Group { return predicate.Group(func(s *sql.Selector) { step := sqlgraph.NewStep( sqlgraph.From(Table, FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, UsersTable, UsersColumn), + sqlgraph.Edge(sqlgraph.M2M, false, UsersTable, UsersPrimaryKey...), ) sqlgraph.HasNeighbors(s, step) }) @@ -517,6 +517,29 @@ func HasStoragePoliciesWith(preds ...predicate.StoragePolicy) predicate.Group { }) } +// HasUserGroup applies the HasEdge predicate on the "user_group" edge. +func HasUserGroup() predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, UserGroupTable, UserGroupColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUserGroupWith applies the HasEdge predicate on the "user_group" edge with a given conditions (other predicates). +func HasUserGroupWith(preds ...predicate.UserGroup) predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := newUserGroupStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Group) predicate.Group { return predicate.Group(sql.AndPredicates(predicates...)) diff --git a/ent/group_create.go b/ent/group_create.go index e11afdd6..2380c229 100644 --- a/ent/group_create.go +++ b/ent/group_create.go @@ -14,6 +14,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" ) @@ -162,6 +163,21 @@ func (gc *GroupCreate) SetStoragePolicies(s *StoragePolicy) *GroupCreate { return gc.SetStoragePoliciesID(s.ID) } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by IDs. +func (gc *GroupCreate) AddUserGroupIDs(ids ...int) *GroupCreate { + gc.mutation.AddUserGroupIDs(ids...) + return gc +} + +// AddUserGroup adds the "user_group" edges to the UserGroup entity. +func (gc *GroupCreate) AddUserGroup(u ...*UserGroup) *GroupCreate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return gc.AddUserGroupIDs(ids...) +} + // Mutation returns the GroupMutation object of the builder. func (gc *GroupCreate) Mutation() *GroupMutation { return gc.mutation @@ -302,10 +318,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { } if nodes := gc.mutation.UsersIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, + Rel: sqlgraph.M2M, Inverse: false, Table: group.UsersTable, - Columns: []string{group.UsersColumn}, + Columns: group.UsersPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), @@ -314,6 +330,10 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } + createE := &UserGroupCreate{config: gc.config, mutation: newUserGroupMutation(gc.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges = append(_spec.Edges, edge) } if nodes := gc.mutation.StoragePoliciesIDs(); len(nodes) > 0 { @@ -333,6 +353,22 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { _node.StoragePolicyID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := gc.mutation.UserGroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.UserGroupTable, + Columns: []string{group.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/ent/group_query.go b/ent/group_query.go index 27d4ac91..38f8e6a6 100644 --- a/ent/group_query.go +++ b/ent/group_query.go @@ -15,6 +15,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" ) // GroupQuery is the builder for querying Group entities. @@ -26,6 +27,7 @@ type GroupQuery struct { predicates []predicate.Group withUsers *UserQuery withStoragePolicies *StoragePolicyQuery + withUserGroup *UserGroupQuery // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -76,7 +78,7 @@ func (gq *GroupQuery) QueryUsers() *UserQuery { step := sqlgraph.NewStep( sqlgraph.From(group.Table, group.FieldID, selector), sqlgraph.To(user.Table, user.FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, group.UsersTable, group.UsersColumn), + sqlgraph.Edge(sqlgraph.M2M, false, group.UsersTable, group.UsersPrimaryKey...), ) fromU = sqlgraph.SetNeighbors(gq.driver.Dialect(), step) return fromU, nil @@ -106,6 +108,28 @@ func (gq *GroupQuery) QueryStoragePolicies() *StoragePolicyQuery { return query } +// QueryUserGroup chains the current query on the "user_group" edge. +func (gq *GroupQuery) QueryUserGroup() *UserGroupQuery { + query := (&UserGroupClient{config: gq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := gq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := gq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, selector), + sqlgraph.To(usergroup.Table, usergroup.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, group.UserGroupTable, group.UserGroupColumn), + ) + fromU = sqlgraph.SetNeighbors(gq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first Group entity from the query. // Returns a *NotFoundError when no Group was found. func (gq *GroupQuery) First(ctx context.Context) (*Group, error) { @@ -300,6 +324,7 @@ func (gq *GroupQuery) Clone() *GroupQuery { predicates: append([]predicate.Group{}, gq.predicates...), withUsers: gq.withUsers.Clone(), withStoragePolicies: gq.withStoragePolicies.Clone(), + withUserGroup: gq.withUserGroup.Clone(), // clone intermediate query. sql: gq.sql.Clone(), path: gq.path, @@ -328,6 +353,17 @@ func (gq *GroupQuery) WithStoragePolicies(opts ...func(*StoragePolicyQuery)) *Gr return gq } +// WithUserGroup tells the query-builder to eager-load the nodes that are connected to +// the "user_group" edge. The optional arguments are used to configure the query builder of the edge. +func (gq *GroupQuery) WithUserGroup(opts ...func(*UserGroupQuery)) *GroupQuery { + query := (&UserGroupClient{config: gq.config}).Query() + for _, opt := range opts { + opt(query) + } + gq.withUserGroup = query + return gq +} + // 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. // @@ -406,9 +442,10 @@ func (gq *GroupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Group, var ( nodes = []*Group{} _spec = gq.querySpec() - loadedTypes = [2]bool{ + loadedTypes = [3]bool{ gq.withUsers != nil, gq.withStoragePolicies != nil, + gq.withUserGroup != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -442,36 +479,74 @@ func (gq *GroupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Group, return nil, err } } + if query := gq.withUserGroup; query != nil { + if err := gq.loadUserGroup(ctx, query, nodes, + func(n *Group) { n.Edges.UserGroup = []*UserGroup{} }, + func(n *Group, e *UserGroup) { n.Edges.UserGroup = append(n.Edges.UserGroup, e) }); err != nil { + return nil, err + } + } return nodes, nil } func (gq *GroupQuery) loadUsers(ctx context.Context, query *UserQuery, nodes []*Group, init func(*Group), assign func(*Group, *User)) error { - fks := make([]driver.Value, 0, len(nodes)) - nodeids := make(map[int]*Group) - for i := range nodes { - fks = append(fks, nodes[i].ID) - nodeids[nodes[i].ID] = nodes[i] + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[int]*Group) + nids := make(map[int]map[*Group]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node if init != nil { - init(nodes[i]) + init(node) } } - if len(query.ctx.Fields) > 0 { - query.ctx.AppendFieldOnce(user.FieldGroupUsers) + query.Where(func(s *sql.Selector) { + joinT := sql.Table(group.UsersTable) + s.Join(joinT).On(s.C(user.FieldID), joinT.C(group.UsersPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(group.UsersPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(group.UsersPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err } - query.Where(predicate.User(func(s *sql.Selector) { - s.Where(sql.InValues(s.C(group.UsersColumn), fks...)) - })) - neighbors, err := query.All(ctx) + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(sql.NullInt64)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := int(values[0].(*sql.NullInt64).Int64) + inValue := int(values[1].(*sql.NullInt64).Int64) + if nids[inValue] == nil { + nids[inValue] = map[*Group]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*User](ctx, query, qr, query.inters) if err != nil { return err } for _, n := range neighbors { - fk := n.GroupUsers - node, ok := nodeids[fk] + nodes, ok := nids[n.ID] if !ok { - return fmt.Errorf(`unexpected referenced foreign-key "group_users" returned %v for node %v`, fk, n.ID) + return fmt.Errorf(`unexpected "users" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) } - assign(node, n) } return nil } @@ -504,6 +579,36 @@ func (gq *GroupQuery) loadStoragePolicies(ctx context.Context, query *StoragePol } return nil } +func (gq *GroupQuery) loadUserGroup(ctx context.Context, query *UserGroupQuery, nodes []*Group, init func(*Group), assign func(*Group, *UserGroup)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*Group) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(usergroup.FieldGroupID) + } + query.Where(predicate.UserGroup(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(group.UserGroupColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.GroupID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "group_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (gq *GroupQuery) sqlCount(ctx context.Context) (int, error) { _spec := gq.querySpec() diff --git a/ent/group_update.go b/ent/group_update.go index ae734535..4c866b42 100644 --- a/ent/group_update.go +++ b/ent/group_update.go @@ -15,6 +15,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" ) @@ -198,6 +199,21 @@ func (gu *GroupUpdate) SetStoragePolicies(s *StoragePolicy) *GroupUpdate { return gu.SetStoragePoliciesID(s.ID) } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by IDs. +func (gu *GroupUpdate) AddUserGroupIDs(ids ...int) *GroupUpdate { + gu.mutation.AddUserGroupIDs(ids...) + return gu +} + +// AddUserGroup adds the "user_group" edges to the UserGroup entity. +func (gu *GroupUpdate) AddUserGroup(u ...*UserGroup) *GroupUpdate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return gu.AddUserGroupIDs(ids...) +} + // Mutation returns the GroupMutation object of the builder. func (gu *GroupUpdate) Mutation() *GroupMutation { return gu.mutation @@ -230,6 +246,27 @@ func (gu *GroupUpdate) ClearStoragePolicies() *GroupUpdate { return gu } +// ClearUserGroup clears all "user_group" edges to the UserGroup entity. +func (gu *GroupUpdate) ClearUserGroup() *GroupUpdate { + gu.mutation.ClearUserGroup() + return gu +} + +// RemoveUserGroupIDs removes the "user_group" edge to UserGroup entities by IDs. +func (gu *GroupUpdate) RemoveUserGroupIDs(ids ...int) *GroupUpdate { + gu.mutation.RemoveUserGroupIDs(ids...) + return gu +} + +// RemoveUserGroup removes "user_group" edges to UserGroup entities. +func (gu *GroupUpdate) RemoveUserGroup(u ...*UserGroup) *GroupUpdate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return gu.RemoveUserGroupIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (gu *GroupUpdate) Save(ctx context.Context) (int, error) { if err := gu.defaults(); err != nil { @@ -322,23 +359,27 @@ func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) { } if gu.mutation.UsersCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, + Rel: sqlgraph.M2M, Inverse: false, Table: group.UsersTable, - Columns: []string{group.UsersColumn}, + Columns: group.UsersPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), }, } + createE := &UserGroupCreate{config: gu.config, mutation: newUserGroupMutation(gu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } if nodes := gu.mutation.RemovedUsersIDs(); len(nodes) > 0 && !gu.mutation.UsersCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, + Rel: sqlgraph.M2M, Inverse: false, Table: group.UsersTable, - Columns: []string{group.UsersColumn}, + Columns: group.UsersPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), @@ -347,14 +388,18 @@ func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) { for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } + createE := &UserGroupCreate{config: gu.config, mutation: newUserGroupMutation(gu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } if nodes := gu.mutation.UsersIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, + Rel: sqlgraph.M2M, Inverse: false, Table: group.UsersTable, - Columns: []string{group.UsersColumn}, + Columns: group.UsersPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), @@ -363,6 +408,10 @@ func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) { for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } + createE := &UserGroupCreate{config: gu.config, mutation: newUserGroupMutation(gu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Add = append(_spec.Edges.Add, edge) } if gu.mutation.StoragePoliciesCleared() { @@ -394,6 +443,51 @@ func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if gu.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.UserGroupTable, + Columns: []string{group.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gu.mutation.RemovedUserGroupIDs(); len(nodes) > 0 && !gu.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.UserGroupTable, + Columns: []string{group.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gu.mutation.UserGroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.UserGroupTable, + Columns: []string{group.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, gu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{group.Label} @@ -580,6 +674,21 @@ func (guo *GroupUpdateOne) SetStoragePolicies(s *StoragePolicy) *GroupUpdateOne return guo.SetStoragePoliciesID(s.ID) } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by IDs. +func (guo *GroupUpdateOne) AddUserGroupIDs(ids ...int) *GroupUpdateOne { + guo.mutation.AddUserGroupIDs(ids...) + return guo +} + +// AddUserGroup adds the "user_group" edges to the UserGroup entity. +func (guo *GroupUpdateOne) AddUserGroup(u ...*UserGroup) *GroupUpdateOne { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return guo.AddUserGroupIDs(ids...) +} + // Mutation returns the GroupMutation object of the builder. func (guo *GroupUpdateOne) Mutation() *GroupMutation { return guo.mutation @@ -612,6 +721,27 @@ func (guo *GroupUpdateOne) ClearStoragePolicies() *GroupUpdateOne { return guo } +// ClearUserGroup clears all "user_group" edges to the UserGroup entity. +func (guo *GroupUpdateOne) ClearUserGroup() *GroupUpdateOne { + guo.mutation.ClearUserGroup() + return guo +} + +// RemoveUserGroupIDs removes the "user_group" edge to UserGroup entities by IDs. +func (guo *GroupUpdateOne) RemoveUserGroupIDs(ids ...int) *GroupUpdateOne { + guo.mutation.RemoveUserGroupIDs(ids...) + return guo +} + +// RemoveUserGroup removes "user_group" edges to UserGroup entities. +func (guo *GroupUpdateOne) RemoveUserGroup(u ...*UserGroup) *GroupUpdateOne { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return guo.RemoveUserGroupIDs(ids...) +} + // Where appends a list predicates to the GroupUpdate builder. func (guo *GroupUpdateOne) Where(ps ...predicate.Group) *GroupUpdateOne { guo.mutation.Where(ps...) @@ -734,23 +864,27 @@ func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error } if guo.mutation.UsersCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, + Rel: sqlgraph.M2M, Inverse: false, Table: group.UsersTable, - Columns: []string{group.UsersColumn}, + Columns: group.UsersPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), }, } + createE := &UserGroupCreate{config: guo.config, mutation: newUserGroupMutation(guo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } if nodes := guo.mutation.RemovedUsersIDs(); len(nodes) > 0 && !guo.mutation.UsersCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, + Rel: sqlgraph.M2M, Inverse: false, Table: group.UsersTable, - Columns: []string{group.UsersColumn}, + Columns: group.UsersPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), @@ -759,14 +893,18 @@ func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } + createE := &UserGroupCreate{config: guo.config, mutation: newUserGroupMutation(guo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } if nodes := guo.mutation.UsersIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, + Rel: sqlgraph.M2M, Inverse: false, Table: group.UsersTable, - Columns: []string{group.UsersColumn}, + Columns: group.UsersPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), @@ -775,6 +913,10 @@ func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } + createE := &UserGroupCreate{config: guo.config, mutation: newUserGroupMutation(guo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Add = append(_spec.Edges.Add, edge) } if guo.mutation.StoragePoliciesCleared() { @@ -806,6 +948,51 @@ func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if guo.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.UserGroupTable, + Columns: []string{group.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := guo.mutation.RemovedUserGroupIDs(); len(nodes) > 0 && !guo.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.UserGroupTable, + Columns: []string{group.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := guo.mutation.UserGroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.UserGroupTable, + Columns: []string{group.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &Group{config: guo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/ent/hook/hook.go b/ent/hook/hook.go index 29b3f1fc..b036ae3e 100644 --- a/ent/hook/hook.go +++ b/ent/hook/hook.go @@ -165,6 +165,18 @@ func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m) } +// The UserGroupFunc type is an adapter to allow the use of ordinary +// function as UserGroup mutator. +type UserGroupFunc func(context.Context, *ent.UserGroupMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f UserGroupFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.UserGroupMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserGroupMutation", m) +} + // Condition is a hook condition function. type Condition func(context.Context, ent.Mutation) bool diff --git a/ent/intercept/intercept.go b/ent/intercept/intercept.go index da350367..373c6e0d 100644 --- a/ent/intercept/intercept.go +++ b/ent/intercept/intercept.go @@ -22,6 +22,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" ) // The Query interface represents an operation that queries a graph. @@ -431,6 +432,33 @@ func (f TraverseUser) Traverse(ctx context.Context, q ent.Query) error { return fmt.Errorf("unexpected query type %T. expect *ent.UserQuery", q) } +// The UserGroupFunc type is an adapter to allow the use of ordinary function as a Querier. +type UserGroupFunc func(context.Context, *ent.UserGroupQuery) (ent.Value, error) + +// Query calls f(ctx, q). +func (f UserGroupFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) { + if q, ok := q.(*ent.UserGroupQuery); ok { + return f(ctx, q) + } + return nil, fmt.Errorf("unexpected query type %T. expect *ent.UserGroupQuery", q) +} + +// The TraverseUserGroup type is an adapter to allow the use of ordinary function as Traverser. +type TraverseUserGroup func(context.Context, *ent.UserGroupQuery) error + +// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline. +func (f TraverseUserGroup) Intercept(next ent.Querier) ent.Querier { + return next +} + +// Traverse calls f(ctx, q). +func (f TraverseUserGroup) Traverse(ctx context.Context, q ent.Query) error { + if q, ok := q.(*ent.UserGroupQuery); ok { + return f(ctx, q) + } + return fmt.Errorf("unexpected query type %T. expect *ent.UserGroupQuery", q) +} + // NewQuery returns the generic Query interface for the given typed query. func NewQuery(q ent.Query) (Query, error) { switch q := q.(type) { @@ -460,6 +488,8 @@ func NewQuery(q ent.Query) (Query, error) { return &query[*ent.TaskQuery, predicate.Task, task.OrderOption]{typ: ent.TypeTask, tq: q}, nil case *ent.UserQuery: return &query[*ent.UserQuery, predicate.User, user.OrderOption]{typ: ent.TypeUser, tq: q}, nil + case *ent.UserGroupQuery: + return &query[*ent.UserGroupQuery, predicate.UserGroup, usergroup.OrderOption]{typ: ent.TypeUserGroup, tq: q}, nil default: return nil, fmt.Errorf("unknown query type %T", q) } diff --git a/ent/internal/schema.go b/ent/internal/schema.go index 160f7aff..659fed7a 100644 --- a/ent/internal/schema.go +++ b/ent/internal/schema.go @@ -6,4 +6,4 @@ // Package internal holds a loadable version of the latest schema. package internal -const Schema = "{\"Schema\":\"github.com/cloudreve/Cloudreve/v4/ent/schema\",\"Package\":\"github.com/cloudreve/Cloudreve/v4/ent\",\"Schemas\":[{\"name\":\"DavAccount\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"owner\",\"type\":\"User\",\"field\":\"owner_id\",\"ref_name\":\"dav_accounts\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"uri\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"options\",\"type\":{\"Type\":5,\"Ident\":\"*boolset.BooleanSet\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"PkgName\":\"boolset\",\"Nillable\":true,\"RType\":{\"Name\":\"BooleanSet\",\"Ident\":\"boolset.BooleanSet\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"Methods\":{\"Enabled\":{\"In\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.DavAccountProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"DavAccountProps\",\"Ident\":\"types.DavAccountProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"owner_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"owner_id\",\"password\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"DirectLink\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"field\":\"file_id\",\"ref_name\":\"direct_links\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"downloads\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"speed\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Entity\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"ref_name\":\"entities\",\"inverse\":true},{\"name\":\"user\",\"type\":\"User\",\"field\":\"created_by\",\"ref_name\":\"entities\",\"unique\":true,\"inverse\":true},{\"name\":\"storage_policy\",\"type\":\"StoragePolicy\",\"field\":\"storage_policy_entities\",\"ref_name\":\"entities\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"source\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"reference_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_entities\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"created_by\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"upload_session_id\",\"type\":{\"Type\":4,\"Ident\":\"uuid.UUID\",\"PkgPath\":\"github.com/gofrs/uuid\",\"PkgName\":\"uuid\",\"Nillable\":false,\"RType\":{\"Name\":\"UUID\",\"Ident\":\"uuid.UUID\",\"Kind\":17,\"PkgPath\":\"github.com/gofrs/uuid\",\"Methods\":{\"Bytes\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"Format\":{\"In\":[{\"Name\":\"State\",\"Ident\":\"fmt.State\",\"Kind\":20,\"PkgPath\":\"fmt\",\"Methods\":null},{\"Name\":\"int32\",\"Ident\":\"int32\",\"Kind\":5,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalText\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"SetVariant\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"SetVersion\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Variant\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]},\"Version\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"recycle_options\",\"type\":{\"Type\":3,\"Ident\":\"*types.EntityRecycleOption\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"EntityRecycleOption\",\"Ident\":\"types.EntityRecycleOption\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"File\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"owner\",\"type\":\"User\",\"field\":\"owner_id\",\"ref_name\":\"files\",\"unique\":true,\"inverse\":true,\"required\":true},{\"name\":\"storage_policies\",\"type\":\"StoragePolicy\",\"field\":\"storage_policy_files\",\"ref_name\":\"files\",\"unique\":true,\"inverse\":true},{\"name\":\"parent\",\"type\":\"File\",\"field\":\"file_children\",\"ref\":{\"name\":\"children\",\"type\":\"File\"},\"unique\":true,\"inverse\":true},{\"name\":\"metadata\",\"type\":\"Metadata\"},{\"name\":\"entities\",\"type\":\"Entity\"},{\"name\":\"shares\",\"type\":\"Share\"},{\"name\":\"direct_links\",\"type\":\"DirectLink\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"owner_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"primary_entity\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_children\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_symbolic\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.FileProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"FileProps\",\"Ident\":\"types.FileProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_files\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"file_children\",\"name\"]},{\"fields\":[\"file_children\",\"type\",\"updated_at\"]},{\"fields\":[\"file_children\",\"type\",\"size\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Group\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"users\",\"type\":\"User\"},{\"name\":\"storage_policies\",\"type\":\"StoragePolicy\",\"field\":\"storage_policy_id\",\"ref_name\":\"groups\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"max_storage\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"speed_limit\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"permissions\",\"type\":{\"Type\":5,\"Ident\":\"*boolset.BooleanSet\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"PkgName\":\"boolset\",\"Nillable\":true,\"RType\":{\"Name\":\"BooleanSet\",\"Ident\":\"boolset.BooleanSet\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"Methods\":{\"Enabled\":{\"In\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.GroupSetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"GroupSetting\",\"Ident\":\"types.GroupSetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Metadata\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"field\":\"file_id\",\"ref_name\":\"metadata\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"value\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_public\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"file_id\",\"name\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Node\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"storage_policy\",\"type\":\"StoragePolicy\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"node.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"suspended\",\"V\":\"suspended\"}],\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"node.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"master\",\"V\":\"master\"},{\"N\":\"slave\",\"V\":\"slave\"}],\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"server\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"slave_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"capabilities\",\"type\":{\"Type\":5,\"Ident\":\"*boolset.BooleanSet\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"PkgName\":\"boolset\",\"Nillable\":true,\"RType\":{\"Name\":\"BooleanSet\",\"Ident\":\"boolset.BooleanSet\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"Methods\":{\"Enabled\":{\"In\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.NodeSetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"NodeSetting\",\"Ident\":\"types.NodeSetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"weight\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Passkey\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"passkey\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"credential_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"credential\",\"type\":{\"Type\":3,\"Ident\":\"*webauthn.Credential\",\"PkgPath\":\"github.com/go-webauthn/webauthn/webauthn\",\"PkgName\":\"webauthn\",\"Nillable\":true,\"RType\":{\"Name\":\"Credential\",\"Ident\":\"webauthn.Credential\",\"Kind\":22,\"PkgPath\":\"github.com/go-webauthn/webauthn/webauthn\",\"Methods\":{\"Descriptor\":{\"In\":[],\"Out\":[{\"Name\":\"CredentialDescriptor\",\"Ident\":\"protocol.CredentialDescriptor\",\"Kind\":25,\"PkgPath\":\"github.com/go-webauthn/webauthn/protocol\",\"Methods\":null}]},\"Verify\":{\"In\":[{\"Name\":\"Provider\",\"Ident\":\"metadata.Provider\",\"Kind\":20,\"PkgPath\":\"github.com/go-webauthn/webauthn/metadata\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"used_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"credential_id\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Setting\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"value\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Share\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"ref_name\":\"shares\",\"unique\":true,\"inverse\":true},{\"name\":\"file\",\"type\":\"File\",\"ref_name\":\"shares\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"views\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"downloads\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"expires\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"remain_downloads\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.ShareProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"ShareProps\",\"Ident\":\"types.ShareProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"StoragePolicy\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"groups\",\"type\":\"Group\"},{\"name\":\"files\",\"type\":\"File\"},{\"name\":\"entities\",\"type\":\"Entity\"},{\"name\":\"node\",\"type\":\"Node\",\"field\":\"node_id\",\"ref_name\":\"storage_policy\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"server\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"bucket_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_private\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"access_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"secret_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"max_size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"dir_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.PolicySetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"PolicySetting\",\"Ident\":\"types.PolicySetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"file_type\":null,\"native_media_processing\":false,\"s3_path_style\":false,\"token\":\"\"},\"default_kind\":22,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"node_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Task\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_tasks\",\"ref_name\":\"tasks\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"task.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"queued\",\"V\":\"queued\"},{\"N\":\"processing\",\"V\":\"processing\"},{\"N\":\"suspending\",\"V\":\"suspending\"},{\"N\":\"error\",\"V\":\"error\"},{\"N\":\"canceled\",\"V\":\"canceled\"},{\"N\":\"completed\",\"V\":\"completed\"}],\"default\":true,\"default_value\":\"queued\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"public_state\",\"type\":{\"Type\":3,\"Ident\":\"*types.TaskPublicState\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"TaskPublicState\",\"Ident\":\"types.TaskPublicState\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"private_state\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"correlation_id\",\"type\":{\"Type\":4,\"Ident\":\"uuid.UUID\",\"PkgPath\":\"github.com/gofrs/uuid\",\"PkgName\":\"uuid\",\"Nillable\":false,\"RType\":{\"Name\":\"UUID\",\"Ident\":\"uuid.UUID\",\"Kind\":17,\"PkgPath\":\"github.com/gofrs/uuid\",\"Methods\":{\"Bytes\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"Format\":{\"In\":[{\"Name\":\"State\",\"Ident\":\"fmt.State\",\"Kind\":20,\"PkgPath\":\"fmt\",\"Methods\":null},{\"Name\":\"int32\",\"Ident\":\"int32\",\"Kind\":5,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalText\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"SetVariant\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"SetVersion\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Variant\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]},\"Version\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"user_tasks\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"User\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"group\",\"type\":\"Group\",\"field\":\"group_users\",\"ref_name\":\"users\",\"unique\":true,\"inverse\":true,\"required\":true},{\"name\":\"files\",\"type\":\"File\"},{\"name\":\"dav_accounts\",\"type\":\"DavAccount\"},{\"name\":\"shares\",\"type\":\"Share\"},{\"name\":\"passkey\",\"type\":\"Passkey\"},{\"name\":\"tasks\",\"type\":\"Task\"},{\"name\":\"entities\",\"type\":\"Entity\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"email\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":100,\"unique\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"nick\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":100,\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"user.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"inactive\",\"V\":\"inactive\"},{\"N\":\"manual_banned\",\"V\":\"manual_banned\"},{\"N\":\"sys_banned\",\"V\":\"sys_banned\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"two_factor_secret\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"avatar\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.UserSetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"UserSetting\",\"Ident\":\"types.UserSetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"group_users\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]}],\"Features\":[\"intercept\",\"schema/snapshot\",\"sql/upsert\",\"sql/upsert\",\"sql/execquery\"]}" +const Schema = "{\"Schema\":\"github.com/cloudreve/Cloudreve/v4/ent/schema\",\"Package\":\"github.com/cloudreve/Cloudreve/v4/ent\",\"Schemas\":[{\"name\":\"DavAccount\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"owner\",\"type\":\"User\",\"field\":\"owner_id\",\"ref_name\":\"dav_accounts\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"uri\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"options\",\"type\":{\"Type\":5,\"Ident\":\"*boolset.BooleanSet\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"PkgName\":\"boolset\",\"Nillable\":true,\"RType\":{\"Name\":\"BooleanSet\",\"Ident\":\"boolset.BooleanSet\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"Methods\":{\"Enabled\":{\"In\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.DavAccountProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"DavAccountProps\",\"Ident\":\"types.DavAccountProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"owner_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"owner_id\",\"password\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"DirectLink\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"field\":\"file_id\",\"ref_name\":\"direct_links\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"downloads\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"speed\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Entity\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"ref_name\":\"entities\",\"inverse\":true},{\"name\":\"user\",\"type\":\"User\",\"field\":\"created_by\",\"ref_name\":\"entities\",\"unique\":true,\"inverse\":true},{\"name\":\"storage_policy\",\"type\":\"StoragePolicy\",\"field\":\"storage_policy_entities\",\"ref_name\":\"entities\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"source\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"reference_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_entities\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"created_by\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"upload_session_id\",\"type\":{\"Type\":4,\"Ident\":\"uuid.UUID\",\"PkgPath\":\"github.com/gofrs/uuid\",\"PkgName\":\"uuid\",\"Nillable\":false,\"RType\":{\"Name\":\"UUID\",\"Ident\":\"uuid.UUID\",\"Kind\":17,\"PkgPath\":\"github.com/gofrs/uuid\",\"Methods\":{\"Bytes\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"Format\":{\"In\":[{\"Name\":\"State\",\"Ident\":\"fmt.State\",\"Kind\":20,\"PkgPath\":\"fmt\",\"Methods\":null},{\"Name\":\"int32\",\"Ident\":\"int32\",\"Kind\":5,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalText\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"SetVariant\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"SetVersion\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Variant\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]},\"Version\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"recycle_options\",\"type\":{\"Type\":3,\"Ident\":\"*types.EntityRecycleOption\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"EntityRecycleOption\",\"Ident\":\"types.EntityRecycleOption\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"File\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"owner\",\"type\":\"User\",\"field\":\"owner_id\",\"ref_name\":\"files\",\"unique\":true,\"inverse\":true,\"required\":true},{\"name\":\"storage_policies\",\"type\":\"StoragePolicy\",\"field\":\"storage_policy_files\",\"ref_name\":\"files\",\"unique\":true,\"inverse\":true},{\"name\":\"parent\",\"type\":\"File\",\"field\":\"file_children\",\"ref\":{\"name\":\"children\",\"type\":\"File\"},\"unique\":true,\"inverse\":true},{\"name\":\"metadata\",\"type\":\"Metadata\"},{\"name\":\"entities\",\"type\":\"Entity\"},{\"name\":\"shares\",\"type\":\"Share\"},{\"name\":\"direct_links\",\"type\":\"DirectLink\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"owner_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"primary_entity\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_children\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_symbolic\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.FileProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"FileProps\",\"Ident\":\"types.FileProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_files\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"file_children\",\"name\"]},{\"fields\":[\"file_children\",\"type\",\"updated_at\"]},{\"fields\":[\"file_children\",\"type\",\"size\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Group\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"users\",\"type\":\"User\",\"through\":{\"N\":\"user_group\",\"T\":\"UserGroup\"}},{\"name\":\"storage_policies\",\"type\":\"StoragePolicy\",\"field\":\"storage_policy_id\",\"ref_name\":\"groups\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"max_storage\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"speed_limit\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"permissions\",\"type\":{\"Type\":5,\"Ident\":\"*boolset.BooleanSet\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"PkgName\":\"boolset\",\"Nillable\":true,\"RType\":{\"Name\":\"BooleanSet\",\"Ident\":\"boolset.BooleanSet\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"Methods\":{\"Enabled\":{\"In\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.GroupSetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"GroupSetting\",\"Ident\":\"types.GroupSetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Metadata\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"field\":\"file_id\",\"ref_name\":\"metadata\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"value\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_public\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"file_id\",\"name\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Node\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"storage_policy\",\"type\":\"StoragePolicy\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"node.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"suspended\",\"V\":\"suspended\"}],\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"node.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"master\",\"V\":\"master\"},{\"N\":\"slave\",\"V\":\"slave\"}],\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"server\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"slave_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"capabilities\",\"type\":{\"Type\":5,\"Ident\":\"*boolset.BooleanSet\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"PkgName\":\"boolset\",\"Nillable\":true,\"RType\":{\"Name\":\"BooleanSet\",\"Ident\":\"boolset.BooleanSet\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/pkg/boolset\",\"Methods\":{\"Enabled\":{\"In\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.NodeSetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"NodeSetting\",\"Ident\":\"types.NodeSetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"weight\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Passkey\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"passkey\",\"unique\":true,\"inverse\":true,\"required\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"credential_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"credential\",\"type\":{\"Type\":3,\"Ident\":\"*webauthn.Credential\",\"PkgPath\":\"github.com/go-webauthn/webauthn/webauthn\",\"PkgName\":\"webauthn\",\"Nillable\":true,\"RType\":{\"Name\":\"Credential\",\"Ident\":\"webauthn.Credential\",\"Kind\":22,\"PkgPath\":\"github.com/go-webauthn/webauthn/webauthn\",\"Methods\":{\"Descriptor\":{\"In\":[],\"Out\":[{\"Name\":\"CredentialDescriptor\",\"Ident\":\"protocol.CredentialDescriptor\",\"Kind\":25,\"PkgPath\":\"github.com/go-webauthn/webauthn/protocol\",\"Methods\":null}]},\"Verify\":{\"In\":[{\"Name\":\"Provider\",\"Ident\":\"metadata.Provider\",\"Kind\":20,\"PkgPath\":\"github.com/go-webauthn/webauthn/metadata\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"used_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"credential_id\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Setting\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"value\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Share\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"ref_name\":\"shares\",\"unique\":true,\"inverse\":true},{\"name\":\"file\",\"type\":\"File\",\"ref_name\":\"shares\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"views\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"downloads\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"expires\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"remain_downloads\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.ShareProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"ShareProps\",\"Ident\":\"types.ShareProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"StoragePolicy\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"groups\",\"type\":\"Group\"},{\"name\":\"files\",\"type\":\"File\"},{\"name\":\"entities\",\"type\":\"Entity\"},{\"name\":\"node\",\"type\":\"Node\",\"field\":\"node_id\",\"ref_name\":\"storage_policy\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"server\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"bucket_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_private\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"access_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"secret_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"max_size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"dir_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.PolicySetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"PolicySetting\",\"Ident\":\"types.PolicySetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"file_type\":null,\"native_media_processing\":false,\"s3_path_style\":false,\"token\":\"\"},\"default_kind\":22,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"node_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Task\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_tasks\",\"ref_name\":\"tasks\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"task.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"queued\",\"V\":\"queued\"},{\"N\":\"processing\",\"V\":\"processing\"},{\"N\":\"suspending\",\"V\":\"suspending\"},{\"N\":\"error\",\"V\":\"error\"},{\"N\":\"canceled\",\"V\":\"canceled\"},{\"N\":\"completed\",\"V\":\"completed\"}],\"default\":true,\"default_value\":\"queued\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"public_state\",\"type\":{\"Type\":3,\"Ident\":\"*types.TaskPublicState\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"TaskPublicState\",\"Ident\":\"types.TaskPublicState\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"private_state\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"correlation_id\",\"type\":{\"Type\":4,\"Ident\":\"uuid.UUID\",\"PkgPath\":\"github.com/gofrs/uuid\",\"PkgName\":\"uuid\",\"Nillable\":false,\"RType\":{\"Name\":\"UUID\",\"Ident\":\"uuid.UUID\",\"Kind\":17,\"PkgPath\":\"github.com/gofrs/uuid\",\"Methods\":{\"Bytes\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"Format\":{\"In\":[{\"Name\":\"State\",\"Ident\":\"fmt.State\",\"Kind\":20,\"PkgPath\":\"fmt\",\"Methods\":null},{\"Name\":\"int32\",\"Ident\":\"int32\",\"Kind\":5,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalText\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"SetVariant\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"SetVersion\":{\"In\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Variant\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]},\"Version\":{\"In\":[],\"Out\":[{\"Name\":\"uint8\",\"Ident\":\"uint8\",\"Kind\":8,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"user_tasks\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"User\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"groups\",\"type\":\"Group\",\"ref_name\":\"users\",\"through\":{\"N\":\"user_group\",\"T\":\"UserGroup\"},\"inverse\":true},{\"name\":\"files\",\"type\":\"File\"},{\"name\":\"dav_accounts\",\"type\":\"DavAccount\"},{\"name\":\"shares\",\"type\":\"Share\"},{\"name\":\"passkey\",\"type\":\"Passkey\"},{\"name\":\"tasks\",\"type\":\"Task\"},{\"name\":\"entities\",\"type\":\"Entity\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"deleted_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":true,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"email\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":100,\"unique\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"nick\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":100,\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"user.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"inactive\",\"V\":\"inactive\"},{\"N\":\"manual_banned\",\"V\":\"manual_banned\"},{\"N\":\"sys_banned\",\"V\":\"sys_banned\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"two_factor_secret\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"avatar\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*types.UserSetting\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"UserSetting\",\"Ident\":\"types.UserSetting\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"UserGroup\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"unique\":true,\"required\":true},{\"name\":\"group\",\"type\":\"Group\",\"field\":\"group_id\",\"unique\":true,\"required\":true}],\"fields\":[{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"group_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_primary\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"expires_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}}]}],\"Features\":[\"intercept\",\"schema/snapshot\",\"sql/upsert\",\"sql/upsert\",\"sql/execquery\"]}" diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 84465d16..e3058fc0 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -398,21 +398,47 @@ var ( {Name: "two_factor_secret", Type: field.TypeString, Nullable: true}, {Name: "avatar", Type: field.TypeString, Nullable: true}, {Name: "settings", Type: field.TypeJSON, Nullable: true}, - {Name: "group_users", Type: field.TypeInt}, } // UsersTable holds the schema information for the "users" table. UsersTable = &schema.Table{ Name: "users", Columns: UsersColumns, PrimaryKey: []*schema.Column{UsersColumns[0]}, + } + // UserGroupsColumns holds the columns for the "user_groups" table. + UserGroupsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "is_primary", Type: field.TypeBool, Default: false}, + {Name: "expires_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"mysql": "datetime"}}, + {Name: "user_id", Type: field.TypeInt}, + {Name: "group_id", Type: field.TypeInt}, + } + // UserGroupsTable holds the schema information for the "user_groups" table. + UserGroupsTable = &schema.Table{ + Name: "user_groups", + Columns: UserGroupsColumns, + PrimaryKey: []*schema.Column{UserGroupsColumns[0]}, ForeignKeys: []*schema.ForeignKey{ { - Symbol: "users_groups_users", - Columns: []*schema.Column{UsersColumns[12]}, + Symbol: "user_groups_users_user", + Columns: []*schema.Column{UserGroupsColumns[3]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.NoAction, + }, + { + Symbol: "user_groups_groups_group", + Columns: []*schema.Column{UserGroupsColumns[4]}, RefColumns: []*schema.Column{GroupsColumns[0]}, OnDelete: schema.NoAction, }, }, + Indexes: []*schema.Index{ + { + Name: "usergroup_group_id_user_id", + Unique: true, + Columns: []*schema.Column{UserGroupsColumns[4], UserGroupsColumns[3]}, + }, + }, } // FileEntitiesColumns holds the columns for the "file_entities" table. FileEntitiesColumns = []*schema.Column{ @@ -454,6 +480,7 @@ var ( StoragePoliciesTable, TasksTable, UsersTable, + UserGroupsTable, FileEntitiesTable, } ) @@ -473,7 +500,8 @@ func init() { SharesTable.ForeignKeys[1].RefTable = UsersTable StoragePoliciesTable.ForeignKeys[0].RefTable = NodesTable TasksTable.ForeignKeys[0].RefTable = UsersTable - UsersTable.ForeignKeys[0].RefTable = GroupsTable + UserGroupsTable.ForeignKeys[0].RefTable = UsersTable + UserGroupsTable.ForeignKeys[1].RefTable = GroupsTable FileEntitiesTable.ForeignKeys[0].RefTable = FilesTable FileEntitiesTable.ForeignKeys[1].RefTable = EntitiesTable } diff --git a/ent/mutation.go b/ent/mutation.go index 2620a599..d404fda6 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -25,6 +25,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" "github.com/go-webauthn/webauthn/webauthn" @@ -53,6 +54,7 @@ const ( TypeStoragePolicy = "StoragePolicy" TypeTask = "Task" TypeUser = "User" + TypeUserGroup = "UserGroup" ) // DavAccountMutation represents an operation that mutates the DavAccount nodes in the graph. @@ -4696,6 +4698,9 @@ type GroupMutation struct { clearedusers bool storage_policies *int clearedstorage_policies bool + user_group map[int]struct{} + removeduser_group map[int]struct{} + cleareduser_group bool done bool oldValue func(context.Context) (*Group, error) predicates []predicate.Group @@ -5324,6 +5329,60 @@ func (m *GroupMutation) ResetStoragePolicies() { m.clearedstorage_policies = false } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by ids. +func (m *GroupMutation) AddUserGroupIDs(ids ...int) { + if m.user_group == nil { + m.user_group = make(map[int]struct{}) + } + for i := range ids { + m.user_group[ids[i]] = struct{}{} + } +} + +// ClearUserGroup clears the "user_group" edge to the UserGroup entity. +func (m *GroupMutation) ClearUserGroup() { + m.cleareduser_group = true +} + +// UserGroupCleared reports if the "user_group" edge to the UserGroup entity was cleared. +func (m *GroupMutation) UserGroupCleared() bool { + return m.cleareduser_group +} + +// RemoveUserGroupIDs removes the "user_group" edge to the UserGroup entity by IDs. +func (m *GroupMutation) RemoveUserGroupIDs(ids ...int) { + if m.removeduser_group == nil { + m.removeduser_group = make(map[int]struct{}) + } + for i := range ids { + delete(m.user_group, ids[i]) + m.removeduser_group[ids[i]] = struct{}{} + } +} + +// RemovedUserGroup returns the removed IDs of the "user_group" edge to the UserGroup entity. +func (m *GroupMutation) RemovedUserGroupIDs() (ids []int) { + for id := range m.removeduser_group { + ids = append(ids, id) + } + return +} + +// UserGroupIDs returns the "user_group" edge IDs in the mutation. +func (m *GroupMutation) UserGroupIDs() (ids []int) { + for id := range m.user_group { + ids = append(ids, id) + } + return +} + +// ResetUserGroup resets all changes to the "user_group" edge. +func (m *GroupMutation) ResetUserGroup() { + m.user_group = nil + m.cleareduser_group = false + m.removeduser_group = nil +} + // Where appends a list predicates to the GroupMutation builder. func (m *GroupMutation) Where(ps ...predicate.Group) { m.predicates = append(m.predicates, ps...) @@ -5653,13 +5712,16 @@ func (m *GroupMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *GroupMutation) AddedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.users != nil { edges = append(edges, group.EdgeUsers) } if m.storage_policies != nil { edges = append(edges, group.EdgeStoragePolicies) } + if m.user_group != nil { + edges = append(edges, group.EdgeUserGroup) + } return edges } @@ -5677,16 +5739,25 @@ func (m *GroupMutation) AddedIDs(name string) []ent.Value { if id := m.storage_policies; id != nil { return []ent.Value{*id} } + case group.EdgeUserGroup: + ids := make([]ent.Value, 0, len(m.user_group)) + for id := range m.user_group { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *GroupMutation) RemovedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.removedusers != nil { edges = append(edges, group.EdgeUsers) } + if m.removeduser_group != nil { + edges = append(edges, group.EdgeUserGroup) + } return edges } @@ -5700,19 +5771,28 @@ func (m *GroupMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case group.EdgeUserGroup: + ids := make([]ent.Value, 0, len(m.removeduser_group)) + for id := range m.removeduser_group { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *GroupMutation) ClearedEdges() []string { - edges := make([]string, 0, 2) + edges := make([]string, 0, 3) if m.clearedusers { edges = append(edges, group.EdgeUsers) } if m.clearedstorage_policies { edges = append(edges, group.EdgeStoragePolicies) } + if m.cleareduser_group { + edges = append(edges, group.EdgeUserGroup) + } return edges } @@ -5724,6 +5804,8 @@ func (m *GroupMutation) EdgeCleared(name string) bool { return m.clearedusers case group.EdgeStoragePolicies: return m.clearedstorage_policies + case group.EdgeUserGroup: + return m.cleareduser_group } return false } @@ -5749,6 +5831,9 @@ func (m *GroupMutation) ResetEdge(name string) error { case group.EdgeStoragePolicies: m.ResetStoragePolicies() return nil + case group.EdgeUserGroup: + m.ResetUserGroup() + return nil } return fmt.Errorf("unknown Group edge %s", name) } @@ -12588,8 +12673,9 @@ type UserMutation struct { avatar *string settings **types.UserSetting clearedFields map[string]struct{} - group *int - clearedgroup bool + groups map[int]struct{} + removedgroups map[int]struct{} + clearedgroups bool files map[int]struct{} removedfiles map[int]struct{} clearedfiles bool @@ -12608,6 +12694,9 @@ type UserMutation struct { entities map[int]struct{} removedentities map[int]struct{} clearedentities bool + user_group map[int]struct{} + removeduser_group map[int]struct{} + cleareduser_group bool done bool oldValue func(context.Context) (*User, error) predicates []predicate.User @@ -13192,80 +13281,58 @@ func (m *UserMutation) ResetSettings() { delete(m.clearedFields, user.FieldSettings) } -// SetGroupUsers sets the "group_users" field. -func (m *UserMutation) SetGroupUsers(i int) { - m.group = &i -} - -// GroupUsers returns the value of the "group_users" field in the mutation. -func (m *UserMutation) GroupUsers() (r int, exists bool) { - v := m.group - if v == nil { - return - } - return *v, true -} - -// OldGroupUsers returns the old "group_users" field's value of the User entity. -// If the User 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 *UserMutation) OldGroupUsers(ctx context.Context) (v int, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldGroupUsers is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldGroupUsers requires an ID field in the mutation") +// AddGroupIDs adds the "groups" edge to the Group entity by ids. +func (m *UserMutation) AddGroupIDs(ids ...int) { + if m.groups == nil { + m.groups = make(map[int]struct{}) } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldGroupUsers: %w", err) + for i := range ids { + m.groups[ids[i]] = struct{}{} } - return oldValue.GroupUsers, nil } -// ResetGroupUsers resets all changes to the "group_users" field. -func (m *UserMutation) ResetGroupUsers() { - m.group = nil -} - -// SetGroupID sets the "group" edge to the Group entity by id. -func (m *UserMutation) SetGroupID(id int) { - m.group = &id +// ClearGroups clears the "groups" edge to the Group entity. +func (m *UserMutation) ClearGroups() { + m.clearedgroups = true } -// ClearGroup clears the "group" edge to the Group entity. -func (m *UserMutation) ClearGroup() { - m.clearedgroup = true - m.clearedFields[user.FieldGroupUsers] = struct{}{} +// GroupsCleared reports if the "groups" edge to the Group entity was cleared. +func (m *UserMutation) GroupsCleared() bool { + return m.clearedgroups } -// GroupCleared reports if the "group" edge to the Group entity was cleared. -func (m *UserMutation) GroupCleared() bool { - return m.clearedgroup +// RemoveGroupIDs removes the "groups" edge to the Group entity by IDs. +func (m *UserMutation) RemoveGroupIDs(ids ...int) { + if m.removedgroups == nil { + m.removedgroups = make(map[int]struct{}) + } + for i := range ids { + delete(m.groups, ids[i]) + m.removedgroups[ids[i]] = struct{}{} + } } -// GroupID returns the "group" edge ID in the mutation. -func (m *UserMutation) GroupID() (id int, exists bool) { - if m.group != nil { - return *m.group, true +// RemovedGroups returns the removed IDs of the "groups" edge to the Group entity. +func (m *UserMutation) RemovedGroupsIDs() (ids []int) { + for id := range m.removedgroups { + ids = append(ids, id) } return } -// GroupIDs returns the "group" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// GroupID instead. It exists only for internal usage by the builders. -func (m *UserMutation) GroupIDs() (ids []int) { - if id := m.group; id != nil { - ids = append(ids, *id) +// GroupsIDs returns the "groups" edge IDs in the mutation. +func (m *UserMutation) GroupsIDs() (ids []int) { + for id := range m.groups { + ids = append(ids, id) } return } -// ResetGroup resets all changes to the "group" edge. -func (m *UserMutation) ResetGroup() { - m.group = nil - m.clearedgroup = false +// ResetGroups resets all changes to the "groups" edge. +func (m *UserMutation) ResetGroups() { + m.groups = nil + m.clearedgroups = false + m.removedgroups = nil } // AddFileIDs adds the "files" edge to the File entity by ids. @@ -13592,6 +13659,60 @@ func (m *UserMutation) ResetEntities() { m.removedentities = nil } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by ids. +func (m *UserMutation) AddUserGroupIDs(ids ...int) { + if m.user_group == nil { + m.user_group = make(map[int]struct{}) + } + for i := range ids { + m.user_group[ids[i]] = struct{}{} + } +} + +// ClearUserGroup clears the "user_group" edge to the UserGroup entity. +func (m *UserMutation) ClearUserGroup() { + m.cleareduser_group = true +} + +// UserGroupCleared reports if the "user_group" edge to the UserGroup entity was cleared. +func (m *UserMutation) UserGroupCleared() bool { + return m.cleareduser_group +} + +// RemoveUserGroupIDs removes the "user_group" edge to the UserGroup entity by IDs. +func (m *UserMutation) RemoveUserGroupIDs(ids ...int) { + if m.removeduser_group == nil { + m.removeduser_group = make(map[int]struct{}) + } + for i := range ids { + delete(m.user_group, ids[i]) + m.removeduser_group[ids[i]] = struct{}{} + } +} + +// RemovedUserGroup returns the removed IDs of the "user_group" edge to the UserGroup entity. +func (m *UserMutation) RemovedUserGroupIDs() (ids []int) { + for id := range m.removeduser_group { + ids = append(ids, id) + } + return +} + +// UserGroupIDs returns the "user_group" edge IDs in the mutation. +func (m *UserMutation) UserGroupIDs() (ids []int) { + for id := range m.user_group { + ids = append(ids, id) + } + return +} + +// ResetUserGroup resets all changes to the "user_group" edge. +func (m *UserMutation) ResetUserGroup() { + m.user_group = nil + m.cleareduser_group = false + m.removeduser_group = nil +} + // Where appends a list predicates to the UserMutation builder. func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) @@ -13626,7 +13747,7 @@ func (m *UserMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *UserMutation) Fields() []string { - fields := make([]string, 0, 12) + fields := make([]string, 0, 11) if m.created_at != nil { fields = append(fields, user.FieldCreatedAt) } @@ -13660,9 +13781,6 @@ func (m *UserMutation) Fields() []string { if m.settings != nil { fields = append(fields, user.FieldSettings) } - if m.group != nil { - fields = append(fields, user.FieldGroupUsers) - } return fields } @@ -13693,8 +13811,6 @@ func (m *UserMutation) Field(name string) (ent.Value, bool) { return m.Avatar() case user.FieldSettings: return m.Settings() - case user.FieldGroupUsers: - return m.GroupUsers() } return nil, false } @@ -13726,8 +13842,6 @@ func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, er return m.OldAvatar(ctx) case user.FieldSettings: return m.OldSettings(ctx) - case user.FieldGroupUsers: - return m.OldGroupUsers(ctx) } return nil, fmt.Errorf("unknown User field %s", name) } @@ -13814,13 +13928,6 @@ func (m *UserMutation) SetField(name string, value ent.Value) error { } m.SetSettings(v) return nil - case user.FieldGroupUsers: - v, ok := value.(int) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetGroupUsers(v) - return nil } return fmt.Errorf("unknown User field %s", name) } @@ -13951,18 +14058,15 @@ func (m *UserMutation) ResetField(name string) error { case user.FieldSettings: m.ResetSettings() return nil - case user.FieldGroupUsers: - m.ResetGroupUsers() - return nil } return fmt.Errorf("unknown User field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 7) - if m.group != nil { - edges = append(edges, user.EdgeGroup) + edges := make([]string, 0, 8) + if m.groups != nil { + edges = append(edges, user.EdgeGroups) } if m.files != nil { edges = append(edges, user.EdgeFiles) @@ -13982,6 +14086,9 @@ func (m *UserMutation) AddedEdges() []string { if m.entities != nil { edges = append(edges, user.EdgeEntities) } + if m.user_group != nil { + edges = append(edges, user.EdgeUserGroup) + } return edges } @@ -13989,10 +14096,12 @@ func (m *UserMutation) AddedEdges() []string { // name in this mutation. func (m *UserMutation) AddedIDs(name string) []ent.Value { switch name { - case user.EdgeGroup: - if id := m.group; id != nil { - return []ent.Value{*id} + case user.EdgeGroups: + ids := make([]ent.Value, 0, len(m.groups)) + for id := range m.groups { + ids = append(ids, id) } + return ids case user.EdgeFiles: ids := make([]ent.Value, 0, len(m.files)) for id := range m.files { @@ -14029,13 +14138,22 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeUserGroup: + ids := make([]ent.Value, 0, len(m.user_group)) + for id := range m.user_group { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) + if m.removedgroups != nil { + edges = append(edges, user.EdgeGroups) + } if m.removedfiles != nil { edges = append(edges, user.EdgeFiles) } @@ -14054,6 +14172,9 @@ func (m *UserMutation) RemovedEdges() []string { if m.removedentities != nil { edges = append(edges, user.EdgeEntities) } + if m.removeduser_group != nil { + edges = append(edges, user.EdgeUserGroup) + } return edges } @@ -14061,6 +14182,12 @@ func (m *UserMutation) RemovedEdges() []string { // the given name in this mutation. func (m *UserMutation) RemovedIDs(name string) []ent.Value { switch name { + case user.EdgeGroups: + ids := make([]ent.Value, 0, len(m.removedgroups)) + for id := range m.removedgroups { + ids = append(ids, id) + } + return ids case user.EdgeFiles: ids := make([]ent.Value, 0, len(m.removedfiles)) for id := range m.removedfiles { @@ -14097,15 +14224,21 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeUserGroup: + ids := make([]ent.Value, 0, len(m.removeduser_group)) + for id := range m.removeduser_group { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 7) - if m.clearedgroup { - edges = append(edges, user.EdgeGroup) + edges := make([]string, 0, 8) + if m.clearedgroups { + edges = append(edges, user.EdgeGroups) } if m.clearedfiles { edges = append(edges, user.EdgeFiles) @@ -14125,6 +14258,9 @@ func (m *UserMutation) ClearedEdges() []string { if m.clearedentities { edges = append(edges, user.EdgeEntities) } + if m.cleareduser_group { + edges = append(edges, user.EdgeUserGroup) + } return edges } @@ -14132,8 +14268,8 @@ func (m *UserMutation) ClearedEdges() []string { // was cleared in this mutation. func (m *UserMutation) EdgeCleared(name string) bool { switch name { - case user.EdgeGroup: - return m.clearedgroup + case user.EdgeGroups: + return m.clearedgroups case user.EdgeFiles: return m.clearedfiles case user.EdgeDavAccounts: @@ -14146,6 +14282,8 @@ func (m *UserMutation) EdgeCleared(name string) bool { return m.clearedtasks case user.EdgeEntities: return m.clearedentities + case user.EdgeUserGroup: + return m.cleareduser_group } return false } @@ -14154,9 +14292,6 @@ func (m *UserMutation) EdgeCleared(name string) bool { // if that edge is not defined in the schema. func (m *UserMutation) ClearEdge(name string) error { switch name { - case user.EdgeGroup: - m.ClearGroup() - return nil } return fmt.Errorf("unknown User unique edge %s", name) } @@ -14165,8 +14300,8 @@ func (m *UserMutation) ClearEdge(name string) error { // It returns an error if the edge is not defined in the schema. func (m *UserMutation) ResetEdge(name string) error { switch name { - case user.EdgeGroup: - m.ResetGroup() + case user.EdgeGroups: + m.ResetGroups() return nil case user.EdgeFiles: m.ResetFiles() @@ -14186,6 +14321,622 @@ func (m *UserMutation) ResetEdge(name string) error { case user.EdgeEntities: m.ResetEntities() return nil + case user.EdgeUserGroup: + m.ResetUserGroup() + return nil } return fmt.Errorf("unknown User edge %s", name) } + +// UserGroupMutation represents an operation that mutates the UserGroup nodes in the graph. +type UserGroupMutation struct { + config + op Op + typ string + id *int + is_primary *bool + expires_at *time.Time + clearedFields map[string]struct{} + user *int + cleareduser bool + group *int + clearedgroup bool + done bool + oldValue func(context.Context) (*UserGroup, error) + predicates []predicate.UserGroup +} + +var _ ent.Mutation = (*UserGroupMutation)(nil) + +// usergroupOption allows management of the mutation configuration using functional options. +type usergroupOption func(*UserGroupMutation) + +// newUserGroupMutation creates new mutation for the UserGroup entity. +func newUserGroupMutation(c config, op Op, opts ...usergroupOption) *UserGroupMutation { + m := &UserGroupMutation{ + config: c, + op: op, + typ: TypeUserGroup, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withUserGroupID sets the ID field of the mutation. +func withUserGroupID(id int) usergroupOption { + return func(m *UserGroupMutation) { + var ( + err error + once sync.Once + value *UserGroup + ) + m.oldValue = func(ctx context.Context) (*UserGroup, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().UserGroup.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withUserGroup sets the old UserGroup of the mutation. +func withUserGroup(node *UserGroup) usergroupOption { + return func(m *UserGroupMutation) { + m.oldValue = func(context.Context) (*UserGroup, 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 UserGroupMutation) 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 UserGroupMutation) 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 *UserGroupMutation) ID() (id int, 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 *UserGroupMutation) IDs(ctx context.Context) ([]int, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().UserGroup.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetUserID sets the "user_id" field. +func (m *UserGroupMutation) SetUserID(i int) { + m.user = &i +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *UserGroupMutation) UserID() (r int, exists bool) { + v := m.user + if v == nil { + return + } + return *v, true +} + +// OldUserID returns the old "user_id" field's value of the UserGroup entity. +// If the UserGroup 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 *UserGroupMutation) OldUserID(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserID: %w", err) + } + return oldValue.UserID, nil +} + +// ResetUserID resets all changes to the "user_id" field. +func (m *UserGroupMutation) ResetUserID() { + m.user = nil +} + +// SetGroupID sets the "group_id" field. +func (m *UserGroupMutation) SetGroupID(i int) { + m.group = &i +} + +// GroupID returns the value of the "group_id" field in the mutation. +func (m *UserGroupMutation) GroupID() (r int, exists bool) { + v := m.group + if v == nil { + return + } + return *v, true +} + +// OldGroupID returns the old "group_id" field's value of the UserGroup entity. +// If the UserGroup 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 *UserGroupMutation) OldGroupID(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGroupID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGroupID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGroupID: %w", err) + } + return oldValue.GroupID, nil +} + +// ResetGroupID resets all changes to the "group_id" field. +func (m *UserGroupMutation) ResetGroupID() { + m.group = nil +} + +// SetIsPrimary sets the "is_primary" field. +func (m *UserGroupMutation) SetIsPrimary(b bool) { + m.is_primary = &b +} + +// IsPrimary returns the value of the "is_primary" field in the mutation. +func (m *UserGroupMutation) IsPrimary() (r bool, exists bool) { + v := m.is_primary + if v == nil { + return + } + return *v, true +} + +// OldIsPrimary returns the old "is_primary" field's value of the UserGroup entity. +// If the UserGroup 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 *UserGroupMutation) OldIsPrimary(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldIsPrimary is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldIsPrimary requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldIsPrimary: %w", err) + } + return oldValue.IsPrimary, nil +} + +// ResetIsPrimary resets all changes to the "is_primary" field. +func (m *UserGroupMutation) ResetIsPrimary() { + m.is_primary = nil +} + +// SetExpiresAt sets the "expires_at" field. +func (m *UserGroupMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t +} + +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *UserGroupMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return + } + return *v, true +} + +// OldExpiresAt returns the old "expires_at" field's value of the UserGroup entity. +// If the UserGroup 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 *UserGroupMutation) OldExpiresAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (m *UserGroupMutation) ClearExpiresAt() { + m.expires_at = nil + m.clearedFields[usergroup.FieldExpiresAt] = struct{}{} +} + +// ExpiresAtCleared returns if the "expires_at" field was cleared in this mutation. +func (m *UserGroupMutation) ExpiresAtCleared() bool { + _, ok := m.clearedFields[usergroup.FieldExpiresAt] + return ok +} + +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *UserGroupMutation) ResetExpiresAt() { + m.expires_at = nil + delete(m.clearedFields, usergroup.FieldExpiresAt) +} + +// ClearUser clears the "user" edge to the User entity. +func (m *UserGroupMutation) ClearUser() { + m.cleareduser = true + m.clearedFields[usergroup.FieldUserID] = struct{}{} +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *UserGroupMutation) UserCleared() bool { + return m.cleareduser +} + +// UserIDs returns the "user" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UserID instead. It exists only for internal usage by the builders. +func (m *UserGroupMutation) UserIDs() (ids []int) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *UserGroupMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// ClearGroup clears the "group" edge to the Group entity. +func (m *UserGroupMutation) ClearGroup() { + m.clearedgroup = true + m.clearedFields[usergroup.FieldGroupID] = struct{}{} +} + +// GroupCleared reports if the "group" edge to the Group entity was cleared. +func (m *UserGroupMutation) GroupCleared() bool { + return m.clearedgroup +} + +// GroupIDs returns the "group" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// GroupID instead. It exists only for internal usage by the builders. +func (m *UserGroupMutation) GroupIDs() (ids []int) { + if id := m.group; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetGroup resets all changes to the "group" edge. +func (m *UserGroupMutation) ResetGroup() { + m.group = nil + m.clearedgroup = false +} + +// Where appends a list predicates to the UserGroupMutation builder. +func (m *UserGroupMutation) Where(ps ...predicate.UserGroup) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the UserGroupMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *UserGroupMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.UserGroup, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *UserGroupMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *UserGroupMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (UserGroup). +func (m *UserGroupMutation) 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 *UserGroupMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.user != nil { + fields = append(fields, usergroup.FieldUserID) + } + if m.group != nil { + fields = append(fields, usergroup.FieldGroupID) + } + if m.is_primary != nil { + fields = append(fields, usergroup.FieldIsPrimary) + } + if m.expires_at != nil { + fields = append(fields, usergroup.FieldExpiresAt) + } + 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 *UserGroupMutation) Field(name string) (ent.Value, bool) { + switch name { + case usergroup.FieldUserID: + return m.UserID() + case usergroup.FieldGroupID: + return m.GroupID() + case usergroup.FieldIsPrimary: + return m.IsPrimary() + case usergroup.FieldExpiresAt: + return m.ExpiresAt() + } + 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 *UserGroupMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case usergroup.FieldUserID: + return m.OldUserID(ctx) + case usergroup.FieldGroupID: + return m.OldGroupID(ctx) + case usergroup.FieldIsPrimary: + return m.OldIsPrimary(ctx) + case usergroup.FieldExpiresAt: + return m.OldExpiresAt(ctx) + } + return nil, fmt.Errorf("unknown UserGroup 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 *UserGroupMutation) SetField(name string, value ent.Value) error { + switch name { + case usergroup.FieldUserID: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case usergroup.FieldGroupID: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGroupID(v) + return nil + case usergroup.FieldIsPrimary: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetIsPrimary(v) + return nil + case usergroup.FieldExpiresAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpiresAt(v) + return nil + } + return fmt.Errorf("unknown UserGroup field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *UserGroupMutation) AddedFields() []string { + var fields []string + return fields +} + +// 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 *UserGroupMutation) AddedField(name string) (ent.Value, bool) { + switch name { + } + 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 *UserGroupMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown UserGroup numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *UserGroupMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(usergroup.FieldExpiresAt) { + fields = append(fields, usergroup.FieldExpiresAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *UserGroupMutation) 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 *UserGroupMutation) ClearField(name string) error { + switch name { + case usergroup.FieldExpiresAt: + m.ClearExpiresAt() + return nil + } + return fmt.Errorf("unknown UserGroup 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 *UserGroupMutation) ResetField(name string) error { + switch name { + case usergroup.FieldUserID: + m.ResetUserID() + return nil + case usergroup.FieldGroupID: + m.ResetGroupID() + return nil + case usergroup.FieldIsPrimary: + m.ResetIsPrimary() + return nil + case usergroup.FieldExpiresAt: + m.ResetExpiresAt() + return nil + } + return fmt.Errorf("unknown UserGroup field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *UserGroupMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.user != nil { + edges = append(edges, usergroup.EdgeUser) + } + if m.group != nil { + edges = append(edges, usergroup.EdgeGroup) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *UserGroupMutation) AddedIDs(name string) []ent.Value { + switch name { + case usergroup.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + case usergroup.EdgeGroup: + if id := m.group; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *UserGroupMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *UserGroupMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *UserGroupMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.cleareduser { + edges = append(edges, usergroup.EdgeUser) + } + if m.clearedgroup { + edges = append(edges, usergroup.EdgeGroup) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *UserGroupMutation) EdgeCleared(name string) bool { + switch name { + case usergroup.EdgeUser: + return m.cleareduser + case usergroup.EdgeGroup: + return m.clearedgroup + } + 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 *UserGroupMutation) ClearEdge(name string) error { + switch name { + case usergroup.EdgeUser: + m.ClearUser() + return nil + case usergroup.EdgeGroup: + m.ClearGroup() + return nil + } + return fmt.Errorf("unknown UserGroup 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 *UserGroupMutation) ResetEdge(name string) error { + switch name { + case usergroup.EdgeUser: + m.ResetUser() + return nil + case usergroup.EdgeGroup: + m.ResetGroup() + return nil + } + return fmt.Errorf("unknown UserGroup edge %s", name) +} diff --git a/ent/mutationhelper.go b/ent/mutationhelper.go index 86d1931f..58a22ba5 100644 --- a/ent/mutationhelper.go +++ b/ent/mutationhelper.go @@ -79,3 +79,9 @@ func (m *TaskMutation) SetRawID(t int) { func (m *UserMutation) SetRawID(t int) { m.id = &t } + +// SetUpdatedAt sets the "updated_at" field. + +func (m *UserGroupMutation) SetRawID(t int) { + m.id = &t +} diff --git a/ent/predicate/predicate.go b/ent/predicate/predicate.go index 3ee71805..5ddc906e 100644 --- a/ent/predicate/predicate.go +++ b/ent/predicate/predicate.go @@ -44,3 +44,6 @@ type Task func(*sql.Selector) // User is the predicate function for user builders. type User func(*sql.Selector) + +// UserGroup is the predicate function for usergroup builders. +type UserGroup func(*sql.Selector) diff --git a/ent/runtime/runtime.go b/ent/runtime/runtime.go index cdc8fa56..d2b26277 100644 --- a/ent/runtime/runtime.go +++ b/ent/runtime/runtime.go @@ -19,6 +19,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" "github.com/cloudreve/Cloudreve/v4/inventory/types" ) @@ -329,6 +330,12 @@ func init() { userDescSettings := userFields[7].Descriptor() // user.DefaultSettings holds the default value on creation for the settings field. user.DefaultSettings = userDescSettings.Default.(*types.UserSetting) + usergroupFields := schema.UserGroup{}.Fields() + _ = usergroupFields + // usergroupDescIsPrimary is the schema descriptor for is_primary field. + usergroupDescIsPrimary := usergroupFields[2].Descriptor() + // usergroup.DefaultIsPrimary holds the default value on creation for the is_primary field. + usergroup.DefaultIsPrimary = usergroupDescIsPrimary.Default.(bool) } const ( diff --git a/ent/schema/group.go b/ent/schema/group.go index ca1613b8..d4935b79 100644 --- a/ent/schema/group.go +++ b/ent/schema/group.go @@ -36,7 +36,8 @@ func (Group) Mixin() []ent.Mixin { func (Group) Edges() []ent.Edge { return []ent.Edge{ - edge.To("users", User.Type), + edge.To("users", User.Type). + Through("user_group", UserGroup.Type), edge.From("storage_policies", StoragePolicy.Type). Ref("groups"). Field("storage_policy_id"). diff --git a/ent/schema/user.go b/ent/schema/user.go index 23cba9c7..212d5b45 100644 --- a/ent/schema/user.go +++ b/ent/schema/user.go @@ -35,17 +35,14 @@ func (User) Fields() []ent.Field { field.JSON("settings", &types.UserSetting{}). Default(&types.UserSetting{}). Optional(), - field.Int("group_users"), } } func (User) Edges() []ent.Edge { return []ent.Edge{ - edge.From("group", Group.Type). + edge.From("groups", Group.Type). Ref("users"). - Field("group_users"). - Unique(). - Required(), + Through("user_group", UserGroup.Type), edge.To("files", File.Type), edge.To("dav_accounts", DavAccount.Type), edge.To("shares", Share.Type), diff --git a/ent/schema/user_group.go b/ent/schema/user_group.go new file mode 100644 index 00000000..1b1054de --- /dev/null +++ b/ent/schema/user_group.go @@ -0,0 +1,40 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" +) + +type UserGroup struct { + ent.Schema +} + +func (UserGroup) Fields() []ent.Field { + return []ent.Field{ + field.Int("user_id"), + field.Int("group_id"), + field.Bool("is_primary"). + Default(false), + field.Time("expires_at"). + Optional(). + Nillable(). + SchemaType(map[string]string{ + dialect.MySQL: "datetime", + }), + } +} + +func (UserGroup) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("user", User.Type). + Required(). + Unique(). + Field("user_id"), + edge.To("group", Group.Type). + Required(). + Unique(). + Field("group_id"), + } +} diff --git a/ent/tx.go b/ent/tx.go index 98f9b540..734634c4 100644 --- a/ent/tx.go +++ b/ent/tx.go @@ -40,6 +40,8 @@ type Tx struct { Task *TaskClient // User is the client for interacting with the User builders. User *UserClient + // UserGroup is the client for interacting with the UserGroup builders. + UserGroup *UserGroupClient // lazily loaded. client *Client @@ -184,6 +186,7 @@ func (tx *Tx) init() { tx.StoragePolicy = NewStoragePolicyClient(tx.config) tx.Task = NewTaskClient(tx.config) tx.User = NewUserClient(tx.config) + tx.UserGroup = NewUserGroupClient(tx.config) } // txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. diff --git a/ent/user.go b/ent/user.go index 8b361f64..d82f7c1d 100644 --- a/ent/user.go +++ b/ent/user.go @@ -10,7 +10,6 @@ import ( "entgo.io/ent" "entgo.io/ent/dialect/sql" - "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/inventory/types" ) @@ -42,8 +41,6 @@ type User struct { Avatar string `json:"avatar,omitempty"` // Settings holds the value of the "settings" field. Settings *types.UserSetting `json:"settings,omitempty"` - // GroupUsers holds the value of the "group_users" field. - GroupUsers int `json:"group_users,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the UserQuery when eager-loading is set. Edges UserEdges `json:"edges"` @@ -52,8 +49,8 @@ type User struct { // UserEdges holds the relations/edges for other nodes in the graph. type UserEdges struct { - // Group holds the value of the group edge. - Group *Group `json:"group,omitempty"` + // Groups holds the value of the groups edge. + Groups []*Group `json:"groups,omitempty"` // Files holds the value of the files edge. Files []*File `json:"files,omitempty"` // DavAccounts holds the value of the dav_accounts edge. @@ -66,22 +63,20 @@ type UserEdges struct { Tasks []*Task `json:"tasks,omitempty"` // Entities holds the value of the entities edge. Entities []*Entity `json:"entities,omitempty"` + // UserGroup holds the value of the user_group edge. + UserGroup []*UserGroup `json:"user_group,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [7]bool + loadedTypes [8]bool } -// GroupOrErr returns the Group value or an error if the edge -// was not loaded in eager-loading, or loaded but was not found. -func (e UserEdges) GroupOrErr() (*Group, error) { +// GroupsOrErr returns the Groups value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) GroupsOrErr() ([]*Group, error) { if e.loadedTypes[0] { - if e.Group == nil { - // Edge was loaded but was not found. - return nil, &NotFoundError{label: group.Label} - } - return e.Group, nil + return e.Groups, nil } - return nil, &NotLoadedError{edge: "group"} + return nil, &NotLoadedError{edge: "groups"} } // FilesOrErr returns the Files value or an error if the edge @@ -138,6 +133,15 @@ func (e UserEdges) EntitiesOrErr() ([]*Entity, error) { return nil, &NotLoadedError{edge: "entities"} } +// UserGroupOrErr returns the UserGroup value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) UserGroupOrErr() ([]*UserGroup, error) { + if e.loadedTypes[7] { + return e.UserGroup, nil + } + return nil, &NotLoadedError{edge: "user_group"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -145,7 +149,7 @@ func (*User) scanValues(columns []string) ([]any, error) { switch columns[i] { case user.FieldSettings: values[i] = new([]byte) - case user.FieldID, user.FieldStorage, user.FieldGroupUsers: + case user.FieldID, user.FieldStorage: values[i] = new(sql.NullInt64) case user.FieldEmail, user.FieldNick, user.FieldPassword, user.FieldStatus, user.FieldTwoFactorSecret, user.FieldAvatar: values[i] = new(sql.NullString) @@ -241,12 +245,6 @@ func (u *User) assignValues(columns []string, values []any) error { return fmt.Errorf("unmarshal field settings: %w", err) } } - case user.FieldGroupUsers: - if value, ok := values[i].(*sql.NullInt64); !ok { - return fmt.Errorf("unexpected type %T for field group_users", values[i]) - } else if value.Valid { - u.GroupUsers = int(value.Int64) - } default: u.selectValues.Set(columns[i], values[i]) } @@ -260,9 +258,9 @@ func (u *User) Value(name string) (ent.Value, error) { return u.selectValues.Get(name) } -// QueryGroup queries the "group" edge of the User entity. -func (u *User) QueryGroup() *GroupQuery { - return NewUserClient(u.config).QueryGroup(u) +// QueryGroups queries the "groups" edge of the User entity. +func (u *User) QueryGroups() *GroupQuery { + return NewUserClient(u.config).QueryGroups(u) } // QueryFiles queries the "files" edge of the User entity. @@ -295,6 +293,11 @@ func (u *User) QueryEntities() *EntityQuery { return NewUserClient(u.config).QueryEntities(u) } +// QueryUserGroup queries the "user_group" edge of the User entity. +func (u *User) QueryUserGroup() *UserGroupQuery { + return NewUserClient(u.config).QueryUserGroup(u) +} + // Update returns a builder for updating this User. // Note that you need to call User.Unwrap() before calling this method if this User // was returned from a transaction, and the transaction was committed or rolled back. @@ -350,16 +353,13 @@ func (u *User) String() string { builder.WriteString(", ") builder.WriteString("settings=") builder.WriteString(fmt.Sprintf("%v", u.Settings)) - builder.WriteString(", ") - builder.WriteString("group_users=") - builder.WriteString(fmt.Sprintf("%v", u.GroupUsers)) builder.WriteByte(')') return builder.String() } -// SetGroup manually set the edge as loaded state. -func (e *User) SetGroup(v *Group) { - e.Edges.Group = v +// SetGroups manually set the edge as loaded state. +func (e *User) SetGroups(v []*Group) { + e.Edges.Groups = v e.Edges.loadedTypes[0] = true } @@ -399,5 +399,11 @@ func (e *User) SetEntities(v []*Entity) { e.Edges.loadedTypes[6] = true } +// SetUserGroup manually set the edge as loaded state. +func (e *User) SetUserGroup(v []*UserGroup) { + e.Edges.UserGroup = v + e.Edges.loadedTypes[7] = true +} + // Users is a parsable slice of User. type Users []*User diff --git a/ent/user/user.go b/ent/user/user.go index 8ff28fbf..b7df51e4 100644 --- a/ent/user/user.go +++ b/ent/user/user.go @@ -39,10 +39,8 @@ const ( FieldAvatar = "avatar" // FieldSettings holds the string denoting the settings field in the database. FieldSettings = "settings" - // FieldGroupUsers holds the string denoting the group_users field in the database. - FieldGroupUsers = "group_users" - // EdgeGroup holds the string denoting the group edge name in mutations. - EdgeGroup = "group" + // EdgeGroups holds the string denoting the groups edge name in mutations. + EdgeGroups = "groups" // EdgeFiles holds the string denoting the files edge name in mutations. EdgeFiles = "files" // EdgeDavAccounts holds the string denoting the dav_accounts edge name in mutations. @@ -55,15 +53,15 @@ const ( EdgeTasks = "tasks" // EdgeEntities holds the string denoting the entities edge name in mutations. EdgeEntities = "entities" + // EdgeUserGroup holds the string denoting the user_group edge name in mutations. + EdgeUserGroup = "user_group" // Table holds the table name of the user in the database. Table = "users" - // GroupTable is the table that holds the group relation/edge. - GroupTable = "users" - // GroupInverseTable is the table name for the Group entity. + // GroupsTable is the table that holds the groups relation/edge. The primary key declared below. + GroupsTable = "user_groups" + // GroupsInverseTable is the table name for the Group entity. // It exists in this package in order to avoid circular dependency with the "group" package. - GroupInverseTable = "groups" - // GroupColumn is the table column denoting the group relation/edge. - GroupColumn = "group_users" + GroupsInverseTable = "groups" // FilesTable is the table that holds the files relation/edge. FilesTable = "files" // FilesInverseTable is the table name for the File entity. @@ -106,6 +104,13 @@ const ( EntitiesInverseTable = "entities" // EntitiesColumn is the table column denoting the entities relation/edge. EntitiesColumn = "created_by" + // UserGroupTable is the table that holds the user_group relation/edge. + UserGroupTable = "user_groups" + // UserGroupInverseTable is the table name for the UserGroup entity. + // It exists in this package in order to avoid circular dependency with the "usergroup" package. + UserGroupInverseTable = "user_groups" + // UserGroupColumn is the table column denoting the user_group relation/edge. + UserGroupColumn = "user_id" ) // Columns holds all SQL columns for user fields. @@ -122,9 +127,14 @@ var Columns = []string{ FieldTwoFactorSecret, FieldAvatar, FieldSettings, - FieldGroupUsers, } +var ( + // GroupsPrimaryKey and GroupsColumn2 are the table columns denoting the + // primary key for the groups relation (M2M). + GroupsPrimaryKey = []string{"group_id", "user_id"} +) + // ValidColumn reports if the column name is valid (part of the table columns). func ValidColumn(column string) bool { for i := range Columns { @@ -245,15 +255,17 @@ func ByAvatar(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldAvatar, opts...).ToFunc() } -// ByGroupUsers orders the results by the group_users field. -func ByGroupUsers(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldGroupUsers, opts...).ToFunc() +// ByGroupsCount orders the results by groups count. +func ByGroupsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newGroupsStep(), opts...) + } } -// ByGroupField orders the results by group field. -func ByGroupField(field string, opts ...sql.OrderTermOption) OrderOption { +// ByGroups orders the results by groups terms. +func ByGroups(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { return func(s *sql.Selector) { - sqlgraph.OrderByNeighborTerms(s, newGroupStep(), sql.OrderByField(field, opts...)) + sqlgraph.OrderByNeighborTerms(s, newGroupsStep(), append([]sql.OrderTerm{term}, terms...)...) } } @@ -340,11 +352,25 @@ func ByEntities(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { sqlgraph.OrderByNeighborTerms(s, newEntitiesStep(), append([]sql.OrderTerm{term}, terms...)...) } } -func newGroupStep() *sqlgraph.Step { + +// ByUserGroupCount orders the results by user_group count. +func ByUserGroupCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newUserGroupStep(), opts...) + } +} + +// ByUserGroup orders the results by user_group terms. +func ByUserGroup(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newUserGroupStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newGroupsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), - sqlgraph.To(GroupInverseTable, FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn), + sqlgraph.To(GroupsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, GroupsTable, GroupsPrimaryKey...), ) } func newFilesStep() *sqlgraph.Step { @@ -389,3 +415,10 @@ func newEntitiesStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, EntitiesTable, EntitiesColumn), ) } +func newUserGroupStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserGroupInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, UserGroupTable, UserGroupColumn), + ) +} diff --git a/ent/user/where.go b/ent/user/where.go index 5400662a..cda1e09c 100644 --- a/ent/user/where.go +++ b/ent/user/where.go @@ -100,11 +100,6 @@ func Avatar(v string) predicate.User { return predicate.User(sql.FieldEQ(FieldAvatar, v)) } -// GroupUsers applies equality check predicate on the "group_users" field. It's identical to GroupUsersEQ. -func GroupUsers(v int) predicate.User { - return predicate.User(sql.FieldEQ(FieldGroupUsers, v)) -} - // CreatedAtEQ applies the EQ predicate on the "created_at" field. func CreatedAtEQ(v time.Time) predicate.User { return predicate.User(sql.FieldEQ(FieldCreatedAt, v)) @@ -660,41 +655,21 @@ func SettingsNotNil() predicate.User { return predicate.User(sql.FieldNotNull(FieldSettings)) } -// GroupUsersEQ applies the EQ predicate on the "group_users" field. -func GroupUsersEQ(v int) predicate.User { - return predicate.User(sql.FieldEQ(FieldGroupUsers, v)) -} - -// GroupUsersNEQ applies the NEQ predicate on the "group_users" field. -func GroupUsersNEQ(v int) predicate.User { - return predicate.User(sql.FieldNEQ(FieldGroupUsers, v)) -} - -// GroupUsersIn applies the In predicate on the "group_users" field. -func GroupUsersIn(vs ...int) predicate.User { - return predicate.User(sql.FieldIn(FieldGroupUsers, vs...)) -} - -// GroupUsersNotIn applies the NotIn predicate on the "group_users" field. -func GroupUsersNotIn(vs ...int) predicate.User { - return predicate.User(sql.FieldNotIn(FieldGroupUsers, vs...)) -} - -// HasGroup applies the HasEdge predicate on the "group" edge. -func HasGroup() predicate.User { +// HasGroups applies the HasEdge predicate on the "groups" edge. +func HasGroups() predicate.User { return predicate.User(func(s *sql.Selector) { step := sqlgraph.NewStep( sqlgraph.From(Table, FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn), + sqlgraph.Edge(sqlgraph.M2M, true, GroupsTable, GroupsPrimaryKey...), ) sqlgraph.HasNeighbors(s, step) }) } -// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates). -func HasGroupWith(preds ...predicate.Group) predicate.User { +// HasGroupsWith applies the HasEdge predicate on the "groups" edge with a given conditions (other predicates). +func HasGroupsWith(preds ...predicate.Group) predicate.User { return predicate.User(func(s *sql.Selector) { - step := newGroupStep() + step := newGroupsStep() sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { for _, p := range preds { p(s) @@ -841,6 +816,29 @@ func HasEntitiesWith(preds ...predicate.Entity) predicate.User { }) } +// HasUserGroup applies the HasEdge predicate on the "user_group" edge. +func HasUserGroup() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, UserGroupTable, UserGroupColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUserGroupWith applies the HasEdge predicate on the "user_group" edge with a given conditions (other predicates). +func HasUserGroupWith(preds ...predicate.UserGroup) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newUserGroupStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User(sql.AndPredicates(predicates...)) diff --git a/ent/user_create.go b/ent/user_create.go index 4e5f9278..39ac9d34 100644 --- a/ent/user_create.go +++ b/ent/user_create.go @@ -19,6 +19,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/share" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" "github.com/cloudreve/Cloudreve/v4/inventory/types" ) @@ -160,21 +161,19 @@ func (uc *UserCreate) SetSettings(ts *types.UserSetting) *UserCreate { return uc } -// SetGroupUsers sets the "group_users" field. -func (uc *UserCreate) SetGroupUsers(i int) *UserCreate { - uc.mutation.SetGroupUsers(i) +// AddGroupIDs adds the "groups" edge to the Group entity by IDs. +func (uc *UserCreate) AddGroupIDs(ids ...int) *UserCreate { + uc.mutation.AddGroupIDs(ids...) return uc } -// SetGroupID sets the "group" edge to the Group entity by ID. -func (uc *UserCreate) SetGroupID(id int) *UserCreate { - uc.mutation.SetGroupID(id) - return uc -} - -// SetGroup sets the "group" edge to the Group entity. -func (uc *UserCreate) SetGroup(g *Group) *UserCreate { - return uc.SetGroupID(g.ID) +// AddGroups adds the "groups" edges to the Group entity. +func (uc *UserCreate) AddGroups(g ...*Group) *UserCreate { + ids := make([]int, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uc.AddGroupIDs(ids...) } // AddFileIDs adds the "files" edge to the File entity by IDs. @@ -267,6 +266,21 @@ func (uc *UserCreate) AddEntities(e ...*Entity) *UserCreate { return uc.AddEntityIDs(ids...) } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by IDs. +func (uc *UserCreate) AddUserGroupIDs(ids ...int) *UserCreate { + uc.mutation.AddUserGroupIDs(ids...) + return uc +} + +// AddUserGroup adds the "user_group" edges to the UserGroup entity. +func (uc *UserCreate) AddUserGroup(u ...*UserGroup) *UserCreate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uc.AddUserGroupIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uc *UserCreate) Mutation() *UserMutation { return uc.mutation @@ -368,12 +382,6 @@ func (uc *UserCreate) check() error { if _, ok := uc.mutation.Storage(); !ok { return &ValidationError{Name: "storage", err: errors.New(`ent: missing required field "User.storage"`)} } - if _, ok := uc.mutation.GroupUsers(); !ok { - return &ValidationError{Name: "group_users", err: errors.New(`ent: missing required field "User.group_users"`)} - } - if _, ok := uc.mutation.GroupID(); !ok { - return &ValidationError{Name: "group", err: errors.New(`ent: missing required edge "User.group"`)} - } return nil } @@ -452,12 +460,12 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { _spec.SetField(user.FieldSettings, field.TypeJSON, value) _node.Settings = value } - if nodes := uc.mutation.GroupIDs(); len(nodes) > 0 { + if nodes := uc.mutation.GroupsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, + Rel: sqlgraph.M2M, Inverse: true, - Table: user.GroupTable, - Columns: []string{user.GroupColumn}, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), @@ -466,7 +474,10 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } - _node.GroupUsers = nodes[0] + createE := &UserGroupCreate{config: uc.config, mutation: newUserGroupMutation(uc.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges = append(_spec.Edges, edge) } if nodes := uc.mutation.FilesIDs(); len(nodes) > 0 { @@ -565,6 +576,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := uc.mutation.UserGroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.UserGroupTable, + Columns: []string{user.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } @@ -773,18 +800,6 @@ func (u *UserUpsert) ClearSettings() *UserUpsert { return u } -// SetGroupUsers sets the "group_users" field. -func (u *UserUpsert) SetGroupUsers(v int) *UserUpsert { - u.Set(user.FieldGroupUsers, v) - return u -} - -// UpdateGroupUsers sets the "group_users" field to the value that was provided on create. -func (u *UserUpsert) UpdateGroupUsers() *UserUpsert { - u.SetExcluded(user.FieldGroupUsers) - return u -} - // UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // @@ -1012,20 +1027,6 @@ func (u *UserUpsertOne) ClearSettings() *UserUpsertOne { }) } -// SetGroupUsers sets the "group_users" field. -func (u *UserUpsertOne) SetGroupUsers(v int) *UserUpsertOne { - return u.Update(func(s *UserUpsert) { - s.SetGroupUsers(v) - }) -} - -// UpdateGroupUsers sets the "group_users" field to the value that was provided on create. -func (u *UserUpsertOne) UpdateGroupUsers() *UserUpsertOne { - return u.Update(func(s *UserUpsert) { - s.UpdateGroupUsers() - }) -} - // Exec executes the query. func (u *UserUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -1424,20 +1425,6 @@ func (u *UserUpsertBulk) ClearSettings() *UserUpsertBulk { }) } -// SetGroupUsers sets the "group_users" field. -func (u *UserUpsertBulk) SetGroupUsers(v int) *UserUpsertBulk { - return u.Update(func(s *UserUpsert) { - s.SetGroupUsers(v) - }) -} - -// UpdateGroupUsers sets the "group_users" field to the value that was provided on create. -func (u *UserUpsertBulk) UpdateGroupUsers() *UserUpsertBulk { - return u.Update(func(s *UserUpsert) { - s.UpdateGroupUsers() - }) -} - // Exec executes the query. func (u *UserUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/ent/user_query.go b/ent/user_query.go index bbe8eb11..30065bc4 100644 --- a/ent/user_query.go +++ b/ent/user_query.go @@ -20,6 +20,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/share" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" ) // UserQuery is the builder for querying User entities. @@ -29,13 +30,14 @@ type UserQuery struct { order []user.OrderOption inters []Interceptor predicates []predicate.User - withGroup *GroupQuery + withGroups *GroupQuery withFiles *FileQuery withDavAccounts *DavAccountQuery withShares *ShareQuery withPasskey *PasskeyQuery withTasks *TaskQuery withEntities *EntityQuery + withUserGroup *UserGroupQuery // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -72,8 +74,8 @@ func (uq *UserQuery) Order(o ...user.OrderOption) *UserQuery { return uq } -// QueryGroup chains the current query on the "group" edge. -func (uq *UserQuery) QueryGroup() *GroupQuery { +// QueryGroups chains the current query on the "groups" edge. +func (uq *UserQuery) QueryGroups() *GroupQuery { query := (&GroupClient{config: uq.config}).Query() query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { if err := uq.prepareQuery(ctx); err != nil { @@ -86,7 +88,7 @@ func (uq *UserQuery) QueryGroup() *GroupQuery { step := sqlgraph.NewStep( sqlgraph.From(user.Table, user.FieldID, selector), sqlgraph.To(group.Table, group.FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, user.GroupTable, user.GroupColumn), + sqlgraph.Edge(sqlgraph.M2M, true, user.GroupsTable, user.GroupsPrimaryKey...), ) fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) return fromU, nil @@ -226,6 +228,28 @@ func (uq *UserQuery) QueryEntities() *EntityQuery { return query } +// QueryUserGroup chains the current query on the "user_group" edge. +func (uq *UserQuery) QueryUserGroup() *UserGroupQuery { + query := (&UserGroupClient{config: uq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := uq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := uq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, selector), + sqlgraph.To(usergroup.Table, usergroup.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, user.UserGroupTable, user.UserGroupColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first User entity from the query. // Returns a *NotFoundError when no User was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { @@ -418,27 +442,28 @@ func (uq *UserQuery) Clone() *UserQuery { order: append([]user.OrderOption{}, uq.order...), inters: append([]Interceptor{}, uq.inters...), predicates: append([]predicate.User{}, uq.predicates...), - withGroup: uq.withGroup.Clone(), + withGroups: uq.withGroups.Clone(), withFiles: uq.withFiles.Clone(), withDavAccounts: uq.withDavAccounts.Clone(), withShares: uq.withShares.Clone(), withPasskey: uq.withPasskey.Clone(), withTasks: uq.withTasks.Clone(), withEntities: uq.withEntities.Clone(), + withUserGroup: uq.withUserGroup.Clone(), // clone intermediate query. sql: uq.sql.Clone(), path: uq.path, } } -// WithGroup tells the query-builder to eager-load the nodes that are connected to -// the "group" edge. The optional arguments are used to configure the query builder of the edge. -func (uq *UserQuery) WithGroup(opts ...func(*GroupQuery)) *UserQuery { +// WithGroups tells the query-builder to eager-load the nodes that are connected to +// the "groups" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery { query := (&GroupClient{config: uq.config}).Query() for _, opt := range opts { opt(query) } - uq.withGroup = query + uq.withGroups = query return uq } @@ -508,6 +533,17 @@ func (uq *UserQuery) WithEntities(opts ...func(*EntityQuery)) *UserQuery { return uq } +// WithUserGroup tells the query-builder to eager-load the nodes that are connected to +// the "user_group" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithUserGroup(opts ...func(*UserGroupQuery)) *UserQuery { + query := (&UserGroupClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withUserGroup = query + return uq +} + // 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. // @@ -586,14 +622,15 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e var ( nodes = []*User{} _spec = uq.querySpec() - loadedTypes = [7]bool{ - uq.withGroup != nil, + loadedTypes = [8]bool{ + uq.withGroups != nil, uq.withFiles != nil, uq.withDavAccounts != nil, uq.withShares != nil, uq.withPasskey != nil, uq.withTasks != nil, uq.withEntities != nil, + uq.withUserGroup != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -614,9 +651,10 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e if len(nodes) == 0 { return nodes, nil } - if query := uq.withGroup; query != nil { - if err := uq.loadGroup(ctx, query, nodes, nil, - func(n *User, e *Group) { n.Edges.Group = e }); err != nil { + if query := uq.withGroups; query != nil { + if err := uq.loadGroups(ctx, query, nodes, + func(n *User) { n.Edges.Groups = []*Group{} }, + func(n *User, e *Group) { n.Edges.Groups = append(n.Edges.Groups, e) }); err != nil { return nil, err } } @@ -662,34 +700,73 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e return nil, err } } + if query := uq.withUserGroup; query != nil { + if err := uq.loadUserGroup(ctx, query, nodes, + func(n *User) { n.Edges.UserGroup = []*UserGroup{} }, + func(n *User, e *UserGroup) { n.Edges.UserGroup = append(n.Edges.UserGroup, e) }); err != nil { + return nil, err + } + } return nodes, nil } -func (uq *UserQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []*User, init func(*User), assign func(*User, *Group)) error { - ids := make([]int, 0, len(nodes)) - nodeids := make(map[int][]*User) - for i := range nodes { - fk := nodes[i].GroupUsers - if _, ok := nodeids[fk]; !ok { - ids = append(ids, fk) +func (uq *UserQuery) loadGroups(ctx context.Context, query *GroupQuery, nodes []*User, init func(*User), assign func(*User, *Group)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[int]*User) + nids := make(map[int]map[*User]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) } - nodeids[fk] = append(nodeids[fk], nodes[i]) } - if len(ids) == 0 { - return nil + query.Where(func(s *sql.Selector) { + joinT := sql.Table(user.GroupsTable) + s.Join(joinT).On(s.C(group.FieldID), joinT.C(user.GroupsPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(user.GroupsPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(user.GroupsPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err } - query.Where(group.IDIn(ids...)) - neighbors, err := query.All(ctx) + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(sql.NullInt64)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := int(values[0].(*sql.NullInt64).Int64) + inValue := int(values[1].(*sql.NullInt64).Int64) + if nids[inValue] == nil { + nids[inValue] = map[*User]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*Group](ctx, query, qr, query.inters) if err != nil { return err } for _, n := range neighbors { - nodes, ok := nodeids[n.ID] + nodes, ok := nids[n.ID] if !ok { - return fmt.Errorf(`unexpected foreign-key "group_users" returned %v`, n.ID) + return fmt.Errorf(`unexpected "groups" node returned %v`, n.ID) } - for i := range nodes { - assign(nodes[i], n) + for kn := range nodes { + assign(kn, n) } } return nil @@ -875,6 +952,36 @@ func (uq *UserQuery) loadEntities(ctx context.Context, query *EntityQuery, nodes } return nil } +func (uq *UserQuery) loadUserGroup(ctx context.Context, query *UserGroupQuery, nodes []*User, init func(*User), assign func(*User, *UserGroup)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(usergroup.FieldUserID) + } + query.Where(predicate.UserGroup(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(user.UserGroupColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.UserID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "user_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { _spec := uq.querySpec() @@ -901,9 +1008,6 @@ func (uq *UserQuery) querySpec() *sqlgraph.QuerySpec { _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) } } - if uq.withGroup != nil { - _spec.Node.AddColumnOnce(user.FieldGroupUsers) - } } if ps := uq.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { diff --git a/ent/user_update.go b/ent/user_update.go index 4d67894a..2bc4ca7c 100644 --- a/ent/user_update.go +++ b/ent/user_update.go @@ -20,6 +20,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/share" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" "github.com/cloudreve/Cloudreve/v4/inventory/types" ) @@ -197,29 +198,19 @@ func (uu *UserUpdate) ClearSettings() *UserUpdate { return uu } -// SetGroupUsers sets the "group_users" field. -func (uu *UserUpdate) SetGroupUsers(i int) *UserUpdate { - uu.mutation.SetGroupUsers(i) +// AddGroupIDs adds the "groups" edge to the Group entity by IDs. +func (uu *UserUpdate) AddGroupIDs(ids ...int) *UserUpdate { + uu.mutation.AddGroupIDs(ids...) return uu } -// SetNillableGroupUsers sets the "group_users" field if the given value is not nil. -func (uu *UserUpdate) SetNillableGroupUsers(i *int) *UserUpdate { - if i != nil { - uu.SetGroupUsers(*i) +// AddGroups adds the "groups" edges to the Group entity. +func (uu *UserUpdate) AddGroups(g ...*Group) *UserUpdate { + ids := make([]int, len(g)) + for i := range g { + ids[i] = g[i].ID } - return uu -} - -// SetGroupID sets the "group" edge to the Group entity by ID. -func (uu *UserUpdate) SetGroupID(id int) *UserUpdate { - uu.mutation.SetGroupID(id) - return uu -} - -// SetGroup sets the "group" edge to the Group entity. -func (uu *UserUpdate) SetGroup(g *Group) *UserUpdate { - return uu.SetGroupID(g.ID) + return uu.AddGroupIDs(ids...) } // AddFileIDs adds the "files" edge to the File entity by IDs. @@ -312,17 +303,47 @@ func (uu *UserUpdate) AddEntities(e ...*Entity) *UserUpdate { return uu.AddEntityIDs(ids...) } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by IDs. +func (uu *UserUpdate) AddUserGroupIDs(ids ...int) *UserUpdate { + uu.mutation.AddUserGroupIDs(ids...) + return uu +} + +// AddUserGroup adds the "user_group" edges to the UserGroup entity. +func (uu *UserUpdate) AddUserGroup(u ...*UserGroup) *UserUpdate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uu.AddUserGroupIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uu *UserUpdate) Mutation() *UserMutation { return uu.mutation } -// ClearGroup clears the "group" edge to the Group entity. -func (uu *UserUpdate) ClearGroup() *UserUpdate { - uu.mutation.ClearGroup() +// ClearGroups clears all "groups" edges to the Group entity. +func (uu *UserUpdate) ClearGroups() *UserUpdate { + uu.mutation.ClearGroups() return uu } +// RemoveGroupIDs removes the "groups" edge to Group entities by IDs. +func (uu *UserUpdate) RemoveGroupIDs(ids ...int) *UserUpdate { + uu.mutation.RemoveGroupIDs(ids...) + return uu +} + +// RemoveGroups removes "groups" edges to Group entities. +func (uu *UserUpdate) RemoveGroups(g ...*Group) *UserUpdate { + ids := make([]int, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uu.RemoveGroupIDs(ids...) +} + // ClearFiles clears all "files" edges to the File entity. func (uu *UserUpdate) ClearFiles() *UserUpdate { uu.mutation.ClearFiles() @@ -449,6 +470,27 @@ func (uu *UserUpdate) RemoveEntities(e ...*Entity) *UserUpdate { return uu.RemoveEntityIDs(ids...) } +// ClearUserGroup clears all "user_group" edges to the UserGroup entity. +func (uu *UserUpdate) ClearUserGroup() *UserUpdate { + uu.mutation.ClearUserGroup() + return uu +} + +// RemoveUserGroupIDs removes the "user_group" edge to UserGroup entities by IDs. +func (uu *UserUpdate) RemoveUserGroupIDs(ids ...int) *UserUpdate { + uu.mutation.RemoveUserGroupIDs(ids...) + return uu +} + +// RemoveUserGroup removes "user_group" edges to UserGroup entities. +func (uu *UserUpdate) RemoveUserGroup(u ...*UserGroup) *UserUpdate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uu.RemoveUserGroupIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { if err := uu.defaults(); err != nil { @@ -508,9 +550,6 @@ func (uu *UserUpdate) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "User.status": %w`, err)} } } - if _, ok := uu.mutation.GroupID(); uu.mutation.GroupCleared() && !ok { - return errors.New(`ent: clearing a required unique edge "User.group"`) - } return nil } @@ -574,25 +613,49 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { if uu.mutation.SettingsCleared() { _spec.ClearField(user.FieldSettings, field.TypeJSON) } - if uu.mutation.GroupCleared() { + if uu.mutation.GroupsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), + }, + } + createE := &UserGroupCreate{config: uu.config, mutation: newUserGroupMutation(uu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedGroupsIDs(); len(nodes) > 0 && !uu.mutation.GroupsCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, + Rel: sqlgraph.M2M, Inverse: true, - Table: user.GroupTable, - Columns: []string{user.GroupColumn}, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), }, } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &UserGroupCreate{config: uu.config, mutation: newUserGroupMutation(uu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } - if nodes := uu.mutation.GroupIDs(); len(nodes) > 0 { + if nodes := uu.mutation.GroupsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, + Rel: sqlgraph.M2M, Inverse: true, - Table: user.GroupTable, - Columns: []string{user.GroupColumn}, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), @@ -601,6 +664,10 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } + createE := &UserGroupCreate{config: uu.config, mutation: newUserGroupMutation(uu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Add = append(_spec.Edges.Add, edge) } if uu.mutation.FilesCleared() { @@ -873,6 +940,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uu.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.UserGroupTable, + Columns: []string{user.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedUserGroupIDs(); len(nodes) > 0 && !uu.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.UserGroupTable, + Columns: []string{user.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.UserGroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.UserGroupTable, + Columns: []string{user.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{user.Label} @@ -1054,29 +1166,19 @@ func (uuo *UserUpdateOne) ClearSettings() *UserUpdateOne { return uuo } -// SetGroupUsers sets the "group_users" field. -func (uuo *UserUpdateOne) SetGroupUsers(i int) *UserUpdateOne { - uuo.mutation.SetGroupUsers(i) +// AddGroupIDs adds the "groups" edge to the Group entity by IDs. +func (uuo *UserUpdateOne) AddGroupIDs(ids ...int) *UserUpdateOne { + uuo.mutation.AddGroupIDs(ids...) return uuo } -// SetNillableGroupUsers sets the "group_users" field if the given value is not nil. -func (uuo *UserUpdateOne) SetNillableGroupUsers(i *int) *UserUpdateOne { - if i != nil { - uuo.SetGroupUsers(*i) +// AddGroups adds the "groups" edges to the Group entity. +func (uuo *UserUpdateOne) AddGroups(g ...*Group) *UserUpdateOne { + ids := make([]int, len(g)) + for i := range g { + ids[i] = g[i].ID } - return uuo -} - -// SetGroupID sets the "group" edge to the Group entity by ID. -func (uuo *UserUpdateOne) SetGroupID(id int) *UserUpdateOne { - uuo.mutation.SetGroupID(id) - return uuo -} - -// SetGroup sets the "group" edge to the Group entity. -func (uuo *UserUpdateOne) SetGroup(g *Group) *UserUpdateOne { - return uuo.SetGroupID(g.ID) + return uuo.AddGroupIDs(ids...) } // AddFileIDs adds the "files" edge to the File entity by IDs. @@ -1169,17 +1271,47 @@ func (uuo *UserUpdateOne) AddEntities(e ...*Entity) *UserUpdateOne { return uuo.AddEntityIDs(ids...) } +// AddUserGroupIDs adds the "user_group" edge to the UserGroup entity by IDs. +func (uuo *UserUpdateOne) AddUserGroupIDs(ids ...int) *UserUpdateOne { + uuo.mutation.AddUserGroupIDs(ids...) + return uuo +} + +// AddUserGroup adds the "user_group" edges to the UserGroup entity. +func (uuo *UserUpdateOne) AddUserGroup(u ...*UserGroup) *UserUpdateOne { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uuo.AddUserGroupIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uuo *UserUpdateOne) Mutation() *UserMutation { return uuo.mutation } -// ClearGroup clears the "group" edge to the Group entity. -func (uuo *UserUpdateOne) ClearGroup() *UserUpdateOne { - uuo.mutation.ClearGroup() +// ClearGroups clears all "groups" edges to the Group entity. +func (uuo *UserUpdateOne) ClearGroups() *UserUpdateOne { + uuo.mutation.ClearGroups() return uuo } +// RemoveGroupIDs removes the "groups" edge to Group entities by IDs. +func (uuo *UserUpdateOne) RemoveGroupIDs(ids ...int) *UserUpdateOne { + uuo.mutation.RemoveGroupIDs(ids...) + return uuo +} + +// RemoveGroups removes "groups" edges to Group entities. +func (uuo *UserUpdateOne) RemoveGroups(g ...*Group) *UserUpdateOne { + ids := make([]int, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uuo.RemoveGroupIDs(ids...) +} + // ClearFiles clears all "files" edges to the File entity. func (uuo *UserUpdateOne) ClearFiles() *UserUpdateOne { uuo.mutation.ClearFiles() @@ -1306,6 +1438,27 @@ func (uuo *UserUpdateOne) RemoveEntities(e ...*Entity) *UserUpdateOne { return uuo.RemoveEntityIDs(ids...) } +// ClearUserGroup clears all "user_group" edges to the UserGroup entity. +func (uuo *UserUpdateOne) ClearUserGroup() *UserUpdateOne { + uuo.mutation.ClearUserGroup() + return uuo +} + +// RemoveUserGroupIDs removes the "user_group" edge to UserGroup entities by IDs. +func (uuo *UserUpdateOne) RemoveUserGroupIDs(ids ...int) *UserUpdateOne { + uuo.mutation.RemoveUserGroupIDs(ids...) + return uuo +} + +// RemoveUserGroup removes "user_group" edges to UserGroup entities. +func (uuo *UserUpdateOne) RemoveUserGroup(u ...*UserGroup) *UserUpdateOne { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uuo.RemoveUserGroupIDs(ids...) +} + // Where appends a list predicates to the UserUpdate builder. func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne { uuo.mutation.Where(ps...) @@ -1378,9 +1531,6 @@ func (uuo *UserUpdateOne) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "User.status": %w`, err)} } } - if _, ok := uuo.mutation.GroupID(); uuo.mutation.GroupCleared() && !ok { - return errors.New(`ent: clearing a required unique edge "User.group"`) - } return nil } @@ -1461,25 +1611,49 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) if uuo.mutation.SettingsCleared() { _spec.ClearField(user.FieldSettings, field.TypeJSON) } - if uuo.mutation.GroupCleared() { + if uuo.mutation.GroupsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), + }, + } + createE := &UserGroupCreate{config: uuo.config, mutation: newUserGroupMutation(uuo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedGroupsIDs(); len(nodes) > 0 && !uuo.mutation.GroupsCleared() { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, + Rel: sqlgraph.M2M, Inverse: true, - Table: user.GroupTable, - Columns: []string{user.GroupColumn}, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), }, } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &UserGroupCreate{config: uuo.config, mutation: newUserGroupMutation(uuo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } - if nodes := uuo.mutation.GroupIDs(); len(nodes) > 0 { + if nodes := uuo.mutation.GroupsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.M2O, + Rel: sqlgraph.M2M, Inverse: true, - Table: user.GroupTable, - Columns: []string{user.GroupColumn}, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, Bidi: false, Target: &sqlgraph.EdgeTarget{ IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), @@ -1488,6 +1662,10 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } + createE := &UserGroupCreate{config: uuo.config, mutation: newUserGroupMutation(uuo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields _spec.Edges.Add = append(_spec.Edges.Add, edge) } if uuo.mutation.FilesCleared() { @@ -1760,6 +1938,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uuo.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.UserGroupTable, + Columns: []string{user.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedUserGroupIDs(); len(nodes) > 0 && !uuo.mutation.UserGroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.UserGroupTable, + Columns: []string{user.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.UserGroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.UserGroupTable, + Columns: []string{user.UserGroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &User{config: uuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/ent/usergroup.go b/ent/usergroup.go new file mode 100644 index 00000000..b7c9aabe --- /dev/null +++ b/ent/usergroup.go @@ -0,0 +1,206 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/cloudreve/Cloudreve/v4/ent/group" + "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" +) + +// UserGroup is the model entity for the UserGroup schema. +type UserGroup struct { + config `json:"-"` + // ID of the ent. + ID int `json:"id,omitempty"` + // UserID holds the value of the "user_id" field. + UserID int `json:"user_id,omitempty"` + // GroupID holds the value of the "group_id" field. + GroupID int `json:"group_id,omitempty"` + // IsPrimary holds the value of the "is_primary" field. + IsPrimary bool `json:"is_primary,omitempty"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt *time.Time `json:"expires_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserGroupQuery when eager-loading is set. + Edges UserGroupEdges `json:"edges"` + selectValues sql.SelectValues +} + +// UserGroupEdges holds the relations/edges for other nodes in the graph. +type UserGroupEdges struct { + // User holds the value of the user edge. + User *User `json:"user,omitempty"` + // Group holds the value of the group edge. + Group *Group `json:"group,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// UserOrErr returns the User value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e UserGroupEdges) UserOrErr() (*User, error) { + if e.loadedTypes[0] { + if e.User == nil { + // Edge was loaded but was not found. + return nil, &NotFoundError{label: user.Label} + } + return e.User, nil + } + return nil, &NotLoadedError{edge: "user"} +} + +// GroupOrErr returns the Group value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e UserGroupEdges) GroupOrErr() (*Group, error) { + if e.loadedTypes[1] { + if e.Group == nil { + // Edge was loaded but was not found. + return nil, &NotFoundError{label: group.Label} + } + return e.Group, nil + } + return nil, &NotLoadedError{edge: "group"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*UserGroup) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case usergroup.FieldIsPrimary: + values[i] = new(sql.NullBool) + case usergroup.FieldID, usergroup.FieldUserID, usergroup.FieldGroupID: + values[i] = new(sql.NullInt64) + case usergroup.FieldExpiresAt: + 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 UserGroup fields. +func (ug *UserGroup) 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 usergroup.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + ug.ID = int(value.Int64) + case usergroup.FieldUserID: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field user_id", values[i]) + } else if value.Valid { + ug.UserID = int(value.Int64) + } + case usergroup.FieldGroupID: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field group_id", values[i]) + } else if value.Valid { + ug.GroupID = int(value.Int64) + } + case usergroup.FieldIsPrimary: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field is_primary", values[i]) + } else if value.Valid { + ug.IsPrimary = value.Bool + } + case usergroup.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + ug.ExpiresAt = new(time.Time) + *ug.ExpiresAt = value.Time + } + default: + ug.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the UserGroup. +// This includes values selected through modifiers, order, etc. +func (ug *UserGroup) Value(name string) (ent.Value, error) { + return ug.selectValues.Get(name) +} + +// QueryUser queries the "user" edge of the UserGroup entity. +func (ug *UserGroup) QueryUser() *UserQuery { + return NewUserGroupClient(ug.config).QueryUser(ug) +} + +// QueryGroup queries the "group" edge of the UserGroup entity. +func (ug *UserGroup) QueryGroup() *GroupQuery { + return NewUserGroupClient(ug.config).QueryGroup(ug) +} + +// Update returns a builder for updating this UserGroup. +// Note that you need to call UserGroup.Unwrap() before calling this method if this UserGroup +// was returned from a transaction, and the transaction was committed or rolled back. +func (ug *UserGroup) Update() *UserGroupUpdateOne { + return NewUserGroupClient(ug.config).UpdateOne(ug) +} + +// Unwrap unwraps the UserGroup 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 (ug *UserGroup) Unwrap() *UserGroup { + _tx, ok := ug.config.driver.(*txDriver) + if !ok { + panic("ent: UserGroup is not a transactional entity") + } + ug.config.driver = _tx.drv + return ug +} + +// String implements the fmt.Stringer. +func (ug *UserGroup) String() string { + var builder strings.Builder + builder.WriteString("UserGroup(") + builder.WriteString(fmt.Sprintf("id=%v, ", ug.ID)) + builder.WriteString("user_id=") + builder.WriteString(fmt.Sprintf("%v", ug.UserID)) + builder.WriteString(", ") + builder.WriteString("group_id=") + builder.WriteString(fmt.Sprintf("%v", ug.GroupID)) + builder.WriteString(", ") + builder.WriteString("is_primary=") + builder.WriteString(fmt.Sprintf("%v", ug.IsPrimary)) + builder.WriteString(", ") + if v := ug.ExpiresAt; v != nil { + builder.WriteString("expires_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteByte(')') + return builder.String() +} + +// SetUser manually set the edge as loaded state. +func (e *UserGroup) SetUser(v *User) { + e.Edges.User = v + e.Edges.loadedTypes[0] = true +} + +// SetGroup manually set the edge as loaded state. +func (e *UserGroup) SetGroup(v *Group) { + e.Edges.Group = v + e.Edges.loadedTypes[1] = true +} + +// UserGroups is a parsable slice of UserGroup. +type UserGroups []*UserGroup diff --git a/ent/usergroup/usergroup.go b/ent/usergroup/usergroup.go new file mode 100644 index 00000000..1089c5f0 --- /dev/null +++ b/ent/usergroup/usergroup.go @@ -0,0 +1,123 @@ +// Code generated by ent, DO NOT EDIT. + +package usergroup + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the usergroup type in the database. + Label = "user_group" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldUserID holds the string denoting the user_id field in the database. + FieldUserID = "user_id" + // FieldGroupID holds the string denoting the group_id field in the database. + FieldGroupID = "group_id" + // FieldIsPrimary holds the string denoting the is_primary field in the database. + FieldIsPrimary = "is_primary" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // EdgeUser holds the string denoting the user edge name in mutations. + EdgeUser = "user" + // EdgeGroup holds the string denoting the group edge name in mutations. + EdgeGroup = "group" + // Table holds the table name of the usergroup in the database. + Table = "user_groups" + // UserTable is the table that holds the user relation/edge. + UserTable = "user_groups" + // UserInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + UserInverseTable = "users" + // UserColumn is the table column denoting the user relation/edge. + UserColumn = "user_id" + // GroupTable is the table that holds the group relation/edge. + GroupTable = "user_groups" + // GroupInverseTable is the table name for the Group entity. + // It exists in this package in order to avoid circular dependency with the "group" package. + GroupInverseTable = "groups" + // GroupColumn is the table column denoting the group relation/edge. + GroupColumn = "group_id" +) + +// Columns holds all SQL columns for usergroup fields. +var Columns = []string{ + FieldID, + FieldUserID, + FieldGroupID, + FieldIsPrimary, + FieldExpiresAt, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultIsPrimary holds the default value on creation for the "is_primary" field. + DefaultIsPrimary bool +) + +// OrderOption defines the ordering options for the UserGroup 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() +} + +// ByUserID orders the results by the user_id field. +func ByUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUserID, opts...).ToFunc() +} + +// ByGroupID orders the results by the group_id field. +func ByGroupID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGroupID, opts...).ToFunc() +} + +// ByIsPrimary orders the results by the is_primary field. +func ByIsPrimary(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldIsPrimary, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByUserField orders the results by user field. +func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...)) + } +} + +// ByGroupField orders the results by group field. +func ByGroupField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newGroupStep(), sql.OrderByField(field, opts...)) + } +} +func newUserStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn), + ) +} +func newGroupStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(GroupInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, GroupTable, GroupColumn), + ) +} diff --git a/ent/usergroup/where.go b/ent/usergroup/where.go new file mode 100644 index 00000000..91abc23d --- /dev/null +++ b/ent/usergroup/where.go @@ -0,0 +1,237 @@ +// Code generated by ent, DO NOT EDIT. + +package usergroup + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldLTE(FieldID, id)) +} + +// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ. +func UserID(v int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldUserID, v)) +} + +// GroupID applies equality check predicate on the "group_id" field. It's identical to GroupIDEQ. +func GroupID(v int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldGroupID, v)) +} + +// IsPrimary applies equality check predicate on the "is_primary" field. It's identical to IsPrimaryEQ. +func IsPrimary(v bool) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldIsPrimary, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldExpiresAt, v)) +} + +// UserIDEQ applies the EQ predicate on the "user_id" field. +func UserIDEQ(v int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldUserID, v)) +} + +// UserIDNEQ applies the NEQ predicate on the "user_id" field. +func UserIDNEQ(v int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNEQ(FieldUserID, v)) +} + +// UserIDIn applies the In predicate on the "user_id" field. +func UserIDIn(vs ...int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldIn(FieldUserID, vs...)) +} + +// UserIDNotIn applies the NotIn predicate on the "user_id" field. +func UserIDNotIn(vs ...int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNotIn(FieldUserID, vs...)) +} + +// GroupIDEQ applies the EQ predicate on the "group_id" field. +func GroupIDEQ(v int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldGroupID, v)) +} + +// GroupIDNEQ applies the NEQ predicate on the "group_id" field. +func GroupIDNEQ(v int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNEQ(FieldGroupID, v)) +} + +// GroupIDIn applies the In predicate on the "group_id" field. +func GroupIDIn(vs ...int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldIn(FieldGroupID, vs...)) +} + +// GroupIDNotIn applies the NotIn predicate on the "group_id" field. +func GroupIDNotIn(vs ...int) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNotIn(FieldGroupID, vs...)) +} + +// IsPrimaryEQ applies the EQ predicate on the "is_primary" field. +func IsPrimaryEQ(v bool) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldIsPrimary, v)) +} + +// IsPrimaryNEQ applies the NEQ predicate on the "is_primary" field. +func IsPrimaryNEQ(v bool) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNEQ(FieldIsPrimary, v)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.UserGroup { + return predicate.UserGroup(sql.FieldLTE(FieldExpiresAt, v)) +} + +// ExpiresAtIsNil applies the IsNil predicate on the "expires_at" field. +func ExpiresAtIsNil() predicate.UserGroup { + return predicate.UserGroup(sql.FieldIsNull(FieldExpiresAt)) +} + +// ExpiresAtNotNil applies the NotNil predicate on the "expires_at" field. +func ExpiresAtNotNil() predicate.UserGroup { + return predicate.UserGroup(sql.FieldNotNull(FieldExpiresAt)) +} + +// HasUser applies the HasEdge predicate on the "user" edge. +func HasUser() predicate.UserGroup { + return predicate.UserGroup(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates). +func HasUserWith(preds ...predicate.User) predicate.UserGroup { + return predicate.UserGroup(func(s *sql.Selector) { + step := newUserStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasGroup applies the HasEdge predicate on the "group" edge. +func HasGroup() predicate.UserGroup { + return predicate.UserGroup(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, GroupTable, GroupColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates). +func HasGroupWith(preds ...predicate.Group) predicate.UserGroup { + return predicate.UserGroup(func(s *sql.Selector) { + step := newGroupStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.UserGroup) predicate.UserGroup { + return predicate.UserGroup(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.UserGroup) predicate.UserGroup { + return predicate.UserGroup(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.UserGroup) predicate.UserGroup { + return predicate.UserGroup(sql.NotPredicates(p)) +} diff --git a/ent/usergroup_create.go b/ent/usergroup_create.go new file mode 100644 index 00000000..2d82b01f --- /dev/null +++ b/ent/usergroup_create.go @@ -0,0 +1,713 @@ +// 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/cloudreve/Cloudreve/v4/ent/group" + "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" +) + +// UserGroupCreate is the builder for creating a UserGroup entity. +type UserGroupCreate struct { + config + mutation *UserGroupMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetUserID sets the "user_id" field. +func (ugc *UserGroupCreate) SetUserID(i int) *UserGroupCreate { + ugc.mutation.SetUserID(i) + return ugc +} + +// SetGroupID sets the "group_id" field. +func (ugc *UserGroupCreate) SetGroupID(i int) *UserGroupCreate { + ugc.mutation.SetGroupID(i) + return ugc +} + +// SetIsPrimary sets the "is_primary" field. +func (ugc *UserGroupCreate) SetIsPrimary(b bool) *UserGroupCreate { + ugc.mutation.SetIsPrimary(b) + return ugc +} + +// SetNillableIsPrimary sets the "is_primary" field if the given value is not nil. +func (ugc *UserGroupCreate) SetNillableIsPrimary(b *bool) *UserGroupCreate { + if b != nil { + ugc.SetIsPrimary(*b) + } + return ugc +} + +// SetExpiresAt sets the "expires_at" field. +func (ugc *UserGroupCreate) SetExpiresAt(t time.Time) *UserGroupCreate { + ugc.mutation.SetExpiresAt(t) + return ugc +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (ugc *UserGroupCreate) SetNillableExpiresAt(t *time.Time) *UserGroupCreate { + if t != nil { + ugc.SetExpiresAt(*t) + } + return ugc +} + +// SetUser sets the "user" edge to the User entity. +func (ugc *UserGroupCreate) SetUser(u *User) *UserGroupCreate { + return ugc.SetUserID(u.ID) +} + +// SetGroup sets the "group" edge to the Group entity. +func (ugc *UserGroupCreate) SetGroup(g *Group) *UserGroupCreate { + return ugc.SetGroupID(g.ID) +} + +// Mutation returns the UserGroupMutation object of the builder. +func (ugc *UserGroupCreate) Mutation() *UserGroupMutation { + return ugc.mutation +} + +// Save creates the UserGroup in the database. +func (ugc *UserGroupCreate) Save(ctx context.Context) (*UserGroup, error) { + ugc.defaults() + return withHooks(ctx, ugc.sqlSave, ugc.mutation, ugc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (ugc *UserGroupCreate) SaveX(ctx context.Context) *UserGroup { + v, err := ugc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (ugc *UserGroupCreate) Exec(ctx context.Context) error { + _, err := ugc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ugc *UserGroupCreate) ExecX(ctx context.Context) { + if err := ugc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (ugc *UserGroupCreate) defaults() { + if _, ok := ugc.mutation.IsPrimary(); !ok { + v := usergroup.DefaultIsPrimary + ugc.mutation.SetIsPrimary(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (ugc *UserGroupCreate) check() error { + if _, ok := ugc.mutation.UserID(); !ok { + return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "UserGroup.user_id"`)} + } + if _, ok := ugc.mutation.GroupID(); !ok { + return &ValidationError{Name: "group_id", err: errors.New(`ent: missing required field "UserGroup.group_id"`)} + } + if _, ok := ugc.mutation.IsPrimary(); !ok { + return &ValidationError{Name: "is_primary", err: errors.New(`ent: missing required field "UserGroup.is_primary"`)} + } + if _, ok := ugc.mutation.UserID(); !ok { + return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "UserGroup.user"`)} + } + if _, ok := ugc.mutation.GroupID(); !ok { + return &ValidationError{Name: "group", err: errors.New(`ent: missing required edge "UserGroup.group"`)} + } + return nil +} + +func (ugc *UserGroupCreate) sqlSave(ctx context.Context) (*UserGroup, error) { + if err := ugc.check(); err != nil { + return nil, err + } + _node, _spec := ugc.createSpec() + if err := sqlgraph.CreateNode(ctx, ugc.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 = int(id) + ugc.mutation.id = &_node.ID + ugc.mutation.done = true + return _node, nil +} + +func (ugc *UserGroupCreate) createSpec() (*UserGroup, *sqlgraph.CreateSpec) { + var ( + _node = &UserGroup{config: ugc.config} + _spec = sqlgraph.NewCreateSpec(usergroup.Table, sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt)) + ) + + if id, ok := ugc.mutation.ID(); ok { + _node.ID = id + id64 := int64(id) + _spec.ID.Value = id64 + } + + _spec.OnConflict = ugc.conflict + if value, ok := ugc.mutation.IsPrimary(); ok { + _spec.SetField(usergroup.FieldIsPrimary, field.TypeBool, value) + _node.IsPrimary = value + } + if value, ok := ugc.mutation.ExpiresAt(); ok { + _spec.SetField(usergroup.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = &value + } + if nodes := ugc.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.UserTable, + Columns: []string{usergroup.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.UserID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := ugc.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.GroupTable, + Columns: []string{usergroup.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.GroupID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.UserGroup.Create(). +// SetUserID(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.UserGroupUpsert) { +// SetUserID(v+v). +// }). +// Exec(ctx) +func (ugc *UserGroupCreate) OnConflict(opts ...sql.ConflictOption) *UserGroupUpsertOne { + ugc.conflict = opts + return &UserGroupUpsertOne{ + create: ugc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.UserGroup.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (ugc *UserGroupCreate) OnConflictColumns(columns ...string) *UserGroupUpsertOne { + ugc.conflict = append(ugc.conflict, sql.ConflictColumns(columns...)) + return &UserGroupUpsertOne{ + create: ugc, + } +} + +type ( + // UserGroupUpsertOne is the builder for "upsert"-ing + // one UserGroup node. + UserGroupUpsertOne struct { + create *UserGroupCreate + } + + // UserGroupUpsert is the "OnConflict" setter. + UserGroupUpsert struct { + *sql.UpdateSet + } +) + +// SetUserID sets the "user_id" field. +func (u *UserGroupUpsert) SetUserID(v int) *UserGroupUpsert { + u.Set(usergroup.FieldUserID, v) + return u +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *UserGroupUpsert) UpdateUserID() *UserGroupUpsert { + u.SetExcluded(usergroup.FieldUserID) + return u +} + +// SetGroupID sets the "group_id" field. +func (u *UserGroupUpsert) SetGroupID(v int) *UserGroupUpsert { + u.Set(usergroup.FieldGroupID, v) + return u +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *UserGroupUpsert) UpdateGroupID() *UserGroupUpsert { + u.SetExcluded(usergroup.FieldGroupID) + return u +} + +// SetIsPrimary sets the "is_primary" field. +func (u *UserGroupUpsert) SetIsPrimary(v bool) *UserGroupUpsert { + u.Set(usergroup.FieldIsPrimary, v) + return u +} + +// UpdateIsPrimary sets the "is_primary" field to the value that was provided on create. +func (u *UserGroupUpsert) UpdateIsPrimary() *UserGroupUpsert { + u.SetExcluded(usergroup.FieldIsPrimary) + return u +} + +// SetExpiresAt sets the "expires_at" field. +func (u *UserGroupUpsert) SetExpiresAt(v time.Time) *UserGroupUpsert { + u.Set(usergroup.FieldExpiresAt, v) + return u +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *UserGroupUpsert) UpdateExpiresAt() *UserGroupUpsert { + u.SetExcluded(usergroup.FieldExpiresAt) + return u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *UserGroupUpsert) ClearExpiresAt() *UserGroupUpsert { + u.SetNull(usergroup.FieldExpiresAt) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create. +// Using this option is equivalent to using: +// +// client.UserGroup.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *UserGroupUpsertOne) UpdateNewValues() *UserGroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.UserGroup.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UserGroupUpsertOne) Ignore() *UserGroupUpsertOne { + 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 *UserGroupUpsertOne) DoNothing() *UserGroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserGroupCreate.OnConflict +// documentation for more info. +func (u *UserGroupUpsertOne) Update(set func(*UserGroupUpsert)) *UserGroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserGroupUpsert{UpdateSet: update}) + })) + return u +} + +// SetUserID sets the "user_id" field. +func (u *UserGroupUpsertOne) SetUserID(v int) *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *UserGroupUpsertOne) UpdateUserID() *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateUserID() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *UserGroupUpsertOne) SetGroupID(v int) *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *UserGroupUpsertOne) UpdateGroupID() *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateGroupID() + }) +} + +// SetIsPrimary sets the "is_primary" field. +func (u *UserGroupUpsertOne) SetIsPrimary(v bool) *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.SetIsPrimary(v) + }) +} + +// UpdateIsPrimary sets the "is_primary" field to the value that was provided on create. +func (u *UserGroupUpsertOne) UpdateIsPrimary() *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateIsPrimary() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *UserGroupUpsertOne) SetExpiresAt(v time.Time) *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *UserGroupUpsertOne) UpdateExpiresAt() *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *UserGroupUpsertOne) ClearExpiresAt() *UserGroupUpsertOne { + return u.Update(func(s *UserGroupUpsert) { + s.ClearExpiresAt() + }) +} + +// Exec executes the query. +func (u *UserGroupUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserGroupCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserGroupUpsertOne) 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 *UserGroupUpsertOne) ID(ctx context.Context) (id int, 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 *UserGroupUpsertOne) IDX(ctx context.Context) int { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +func (m *UserGroupCreate) SetRawID(t int) *UserGroupCreate { + m.mutation.SetRawID(t) + return m +} + +// UserGroupCreateBulk is the builder for creating many UserGroup entities in bulk. +type UserGroupCreateBulk struct { + config + err error + builders []*UserGroupCreate + conflict []sql.ConflictOption +} + +// Save creates the UserGroup entities in the database. +func (ugcb *UserGroupCreateBulk) Save(ctx context.Context) ([]*UserGroup, error) { + if ugcb.err != nil { + return nil, ugcb.err + } + specs := make([]*sqlgraph.CreateSpec, len(ugcb.builders)) + nodes := make([]*UserGroup, len(ugcb.builders)) + mutators := make([]Mutator, len(ugcb.builders)) + for i := range ugcb.builders { + func(i int, root context.Context) { + builder := ugcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*UserGroupMutation) + 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, ugcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = ugcb.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, ugcb.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 = int(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, ugcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (ugcb *UserGroupCreateBulk) SaveX(ctx context.Context) []*UserGroup { + v, err := ugcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (ugcb *UserGroupCreateBulk) Exec(ctx context.Context) error { + _, err := ugcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ugcb *UserGroupCreateBulk) ExecX(ctx context.Context) { + if err := ugcb.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.UserGroup.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.UserGroupUpsert) { +// SetUserID(v+v). +// }). +// Exec(ctx) +func (ugcb *UserGroupCreateBulk) OnConflict(opts ...sql.ConflictOption) *UserGroupUpsertBulk { + ugcb.conflict = opts + return &UserGroupUpsertBulk{ + create: ugcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.UserGroup.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (ugcb *UserGroupCreateBulk) OnConflictColumns(columns ...string) *UserGroupUpsertBulk { + ugcb.conflict = append(ugcb.conflict, sql.ConflictColumns(columns...)) + return &UserGroupUpsertBulk{ + create: ugcb, + } +} + +// UserGroupUpsertBulk is the builder for "upsert"-ing +// a bulk of UserGroup nodes. +type UserGroupUpsertBulk struct { + create *UserGroupCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.UserGroup.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *UserGroupUpsertBulk) UpdateNewValues() *UserGroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.UserGroup.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UserGroupUpsertBulk) Ignore() *UserGroupUpsertBulk { + 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 *UserGroupUpsertBulk) DoNothing() *UserGroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserGroupCreateBulk.OnConflict +// documentation for more info. +func (u *UserGroupUpsertBulk) Update(set func(*UserGroupUpsert)) *UserGroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserGroupUpsert{UpdateSet: update}) + })) + return u +} + +// SetUserID sets the "user_id" field. +func (u *UserGroupUpsertBulk) SetUserID(v int) *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *UserGroupUpsertBulk) UpdateUserID() *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateUserID() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *UserGroupUpsertBulk) SetGroupID(v int) *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *UserGroupUpsertBulk) UpdateGroupID() *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateGroupID() + }) +} + +// SetIsPrimary sets the "is_primary" field. +func (u *UserGroupUpsertBulk) SetIsPrimary(v bool) *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.SetIsPrimary(v) + }) +} + +// UpdateIsPrimary sets the "is_primary" field to the value that was provided on create. +func (u *UserGroupUpsertBulk) UpdateIsPrimary() *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateIsPrimary() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *UserGroupUpsertBulk) SetExpiresAt(v time.Time) *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *UserGroupUpsertBulk) UpdateExpiresAt() *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *UserGroupUpsertBulk) ClearExpiresAt() *UserGroupUpsertBulk { + return u.Update(func(s *UserGroupUpsert) { + s.ClearExpiresAt() + }) +} + +// Exec executes the query. +func (u *UserGroupUpsertBulk) 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 UserGroupCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserGroupCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserGroupUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/usergroup_delete.go b/ent/usergroup_delete.go new file mode 100644 index 00000000..75c1464b --- /dev/null +++ b/ent/usergroup_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/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" +) + +// UserGroupDelete is the builder for deleting a UserGroup entity. +type UserGroupDelete struct { + config + hooks []Hook + mutation *UserGroupMutation +} + +// Where appends a list predicates to the UserGroupDelete builder. +func (ugd *UserGroupDelete) Where(ps ...predicate.UserGroup) *UserGroupDelete { + ugd.mutation.Where(ps...) + return ugd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (ugd *UserGroupDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, ugd.sqlExec, ugd.mutation, ugd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (ugd *UserGroupDelete) ExecX(ctx context.Context) int { + n, err := ugd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (ugd *UserGroupDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(usergroup.Table, sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt)) + if ps := ugd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, ugd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + ugd.mutation.done = true + return affected, err +} + +// UserGroupDeleteOne is the builder for deleting a single UserGroup entity. +type UserGroupDeleteOne struct { + ugd *UserGroupDelete +} + +// Where appends a list predicates to the UserGroupDelete builder. +func (ugdo *UserGroupDeleteOne) Where(ps ...predicate.UserGroup) *UserGroupDeleteOne { + ugdo.ugd.mutation.Where(ps...) + return ugdo +} + +// Exec executes the deletion query. +func (ugdo *UserGroupDeleteOne) Exec(ctx context.Context) error { + n, err := ugdo.ugd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{usergroup.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (ugdo *UserGroupDeleteOne) ExecX(ctx context.Context) { + if err := ugdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/usergroup_query.go b/ent/usergroup_query.go new file mode 100644 index 00000000..44f88ce7 --- /dev/null +++ b/ent/usergroup_query.go @@ -0,0 +1,680 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/cloudreve/Cloudreve/v4/ent/group" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" +) + +// UserGroupQuery is the builder for querying UserGroup entities. +type UserGroupQuery struct { + config + ctx *QueryContext + order []usergroup.OrderOption + inters []Interceptor + predicates []predicate.UserGroup + withUser *UserQuery + withGroup *GroupQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the UserGroupQuery builder. +func (ugq *UserGroupQuery) Where(ps ...predicate.UserGroup) *UserGroupQuery { + ugq.predicates = append(ugq.predicates, ps...) + return ugq +} + +// Limit the number of records to be returned by this query. +func (ugq *UserGroupQuery) Limit(limit int) *UserGroupQuery { + ugq.ctx.Limit = &limit + return ugq +} + +// Offset to start from. +func (ugq *UserGroupQuery) Offset(offset int) *UserGroupQuery { + ugq.ctx.Offset = &offset + return ugq +} + +// 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 (ugq *UserGroupQuery) Unique(unique bool) *UserGroupQuery { + ugq.ctx.Unique = &unique + return ugq +} + +// Order specifies how the records should be ordered. +func (ugq *UserGroupQuery) Order(o ...usergroup.OrderOption) *UserGroupQuery { + ugq.order = append(ugq.order, o...) + return ugq +} + +// QueryUser chains the current query on the "user" edge. +func (ugq *UserGroupQuery) QueryUser() *UserQuery { + query := (&UserClient{config: ugq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := ugq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := ugq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(usergroup.Table, usergroup.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, usergroup.UserTable, usergroup.UserColumn), + ) + fromU = sqlgraph.SetNeighbors(ugq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryGroup chains the current query on the "group" edge. +func (ugq *UserGroupQuery) QueryGroup() *GroupQuery { + query := (&GroupClient{config: ugq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := ugq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := ugq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(usergroup.Table, usergroup.FieldID, selector), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, usergroup.GroupTable, usergroup.GroupColumn), + ) + fromU = sqlgraph.SetNeighbors(ugq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first UserGroup entity from the query. +// Returns a *NotFoundError when no UserGroup was found. +func (ugq *UserGroupQuery) First(ctx context.Context) (*UserGroup, error) { + nodes, err := ugq.Limit(1).All(setContextOp(ctx, ugq.ctx, "First")) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{usergroup.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (ugq *UserGroupQuery) FirstX(ctx context.Context) *UserGroup { + node, err := ugq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first UserGroup ID from the query. +// Returns a *NotFoundError when no UserGroup ID was found. +func (ugq *UserGroupQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = ugq.Limit(1).IDs(setContextOp(ctx, ugq.ctx, "FirstID")); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{usergroup.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (ugq *UserGroupQuery) FirstIDX(ctx context.Context) int { + id, err := ugq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single UserGroup entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one UserGroup entity is found. +// Returns a *NotFoundError when no UserGroup entities are found. +func (ugq *UserGroupQuery) Only(ctx context.Context) (*UserGroup, error) { + nodes, err := ugq.Limit(2).All(setContextOp(ctx, ugq.ctx, "Only")) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{usergroup.Label} + default: + return nil, &NotSingularError{usergroup.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (ugq *UserGroupQuery) OnlyX(ctx context.Context) *UserGroup { + node, err := ugq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only UserGroup ID in the query. +// Returns a *NotSingularError when more than one UserGroup ID is found. +// Returns a *NotFoundError when no entities are found. +func (ugq *UserGroupQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = ugq.Limit(2).IDs(setContextOp(ctx, ugq.ctx, "OnlyID")); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{usergroup.Label} + default: + err = &NotSingularError{usergroup.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (ugq *UserGroupQuery) OnlyIDX(ctx context.Context) int { + id, err := ugq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of UserGroups. +func (ugq *UserGroupQuery) All(ctx context.Context) ([]*UserGroup, error) { + ctx = setContextOp(ctx, ugq.ctx, "All") + if err := ugq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*UserGroup, *UserGroupQuery]() + return withInterceptors[[]*UserGroup](ctx, ugq, qr, ugq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (ugq *UserGroupQuery) AllX(ctx context.Context) []*UserGroup { + nodes, err := ugq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of UserGroup IDs. +func (ugq *UserGroupQuery) IDs(ctx context.Context) (ids []int, err error) { + if ugq.ctx.Unique == nil && ugq.path != nil { + ugq.Unique(true) + } + ctx = setContextOp(ctx, ugq.ctx, "IDs") + if err = ugq.Select(usergroup.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (ugq *UserGroupQuery) IDsX(ctx context.Context) []int { + ids, err := ugq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (ugq *UserGroupQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, ugq.ctx, "Count") + if err := ugq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, ugq, querierCount[*UserGroupQuery](), ugq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (ugq *UserGroupQuery) CountX(ctx context.Context) int { + count, err := ugq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (ugq *UserGroupQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, ugq.ctx, "Exist") + switch _, err := ugq.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 (ugq *UserGroupQuery) ExistX(ctx context.Context) bool { + exist, err := ugq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the UserGroupQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (ugq *UserGroupQuery) Clone() *UserGroupQuery { + if ugq == nil { + return nil + } + return &UserGroupQuery{ + config: ugq.config, + ctx: ugq.ctx.Clone(), + order: append([]usergroup.OrderOption{}, ugq.order...), + inters: append([]Interceptor{}, ugq.inters...), + predicates: append([]predicate.UserGroup{}, ugq.predicates...), + withUser: ugq.withUser.Clone(), + withGroup: ugq.withGroup.Clone(), + // clone intermediate query. + sql: ugq.sql.Clone(), + path: ugq.path, + } +} + +// WithUser tells the query-builder to eager-load the nodes that are connected to +// the "user" edge. The optional arguments are used to configure the query builder of the edge. +func (ugq *UserGroupQuery) WithUser(opts ...func(*UserQuery)) *UserGroupQuery { + query := (&UserClient{config: ugq.config}).Query() + for _, opt := range opts { + opt(query) + } + ugq.withUser = query + return ugq +} + +// WithGroup tells the query-builder to eager-load the nodes that are connected to +// the "group" edge. The optional arguments are used to configure the query builder of the edge. +func (ugq *UserGroupQuery) WithGroup(opts ...func(*GroupQuery)) *UserGroupQuery { + query := (&GroupClient{config: ugq.config}).Query() + for _, opt := range opts { + opt(query) + } + ugq.withGroup = query + return ugq +} + +// 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 { +// UserID int `json:"user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.UserGroup.Query(). +// GroupBy(usergroup.FieldUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (ugq *UserGroupQuery) GroupBy(field string, fields ...string) *UserGroupGroupBy { + ugq.ctx.Fields = append([]string{field}, fields...) + grbuild := &UserGroupGroupBy{build: ugq} + grbuild.flds = &ugq.ctx.Fields + grbuild.label = usergroup.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 { +// UserID int `json:"user_id,omitempty"` +// } +// +// client.UserGroup.Query(). +// Select(usergroup.FieldUserID). +// Scan(ctx, &v) +func (ugq *UserGroupQuery) Select(fields ...string) *UserGroupSelect { + ugq.ctx.Fields = append(ugq.ctx.Fields, fields...) + sbuild := &UserGroupSelect{UserGroupQuery: ugq} + sbuild.label = usergroup.Label + sbuild.flds, sbuild.scan = &ugq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a UserGroupSelect configured with the given aggregations. +func (ugq *UserGroupQuery) Aggregate(fns ...AggregateFunc) *UserGroupSelect { + return ugq.Select().Aggregate(fns...) +} + +func (ugq *UserGroupQuery) prepareQuery(ctx context.Context) error { + for _, inter := range ugq.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, ugq); err != nil { + return err + } + } + } + for _, f := range ugq.ctx.Fields { + if !usergroup.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if ugq.path != nil { + prev, err := ugq.path(ctx) + if err != nil { + return err + } + ugq.sql = prev + } + return nil +} + +func (ugq *UserGroupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserGroup, error) { + var ( + nodes = []*UserGroup{} + _spec = ugq.querySpec() + loadedTypes = [2]bool{ + ugq.withUser != nil, + ugq.withGroup != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*UserGroup).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &UserGroup{config: ugq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, ugq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := ugq.withUser; query != nil { + if err := ugq.loadUser(ctx, query, nodes, nil, + func(n *UserGroup, e *User) { n.Edges.User = e }); err != nil { + return nil, err + } + } + if query := ugq.withGroup; query != nil { + if err := ugq.loadGroup(ctx, query, nodes, nil, + func(n *UserGroup, e *Group) { n.Edges.Group = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (ugq *UserGroupQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*UserGroup, init func(*UserGroup), assign func(*UserGroup, *User)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*UserGroup) + for i := range nodes { + fk := nodes[i].UserID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (ugq *UserGroupQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []*UserGroup, init func(*UserGroup), assign func(*UserGroup, *Group)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*UserGroup) + for i := range nodes { + fk := nodes[i].GroupID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(group.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "group_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (ugq *UserGroupQuery) sqlCount(ctx context.Context) (int, error) { + _spec := ugq.querySpec() + _spec.Node.Columns = ugq.ctx.Fields + if len(ugq.ctx.Fields) > 0 { + _spec.Unique = ugq.ctx.Unique != nil && *ugq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, ugq.driver, _spec) +} + +func (ugq *UserGroupQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(usergroup.Table, usergroup.Columns, sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt)) + _spec.From = ugq.sql + if unique := ugq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if ugq.path != nil { + _spec.Unique = true + } + if fields := ugq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, usergroup.FieldID) + for i := range fields { + if fields[i] != usergroup.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if ugq.withUser != nil { + _spec.Node.AddColumnOnce(usergroup.FieldUserID) + } + if ugq.withGroup != nil { + _spec.Node.AddColumnOnce(usergroup.FieldGroupID) + } + } + if ps := ugq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := ugq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := ugq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := ugq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (ugq *UserGroupQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(ugq.driver.Dialect()) + t1 := builder.Table(usergroup.Table) + columns := ugq.ctx.Fields + if len(columns) == 0 { + columns = usergroup.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if ugq.sql != nil { + selector = ugq.sql + selector.Select(selector.Columns(columns...)...) + } + if ugq.ctx.Unique != nil && *ugq.ctx.Unique { + selector.Distinct() + } + for _, p := range ugq.predicates { + p(selector) + } + for _, p := range ugq.order { + p(selector) + } + if offset := ugq.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 := ugq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// UserGroupGroupBy is the group-by builder for UserGroup entities. +type UserGroupGroupBy struct { + selector + build *UserGroupQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (uggb *UserGroupGroupBy) Aggregate(fns ...AggregateFunc) *UserGroupGroupBy { + uggb.fns = append(uggb.fns, fns...) + return uggb +} + +// Scan applies the selector query and scans the result into the given value. +func (uggb *UserGroupGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, uggb.build.ctx, "GroupBy") + if err := uggb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*UserGroupQuery, *UserGroupGroupBy](ctx, uggb.build, uggb, uggb.build.inters, v) +} + +func (uggb *UserGroupGroupBy) sqlScan(ctx context.Context, root *UserGroupQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(uggb.fns)) + for _, fn := range uggb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*uggb.flds)+len(uggb.fns)) + for _, f := range *uggb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*uggb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := uggb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// UserGroupSelect is the builder for selecting fields of UserGroup entities. +type UserGroupSelect struct { + *UserGroupQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (ugs *UserGroupSelect) Aggregate(fns ...AggregateFunc) *UserGroupSelect { + ugs.fns = append(ugs.fns, fns...) + return ugs +} + +// Scan applies the selector query and scans the result into the given value. +func (ugs *UserGroupSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, ugs.ctx, "Select") + if err := ugs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*UserGroupQuery, *UserGroupSelect](ctx, ugs.UserGroupQuery, ugs, ugs.inters, v) +} + +func (ugs *UserGroupSelect) sqlScan(ctx context.Context, root *UserGroupQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(ugs.fns)) + for _, fn := range ugs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*ugs.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 := ugs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/usergroup_update.go b/ent/usergroup_update.go new file mode 100644 index 00000000..77c4156e --- /dev/null +++ b/ent/usergroup_update.go @@ -0,0 +1,508 @@ +// 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/cloudreve/Cloudreve/v4/ent/group" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/cloudreve/Cloudreve/v4/ent/usergroup" +) + +// UserGroupUpdate is the builder for updating UserGroup entities. +type UserGroupUpdate struct { + config + hooks []Hook + mutation *UserGroupMutation +} + +// Where appends a list predicates to the UserGroupUpdate builder. +func (ugu *UserGroupUpdate) Where(ps ...predicate.UserGroup) *UserGroupUpdate { + ugu.mutation.Where(ps...) + return ugu +} + +// SetUserID sets the "user_id" field. +func (ugu *UserGroupUpdate) SetUserID(i int) *UserGroupUpdate { + ugu.mutation.SetUserID(i) + return ugu +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (ugu *UserGroupUpdate) SetNillableUserID(i *int) *UserGroupUpdate { + if i != nil { + ugu.SetUserID(*i) + } + return ugu +} + +// SetGroupID sets the "group_id" field. +func (ugu *UserGroupUpdate) SetGroupID(i int) *UserGroupUpdate { + ugu.mutation.SetGroupID(i) + return ugu +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (ugu *UserGroupUpdate) SetNillableGroupID(i *int) *UserGroupUpdate { + if i != nil { + ugu.SetGroupID(*i) + } + return ugu +} + +// SetIsPrimary sets the "is_primary" field. +func (ugu *UserGroupUpdate) SetIsPrimary(b bool) *UserGroupUpdate { + ugu.mutation.SetIsPrimary(b) + return ugu +} + +// SetNillableIsPrimary sets the "is_primary" field if the given value is not nil. +func (ugu *UserGroupUpdate) SetNillableIsPrimary(b *bool) *UserGroupUpdate { + if b != nil { + ugu.SetIsPrimary(*b) + } + return ugu +} + +// SetExpiresAt sets the "expires_at" field. +func (ugu *UserGroupUpdate) SetExpiresAt(t time.Time) *UserGroupUpdate { + ugu.mutation.SetExpiresAt(t) + return ugu +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (ugu *UserGroupUpdate) SetNillableExpiresAt(t *time.Time) *UserGroupUpdate { + if t != nil { + ugu.SetExpiresAt(*t) + } + return ugu +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (ugu *UserGroupUpdate) ClearExpiresAt() *UserGroupUpdate { + ugu.mutation.ClearExpiresAt() + return ugu +} + +// SetUser sets the "user" edge to the User entity. +func (ugu *UserGroupUpdate) SetUser(u *User) *UserGroupUpdate { + return ugu.SetUserID(u.ID) +} + +// SetGroup sets the "group" edge to the Group entity. +func (ugu *UserGroupUpdate) SetGroup(g *Group) *UserGroupUpdate { + return ugu.SetGroupID(g.ID) +} + +// Mutation returns the UserGroupMutation object of the builder. +func (ugu *UserGroupUpdate) Mutation() *UserGroupMutation { + return ugu.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (ugu *UserGroupUpdate) ClearUser() *UserGroupUpdate { + ugu.mutation.ClearUser() + return ugu +} + +// ClearGroup clears the "group" edge to the Group entity. +func (ugu *UserGroupUpdate) ClearGroup() *UserGroupUpdate { + ugu.mutation.ClearGroup() + return ugu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (ugu *UserGroupUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, ugu.sqlSave, ugu.mutation, ugu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (ugu *UserGroupUpdate) SaveX(ctx context.Context) int { + affected, err := ugu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (ugu *UserGroupUpdate) Exec(ctx context.Context) error { + _, err := ugu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ugu *UserGroupUpdate) ExecX(ctx context.Context) { + if err := ugu.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (ugu *UserGroupUpdate) check() error { + if _, ok := ugu.mutation.UserID(); ugu.mutation.UserCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "UserGroup.user"`) + } + if _, ok := ugu.mutation.GroupID(); ugu.mutation.GroupCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "UserGroup.group"`) + } + return nil +} + +func (ugu *UserGroupUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := ugu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(usergroup.Table, usergroup.Columns, sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt)) + if ps := ugu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := ugu.mutation.IsPrimary(); ok { + _spec.SetField(usergroup.FieldIsPrimary, field.TypeBool, value) + } + if value, ok := ugu.mutation.ExpiresAt(); ok { + _spec.SetField(usergroup.FieldExpiresAt, field.TypeTime, value) + } + if ugu.mutation.ExpiresAtCleared() { + _spec.ClearField(usergroup.FieldExpiresAt, field.TypeTime) + } + if ugu.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.UserTable, + Columns: []string{usergroup.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ugu.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.UserTable, + Columns: []string{usergroup.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if ugu.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.GroupTable, + Columns: []string{usergroup.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ugu.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.GroupTable, + Columns: []string{usergroup.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, ugu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{usergroup.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + ugu.mutation.done = true + return n, nil +} + +// UserGroupUpdateOne is the builder for updating a single UserGroup entity. +type UserGroupUpdateOne struct { + config + fields []string + hooks []Hook + mutation *UserGroupMutation +} + +// SetUserID sets the "user_id" field. +func (uguo *UserGroupUpdateOne) SetUserID(i int) *UserGroupUpdateOne { + uguo.mutation.SetUserID(i) + return uguo +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (uguo *UserGroupUpdateOne) SetNillableUserID(i *int) *UserGroupUpdateOne { + if i != nil { + uguo.SetUserID(*i) + } + return uguo +} + +// SetGroupID sets the "group_id" field. +func (uguo *UserGroupUpdateOne) SetGroupID(i int) *UserGroupUpdateOne { + uguo.mutation.SetGroupID(i) + return uguo +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (uguo *UserGroupUpdateOne) SetNillableGroupID(i *int) *UserGroupUpdateOne { + if i != nil { + uguo.SetGroupID(*i) + } + return uguo +} + +// SetIsPrimary sets the "is_primary" field. +func (uguo *UserGroupUpdateOne) SetIsPrimary(b bool) *UserGroupUpdateOne { + uguo.mutation.SetIsPrimary(b) + return uguo +} + +// SetNillableIsPrimary sets the "is_primary" field if the given value is not nil. +func (uguo *UserGroupUpdateOne) SetNillableIsPrimary(b *bool) *UserGroupUpdateOne { + if b != nil { + uguo.SetIsPrimary(*b) + } + return uguo +} + +// SetExpiresAt sets the "expires_at" field. +func (uguo *UserGroupUpdateOne) SetExpiresAt(t time.Time) *UserGroupUpdateOne { + uguo.mutation.SetExpiresAt(t) + return uguo +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (uguo *UserGroupUpdateOne) SetNillableExpiresAt(t *time.Time) *UserGroupUpdateOne { + if t != nil { + uguo.SetExpiresAt(*t) + } + return uguo +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (uguo *UserGroupUpdateOne) ClearExpiresAt() *UserGroupUpdateOne { + uguo.mutation.ClearExpiresAt() + return uguo +} + +// SetUser sets the "user" edge to the User entity. +func (uguo *UserGroupUpdateOne) SetUser(u *User) *UserGroupUpdateOne { + return uguo.SetUserID(u.ID) +} + +// SetGroup sets the "group" edge to the Group entity. +func (uguo *UserGroupUpdateOne) SetGroup(g *Group) *UserGroupUpdateOne { + return uguo.SetGroupID(g.ID) +} + +// Mutation returns the UserGroupMutation object of the builder. +func (uguo *UserGroupUpdateOne) Mutation() *UserGroupMutation { + return uguo.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (uguo *UserGroupUpdateOne) ClearUser() *UserGroupUpdateOne { + uguo.mutation.ClearUser() + return uguo +} + +// ClearGroup clears the "group" edge to the Group entity. +func (uguo *UserGroupUpdateOne) ClearGroup() *UserGroupUpdateOne { + uguo.mutation.ClearGroup() + return uguo +} + +// Where appends a list predicates to the UserGroupUpdate builder. +func (uguo *UserGroupUpdateOne) Where(ps ...predicate.UserGroup) *UserGroupUpdateOne { + uguo.mutation.Where(ps...) + return uguo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (uguo *UserGroupUpdateOne) Select(field string, fields ...string) *UserGroupUpdateOne { + uguo.fields = append([]string{field}, fields...) + return uguo +} + +// Save executes the query and returns the updated UserGroup entity. +func (uguo *UserGroupUpdateOne) Save(ctx context.Context) (*UserGroup, error) { + return withHooks(ctx, uguo.sqlSave, uguo.mutation, uguo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (uguo *UserGroupUpdateOne) SaveX(ctx context.Context) *UserGroup { + node, err := uguo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (uguo *UserGroupUpdateOne) Exec(ctx context.Context) error { + _, err := uguo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (uguo *UserGroupUpdateOne) ExecX(ctx context.Context) { + if err := uguo.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (uguo *UserGroupUpdateOne) check() error { + if _, ok := uguo.mutation.UserID(); uguo.mutation.UserCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "UserGroup.user"`) + } + if _, ok := uguo.mutation.GroupID(); uguo.mutation.GroupCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "UserGroup.group"`) + } + return nil +} + +func (uguo *UserGroupUpdateOne) sqlSave(ctx context.Context) (_node *UserGroup, err error) { + if err := uguo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(usergroup.Table, usergroup.Columns, sqlgraph.NewFieldSpec(usergroup.FieldID, field.TypeInt)) + id, ok := uguo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UserGroup.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := uguo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, usergroup.FieldID) + for _, f := range fields { + if !usergroup.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != usergroup.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := uguo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := uguo.mutation.IsPrimary(); ok { + _spec.SetField(usergroup.FieldIsPrimary, field.TypeBool, value) + } + if value, ok := uguo.mutation.ExpiresAt(); ok { + _spec.SetField(usergroup.FieldExpiresAt, field.TypeTime, value) + } + if uguo.mutation.ExpiresAtCleared() { + _spec.ClearField(usergroup.FieldExpiresAt, field.TypeTime) + } + if uguo.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.UserTable, + Columns: []string{usergroup.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uguo.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.UserTable, + Columns: []string{usergroup.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if uguo.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.GroupTable, + Columns: []string{usergroup.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uguo.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: usergroup.GroupTable, + Columns: []string{usergroup.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &UserGroup{config: uguo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, uguo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{usergroup.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + uguo.mutation.done = true + return _node, nil +} diff --git a/inventory/group.go b/inventory/group.go index c40b6e89..41341bda 100644 --- a/inventory/group.go +++ b/inventory/group.go @@ -16,6 +16,9 @@ type ( ) const ( + AdminGroupID = 1 + UserGroupID = 2 + // Deprecated: AnonymousGroupID is no longer used. AnonymousGroupID = 3 ) @@ -23,6 +26,7 @@ type ( GroupClient interface { TxOperator // AnonymousGroup returns the anonymous group. + // Deprecated: anonymous group is no longer used. AnonymousGroup(ctx context.Context) (*ent.Group, error) // ListAll returns all groups. ListAll(ctx context.Context) ([]*ent.Group, error) diff --git a/inventory/user.go b/inventory/user.go index 26470ed9..242b6aea 100644 --- a/inventory/user.go +++ b/inventory/user.go @@ -16,6 +16,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/ent/davaccount" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/schema" "github.com/cloudreve/Cloudreve/v4/ent/task" @@ -288,8 +289,8 @@ func (c *userClient) Create(ctx context.Context, args *NewUserArgs) (*ent.User, SetEmail(args.Email). SetNick(nick). SetStatus(args.Status). - SetGroupID(args.GroupID). - SetAvatar(args.Avatar) + SetAvatar(args.Avatar). + AddGroupIDs(args.GroupID) if args.PlainPassword != "" { pwdDigest, err := digestPassword(args.PlainPassword) @@ -313,7 +314,7 @@ func (c *userClient) Create(ctx context.Context, args *NewUserArgs) (*ent.User, if newUser.ID == 1 { // For the first user registered, elevate it to admin group. - if _, err := newUser.Update().SetGroupID(1).Save(ctx); err != nil { + if _, err := newUser.Update().AddGroupIDs(AdminGroupID).Save(ctx); err != nil { return newUser, fmt.Errorf("failed to elevate user to admin: %w", err) } } @@ -403,24 +404,18 @@ func UserIDFromContext(ctx context.Context) int { } func (c *userClient) AnonymousUser(ctx context.Context) (*ent.User, error) { - groupClient := NewGroupClient(c.client, "", nil) - anonymousGroup, err := groupClient.AnonymousGroup(ctx) - if err != nil { - return nil, fmt.Errorf("anyonymous group not found: %w", err) - } - // TODO: save into cache anonymous := &ent.User{ Settings: &types.UserSetting{}, } - anonymous.SetGroup(anonymousGroup) + anonymous.SetGroups(nil) return anonymous, nil } func (c *userClient) ListUsers(ctx context.Context, args *ListUserParameters) (*ListUserResult, error) { query := c.client.User.Query() if args.GroupID != 0 { - query = query.Where(user.GroupUsers(args.GroupID)) + query = query.QueryGroups().Where(group.ID(args.GroupID)).QueryUsers() } if args.Status != "" { query = query.Where(user.StatusEQ(args.Status)) @@ -461,7 +456,7 @@ func (c *userClient) Upsert(ctx context.Context, u *ent.User, password, twoFa st SetNick(u.Nick). SetAvatar(u.Avatar). SetStatus(u.Status). - SetGroupID(u.GroupUsers). + //SetGroupID(u.GroupUsers). SetPassword(u.Password). SetSettings(&types.UserSetting{}) @@ -480,8 +475,8 @@ func (c *userClient) Upsert(ctx context.Context, u *ent.User, password, twoFa st SetEmail(u.Email). SetNick(u.Nick). SetAvatar(u.Avatar). - SetStatus(u.Status). - SetGroupID(u.GroupUsers) + SetStatus(u.Status) + //SetGroupID(u.GroupUsers) if password != "" { pwdDigest, err := digestPassword(password) @@ -567,7 +562,7 @@ func CheckPassword(u *ent.User, password string) error { func withUserEagerLoading(ctx context.Context, q *ent.UserQuery) *ent.UserQuery { if v, ok := ctx.Value(LoadUserGroup{}).(bool); ok && v { - q.WithGroup(func(gq *ent.GroupQuery) { + q.WithGroups(func(gq *ent.GroupQuery) { withGroupEagerLoading(ctx, gq) }) } diff --git a/middleware/auth.go b/middleware/auth.go index 4df7ed7f..cc357e1f 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -7,16 +7,16 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/inventory" "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/auth" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/driver/oss" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager" "github.com/cloudreve/Cloudreve/v4/pkg/logging" "github.com/cloudreve/Cloudreve/v4/pkg/request" - "github.com/cloudreve/Cloudreve/v4/pkg/util" - - "github.com/cloudreve/Cloudreve/v4/pkg/auth" "github.com/cloudreve/Cloudreve/v4/pkg/serializer" + "github.com/cloudreve/Cloudreve/v4/pkg/util" "github.com/gin-gonic/gin" + "github.com/samber/lo" ) const ( @@ -144,7 +144,7 @@ func WebDAVAuth() gin.HandlerFunc { } // 用户组已启用WebDAV? - group, err := expectedUser.Edges.GroupOrErr() + groups, err := expectedUser.Edges.GroupsOrErr() if err != nil { l.Debug("WebDAVAuth: user group not found: %s", err) c.Status(http.StatusInternalServerError) @@ -152,7 +152,9 @@ func WebDAVAuth() gin.HandlerFunc { return } - if !group.Permissions.Enabled(int(types.GroupPermissionWebDAV)) { + if !lo.ContainsBy(groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionWebDAV)) + }) { c.Status(http.StatusForbidden) l.Debug("WebDAVAuth: user %q does not have WebDAV permission.", expectedUser.Email) c.Abort() @@ -262,7 +264,9 @@ func OSSCallbackAuth() gin.HandlerFunc { func IsAdmin() gin.HandlerFunc { return func(c *gin.Context) { user := inventory.UserFromContext(c) - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + if !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { c.JSON(200, serializer.ErrWithDetails(c, serializer.CodeNoPermissionErr, "", nil)) c.Abort() return diff --git a/pkg/filemanager/fs/dbfs/dbfs.go b/pkg/filemanager/fs/dbfs/dbfs.go index cb7d3dc7..72e6e91a 100644 --- a/pkg/filemanager/fs/dbfs/dbfs.go +++ b/pkg/filemanager/fs/dbfs/dbfs.go @@ -228,13 +228,15 @@ func (f *DBFS) Capacity(ctx context.Context, u *ent.User) (*fs.Capacity, error) res = &fs.Capacity{} ) - requesterGroup, err := u.Edges.GroupOrErr() + requesterGroup, err := u.Edges.GroupsOrErr() if err != nil { return nil, serializer.NewError(serializer.CodeDBError, "Failed to get user's group", err) } res.Used = f.user.Storage - res.Total = requesterGroup.MaxStorage + res.Total = lo.Max(lo.Map(requesterGroup, func(g *ent.Group, _ int) int64 { + return g.MaxStorage + })) return res, nil } @@ -416,7 +418,9 @@ func (f *DBFS) Get(ctx context.Context, path *fs.URI, opts ...fs.Option) (fs.Fil } target.FileExtendedInfo = extendedInfo - if target.OwnerID() == f.user.ID || f.user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + if target.OwnerID() == f.user.ID || lo.ContainsBy(f.user.Edges.Groups, func(g *ent.Group) bool { + return g.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { target.FileExtendedInfo.Shares = target.Model.Edges.Shares if target.Model.Props != nil { target.FileExtendedInfo.View = target.Model.Props.View @@ -450,10 +454,12 @@ func (f *DBFS) Get(ctx context.Context, path *fs.URI, opts ...fs.Option) (fs.Fil } else { // cache miss, walk the folder to get the summary newSummary := &fs.FolderSummary{Completed: true} - if f.user.Edges.Group == nil { + if len(f.user.Edges.Groups) == 0 { return nil, fmt.Errorf("user group not loaded") } - limit := max(f.user.Edges.Group.Settings.MaxWalkedFiles, 1) + limit := max(lo.Max(lo.Map(f.user.Edges.Groups, func(g *ent.Group, _ int) int { + return g.Settings.MaxWalkedFiles + })), 1) // disable load metadata to speed up ctxWalk := context.WithValue(ctx, inventory.LoadFilePublicMetadata{}, false) @@ -539,10 +545,12 @@ func (f *DBFS) Walk(ctx context.Context, path *fs.URI, depth int, walk fs.WalkFu } // Walk - if f.user.Edges.Group == nil { + if len(f.user.Edges.Groups) == 0 { return fmt.Errorf("user group not loaded") } - limit := max(f.user.Edges.Group.Settings.MaxWalkedFiles, 1) + limit := max(lo.Max(lo.Map(f.user.Edges.Groups, func(g *ent.Group, _ int) int { + return g.Settings.MaxWalkedFiles + })), 1) if err := navigator.Walk(ctx, []*File{target}, limit, depth, func(files []*File, l int) error { for _, file := range files { @@ -637,12 +645,12 @@ func (f *DBFS) createFile(ctx context.Context, parent *File, name string, fileTy // getPreferredPolicy tries to get the preferred storage policy for the given file. func (f *DBFS) getPreferredPolicy(ctx context.Context, file *File) (*ent.StoragePolicy, error) { - ownerGroup := file.Owner().Edges.Group - if ownerGroup == nil { + ownerGroup := file.Owner().Edges.Groups + if len(ownerGroup) == 0 { return nil, fmt.Errorf("owner group not loaded") } - groupPolicy, err := f.storagePolicyClient.GetByGroup(ctx, ownerGroup) + groupPolicy, err := f.storagePolicyClient.GetByGroup(ctx, ownerGroup[0]) if err != nil { return nil, serializer.NewError(serializer.CodeDBError, "Failed to get available storage policies", err) } diff --git a/pkg/filemanager/fs/dbfs/manage.go b/pkg/filemanager/fs/dbfs/manage.go index 00d5542f..72bfde55 100644 --- a/pkg/filemanager/fs/dbfs/manage.go +++ b/pkg/filemanager/fs/dbfs/manage.go @@ -267,12 +267,13 @@ func (f *DBFS) SoftDelete(ctx context.Context, path ...*fs.URI) error { _ = inventory.Rollback(tx) return serializer.NewError(serializer.CodeDBError, "failed to soft-delete file", err) } - // Save restore uri into metadata if err := fc.UpsertMetadata(ctx, target.Model, map[string]string{ MetadataRestoreUri: target.Uri(true).String(), MetadataExpectedCollectTime: strconv.FormatInt( - time.Now().Add(time.Duration(target.Owner().Edges.Group.Settings.TrashRetention)*time.Second).Unix(), + time.Now().Add(time.Duration(lo.Max(lo.Map(target.Owner().Edges.Groups, func(item *ent.Group, index int) int { + return item.Settings.TrashRetention + })))*time.Second).Unix(), 10), }, nil); err != nil { _ = inventory.Rollback(tx) @@ -647,7 +648,9 @@ func (f *DBFS) TraverseFile(ctx context.Context, fileID int) (fs.File, error) { return nil, err } - if fileModel.OwnerID != f.user.ID && !f.user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + if fileModel.OwnerID != f.user.ID && !lo.ContainsBy(f.user.Edges.Groups, func(g *ent.Group) bool { + return g.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { return nil, fs.ErrOwnerOnly.WithError(fmt.Errorf("only file owner can traverse file's uri")) } @@ -739,10 +742,12 @@ func (f *DBFS) setCurrentVersion(ctx context.Context, target *File, versionId in } func (f *DBFS) deleteFiles(ctx context.Context, targets map[Navigator][]*File, fc inventory.FileClient, opt *types.EntityRecycleOption) ([]fs.Entity, inventory.StorageDiff, error) { - if f.user.Edges.Group == nil { + if len(f.user.Edges.Groups) == 0 { return nil, nil, fmt.Errorf("user group not loaded") } - limit := max(f.user.Edges.Group.Settings.MaxWalkedFiles, 1) + limit := max(lo.Max(lo.Map(f.user.Edges.Groups, func(g *ent.Group, index int) int { + return g.Settings.MaxWalkedFiles + })), 1) allStaleEntities := make([]fs.Entity, 0, len(targets)) storageDiff := make(inventory.StorageDiff) for n, files := range targets { @@ -781,10 +786,12 @@ func (f *DBFS) deleteFiles(ctx context.Context, targets map[Navigator][]*File, f } func (f *DBFS) copyFiles(ctx context.Context, targets map[Navigator][]*File, destination *File, fc inventory.FileClient) (map[int]*ent.File, inventory.StorageDiff, error) { - if f.user.Edges.Group == nil { + if len(f.user.Edges.Groups) == 0 { return nil, nil, fmt.Errorf("user group not loaded") } - limit := max(f.user.Edges.Group.Settings.MaxWalkedFiles, 1) + limit := max(lo.Max(lo.Map(f.user.Edges.Groups, func(g *ent.Group, index int) int { + return g.Settings.MaxWalkedFiles + })), 1) capacity, err := f.Capacity(ctx, destination.Owner()) if err != nil { return nil, nil, fmt.Errorf("copy files: failed to destination owner capacity: %w", err) diff --git a/pkg/filemanager/fs/dbfs/my_navigator.go b/pkg/filemanager/fs/dbfs/my_navigator.go index b607ba21..0b922ae4 100644 --- a/pkg/filemanager/fs/dbfs/my_navigator.go +++ b/pkg/filemanager/fs/dbfs/my_navigator.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/samber/lo" "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/ent/user" @@ -92,7 +93,9 @@ func (n *myNavigator) To(ctx context.Context, path *fs.URI) (*File, error) { return nil, fs.ErrPathNotExist.WithError(fmt.Errorf("user not found: %w", err)) } - if targetUser.Status != user.StatusActive && !n.user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + if targetUser.Status != user.StatusActive && !lo.ContainsBy(n.user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { return nil, fs.ErrPathNotExist.WithError(fmt.Errorf("inactive user")) } diff --git a/pkg/filemanager/fs/dbfs/props.go b/pkg/filemanager/fs/dbfs/props.go index 34c29100..c72c6421 100644 --- a/pkg/filemanager/fs/dbfs/props.go +++ b/pkg/filemanager/fs/dbfs/props.go @@ -3,6 +3,7 @@ package dbfs import ( "context" "fmt" + "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/inventory" "github.com/cloudreve/Cloudreve/v4/inventory/types" @@ -22,7 +23,9 @@ func (f *DBFS) PatchProps(ctx context.Context, uri *fs.URI, props *types.FilePro return fmt.Errorf("failed to get target file: %w", err) } - if target.OwnerID() != f.user.ID && !f.user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + if target.OwnerID() != f.user.ID && !lo.ContainsBy(f.user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { return fs.ErrOwnerOnly.WithError(fmt.Errorf("only file owner can modify file props")) } diff --git a/pkg/filemanager/fs/dbfs/share_navigator.go b/pkg/filemanager/fs/dbfs/share_navigator.go index 374994f3..175d29de 100644 --- a/pkg/filemanager/fs/dbfs/share_navigator.go +++ b/pkg/filemanager/fs/dbfs/share_navigator.go @@ -3,6 +3,7 @@ package dbfs import ( "context" "fmt" + "github.com/samber/lo" "github.com/cloudreve/Cloudreve/v4/application/constants" "github.com/cloudreve/Cloudreve/v4/ent" @@ -156,7 +157,9 @@ func (n *shareNavigator) Root(ctx context.Context, path *fs.URI) (*File, error) return nil, ErrShareNotFound } - if n.user.ID != n.owner.ID && !n.user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionShareDownload)) { + if n.user.ID != n.owner.ID && !lo.ContainsBy(n.user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionShareDownload)) + }) { return nil, serializer.NewError( serializer.CodeNoPermissionErr, fmt.Sprintf("You don't have permission to access share links"), diff --git a/pkg/filemanager/manager/entity.go b/pkg/filemanager/manager/entity.go index 7198688c..cd240741 100644 --- a/pkg/filemanager/manager/entity.go +++ b/pkg/filemanager/manager/entity.go @@ -61,7 +61,9 @@ type ( func (m *manager) GetDirectLink(ctx context.Context, urls ...*fs.URI) ([]DirectLink, error) { ae := serializer.NewAggregateError() res := make([]DirectLink, 0, len(urls)) - useRedirect := m.user.Edges.Group.Settings.RedirectedSource + useRedirect := lo.ContainsBy(m.user.Edges.Groups, func(item *ent.Group) bool { + return item.Settings.RedirectedSource + }) fileClient := m.dep.FileClient() siteUrl := m.settings.SiteURL(ctx) @@ -76,7 +78,9 @@ func (m *manager) GetDirectLink(ctx context.Context, urls ...*fs.URI) ([]DirectL continue } - if file.OwnerID() != m.user.ID && !m.user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + if file.OwnerID() != m.user.ID && !lo.ContainsBy(m.user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { ae.Add(url.String(), fs.ErrOwnerOnly) continue } @@ -99,7 +103,9 @@ func (m *manager) GetDirectLink(ctx context.Context, urls ...*fs.URI) ([]DirectL if useRedirect { // Use redirect source - link, err := fileClient.CreateDirectLink(ctx, file.ID(), file.Name(), m.user.Edges.Group.SpeedLimit) + link, err := fileClient.CreateDirectLink(ctx, file.ID(), file.Name(), lo.Max(lo.Map(m.user.Edges.Groups, func(item *ent.Group, _ int) int { + return item.SpeedLimit + }))) if err != nil { ae.Add(url.String(), err) continue @@ -121,7 +127,9 @@ func (m *manager) GetDirectLink(ctx context.Context, urls ...*fs.URI) ([]DirectL source := entitysource.NewEntitySource(target, d, policy, m.auth, m.settings, m.hasher, m.dep.RequestClient(), m.l, m.config, m.dep.MimeDetector(ctx)) sourceUrl, err := source.Url(ctx, - entitysource.WithSpeedLimit(int64(m.user.Edges.Group.SpeedLimit)), + entitysource.WithSpeedLimit(int64(lo.Max(lo.Map(m.user.Edges.Groups, func(item *ent.Group, _ int) int { + return item.SpeedLimit + })))), entitysource.WithDisplayName(file.Name()), ) if err != nil { diff --git a/pkg/filemanager/workflows/archive.go b/pkg/filemanager/workflows/archive.go index bcef9bb2..67aaad16 100644 --- a/pkg/filemanager/workflows/archive.go +++ b/pkg/filemanager/workflows/archive.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/samber/lo" "io" "os" "path/filepath" @@ -233,7 +234,9 @@ func (m *CreateArchiveTask) listEntitiesAndSendToSlave(ctx context.Context, dep } } }), - fs.WithMaxArchiveSize(user.Edges.Group.Settings.CompressSize), + fs.WithMaxArchiveSize(lo.Max(lo.Map(user.Edges.Groups, func(item *ent.Group, index int) int64 { + return item.Settings.CompressSize + }))), ) if err != nil { return task.StatusError, fmt.Errorf("failed to compress files: %w", err) @@ -390,7 +393,9 @@ func (m *CreateArchiveTask) createArchiveFile(ctx context.Context, dep dependenc m.Unlock() failed, err := fm.CreateArchive(ctx, uris, zipFile, fs.WithArchiveCompression(true), - fs.WithMaxArchiveSize(user.Edges.Group.Settings.CompressSize), + fs.WithMaxArchiveSize(lo.Max(lo.Map(user.Edges.Groups, func(item *ent.Group, index int) int64 { + return item.Settings.CompressSize + }))), fs.WithProgressFunc(func(current, diff int64, total int64) { atomic.AddInt64(&m.progress[ProgressTypeArchiveSize].Current, diff) atomic.AddInt64(&m.progress[ProgressTypeArchiveCount].Current, 1) diff --git a/pkg/filemanager/workflows/extract.go b/pkg/filemanager/workflows/extract.go index 7126e60b..c3d3dcf6 100644 --- a/pkg/filemanager/workflows/extract.go +++ b/pkg/filemanager/workflows/extract.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/samber/lo" "io" "os" "path" @@ -178,9 +179,12 @@ func (m *ExtractArchiveTask) createSlaveExtractTask(ctx context.Context, dep dep } // Validate file size - if user.Edges.Group.Settings.DecompressSize > 0 && archiveFile.Size() > user.Edges.Group.Settings.DecompressSize { + decompressSize := lo.Max(lo.Map(user.Edges.Groups, func(item *ent.Group, index int) int64 { + return item.Settings.DecompressSize + })) + if decompressSize > 0 && archiveFile.Size() > decompressSize { return task.StatusError, - fmt.Errorf("file size %d exceeds the limit %d (%w)", archiveFile.Size(), user.Edges.Group.Settings.DecompressSize, queue.CriticalErr) + fmt.Errorf("file size %d exceeds the limit %d (%w)", archiveFile.Size(), decompressSize, queue.CriticalErr) } // Create slave task @@ -263,9 +267,12 @@ func (m *ExtractArchiveTask) masterExtractArchive(ctx context.Context, dep depen } // Validate file size - if user.Edges.Group.Settings.DecompressSize > 0 && archiveFile.Size() > user.Edges.Group.Settings.DecompressSize { + decompressSize := lo.Max(lo.Map(user.Edges.Groups, func(item *ent.Group, index int) int64 { + return item.Settings.DecompressSize + })) + if decompressSize > 0 && archiveFile.Size() > decompressSize { return task.StatusError, - fmt.Errorf("file size %d exceeds the limit %d (%w)", archiveFile.Size(), user.Edges.Group.Settings.DecompressSize, queue.CriticalErr) + fmt.Errorf("file size %d exceeds the limit %d (%w)", archiveFile.Size(), decompressSize, queue.CriticalErr) } es, err := fm.GetEntitySource(ctx, 0, fs.WithEntity(archiveFile.PrimaryEntity())) diff --git a/pkg/filemanager/workflows/remote_download.go b/pkg/filemanager/workflows/remote_download.go index 7b06178d..e318ae2b 100644 --- a/pkg/filemanager/workflows/remote_download.go +++ b/pkg/filemanager/workflows/remote_download.go @@ -198,7 +198,7 @@ func (m *RemoteDownloadTask) createDownloadTask(ctx context.Context, dep depende } // Create download task - handle, err := m.d.CreateTask(ctx, torrentUrl, user.Edges.Group.Settings.RemoteDownloadOptions) + handle, err := m.d.CreateTask(ctx, torrentUrl, user.Edges.Groups[0].Settings.RemoteDownloadOptions) if err != nil { return task.StatusError, fmt.Errorf("failed to create download task: %w", err) } diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index da20873b..265e8a1e 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -340,10 +340,14 @@ func handleGetHeadPost(c *gin.Context, user *ent.User, fm manager.FileManager) ( defer es.Close() - es.Apply(entitysource.WithSpeedLimit(int64(user.Edges.Group.SpeedLimit))) + es.Apply(entitysource.WithSpeedLimit(int64(lo.Max(lo.Map(user.Edges.Groups, func(item *ent.Group, index int) int { + return item.SpeedLimit + }))))) if es.ShouldInternalProxy() || (user.Edges.DavAccounts[0].Options.Enabled(int(types.DavAccountProxy)) && - user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionWebDAVProxy))) { + lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionWebDAVProxy)) + })) { es.Serve(c.Writer, c.Request) } else { settings := dependency.FromContext(c).SettingProvider() diff --git a/service/admin/user.go b/service/admin/user.go index 8ae2aea9..4484314b 100644 --- a/service/admin/user.go +++ b/service/admin/user.go @@ -154,10 +154,12 @@ func (s *UpsertUserService) Update(c *gin.Context) (*GetUserResponse, error) { return nil, serializer.NewError(serializer.CodeDBError, "Failed to get user", err) } - if s.User.ID == 1 && existing.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { - if s.User.GroupUsers != existing.GroupUsers { - return nil, serializer.NewError(serializer.CodeInvalidActionOnDefaultUser, "Cannot change default user's group", nil) - } + if s.User.ID == 1 && lo.ContainsBy(existing.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { + //if s.User.GroupUsers != existing.GroupUsers { + // return nil, serializer.NewError(serializer.CodeInvalidActionOnDefaultUser, "Cannot change default user's group", nil) + //} if s.User.Status != user.StatusActive { return nil, serializer.NewError(serializer.CodeInvalidActionOnDefaultUser, "Cannot change default user's status", nil) diff --git a/service/explorer/file.go b/service/explorer/file.go index 83aa8356..88e16f4f 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -4,6 +4,7 @@ import ( "context" "encoding/gob" "fmt" + "github.com/cloudreve/Cloudreve/v4/ent" "net/http" "time" @@ -100,11 +101,14 @@ func (s *GetDirectLinkService) Get(c *gin.Context) ([]DirectLinkResponse, error) dep := dependency.FromContext(c) u := inventory.UserFromContext(c) - if u.Edges.Group.Settings.SourceBatchSize == 0 { + sourceBatchSize := lo.Max(lo.Map(u.Edges.Groups, func(item *ent.Group, index int) int { + return item.Settings.SourceBatchSize + })) + if sourceBatchSize == 0 { return nil, serializer.NewError(serializer.CodeGroupNotAllowed, "", nil) } - if len(s.Uris) > u.Edges.Group.Settings.SourceBatchSize { + if len(s.Uris) > sourceBatchSize { return nil, serializer.NewError(serializer.CodeBatchSourceSize, "", nil) } @@ -369,7 +373,9 @@ func (s *FileURLService) GetArchiveDownloadSession(c *gin.Context) (*FileURLResp return nil, serializer.NewError(serializer.CodeParamErr, "unknown uri", err) } - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionArchiveDownload)) { + if !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionArchiveDownload)) + }) { return nil, serializer.NewError(serializer.CodeGroupNotAllowed, "", nil) } @@ -429,7 +435,9 @@ func (s *FileURLService) Get(c *gin.Context) (*FileURLResponse, error) { } res, earliestExpire, err := m.GetEntityUrls(ctx, urlReq, - fs.WithDownloadSpeed(int64(user.Edges.Group.SpeedLimit)), + fs.WithDownloadSpeed(int64(lo.Max(lo.Map(user.Edges.Groups, func(item *ent.Group, index int) int { + return item.SpeedLimit + })))), fs.WithIsDownload(s.Download), fs.WithNoCache(s.NoCache), fs.WithUrlExpire(&expire), @@ -522,7 +530,9 @@ func (s *DeleteFileService) Delete(c *gin.Context) error { return serializer.NewError(serializer.CodeParamErr, "unknown uri", err) } - if s.UnlinkOnly && !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionAdvanceDelete)) { + if s.UnlinkOnly && !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionAdvanceDelete)) + }) { return serializer.NewError(serializer.CodeNoPermissionErr, "advance delete permission is required", nil) } diff --git a/service/explorer/workflows.go b/service/explorer/workflows.go index 7d54bfc6..9db021cf 100644 --- a/service/explorer/workflows.go +++ b/service/explorer/workflows.go @@ -85,7 +85,9 @@ func (service *DownloadWorkflowService) CreateDownloadTask(c *gin.Context) ([]*T m := manager.NewFileManager(dep, user) defer m.Recycle() - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionRemoteDownload)) { + if !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionRemoteDownload)) + }) { return nil, serializer.NewError(serializer.CodeGroupNotAllowed, "Group not allowed to download files", nil) } @@ -111,7 +113,9 @@ func (service *DownloadWorkflowService) CreateDownloadTask(c *gin.Context) ([]*T } // 检查批量任务数量 - limit := user.Edges.Group.Settings.Aria2BatchSize + limit := lo.Max(lo.Map(user.Edges.Groups, func(item *ent.Group, index int) int { + return item.Settings.Aria2BatchSize + })) if limit > 0 && len(service.Src) > limit { return nil, serializer.NewError(serializer.CodeBatchAria2Size, "", nil) } @@ -184,7 +188,9 @@ func (service *ArchiveWorkflowService) CreateExtractTask(c *gin.Context) (*TaskR m := manager.NewFileManager(dep, user) defer m.Recycle() - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionArchiveTask)) { + if !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionArchiveTask)) + }) { return nil, serializer.NewError(serializer.CodeGroupNotAllowed, "Group not allowed to compress files", nil) } @@ -223,7 +229,9 @@ func (service *ArchiveWorkflowService) CreateCompressTask(c *gin.Context) (*Task m := manager.NewFileManager(dep, user) defer m.Recycle() - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionArchiveTask)) { + if !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionArchiveTask)) + }) { return nil, serializer.NewError(serializer.CodeGroupNotAllowed, "Group not allowed to compress files", nil) } @@ -278,7 +286,9 @@ func (service *ImportWorkflowService) CreateImportTask(c *gin.Context) (*TaskRes m := manager.NewFileManager(dep, user) defer m.Recycle() - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + if !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { return nil, serializer.NewError(serializer.CodeGroupNotAllowed, "Only admin can import files", nil) } @@ -386,7 +396,10 @@ func TaskPhaseProgress(c *gin.Context, taskID int) (queue.Progresses, error) { u := inventory.UserFromContext(c) r := dep.TaskRegistry() t, found := r.Get(taskID) - if !found || (t.Owner().ID != u.ID && !u.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin))) { + + if !found || (t.Owner().ID != u.ID && !lo.ContainsBy(u.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + })) { return queue.Progresses{}, nil } diff --git a/service/setting/webdav.go b/service/setting/webdav.go index 640776c7..a03d7826 100644 --- a/service/setting/webdav.go +++ b/service/setting/webdav.go @@ -12,6 +12,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/pkg/serializer" "github.com/cloudreve/Cloudreve/v4/pkg/util" "github.com/gin-gonic/gin" + "github.com/samber/lo" ) // WebDAVAccountService WebDAV 账号管理服务 @@ -153,7 +154,9 @@ func (service *CreateDavAccountService) Update(c *gin.Context) (*DavAccount, err } func (service *CreateDavAccountService) validateAndGetBs(user *ent.User) (*boolset.BooleanSet, error) { - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionWebDAV)) { + if !lo.ContainsBy(user.Edges.Groups, func(g *ent.Group) bool { + return g.Permissions.Enabled(int(types.GroupPermissionWebDAV)) + }) { return nil, serializer.NewError(serializer.CodeGroupNotAllowed, "WebDAV is not enabled for this user group", nil) } @@ -173,7 +176,9 @@ func (service *CreateDavAccountService) validateAndGetBs(user *ent.User) (*bools boolset.Set(types.DavAccountReadOnly, true, &bs) } - if service.Proxy && user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionWebDAVProxy)) { + if service.Proxy && lo.ContainsBy(user.Edges.Groups, func(g *ent.Group) bool { + return g.Permissions.Enabled(int(types.GroupPermissionWebDAVProxy)) + }) { boolset.Set(types.DavAccountProxy, true, &bs) } return &bs, nil diff --git a/service/share/manage.go b/service/share/manage.go index f3c3fdf2..2fa74562 100644 --- a/service/share/manage.go +++ b/service/share/manage.go @@ -2,6 +2,7 @@ package share import ( "context" + "github.com/samber/lo" "time" "github.com/cloudreve/Cloudreve/v4/application/dependency" @@ -36,7 +37,9 @@ func (service *ShareCreateService) Upsert(c *gin.Context, existed int) (string, defer m.Recycle() // Check group permission for creating share link - if !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionShare)) { + if !lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionShare)) + }) { return "", serializer.NewError(serializer.CodeGroupNotAllowed, "Group permission denied", nil) } @@ -77,7 +80,10 @@ func DeleteShare(c *gin.Context, shareId int) error { share *ent.Share err error ) - if user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) { + + if lo.ContainsBy(user.Edges.Groups, func(item *ent.Group) bool { + return item.Permissions.Enabled(int(types.GroupPermissionIsAdmin)) + }) { share, err = shareClient.GetByID(ctx, shareId) } else { share, err = shareClient.GetByIDUser(ctx, shareId, user.ID) diff --git a/service/user/response.go b/service/user/response.go index e83e99ed..b29bbbf2 100644 --- a/service/user/response.go +++ b/service/user/response.go @@ -105,7 +105,7 @@ type User struct { CreatedAt time.Time `json:"created_at"` PreferredTheme string `json:"preferred_theme,omitempty"` Anonymous bool `json:"anonymous,omitempty"` - Group *Group `json:"group,omitempty"` + Groups []*Group `json:"groups,omitempty"` Pined []types.PinedFile `json:"pined,omitempty"` Language string `json:"language,omitempty"` DisableViewSync bool `json:"disable_view_sync,omitempty"` @@ -153,15 +153,17 @@ func BuildWebAuthnList(credentials []webauthn.Credential) []WebAuthnCredentials // BuildUser 序列化用户 func BuildUser(user *ent.User, idEncoder hashid.Encoder) User { return User{ - ID: hashid.EncodeUserID(idEncoder, user.ID), - Email: user.Email, - Nickname: user.Nick, - Status: user.Status, - Avatar: user.Avatar, - CreatedAt: user.CreatedAt, - PreferredTheme: user.Settings.PreferredTheme, - Anonymous: user.ID == 0, - Group: BuildGroup(user.Edges.Group, idEncoder), + ID: hashid.EncodeUserID(idEncoder, user.ID), + Email: user.Email, + Nickname: user.Nick, + Status: user.Status, + Avatar: user.Avatar, + CreatedAt: user.CreatedAt, + PreferredTheme: user.Settings.PreferredTheme, + Anonymous: user.ID == 0, + Groups: lo.Map(user.Edges.Groups, func(item *ent.Group, index int) *Group { + return BuildGroup(item, idEncoder) + }), Pined: user.Settings.Pined, Language: user.Settings.Language, DisableViewSync: user.Settings.DisableViewSync, @@ -199,8 +201,10 @@ func BuildUserRedacted(u *ent.User, level int, idEncoder hashid.Encoder) User { CreatedAt: userRaw.CreatedAt, } - if userRaw.Group != nil { - user.Group = RedactedGroup(userRaw.Group) + if userRaw.Groups != nil { + user.Groups = lo.Map(userRaw.Groups, func(item *Group, index int) *Group { + return RedactedGroup(item) + }) } if level == RedactLevelUser {