diff --git a/application/application.go b/application/application.go index 20788810..591faa28 100644 --- a/application/application.go +++ b/application/application.go @@ -178,6 +178,8 @@ func (s *server) Close() { defer cancel() } + s.dep.EventHub().Close() + // Shutdown http server if s.server != nil { err := s.server.Shutdown(ctx) diff --git a/application/dependency/dependency.go b/application/dependency/dependency.go index ae965538..06f10dd2 100644 --- a/application/dependency/dependency.go +++ b/application/dependency/dependency.go @@ -18,6 +18,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/pkg/credmanager" "github.com/cloudreve/Cloudreve/v4/pkg/email" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/encrypt" + "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/eventhub" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs/mime" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/lock" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" @@ -134,6 +135,8 @@ type Dep interface { MasterEncryptKeyVault(ctx context.Context) encrypt.MasterEncryptKeyVault // EncryptorFactory Get a new encrypt.CryptorFactory instance. EncryptorFactory(ctx context.Context) encrypt.CryptorFactory + // EventHub Get a singleton eventhub.EventHub instance for event publishing. + EventHub() eventhub.EventHub } type dependency struct { @@ -156,6 +159,7 @@ type dependency struct { nodeClient inventory.NodeClient davAccountClient inventory.DavAccountClient directLinkClient inventory.DirectLinkClient + fsEventClient inventory.FsEventClient emailClient email.Driver generalAuth auth.Auth hashidEncoder hashid.Encoder @@ -179,6 +183,7 @@ type dependency struct { parser *uaparser.Parser cron *cron.Cron masterEncryptKeyVault encrypt.MasterEncryptKeyVault + eventHub eventhub.EventHub configPath string isPro bool @@ -364,6 +369,21 @@ func (d *dependency) NavigatorStateKV() cache.Driver { return d.navigatorStateKv } +func (d *dependency) EventHub() eventhub.EventHub { + if d.eventHub != nil { + return d.eventHub + } + d.eventHub = eventhub.NewEventHub(d.UserClient(), d.FsEventClient()) + return d.eventHub +} + +func (d *dependency) FsEventClient() inventory.FsEventClient { + if d.fsEventClient != nil { + return d.fsEventClient + } + return inventory.NewFsEventClient(d.DBClient(), d.ConfigProvider().Database().Type) +} + func (d *dependency) SettingClient() inventory.SettingClient { if d.settingClient != nil { return d.settingClient @@ -861,6 +881,14 @@ func (d *dependency) Shutdown(ctx context.Context) error { }() } + if d.eventHub != nil { + wg.Add(1) + go func() { + d.eventHub.Close() + defer wg.Done() + }() + } + d.mu.Unlock() wg.Wait() diff --git a/ent/client.go b/ent/client.go index 06129e13..503d2998 100644 --- a/ent/client.go +++ b/ent/client.go @@ -19,6 +19,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/directlink" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/metadata" "github.com/cloudreve/Cloudreve/v4/ent/node" @@ -45,6 +46,8 @@ type Client struct { Entity *EntityClient // File is the client for interacting with the File builders. File *FileClient + // FsEvent is the client for interacting with the FsEvent builders. + FsEvent *FsEventClient // Group is the client for interacting with the Group builders. Group *GroupClient // Metadata is the client for interacting with the Metadata builders. @@ -78,6 +81,7 @@ func (c *Client) init() { c.DirectLink = NewDirectLinkClient(c.config) c.Entity = NewEntityClient(c.config) c.File = NewFileClient(c.config) + c.FsEvent = NewFsEventClient(c.config) c.Group = NewGroupClient(c.config) c.Metadata = NewMetadataClient(c.config) c.Node = NewNodeClient(c.config) @@ -183,6 +187,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { DirectLink: NewDirectLinkClient(cfg), Entity: NewEntityClient(cfg), File: NewFileClient(cfg), + FsEvent: NewFsEventClient(cfg), Group: NewGroupClient(cfg), Metadata: NewMetadataClient(cfg), Node: NewNodeClient(cfg), @@ -215,6 +220,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) DirectLink: NewDirectLinkClient(cfg), Entity: NewEntityClient(cfg), File: NewFileClient(cfg), + FsEvent: NewFsEventClient(cfg), Group: NewGroupClient(cfg), Metadata: NewMetadataClient(cfg), Node: NewNodeClient(cfg), @@ -253,8 +259,8 @@ func (c *Client) Close() error { // In order to add hooks to a specific client, call: `client.Node.Use(...)`. 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.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.Group, c.Metadata, + c.Node, c.Passkey, c.Setting, c.Share, c.StoragePolicy, c.Task, c.User, } { n.Use(hooks...) } @@ -264,8 +270,8 @@ func (c *Client) Use(hooks ...Hook) { // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. 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.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.Group, c.Metadata, + c.Node, c.Passkey, c.Setting, c.Share, c.StoragePolicy, c.Task, c.User, } { n.Intercept(interceptors...) } @@ -282,6 +288,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.Entity.mutate(ctx, m) case *FileMutation: return c.File.mutate(ctx, m) + case *FsEventMutation: + return c.FsEvent.mutate(ctx, m) case *GroupMutation: return c.Group.mutate(ctx, m) case *MetadataMutation: @@ -1052,6 +1060,157 @@ func (c *FileClient) mutate(ctx context.Context, m *FileMutation) (Value, error) } } +// FsEventClient is a client for the FsEvent schema. +type FsEventClient struct { + config +} + +// NewFsEventClient returns a client for the FsEvent from the given config. +func NewFsEventClient(c config) *FsEventClient { + return &FsEventClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `fsevent.Hooks(f(g(h())))`. +func (c *FsEventClient) Use(hooks ...Hook) { + c.hooks.FsEvent = append(c.hooks.FsEvent, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `fsevent.Intercept(f(g(h())))`. +func (c *FsEventClient) Intercept(interceptors ...Interceptor) { + c.inters.FsEvent = append(c.inters.FsEvent, interceptors...) +} + +// Create returns a builder for creating a FsEvent entity. +func (c *FsEventClient) Create() *FsEventCreate { + mutation := newFsEventMutation(c.config, OpCreate) + return &FsEventCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of FsEvent entities. +func (c *FsEventClient) CreateBulk(builders ...*FsEventCreate) *FsEventCreateBulk { + return &FsEventCreateBulk{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 *FsEventClient) MapCreateBulk(slice any, setFunc func(*FsEventCreate, int)) *FsEventCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &FsEventCreateBulk{err: fmt.Errorf("calling to FsEventClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*FsEventCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &FsEventCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for FsEvent. +func (c *FsEventClient) Update() *FsEventUpdate { + mutation := newFsEventMutation(c.config, OpUpdate) + return &FsEventUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *FsEventClient) UpdateOne(fe *FsEvent) *FsEventUpdateOne { + mutation := newFsEventMutation(c.config, OpUpdateOne, withFsEvent(fe)) + return &FsEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *FsEventClient) UpdateOneID(id int) *FsEventUpdateOne { + mutation := newFsEventMutation(c.config, OpUpdateOne, withFsEventID(id)) + return &FsEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for FsEvent. +func (c *FsEventClient) Delete() *FsEventDelete { + mutation := newFsEventMutation(c.config, OpDelete) + return &FsEventDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *FsEventClient) DeleteOne(fe *FsEvent) *FsEventDeleteOne { + return c.DeleteOneID(fe.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *FsEventClient) DeleteOneID(id int) *FsEventDeleteOne { + builder := c.Delete().Where(fsevent.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &FsEventDeleteOne{builder} +} + +// Query returns a query builder for FsEvent. +func (c *FsEventClient) Query() *FsEventQuery { + return &FsEventQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeFsEvent}, + inters: c.Interceptors(), + } +} + +// Get returns a FsEvent entity by its id. +func (c *FsEventClient) Get(ctx context.Context, id int) (*FsEvent, error) { + return c.Query().Where(fsevent.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *FsEventClient) GetX(ctx context.Context, id int) *FsEvent { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryUser queries the user edge of a FsEvent. +func (c *FsEventClient) QueryUser(fe *FsEvent) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := fe.ID + step := sqlgraph.NewStep( + sqlgraph.From(fsevent.Table, fsevent.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, fsevent.UserTable, fsevent.UserColumn), + ) + fromV = sqlgraph.Neighbors(fe.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *FsEventClient) Hooks() []Hook { + hooks := c.hooks.FsEvent + return append(hooks[:len(hooks):len(hooks)], fsevent.Hooks[:]...) +} + +// Interceptors returns the client interceptors. +func (c *FsEventClient) Interceptors() []Interceptor { + inters := c.inters.FsEvent + return append(inters[:len(inters):len(inters)], fsevent.Interceptors[:]...) +} + +func (c *FsEventClient) mutate(ctx context.Context, m *FsEventMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&FsEventCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&FsEventUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&FsEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&FsEventDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown FsEvent mutation op: %q", m.Op()) + } +} + // GroupClient is a client for the Group schema. type GroupClient struct { config @@ -2528,6 +2687,22 @@ func (c *UserClient) QueryTasks(u *User) *TaskQuery { return query } +// QueryFsevents queries the fsevents edge of a User. +func (c *UserClient) QueryFsevents(u *User) *FsEventQuery { + query := (&FsEventClient{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(fsevent.Table, fsevent.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.FseventsTable, user.FseventsColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // QueryEntities queries the entities edge of a User. func (c *UserClient) QueryEntities(u *User) *EntityQuery { query := (&EntityClient{config: c.config}).Query() @@ -2574,12 +2749,12 @@ func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error) // 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 + DavAccount, DirectLink, Entity, File, FsEvent, Group, Metadata, Node, Passkey, + Setting, Share, StoragePolicy, Task, User []ent.Hook } inters struct { - DavAccount, DirectLink, Entity, File, Group, Metadata, Node, Passkey, Setting, - Share, StoragePolicy, Task, User []ent.Interceptor + DavAccount, DirectLink, Entity, File, FsEvent, Group, Metadata, Node, Passkey, + Setting, Share, StoragePolicy, Task, User []ent.Interceptor } ) diff --git a/ent/ent.go b/ent/ent.go index 4026fe67..3ea446e5 100644 --- a/ent/ent.go +++ b/ent/ent.go @@ -16,6 +16,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/directlink" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/metadata" "github.com/cloudreve/Cloudreve/v4/ent/node" @@ -89,6 +90,7 @@ func checkColumn(table, column string) error { directlink.Table: directlink.ValidColumn, entity.Table: entity.ValidColumn, file.Table: file.ValidColumn, + fsevent.Table: fsevent.ValidColumn, group.Table: group.ValidColumn, metadata.Table: metadata.ValidColumn, node.Table: node.ValidColumn, diff --git a/ent/fsevent.go b/ent/fsevent.go new file mode 100644 index 00000000..50e83e4c --- /dev/null +++ b/ent/fsevent.go @@ -0,0 +1,204 @@ +// 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/fsevent" + "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/gofrs/uuid" +) + +// FsEvent is the model entity for the FsEvent schema. +type FsEvent struct { + config `json:"-"` + // ID of the ent. + ID int `json:"id,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // DeletedAt holds the value of the "deleted_at" field. + DeletedAt *time.Time `json:"deleted_at,omitempty"` + // Event holds the value of the "event" field. + Event string `json:"event,omitempty"` + // Subscriber holds the value of the "subscriber" field. + Subscriber uuid.UUID `json:"subscriber,omitempty"` + // UserFsevent holds the value of the "user_fsevent" field. + UserFsevent int `json:"user_fsevent,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the FsEventQuery when eager-loading is set. + Edges FsEventEdges `json:"edges"` + selectValues sql.SelectValues +} + +// FsEventEdges holds the relations/edges for other nodes in the graph. +type FsEventEdges struct { + // User holds the value of the user edge. + User *User `json:"user,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]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 FsEventEdges) 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"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*FsEvent) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case fsevent.FieldID, fsevent.FieldUserFsevent: + values[i] = new(sql.NullInt64) + case fsevent.FieldEvent: + values[i] = new(sql.NullString) + case fsevent.FieldCreatedAt, fsevent.FieldUpdatedAt, fsevent.FieldDeletedAt: + values[i] = new(sql.NullTime) + case fsevent.FieldSubscriber: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the FsEvent fields. +func (fe *FsEvent) 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 fsevent.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + fe.ID = int(value.Int64) + case fsevent.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + fe.CreatedAt = value.Time + } + case fsevent.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + fe.UpdatedAt = value.Time + } + case fsevent.FieldDeletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field deleted_at", values[i]) + } else if value.Valid { + fe.DeletedAt = new(time.Time) + *fe.DeletedAt = value.Time + } + case fsevent.FieldEvent: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field event", values[i]) + } else if value.Valid { + fe.Event = value.String + } + case fsevent.FieldSubscriber: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field subscriber", values[i]) + } else if value != nil { + fe.Subscriber = *value + } + case fsevent.FieldUserFsevent: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field user_fsevent", values[i]) + } else if value.Valid { + fe.UserFsevent = int(value.Int64) + } + default: + fe.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the FsEvent. +// This includes values selected through modifiers, order, etc. +func (fe *FsEvent) Value(name string) (ent.Value, error) { + return fe.selectValues.Get(name) +} + +// QueryUser queries the "user" edge of the FsEvent entity. +func (fe *FsEvent) QueryUser() *UserQuery { + return NewFsEventClient(fe.config).QueryUser(fe) +} + +// Update returns a builder for updating this FsEvent. +// Note that you need to call FsEvent.Unwrap() before calling this method if this FsEvent +// was returned from a transaction, and the transaction was committed or rolled back. +func (fe *FsEvent) Update() *FsEventUpdateOne { + return NewFsEventClient(fe.config).UpdateOne(fe) +} + +// Unwrap unwraps the FsEvent 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 (fe *FsEvent) Unwrap() *FsEvent { + _tx, ok := fe.config.driver.(*txDriver) + if !ok { + panic("ent: FsEvent is not a transactional entity") + } + fe.config.driver = _tx.drv + return fe +} + +// String implements the fmt.Stringer. +func (fe *FsEvent) String() string { + var builder strings.Builder + builder.WriteString("FsEvent(") + builder.WriteString(fmt.Sprintf("id=%v, ", fe.ID)) + builder.WriteString("created_at=") + builder.WriteString(fe.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(fe.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + if v := fe.DeletedAt; v != nil { + builder.WriteString("deleted_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("event=") + builder.WriteString(fe.Event) + builder.WriteString(", ") + builder.WriteString("subscriber=") + builder.WriteString(fmt.Sprintf("%v", fe.Subscriber)) + builder.WriteString(", ") + builder.WriteString("user_fsevent=") + builder.WriteString(fmt.Sprintf("%v", fe.UserFsevent)) + builder.WriteByte(')') + return builder.String() +} + +// SetUser manually set the edge as loaded state. +func (e *FsEvent) SetUser(v *User) { + e.Edges.User = v + e.Edges.loadedTypes[0] = true +} + +// FsEvents is a parsable slice of FsEvent. +type FsEvents []*FsEvent diff --git a/ent/fsevent/fsevent.go b/ent/fsevent/fsevent.go new file mode 100644 index 00000000..9b6830c7 --- /dev/null +++ b/ent/fsevent/fsevent.go @@ -0,0 +1,130 @@ +// Code generated by ent, DO NOT EDIT. + +package fsevent + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the fsevent type in the database. + Label = "fs_event" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldDeletedAt holds the string denoting the deleted_at field in the database. + FieldDeletedAt = "deleted_at" + // FieldEvent holds the string denoting the event field in the database. + FieldEvent = "event" + // FieldSubscriber holds the string denoting the subscriber field in the database. + FieldSubscriber = "subscriber" + // FieldUserFsevent holds the string denoting the user_fsevent field in the database. + FieldUserFsevent = "user_fsevent" + // EdgeUser holds the string denoting the user edge name in mutations. + EdgeUser = "user" + // Table holds the table name of the fsevent in the database. + Table = "fs_events" + // UserTable is the table that holds the user relation/edge. + UserTable = "fs_events" + // 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_fsevent" +) + +// Columns holds all SQL columns for fsevent fields. +var Columns = []string{ + FieldID, + FieldCreatedAt, + FieldUpdatedAt, + FieldDeletedAt, + FieldEvent, + FieldSubscriber, + FieldUserFsevent, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +// Note that the variables below are initialized by the runtime +// package on the initialization of the application. Therefore, +// it should be imported in the main as follows: +// +// import _ "github.com/cloudreve/Cloudreve/v4/ent/runtime" +var ( + Hooks [1]ent.Hook + Interceptors [1]ent.Interceptor + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time +) + +// OrderOption defines the ordering options for the FsEvent queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByDeletedAt orders the results by the deleted_at field. +func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDeletedAt, opts...).ToFunc() +} + +// ByEvent orders the results by the event field. +func ByEvent(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEvent, opts...).ToFunc() +} + +// BySubscriber orders the results by the subscriber field. +func BySubscriber(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubscriber, opts...).ToFunc() +} + +// ByUserFsevent orders the results by the user_fsevent field. +func ByUserFsevent(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUserFsevent, 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...)) + } +} +func newUserStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), + ) +} diff --git a/ent/fsevent/where.go b/ent/fsevent/where.go new file mode 100644 index 00000000..015f0c79 --- /dev/null +++ b/ent/fsevent/where.go @@ -0,0 +1,390 @@ +// Code generated by ent, DO NOT EDIT. + +package fsevent + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/gofrs/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLTE(FieldID, id)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ. +func DeletedAt(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldDeletedAt, v)) +} + +// Event applies equality check predicate on the "event" field. It's identical to EventEQ. +func Event(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldEvent, v)) +} + +// Subscriber applies equality check predicate on the "subscriber" field. It's identical to SubscriberEQ. +func Subscriber(v uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldSubscriber, v)) +} + +// UserFsevent applies equality check predicate on the "user_fsevent" field. It's identical to UserFseventEQ. +func UserFsevent(v int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldUserFsevent, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// DeletedAtEQ applies the EQ predicate on the "deleted_at" field. +func DeletedAtEQ(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldDeletedAt, v)) +} + +// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field. +func DeletedAtNEQ(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNEQ(FieldDeletedAt, v)) +} + +// DeletedAtIn applies the In predicate on the "deleted_at" field. +func DeletedAtIn(vs ...time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldIn(FieldDeletedAt, vs...)) +} + +// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field. +func DeletedAtNotIn(vs ...time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotIn(FieldDeletedAt, vs...)) +} + +// DeletedAtGT applies the GT predicate on the "deleted_at" field. +func DeletedAtGT(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGT(FieldDeletedAt, v)) +} + +// DeletedAtGTE applies the GTE predicate on the "deleted_at" field. +func DeletedAtGTE(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGTE(FieldDeletedAt, v)) +} + +// DeletedAtLT applies the LT predicate on the "deleted_at" field. +func DeletedAtLT(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLT(FieldDeletedAt, v)) +} + +// DeletedAtLTE applies the LTE predicate on the "deleted_at" field. +func DeletedAtLTE(v time.Time) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLTE(FieldDeletedAt, v)) +} + +// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field. +func DeletedAtIsNil() predicate.FsEvent { + return predicate.FsEvent(sql.FieldIsNull(FieldDeletedAt)) +} + +// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field. +func DeletedAtNotNil() predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotNull(FieldDeletedAt)) +} + +// EventEQ applies the EQ predicate on the "event" field. +func EventEQ(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldEvent, v)) +} + +// EventNEQ applies the NEQ predicate on the "event" field. +func EventNEQ(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNEQ(FieldEvent, v)) +} + +// EventIn applies the In predicate on the "event" field. +func EventIn(vs ...string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldIn(FieldEvent, vs...)) +} + +// EventNotIn applies the NotIn predicate on the "event" field. +func EventNotIn(vs ...string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotIn(FieldEvent, vs...)) +} + +// EventGT applies the GT predicate on the "event" field. +func EventGT(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGT(FieldEvent, v)) +} + +// EventGTE applies the GTE predicate on the "event" field. +func EventGTE(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGTE(FieldEvent, v)) +} + +// EventLT applies the LT predicate on the "event" field. +func EventLT(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLT(FieldEvent, v)) +} + +// EventLTE applies the LTE predicate on the "event" field. +func EventLTE(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLTE(FieldEvent, v)) +} + +// EventContains applies the Contains predicate on the "event" field. +func EventContains(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldContains(FieldEvent, v)) +} + +// EventHasPrefix applies the HasPrefix predicate on the "event" field. +func EventHasPrefix(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldHasPrefix(FieldEvent, v)) +} + +// EventHasSuffix applies the HasSuffix predicate on the "event" field. +func EventHasSuffix(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldHasSuffix(FieldEvent, v)) +} + +// EventEqualFold applies the EqualFold predicate on the "event" field. +func EventEqualFold(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEqualFold(FieldEvent, v)) +} + +// EventContainsFold applies the ContainsFold predicate on the "event" field. +func EventContainsFold(v string) predicate.FsEvent { + return predicate.FsEvent(sql.FieldContainsFold(FieldEvent, v)) +} + +// SubscriberEQ applies the EQ predicate on the "subscriber" field. +func SubscriberEQ(v uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldSubscriber, v)) +} + +// SubscriberNEQ applies the NEQ predicate on the "subscriber" field. +func SubscriberNEQ(v uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNEQ(FieldSubscriber, v)) +} + +// SubscriberIn applies the In predicate on the "subscriber" field. +func SubscriberIn(vs ...uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldIn(FieldSubscriber, vs...)) +} + +// SubscriberNotIn applies the NotIn predicate on the "subscriber" field. +func SubscriberNotIn(vs ...uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotIn(FieldSubscriber, vs...)) +} + +// SubscriberGT applies the GT predicate on the "subscriber" field. +func SubscriberGT(v uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGT(FieldSubscriber, v)) +} + +// SubscriberGTE applies the GTE predicate on the "subscriber" field. +func SubscriberGTE(v uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldGTE(FieldSubscriber, v)) +} + +// SubscriberLT applies the LT predicate on the "subscriber" field. +func SubscriberLT(v uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLT(FieldSubscriber, v)) +} + +// SubscriberLTE applies the LTE predicate on the "subscriber" field. +func SubscriberLTE(v uuid.UUID) predicate.FsEvent { + return predicate.FsEvent(sql.FieldLTE(FieldSubscriber, v)) +} + +// UserFseventEQ applies the EQ predicate on the "user_fsevent" field. +func UserFseventEQ(v int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldEQ(FieldUserFsevent, v)) +} + +// UserFseventNEQ applies the NEQ predicate on the "user_fsevent" field. +func UserFseventNEQ(v int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNEQ(FieldUserFsevent, v)) +} + +// UserFseventIn applies the In predicate on the "user_fsevent" field. +func UserFseventIn(vs ...int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldIn(FieldUserFsevent, vs...)) +} + +// UserFseventNotIn applies the NotIn predicate on the "user_fsevent" field. +func UserFseventNotIn(vs ...int) predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotIn(FieldUserFsevent, vs...)) +} + +// UserFseventIsNil applies the IsNil predicate on the "user_fsevent" field. +func UserFseventIsNil() predicate.FsEvent { + return predicate.FsEvent(sql.FieldIsNull(FieldUserFsevent)) +} + +// UserFseventNotNil applies the NotNil predicate on the "user_fsevent" field. +func UserFseventNotNil() predicate.FsEvent { + return predicate.FsEvent(sql.FieldNotNull(FieldUserFsevent)) +} + +// HasUser applies the HasEdge predicate on the "user" edge. +func HasUser() predicate.FsEvent { + return predicate.FsEvent(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, 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.FsEvent { + return predicate.FsEvent(func(s *sql.Selector) { + step := newUserStep() + 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.FsEvent) predicate.FsEvent { + return predicate.FsEvent(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.FsEvent) predicate.FsEvent { + return predicate.FsEvent(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.FsEvent) predicate.FsEvent { + return predicate.FsEvent(sql.NotPredicates(p)) +} diff --git a/ent/fsevent_create.go b/ent/fsevent_create.go new file mode 100644 index 00000000..20020d8e --- /dev/null +++ b/ent/fsevent_create.go @@ -0,0 +1,827 @@ +// 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/fsevent" + "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/gofrs/uuid" +) + +// FsEventCreate is the builder for creating a FsEvent entity. +type FsEventCreate struct { + config + mutation *FsEventMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetCreatedAt sets the "created_at" field. +func (fec *FsEventCreate) SetCreatedAt(t time.Time) *FsEventCreate { + fec.mutation.SetCreatedAt(t) + return fec +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (fec *FsEventCreate) SetNillableCreatedAt(t *time.Time) *FsEventCreate { + if t != nil { + fec.SetCreatedAt(*t) + } + return fec +} + +// SetUpdatedAt sets the "updated_at" field. +func (fec *FsEventCreate) SetUpdatedAt(t time.Time) *FsEventCreate { + fec.mutation.SetUpdatedAt(t) + return fec +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (fec *FsEventCreate) SetNillableUpdatedAt(t *time.Time) *FsEventCreate { + if t != nil { + fec.SetUpdatedAt(*t) + } + return fec +} + +// SetDeletedAt sets the "deleted_at" field. +func (fec *FsEventCreate) SetDeletedAt(t time.Time) *FsEventCreate { + fec.mutation.SetDeletedAt(t) + return fec +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (fec *FsEventCreate) SetNillableDeletedAt(t *time.Time) *FsEventCreate { + if t != nil { + fec.SetDeletedAt(*t) + } + return fec +} + +// SetEvent sets the "event" field. +func (fec *FsEventCreate) SetEvent(s string) *FsEventCreate { + fec.mutation.SetEvent(s) + return fec +} + +// SetSubscriber sets the "subscriber" field. +func (fec *FsEventCreate) SetSubscriber(u uuid.UUID) *FsEventCreate { + fec.mutation.SetSubscriber(u) + return fec +} + +// SetUserFsevent sets the "user_fsevent" field. +func (fec *FsEventCreate) SetUserFsevent(i int) *FsEventCreate { + fec.mutation.SetUserFsevent(i) + return fec +} + +// SetNillableUserFsevent sets the "user_fsevent" field if the given value is not nil. +func (fec *FsEventCreate) SetNillableUserFsevent(i *int) *FsEventCreate { + if i != nil { + fec.SetUserFsevent(*i) + } + return fec +} + +// SetUserID sets the "user" edge to the User entity by ID. +func (fec *FsEventCreate) SetUserID(id int) *FsEventCreate { + fec.mutation.SetUserID(id) + return fec +} + +// SetNillableUserID sets the "user" edge to the User entity by ID if the given value is not nil. +func (fec *FsEventCreate) SetNillableUserID(id *int) *FsEventCreate { + if id != nil { + fec = fec.SetUserID(*id) + } + return fec +} + +// SetUser sets the "user" edge to the User entity. +func (fec *FsEventCreate) SetUser(u *User) *FsEventCreate { + return fec.SetUserID(u.ID) +} + +// Mutation returns the FsEventMutation object of the builder. +func (fec *FsEventCreate) Mutation() *FsEventMutation { + return fec.mutation +} + +// Save creates the FsEvent in the database. +func (fec *FsEventCreate) Save(ctx context.Context) (*FsEvent, error) { + if err := fec.defaults(); err != nil { + return nil, err + } + return withHooks(ctx, fec.sqlSave, fec.mutation, fec.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (fec *FsEventCreate) SaveX(ctx context.Context) *FsEvent { + v, err := fec.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (fec *FsEventCreate) Exec(ctx context.Context) error { + _, err := fec.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (fec *FsEventCreate) ExecX(ctx context.Context) { + if err := fec.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (fec *FsEventCreate) defaults() error { + if _, ok := fec.mutation.CreatedAt(); !ok { + if fsevent.DefaultCreatedAt == nil { + return fmt.Errorf("ent: uninitialized fsevent.DefaultCreatedAt (forgotten import ent/runtime?)") + } + v := fsevent.DefaultCreatedAt() + fec.mutation.SetCreatedAt(v) + } + if _, ok := fec.mutation.UpdatedAt(); !ok { + if fsevent.DefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized fsevent.DefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := fsevent.DefaultUpdatedAt() + fec.mutation.SetUpdatedAt(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (fec *FsEventCreate) check() error { + if _, ok := fec.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "FsEvent.created_at"`)} + } + if _, ok := fec.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "FsEvent.updated_at"`)} + } + if _, ok := fec.mutation.Event(); !ok { + return &ValidationError{Name: "event", err: errors.New(`ent: missing required field "FsEvent.event"`)} + } + if _, ok := fec.mutation.Subscriber(); !ok { + return &ValidationError{Name: "subscriber", err: errors.New(`ent: missing required field "FsEvent.subscriber"`)} + } + return nil +} + +func (fec *FsEventCreate) sqlSave(ctx context.Context) (*FsEvent, error) { + if err := fec.check(); err != nil { + return nil, err + } + _node, _spec := fec.createSpec() + if err := sqlgraph.CreateNode(ctx, fec.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) + fec.mutation.id = &_node.ID + fec.mutation.done = true + return _node, nil +} + +func (fec *FsEventCreate) createSpec() (*FsEvent, *sqlgraph.CreateSpec) { + var ( + _node = &FsEvent{config: fec.config} + _spec = sqlgraph.NewCreateSpec(fsevent.Table, sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt)) + ) + + if id, ok := fec.mutation.ID(); ok { + _node.ID = id + id64 := int64(id) + _spec.ID.Value = id64 + } + + _spec.OnConflict = fec.conflict + if value, ok := fec.mutation.CreatedAt(); ok { + _spec.SetField(fsevent.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := fec.mutation.UpdatedAt(); ok { + _spec.SetField(fsevent.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := fec.mutation.DeletedAt(); ok { + _spec.SetField(fsevent.FieldDeletedAt, field.TypeTime, value) + _node.DeletedAt = &value + } + if value, ok := fec.mutation.Event(); ok { + _spec.SetField(fsevent.FieldEvent, field.TypeString, value) + _node.Event = value + } + if value, ok := fec.mutation.Subscriber(); ok { + _spec.SetField(fsevent.FieldSubscriber, field.TypeUUID, value) + _node.Subscriber = value + } + if nodes := fec.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: fsevent.UserTable, + Columns: []string{fsevent.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.UserFsevent = 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.FsEvent.Create(). +// SetCreatedAt(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.FsEventUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (fec *FsEventCreate) OnConflict(opts ...sql.ConflictOption) *FsEventUpsertOne { + fec.conflict = opts + return &FsEventUpsertOne{ + create: fec, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.FsEvent.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (fec *FsEventCreate) OnConflictColumns(columns ...string) *FsEventUpsertOne { + fec.conflict = append(fec.conflict, sql.ConflictColumns(columns...)) + return &FsEventUpsertOne{ + create: fec, + } +} + +type ( + // FsEventUpsertOne is the builder for "upsert"-ing + // one FsEvent node. + FsEventUpsertOne struct { + create *FsEventCreate + } + + // FsEventUpsert is the "OnConflict" setter. + FsEventUpsert struct { + *sql.UpdateSet + } +) + +// SetUpdatedAt sets the "updated_at" field. +func (u *FsEventUpsert) SetUpdatedAt(v time.Time) *FsEventUpsert { + u.Set(fsevent.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *FsEventUpsert) UpdateUpdatedAt() *FsEventUpsert { + u.SetExcluded(fsevent.FieldUpdatedAt) + return u +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *FsEventUpsert) SetDeletedAt(v time.Time) *FsEventUpsert { + u.Set(fsevent.FieldDeletedAt, v) + return u +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *FsEventUpsert) UpdateDeletedAt() *FsEventUpsert { + u.SetExcluded(fsevent.FieldDeletedAt) + return u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *FsEventUpsert) ClearDeletedAt() *FsEventUpsert { + u.SetNull(fsevent.FieldDeletedAt) + return u +} + +// SetEvent sets the "event" field. +func (u *FsEventUpsert) SetEvent(v string) *FsEventUpsert { + u.Set(fsevent.FieldEvent, v) + return u +} + +// UpdateEvent sets the "event" field to the value that was provided on create. +func (u *FsEventUpsert) UpdateEvent() *FsEventUpsert { + u.SetExcluded(fsevent.FieldEvent) + return u +} + +// SetSubscriber sets the "subscriber" field. +func (u *FsEventUpsert) SetSubscriber(v uuid.UUID) *FsEventUpsert { + u.Set(fsevent.FieldSubscriber, v) + return u +} + +// UpdateSubscriber sets the "subscriber" field to the value that was provided on create. +func (u *FsEventUpsert) UpdateSubscriber() *FsEventUpsert { + u.SetExcluded(fsevent.FieldSubscriber) + return u +} + +// SetUserFsevent sets the "user_fsevent" field. +func (u *FsEventUpsert) SetUserFsevent(v int) *FsEventUpsert { + u.Set(fsevent.FieldUserFsevent, v) + return u +} + +// UpdateUserFsevent sets the "user_fsevent" field to the value that was provided on create. +func (u *FsEventUpsert) UpdateUserFsevent() *FsEventUpsert { + u.SetExcluded(fsevent.FieldUserFsevent) + return u +} + +// ClearUserFsevent clears the value of the "user_fsevent" field. +func (u *FsEventUpsert) ClearUserFsevent() *FsEventUpsert { + u.SetNull(fsevent.FieldUserFsevent) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create. +// Using this option is equivalent to using: +// +// client.FsEvent.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *FsEventUpsertOne) UpdateNewValues() *FsEventUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.CreatedAt(); exists { + s.SetIgnore(fsevent.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.FsEvent.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *FsEventUpsertOne) Ignore() *FsEventUpsertOne { + 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 *FsEventUpsertOne) DoNothing() *FsEventUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the FsEventCreate.OnConflict +// documentation for more info. +func (u *FsEventUpsertOne) Update(set func(*FsEventUpsert)) *FsEventUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&FsEventUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *FsEventUpsertOne) SetUpdatedAt(v time.Time) *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *FsEventUpsertOne) UpdateUpdatedAt() *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *FsEventUpsertOne) SetDeletedAt(v time.Time) *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *FsEventUpsertOne) UpdateDeletedAt() *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *FsEventUpsertOne) ClearDeletedAt() *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.ClearDeletedAt() + }) +} + +// SetEvent sets the "event" field. +func (u *FsEventUpsertOne) SetEvent(v string) *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.SetEvent(v) + }) +} + +// UpdateEvent sets the "event" field to the value that was provided on create. +func (u *FsEventUpsertOne) UpdateEvent() *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.UpdateEvent() + }) +} + +// SetSubscriber sets the "subscriber" field. +func (u *FsEventUpsertOne) SetSubscriber(v uuid.UUID) *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.SetSubscriber(v) + }) +} + +// UpdateSubscriber sets the "subscriber" field to the value that was provided on create. +func (u *FsEventUpsertOne) UpdateSubscriber() *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.UpdateSubscriber() + }) +} + +// SetUserFsevent sets the "user_fsevent" field. +func (u *FsEventUpsertOne) SetUserFsevent(v int) *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.SetUserFsevent(v) + }) +} + +// UpdateUserFsevent sets the "user_fsevent" field to the value that was provided on create. +func (u *FsEventUpsertOne) UpdateUserFsevent() *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.UpdateUserFsevent() + }) +} + +// ClearUserFsevent clears the value of the "user_fsevent" field. +func (u *FsEventUpsertOne) ClearUserFsevent() *FsEventUpsertOne { + return u.Update(func(s *FsEventUpsert) { + s.ClearUserFsevent() + }) +} + +// Exec executes the query. +func (u *FsEventUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for FsEventCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *FsEventUpsertOne) 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 *FsEventUpsertOne) 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 *FsEventUpsertOne) IDX(ctx context.Context) int { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +func (m *FsEventCreate) SetRawID(t int) *FsEventCreate { + m.mutation.SetRawID(t) + return m +} + +// FsEventCreateBulk is the builder for creating many FsEvent entities in bulk. +type FsEventCreateBulk struct { + config + err error + builders []*FsEventCreate + conflict []sql.ConflictOption +} + +// Save creates the FsEvent entities in the database. +func (fecb *FsEventCreateBulk) Save(ctx context.Context) ([]*FsEvent, error) { + if fecb.err != nil { + return nil, fecb.err + } + specs := make([]*sqlgraph.CreateSpec, len(fecb.builders)) + nodes := make([]*FsEvent, len(fecb.builders)) + mutators := make([]Mutator, len(fecb.builders)) + for i := range fecb.builders { + func(i int, root context.Context) { + builder := fecb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*FsEventMutation) + 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, fecb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = fecb.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, fecb.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, fecb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (fecb *FsEventCreateBulk) SaveX(ctx context.Context) []*FsEvent { + v, err := fecb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (fecb *FsEventCreateBulk) Exec(ctx context.Context) error { + _, err := fecb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (fecb *FsEventCreateBulk) ExecX(ctx context.Context) { + if err := fecb.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.FsEvent.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.FsEventUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (fecb *FsEventCreateBulk) OnConflict(opts ...sql.ConflictOption) *FsEventUpsertBulk { + fecb.conflict = opts + return &FsEventUpsertBulk{ + create: fecb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.FsEvent.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (fecb *FsEventCreateBulk) OnConflictColumns(columns ...string) *FsEventUpsertBulk { + fecb.conflict = append(fecb.conflict, sql.ConflictColumns(columns...)) + return &FsEventUpsertBulk{ + create: fecb, + } +} + +// FsEventUpsertBulk is the builder for "upsert"-ing +// a bulk of FsEvent nodes. +type FsEventUpsertBulk struct { + create *FsEventCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.FsEvent.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *FsEventUpsertBulk) UpdateNewValues() *FsEventUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.CreatedAt(); exists { + s.SetIgnore(fsevent.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.FsEvent.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *FsEventUpsertBulk) Ignore() *FsEventUpsertBulk { + 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 *FsEventUpsertBulk) DoNothing() *FsEventUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the FsEventCreateBulk.OnConflict +// documentation for more info. +func (u *FsEventUpsertBulk) Update(set func(*FsEventUpsert)) *FsEventUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&FsEventUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *FsEventUpsertBulk) SetUpdatedAt(v time.Time) *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *FsEventUpsertBulk) UpdateUpdatedAt() *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *FsEventUpsertBulk) SetDeletedAt(v time.Time) *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *FsEventUpsertBulk) UpdateDeletedAt() *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *FsEventUpsertBulk) ClearDeletedAt() *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.ClearDeletedAt() + }) +} + +// SetEvent sets the "event" field. +func (u *FsEventUpsertBulk) SetEvent(v string) *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.SetEvent(v) + }) +} + +// UpdateEvent sets the "event" field to the value that was provided on create. +func (u *FsEventUpsertBulk) UpdateEvent() *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.UpdateEvent() + }) +} + +// SetSubscriber sets the "subscriber" field. +func (u *FsEventUpsertBulk) SetSubscriber(v uuid.UUID) *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.SetSubscriber(v) + }) +} + +// UpdateSubscriber sets the "subscriber" field to the value that was provided on create. +func (u *FsEventUpsertBulk) UpdateSubscriber() *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.UpdateSubscriber() + }) +} + +// SetUserFsevent sets the "user_fsevent" field. +func (u *FsEventUpsertBulk) SetUserFsevent(v int) *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.SetUserFsevent(v) + }) +} + +// UpdateUserFsevent sets the "user_fsevent" field to the value that was provided on create. +func (u *FsEventUpsertBulk) UpdateUserFsevent() *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.UpdateUserFsevent() + }) +} + +// ClearUserFsevent clears the value of the "user_fsevent" field. +func (u *FsEventUpsertBulk) ClearUserFsevent() *FsEventUpsertBulk { + return u.Update(func(s *FsEventUpsert) { + s.ClearUserFsevent() + }) +} + +// Exec executes the query. +func (u *FsEventUpsertBulk) 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 FsEventCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for FsEventCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *FsEventUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/fsevent_delete.go b/ent/fsevent_delete.go new file mode 100644 index 00000000..5b9e05db --- /dev/null +++ b/ent/fsevent_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/fsevent" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" +) + +// FsEventDelete is the builder for deleting a FsEvent entity. +type FsEventDelete struct { + config + hooks []Hook + mutation *FsEventMutation +} + +// Where appends a list predicates to the FsEventDelete builder. +func (fed *FsEventDelete) Where(ps ...predicate.FsEvent) *FsEventDelete { + fed.mutation.Where(ps...) + return fed +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (fed *FsEventDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, fed.sqlExec, fed.mutation, fed.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (fed *FsEventDelete) ExecX(ctx context.Context) int { + n, err := fed.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (fed *FsEventDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(fsevent.Table, sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt)) + if ps := fed.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, fed.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + fed.mutation.done = true + return affected, err +} + +// FsEventDeleteOne is the builder for deleting a single FsEvent entity. +type FsEventDeleteOne struct { + fed *FsEventDelete +} + +// Where appends a list predicates to the FsEventDelete builder. +func (fedo *FsEventDeleteOne) Where(ps ...predicate.FsEvent) *FsEventDeleteOne { + fedo.fed.mutation.Where(ps...) + return fedo +} + +// Exec executes the deletion query. +func (fedo *FsEventDeleteOne) Exec(ctx context.Context) error { + n, err := fedo.fed.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{fsevent.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (fedo *FsEventDeleteOne) ExecX(ctx context.Context) { + if err := fedo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/fsevent_query.go b/ent/fsevent_query.go new file mode 100644 index 00000000..5f55219b --- /dev/null +++ b/ent/fsevent_query.go @@ -0,0 +1,605 @@ +// 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/fsevent" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/user" +) + +// FsEventQuery is the builder for querying FsEvent entities. +type FsEventQuery struct { + config + ctx *QueryContext + order []fsevent.OrderOption + inters []Interceptor + predicates []predicate.FsEvent + withUser *UserQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the FsEventQuery builder. +func (feq *FsEventQuery) Where(ps ...predicate.FsEvent) *FsEventQuery { + feq.predicates = append(feq.predicates, ps...) + return feq +} + +// Limit the number of records to be returned by this query. +func (feq *FsEventQuery) Limit(limit int) *FsEventQuery { + feq.ctx.Limit = &limit + return feq +} + +// Offset to start from. +func (feq *FsEventQuery) Offset(offset int) *FsEventQuery { + feq.ctx.Offset = &offset + return feq +} + +// 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 (feq *FsEventQuery) Unique(unique bool) *FsEventQuery { + feq.ctx.Unique = &unique + return feq +} + +// Order specifies how the records should be ordered. +func (feq *FsEventQuery) Order(o ...fsevent.OrderOption) *FsEventQuery { + feq.order = append(feq.order, o...) + return feq +} + +// QueryUser chains the current query on the "user" edge. +func (feq *FsEventQuery) QueryUser() *UserQuery { + query := (&UserClient{config: feq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := feq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := feq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(fsevent.Table, fsevent.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, fsevent.UserTable, fsevent.UserColumn), + ) + fromU = sqlgraph.SetNeighbors(feq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first FsEvent entity from the query. +// Returns a *NotFoundError when no FsEvent was found. +func (feq *FsEventQuery) First(ctx context.Context) (*FsEvent, error) { + nodes, err := feq.Limit(1).All(setContextOp(ctx, feq.ctx, "First")) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{fsevent.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (feq *FsEventQuery) FirstX(ctx context.Context) *FsEvent { + node, err := feq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first FsEvent ID from the query. +// Returns a *NotFoundError when no FsEvent ID was found. +func (feq *FsEventQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = feq.Limit(1).IDs(setContextOp(ctx, feq.ctx, "FirstID")); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{fsevent.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (feq *FsEventQuery) FirstIDX(ctx context.Context) int { + id, err := feq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single FsEvent entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one FsEvent entity is found. +// Returns a *NotFoundError when no FsEvent entities are found. +func (feq *FsEventQuery) Only(ctx context.Context) (*FsEvent, error) { + nodes, err := feq.Limit(2).All(setContextOp(ctx, feq.ctx, "Only")) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{fsevent.Label} + default: + return nil, &NotSingularError{fsevent.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (feq *FsEventQuery) OnlyX(ctx context.Context) *FsEvent { + node, err := feq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only FsEvent ID in the query. +// Returns a *NotSingularError when more than one FsEvent ID is found. +// Returns a *NotFoundError when no entities are found. +func (feq *FsEventQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = feq.Limit(2).IDs(setContextOp(ctx, feq.ctx, "OnlyID")); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{fsevent.Label} + default: + err = &NotSingularError{fsevent.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (feq *FsEventQuery) OnlyIDX(ctx context.Context) int { + id, err := feq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of FsEvents. +func (feq *FsEventQuery) All(ctx context.Context) ([]*FsEvent, error) { + ctx = setContextOp(ctx, feq.ctx, "All") + if err := feq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*FsEvent, *FsEventQuery]() + return withInterceptors[[]*FsEvent](ctx, feq, qr, feq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (feq *FsEventQuery) AllX(ctx context.Context) []*FsEvent { + nodes, err := feq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of FsEvent IDs. +func (feq *FsEventQuery) IDs(ctx context.Context) (ids []int, err error) { + if feq.ctx.Unique == nil && feq.path != nil { + feq.Unique(true) + } + ctx = setContextOp(ctx, feq.ctx, "IDs") + if err = feq.Select(fsevent.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (feq *FsEventQuery) IDsX(ctx context.Context) []int { + ids, err := feq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (feq *FsEventQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, feq.ctx, "Count") + if err := feq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, feq, querierCount[*FsEventQuery](), feq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (feq *FsEventQuery) CountX(ctx context.Context) int { + count, err := feq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (feq *FsEventQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, feq.ctx, "Exist") + switch _, err := feq.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 (feq *FsEventQuery) ExistX(ctx context.Context) bool { + exist, err := feq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the FsEventQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (feq *FsEventQuery) Clone() *FsEventQuery { + if feq == nil { + return nil + } + return &FsEventQuery{ + config: feq.config, + ctx: feq.ctx.Clone(), + order: append([]fsevent.OrderOption{}, feq.order...), + inters: append([]Interceptor{}, feq.inters...), + predicates: append([]predicate.FsEvent{}, feq.predicates...), + withUser: feq.withUser.Clone(), + // clone intermediate query. + sql: feq.sql.Clone(), + path: feq.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 (feq *FsEventQuery) WithUser(opts ...func(*UserQuery)) *FsEventQuery { + query := (&UserClient{config: feq.config}).Query() + for _, opt := range opts { + opt(query) + } + feq.withUser = query + return feq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"created_at,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.FsEvent.Query(). +// GroupBy(fsevent.FieldCreatedAt). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (feq *FsEventQuery) GroupBy(field string, fields ...string) *FsEventGroupBy { + feq.ctx.Fields = append([]string{field}, fields...) + grbuild := &FsEventGroupBy{build: feq} + grbuild.flds = &feq.ctx.Fields + grbuild.label = fsevent.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"created_at,omitempty"` +// } +// +// client.FsEvent.Query(). +// Select(fsevent.FieldCreatedAt). +// Scan(ctx, &v) +func (feq *FsEventQuery) Select(fields ...string) *FsEventSelect { + feq.ctx.Fields = append(feq.ctx.Fields, fields...) + sbuild := &FsEventSelect{FsEventQuery: feq} + sbuild.label = fsevent.Label + sbuild.flds, sbuild.scan = &feq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a FsEventSelect configured with the given aggregations. +func (feq *FsEventQuery) Aggregate(fns ...AggregateFunc) *FsEventSelect { + return feq.Select().Aggregate(fns...) +} + +func (feq *FsEventQuery) prepareQuery(ctx context.Context) error { + for _, inter := range feq.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, feq); err != nil { + return err + } + } + } + for _, f := range feq.ctx.Fields { + if !fsevent.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if feq.path != nil { + prev, err := feq.path(ctx) + if err != nil { + return err + } + feq.sql = prev + } + return nil +} + +func (feq *FsEventQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*FsEvent, error) { + var ( + nodes = []*FsEvent{} + _spec = feq.querySpec() + loadedTypes = [1]bool{ + feq.withUser != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*FsEvent).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &FsEvent{config: feq.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, feq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := feq.withUser; query != nil { + if err := feq.loadUser(ctx, query, nodes, nil, + func(n *FsEvent, e *User) { n.Edges.User = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (feq *FsEventQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*FsEvent, init func(*FsEvent), assign func(*FsEvent, *User)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*FsEvent) + for i := range nodes { + fk := nodes[i].UserFsevent + 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_fsevent" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (feq *FsEventQuery) sqlCount(ctx context.Context) (int, error) { + _spec := feq.querySpec() + _spec.Node.Columns = feq.ctx.Fields + if len(feq.ctx.Fields) > 0 { + _spec.Unique = feq.ctx.Unique != nil && *feq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, feq.driver, _spec) +} + +func (feq *FsEventQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(fsevent.Table, fsevent.Columns, sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt)) + _spec.From = feq.sql + if unique := feq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if feq.path != nil { + _spec.Unique = true + } + if fields := feq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, fsevent.FieldID) + for i := range fields { + if fields[i] != fsevent.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if feq.withUser != nil { + _spec.Node.AddColumnOnce(fsevent.FieldUserFsevent) + } + } + if ps := feq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := feq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := feq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := feq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (feq *FsEventQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(feq.driver.Dialect()) + t1 := builder.Table(fsevent.Table) + columns := feq.ctx.Fields + if len(columns) == 0 { + columns = fsevent.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if feq.sql != nil { + selector = feq.sql + selector.Select(selector.Columns(columns...)...) + } + if feq.ctx.Unique != nil && *feq.ctx.Unique { + selector.Distinct() + } + for _, p := range feq.predicates { + p(selector) + } + for _, p := range feq.order { + p(selector) + } + if offset := feq.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 := feq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// FsEventGroupBy is the group-by builder for FsEvent entities. +type FsEventGroupBy struct { + selector + build *FsEventQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (fegb *FsEventGroupBy) Aggregate(fns ...AggregateFunc) *FsEventGroupBy { + fegb.fns = append(fegb.fns, fns...) + return fegb +} + +// Scan applies the selector query and scans the result into the given value. +func (fegb *FsEventGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, fegb.build.ctx, "GroupBy") + if err := fegb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*FsEventQuery, *FsEventGroupBy](ctx, fegb.build, fegb, fegb.build.inters, v) +} + +func (fegb *FsEventGroupBy) sqlScan(ctx context.Context, root *FsEventQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(fegb.fns)) + for _, fn := range fegb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*fegb.flds)+len(fegb.fns)) + for _, f := range *fegb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*fegb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := fegb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// FsEventSelect is the builder for selecting fields of FsEvent entities. +type FsEventSelect struct { + *FsEventQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (fes *FsEventSelect) Aggregate(fns ...AggregateFunc) *FsEventSelect { + fes.fns = append(fes.fns, fns...) + return fes +} + +// Scan applies the selector query and scans the result into the given value. +func (fes *FsEventSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, fes.ctx, "Select") + if err := fes.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*FsEventQuery, *FsEventSelect](ctx, fes.FsEventQuery, fes, fes.inters, v) +} + +func (fes *FsEventSelect) sqlScan(ctx context.Context, root *FsEventQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(fes.fns)) + for _, fn := range fes.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*fes.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 := fes.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/fsevent_update.go b/ent/fsevent_update.go new file mode 100644 index 00000000..8118dae0 --- /dev/null +++ b/ent/fsevent_update.go @@ -0,0 +1,494 @@ +// 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/fsevent" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/user" + "github.com/gofrs/uuid" +) + +// FsEventUpdate is the builder for updating FsEvent entities. +type FsEventUpdate struct { + config + hooks []Hook + mutation *FsEventMutation +} + +// Where appends a list predicates to the FsEventUpdate builder. +func (feu *FsEventUpdate) Where(ps ...predicate.FsEvent) *FsEventUpdate { + feu.mutation.Where(ps...) + return feu +} + +// SetUpdatedAt sets the "updated_at" field. +func (feu *FsEventUpdate) SetUpdatedAt(t time.Time) *FsEventUpdate { + feu.mutation.SetUpdatedAt(t) + return feu +} + +// SetDeletedAt sets the "deleted_at" field. +func (feu *FsEventUpdate) SetDeletedAt(t time.Time) *FsEventUpdate { + feu.mutation.SetDeletedAt(t) + return feu +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (feu *FsEventUpdate) SetNillableDeletedAt(t *time.Time) *FsEventUpdate { + if t != nil { + feu.SetDeletedAt(*t) + } + return feu +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (feu *FsEventUpdate) ClearDeletedAt() *FsEventUpdate { + feu.mutation.ClearDeletedAt() + return feu +} + +// SetEvent sets the "event" field. +func (feu *FsEventUpdate) SetEvent(s string) *FsEventUpdate { + feu.mutation.SetEvent(s) + return feu +} + +// SetNillableEvent sets the "event" field if the given value is not nil. +func (feu *FsEventUpdate) SetNillableEvent(s *string) *FsEventUpdate { + if s != nil { + feu.SetEvent(*s) + } + return feu +} + +// SetSubscriber sets the "subscriber" field. +func (feu *FsEventUpdate) SetSubscriber(u uuid.UUID) *FsEventUpdate { + feu.mutation.SetSubscriber(u) + return feu +} + +// SetNillableSubscriber sets the "subscriber" field if the given value is not nil. +func (feu *FsEventUpdate) SetNillableSubscriber(u *uuid.UUID) *FsEventUpdate { + if u != nil { + feu.SetSubscriber(*u) + } + return feu +} + +// SetUserFsevent sets the "user_fsevent" field. +func (feu *FsEventUpdate) SetUserFsevent(i int) *FsEventUpdate { + feu.mutation.SetUserFsevent(i) + return feu +} + +// SetNillableUserFsevent sets the "user_fsevent" field if the given value is not nil. +func (feu *FsEventUpdate) SetNillableUserFsevent(i *int) *FsEventUpdate { + if i != nil { + feu.SetUserFsevent(*i) + } + return feu +} + +// ClearUserFsevent clears the value of the "user_fsevent" field. +func (feu *FsEventUpdate) ClearUserFsevent() *FsEventUpdate { + feu.mutation.ClearUserFsevent() + return feu +} + +// SetUserID sets the "user" edge to the User entity by ID. +func (feu *FsEventUpdate) SetUserID(id int) *FsEventUpdate { + feu.mutation.SetUserID(id) + return feu +} + +// SetNillableUserID sets the "user" edge to the User entity by ID if the given value is not nil. +func (feu *FsEventUpdate) SetNillableUserID(id *int) *FsEventUpdate { + if id != nil { + feu = feu.SetUserID(*id) + } + return feu +} + +// SetUser sets the "user" edge to the User entity. +func (feu *FsEventUpdate) SetUser(u *User) *FsEventUpdate { + return feu.SetUserID(u.ID) +} + +// Mutation returns the FsEventMutation object of the builder. +func (feu *FsEventUpdate) Mutation() *FsEventMutation { + return feu.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (feu *FsEventUpdate) ClearUser() *FsEventUpdate { + feu.mutation.ClearUser() + return feu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (feu *FsEventUpdate) Save(ctx context.Context) (int, error) { + if err := feu.defaults(); err != nil { + return 0, err + } + return withHooks(ctx, feu.sqlSave, feu.mutation, feu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (feu *FsEventUpdate) SaveX(ctx context.Context) int { + affected, err := feu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (feu *FsEventUpdate) Exec(ctx context.Context) error { + _, err := feu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (feu *FsEventUpdate) ExecX(ctx context.Context) { + if err := feu.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (feu *FsEventUpdate) defaults() error { + if _, ok := feu.mutation.UpdatedAt(); !ok { + if fsevent.UpdateDefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized fsevent.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := fsevent.UpdateDefaultUpdatedAt() + feu.mutation.SetUpdatedAt(v) + } + return nil +} + +func (feu *FsEventUpdate) sqlSave(ctx context.Context) (n int, err error) { + _spec := sqlgraph.NewUpdateSpec(fsevent.Table, fsevent.Columns, sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt)) + if ps := feu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := feu.mutation.UpdatedAt(); ok { + _spec.SetField(fsevent.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := feu.mutation.DeletedAt(); ok { + _spec.SetField(fsevent.FieldDeletedAt, field.TypeTime, value) + } + if feu.mutation.DeletedAtCleared() { + _spec.ClearField(fsevent.FieldDeletedAt, field.TypeTime) + } + if value, ok := feu.mutation.Event(); ok { + _spec.SetField(fsevent.FieldEvent, field.TypeString, value) + } + if value, ok := feu.mutation.Subscriber(); ok { + _spec.SetField(fsevent.FieldSubscriber, field.TypeUUID, value) + } + if feu.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: fsevent.UserTable, + Columns: []string{fsevent.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := feu.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: fsevent.UserTable, + Columns: []string{fsevent.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 n, err = sqlgraph.UpdateNodes(ctx, feu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{fsevent.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + feu.mutation.done = true + return n, nil +} + +// FsEventUpdateOne is the builder for updating a single FsEvent entity. +type FsEventUpdateOne struct { + config + fields []string + hooks []Hook + mutation *FsEventMutation +} + +// SetUpdatedAt sets the "updated_at" field. +func (feuo *FsEventUpdateOne) SetUpdatedAt(t time.Time) *FsEventUpdateOne { + feuo.mutation.SetUpdatedAt(t) + return feuo +} + +// SetDeletedAt sets the "deleted_at" field. +func (feuo *FsEventUpdateOne) SetDeletedAt(t time.Time) *FsEventUpdateOne { + feuo.mutation.SetDeletedAt(t) + return feuo +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (feuo *FsEventUpdateOne) SetNillableDeletedAt(t *time.Time) *FsEventUpdateOne { + if t != nil { + feuo.SetDeletedAt(*t) + } + return feuo +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (feuo *FsEventUpdateOne) ClearDeletedAt() *FsEventUpdateOne { + feuo.mutation.ClearDeletedAt() + return feuo +} + +// SetEvent sets the "event" field. +func (feuo *FsEventUpdateOne) SetEvent(s string) *FsEventUpdateOne { + feuo.mutation.SetEvent(s) + return feuo +} + +// SetNillableEvent sets the "event" field if the given value is not nil. +func (feuo *FsEventUpdateOne) SetNillableEvent(s *string) *FsEventUpdateOne { + if s != nil { + feuo.SetEvent(*s) + } + return feuo +} + +// SetSubscriber sets the "subscriber" field. +func (feuo *FsEventUpdateOne) SetSubscriber(u uuid.UUID) *FsEventUpdateOne { + feuo.mutation.SetSubscriber(u) + return feuo +} + +// SetNillableSubscriber sets the "subscriber" field if the given value is not nil. +func (feuo *FsEventUpdateOne) SetNillableSubscriber(u *uuid.UUID) *FsEventUpdateOne { + if u != nil { + feuo.SetSubscriber(*u) + } + return feuo +} + +// SetUserFsevent sets the "user_fsevent" field. +func (feuo *FsEventUpdateOne) SetUserFsevent(i int) *FsEventUpdateOne { + feuo.mutation.SetUserFsevent(i) + return feuo +} + +// SetNillableUserFsevent sets the "user_fsevent" field if the given value is not nil. +func (feuo *FsEventUpdateOne) SetNillableUserFsevent(i *int) *FsEventUpdateOne { + if i != nil { + feuo.SetUserFsevent(*i) + } + return feuo +} + +// ClearUserFsevent clears the value of the "user_fsevent" field. +func (feuo *FsEventUpdateOne) ClearUserFsevent() *FsEventUpdateOne { + feuo.mutation.ClearUserFsevent() + return feuo +} + +// SetUserID sets the "user" edge to the User entity by ID. +func (feuo *FsEventUpdateOne) SetUserID(id int) *FsEventUpdateOne { + feuo.mutation.SetUserID(id) + return feuo +} + +// SetNillableUserID sets the "user" edge to the User entity by ID if the given value is not nil. +func (feuo *FsEventUpdateOne) SetNillableUserID(id *int) *FsEventUpdateOne { + if id != nil { + feuo = feuo.SetUserID(*id) + } + return feuo +} + +// SetUser sets the "user" edge to the User entity. +func (feuo *FsEventUpdateOne) SetUser(u *User) *FsEventUpdateOne { + return feuo.SetUserID(u.ID) +} + +// Mutation returns the FsEventMutation object of the builder. +func (feuo *FsEventUpdateOne) Mutation() *FsEventMutation { + return feuo.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (feuo *FsEventUpdateOne) ClearUser() *FsEventUpdateOne { + feuo.mutation.ClearUser() + return feuo +} + +// Where appends a list predicates to the FsEventUpdate builder. +func (feuo *FsEventUpdateOne) Where(ps ...predicate.FsEvent) *FsEventUpdateOne { + feuo.mutation.Where(ps...) + return feuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (feuo *FsEventUpdateOne) Select(field string, fields ...string) *FsEventUpdateOne { + feuo.fields = append([]string{field}, fields...) + return feuo +} + +// Save executes the query and returns the updated FsEvent entity. +func (feuo *FsEventUpdateOne) Save(ctx context.Context) (*FsEvent, error) { + if err := feuo.defaults(); err != nil { + return nil, err + } + return withHooks(ctx, feuo.sqlSave, feuo.mutation, feuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (feuo *FsEventUpdateOne) SaveX(ctx context.Context) *FsEvent { + node, err := feuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (feuo *FsEventUpdateOne) Exec(ctx context.Context) error { + _, err := feuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (feuo *FsEventUpdateOne) ExecX(ctx context.Context) { + if err := feuo.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (feuo *FsEventUpdateOne) defaults() error { + if _, ok := feuo.mutation.UpdatedAt(); !ok { + if fsevent.UpdateDefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized fsevent.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := fsevent.UpdateDefaultUpdatedAt() + feuo.mutation.SetUpdatedAt(v) + } + return nil +} + +func (feuo *FsEventUpdateOne) sqlSave(ctx context.Context) (_node *FsEvent, err error) { + _spec := sqlgraph.NewUpdateSpec(fsevent.Table, fsevent.Columns, sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt)) + id, ok := feuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "FsEvent.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := feuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, fsevent.FieldID) + for _, f := range fields { + if !fsevent.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != fsevent.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := feuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := feuo.mutation.UpdatedAt(); ok { + _spec.SetField(fsevent.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := feuo.mutation.DeletedAt(); ok { + _spec.SetField(fsevent.FieldDeletedAt, field.TypeTime, value) + } + if feuo.mutation.DeletedAtCleared() { + _spec.ClearField(fsevent.FieldDeletedAt, field.TypeTime) + } + if value, ok := feuo.mutation.Event(); ok { + _spec.SetField(fsevent.FieldEvent, field.TypeString, value) + } + if value, ok := feuo.mutation.Subscriber(); ok { + _spec.SetField(fsevent.FieldSubscriber, field.TypeUUID, value) + } + if feuo.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: fsevent.UserTable, + Columns: []string{fsevent.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := feuo.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: fsevent.UserTable, + Columns: []string{fsevent.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) + } + _node = &FsEvent{config: feuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, feuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{fsevent.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + feuo.mutation.done = true + return _node, nil +} diff --git a/ent/hook/hook.go b/ent/hook/hook.go index 29b3f1fc..5a0f7e64 100644 --- a/ent/hook/hook.go +++ b/ent/hook/hook.go @@ -57,6 +57,18 @@ func (f FileFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.FileMutation", m) } +// The FsEventFunc type is an adapter to allow the use of ordinary +// function as FsEvent mutator. +type FsEventFunc func(context.Context, *ent.FsEventMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f FsEventFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.FsEventMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.FsEventMutation", m) +} + // The GroupFunc type is an adapter to allow the use of ordinary // function as Group mutator. type GroupFunc func(context.Context, *ent.GroupMutation) (ent.Value, error) diff --git a/ent/intercept/intercept.go b/ent/intercept/intercept.go index da350367..cd4ee317 100644 --- a/ent/intercept/intercept.go +++ b/ent/intercept/intercept.go @@ -12,6 +12,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/directlink" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/metadata" "github.com/cloudreve/Cloudreve/v4/ent/node" @@ -188,6 +189,33 @@ func (f TraverseFile) Traverse(ctx context.Context, q ent.Query) error { return fmt.Errorf("unexpected query type %T. expect *ent.FileQuery", q) } +// The FsEventFunc type is an adapter to allow the use of ordinary function as a Querier. +type FsEventFunc func(context.Context, *ent.FsEventQuery) (ent.Value, error) + +// Query calls f(ctx, q). +func (f FsEventFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) { + if q, ok := q.(*ent.FsEventQuery); ok { + return f(ctx, q) + } + return nil, fmt.Errorf("unexpected query type %T. expect *ent.FsEventQuery", q) +} + +// The TraverseFsEvent type is an adapter to allow the use of ordinary function as Traverser. +type TraverseFsEvent func(context.Context, *ent.FsEventQuery) error + +// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline. +func (f TraverseFsEvent) Intercept(next ent.Querier) ent.Querier { + return next +} + +// Traverse calls f(ctx, q). +func (f TraverseFsEvent) Traverse(ctx context.Context, q ent.Query) error { + if q, ok := q.(*ent.FsEventQuery); ok { + return f(ctx, q) + } + return fmt.Errorf("unexpected query type %T. expect *ent.FsEventQuery", q) +} + // The GroupFunc type is an adapter to allow the use of ordinary function as a Querier. type GroupFunc func(context.Context, *ent.GroupQuery) (ent.Value, error) @@ -442,6 +470,8 @@ func NewQuery(q ent.Query) (Query, error) { return &query[*ent.EntityQuery, predicate.Entity, entity.OrderOption]{typ: ent.TypeEntity, tq: q}, nil case *ent.FileQuery: return &query[*ent.FileQuery, predicate.File, file.OrderOption]{typ: ent.TypeFile, tq: q}, nil + case *ent.FsEventQuery: + return &query[*ent.FsEventQuery, predicate.FsEvent, fsevent.OrderOption]{typ: ent.TypeFsEvent, tq: q}, nil case *ent.GroupQuery: return &query[*ent.GroupQuery, predicate.Group, group.OrderOption]{typ: ent.TypeGroup, tq: q}, nil case *ent.MetadataQuery: diff --git a/ent/internal/schema.go b/ent/internal/schema.go index 1c9c1d99..c3bb3567 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\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.EntityProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"EntityProps\",\"Ident\":\"types.EntityProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"storage_key\":\"recycle_options\",\"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\":false,\"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,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"owner_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"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\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"primary_entity\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_children\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"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\":8,\"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\":9,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_files\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"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\":false,\"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\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.EntityProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"EntityProps\",\"Ident\":\"types.EntityProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"optional\":true,\"storage_key\":\"recycle_options\",\"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\":false,\"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,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}},{\"name\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"owner_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"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\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"primary_entity\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_children\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"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\":8,\"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\":9,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"storage_policy_files\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"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\":false,\"MixinIndex\":0}]},{\"name\":\"FsEvent\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_fsevent\",\"ref_name\":\"fsevents\",\"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\":\"event\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"subscriber\",\"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}]}}}},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"user_fsevent\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}}],\"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\":\"fsevents\",\"type\":\"FsEvent\"},{\"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\"]}" diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 8b85556a..5e35c32f 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -160,6 +160,30 @@ var ( }, }, } + // FsEventsColumns holds the columns for the "fs_events" table. + FsEventsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"mysql": "datetime"}}, + {Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"mysql": "datetime"}}, + {Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"mysql": "datetime"}}, + {Name: "event", Type: field.TypeString, Size: 2147483647}, + {Name: "subscriber", Type: field.TypeUUID}, + {Name: "user_fsevent", Type: field.TypeInt, Nullable: true}, + } + // FsEventsTable holds the schema information for the "fs_events" table. + FsEventsTable = &schema.Table{ + Name: "fs_events", + Columns: FsEventsColumns, + PrimaryKey: []*schema.Column{FsEventsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "fs_events_users_fsevents", + Columns: []*schema.Column{FsEventsColumns[6]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.SetNull, + }, + }, + } // GroupsColumns holds the columns for the "groups" table. GroupsColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, @@ -444,6 +468,7 @@ var ( DirectLinksTable, EntitiesTable, FilesTable, + FsEventsTable, GroupsTable, MetadataTable, NodesTable, @@ -465,6 +490,7 @@ func init() { FilesTable.ForeignKeys[0].RefTable = FilesTable FilesTable.ForeignKeys[1].RefTable = StoragePoliciesTable FilesTable.ForeignKeys[2].RefTable = UsersTable + FsEventsTable.ForeignKeys[0].RefTable = UsersTable GroupsTable.ForeignKeys[0].RefTable = StoragePoliciesTable MetadataTable.ForeignKeys[0].RefTable = FilesTable PasskeysTable.ForeignKeys[0].RefTable = UsersTable diff --git a/ent/mutation.go b/ent/mutation.go index fffa45ab..4a528b6e 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -15,6 +15,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/directlink" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/metadata" "github.com/cloudreve/Cloudreve/v4/ent/node" @@ -44,6 +45,7 @@ const ( TypeDirectLink = "DirectLink" TypeEntity = "Entity" TypeFile = "File" + TypeFsEvent = "FsEvent" TypeGroup = "Group" TypeMetadata = "Metadata" TypeNode = "Node" @@ -4601,6 +4603,713 @@ func (m *FileMutation) ResetEdge(name string) error { return fmt.Errorf("unknown File edge %s", name) } +// FsEventMutation represents an operation that mutates the FsEvent nodes in the graph. +type FsEventMutation struct { + config + op Op + typ string + id *int + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + event *string + subscriber *uuid.UUID + clearedFields map[string]struct{} + user *int + cleareduser bool + done bool + oldValue func(context.Context) (*FsEvent, error) + predicates []predicate.FsEvent +} + +var _ ent.Mutation = (*FsEventMutation)(nil) + +// fseventOption allows management of the mutation configuration using functional options. +type fseventOption func(*FsEventMutation) + +// newFsEventMutation creates new mutation for the FsEvent entity. +func newFsEventMutation(c config, op Op, opts ...fseventOption) *FsEventMutation { + m := &FsEventMutation{ + config: c, + op: op, + typ: TypeFsEvent, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withFsEventID sets the ID field of the mutation. +func withFsEventID(id int) fseventOption { + return func(m *FsEventMutation) { + var ( + err error + once sync.Once + value *FsEvent + ) + m.oldValue = func(ctx context.Context) (*FsEvent, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().FsEvent.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withFsEvent sets the old FsEvent of the mutation. +func withFsEvent(node *FsEvent) fseventOption { + return func(m *FsEventMutation) { + m.oldValue = func(context.Context) (*FsEvent, 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 FsEventMutation) 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 FsEventMutation) 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 *FsEventMutation) 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 *FsEventMutation) 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().FsEvent.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedAt sets the "created_at" field. +func (m *FsEventMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *FsEventMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the FsEvent entity. +// If the FsEvent 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 *FsEventMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *FsEventMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *FsEventMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *FsEventMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the FsEvent entity. +// If the FsEvent 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 *FsEventMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *FsEventMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetDeletedAt sets the "deleted_at" field. +func (m *FsEventMutation) SetDeletedAt(t time.Time) { + m.deleted_at = &t +} + +// DeletedAt returns the value of the "deleted_at" field in the mutation. +func (m *FsEventMutation) DeletedAt() (r time.Time, exists bool) { + v := m.deleted_at + if v == nil { + return + } + return *v, true +} + +// OldDeletedAt returns the old "deleted_at" field's value of the FsEvent entity. +// If the FsEvent 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 *FsEventMutation) OldDeletedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDeletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err) + } + return oldValue.DeletedAt, nil +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (m *FsEventMutation) ClearDeletedAt() { + m.deleted_at = nil + m.clearedFields[fsevent.FieldDeletedAt] = struct{}{} +} + +// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation. +func (m *FsEventMutation) DeletedAtCleared() bool { + _, ok := m.clearedFields[fsevent.FieldDeletedAt] + return ok +} + +// ResetDeletedAt resets all changes to the "deleted_at" field. +func (m *FsEventMutation) ResetDeletedAt() { + m.deleted_at = nil + delete(m.clearedFields, fsevent.FieldDeletedAt) +} + +// SetEvent sets the "event" field. +func (m *FsEventMutation) SetEvent(s string) { + m.event = &s +} + +// Event returns the value of the "event" field in the mutation. +func (m *FsEventMutation) Event() (r string, exists bool) { + v := m.event + if v == nil { + return + } + return *v, true +} + +// OldEvent returns the old "event" field's value of the FsEvent entity. +// If the FsEvent 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 *FsEventMutation) OldEvent(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEvent is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEvent requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEvent: %w", err) + } + return oldValue.Event, nil +} + +// ResetEvent resets all changes to the "event" field. +func (m *FsEventMutation) ResetEvent() { + m.event = nil +} + +// SetSubscriber sets the "subscriber" field. +func (m *FsEventMutation) SetSubscriber(u uuid.UUID) { + m.subscriber = &u +} + +// Subscriber returns the value of the "subscriber" field in the mutation. +func (m *FsEventMutation) Subscriber() (r uuid.UUID, exists bool) { + v := m.subscriber + if v == nil { + return + } + return *v, true +} + +// OldSubscriber returns the old "subscriber" field's value of the FsEvent entity. +// If the FsEvent 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 *FsEventMutation) OldSubscriber(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubscriber is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubscriber requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubscriber: %w", err) + } + return oldValue.Subscriber, nil +} + +// ResetSubscriber resets all changes to the "subscriber" field. +func (m *FsEventMutation) ResetSubscriber() { + m.subscriber = nil +} + +// SetUserFsevent sets the "user_fsevent" field. +func (m *FsEventMutation) SetUserFsevent(i int) { + m.user = &i +} + +// UserFsevent returns the value of the "user_fsevent" field in the mutation. +func (m *FsEventMutation) UserFsevent() (r int, exists bool) { + v := m.user + if v == nil { + return + } + return *v, true +} + +// OldUserFsevent returns the old "user_fsevent" field's value of the FsEvent entity. +// If the FsEvent 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 *FsEventMutation) OldUserFsevent(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUserFsevent is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUserFsevent requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserFsevent: %w", err) + } + return oldValue.UserFsevent, nil +} + +// ClearUserFsevent clears the value of the "user_fsevent" field. +func (m *FsEventMutation) ClearUserFsevent() { + m.user = nil + m.clearedFields[fsevent.FieldUserFsevent] = struct{}{} +} + +// UserFseventCleared returns if the "user_fsevent" field was cleared in this mutation. +func (m *FsEventMutation) UserFseventCleared() bool { + _, ok := m.clearedFields[fsevent.FieldUserFsevent] + return ok +} + +// ResetUserFsevent resets all changes to the "user_fsevent" field. +func (m *FsEventMutation) ResetUserFsevent() { + m.user = nil + delete(m.clearedFields, fsevent.FieldUserFsevent) +} + +// SetUserID sets the "user" edge to the User entity by id. +func (m *FsEventMutation) SetUserID(id int) { + m.user = &id +} + +// ClearUser clears the "user" edge to the User entity. +func (m *FsEventMutation) ClearUser() { + m.cleareduser = true + m.clearedFields[fsevent.FieldUserFsevent] = struct{}{} +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *FsEventMutation) UserCleared() bool { + return m.UserFseventCleared() || m.cleareduser +} + +// UserID returns the "user" edge ID in the mutation. +func (m *FsEventMutation) UserID() (id int, exists bool) { + if m.user != nil { + return *m.user, true + } + return +} + +// 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 *FsEventMutation) UserIDs() (ids []int) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *FsEventMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// Where appends a list predicates to the FsEventMutation builder. +func (m *FsEventMutation) Where(ps ...predicate.FsEvent) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the FsEventMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *FsEventMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.FsEvent, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *FsEventMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *FsEventMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (FsEvent). +func (m *FsEventMutation) 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 *FsEventMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.created_at != nil { + fields = append(fields, fsevent.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, fsevent.FieldUpdatedAt) + } + if m.deleted_at != nil { + fields = append(fields, fsevent.FieldDeletedAt) + } + if m.event != nil { + fields = append(fields, fsevent.FieldEvent) + } + if m.subscriber != nil { + fields = append(fields, fsevent.FieldSubscriber) + } + if m.user != nil { + fields = append(fields, fsevent.FieldUserFsevent) + } + 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 *FsEventMutation) Field(name string) (ent.Value, bool) { + switch name { + case fsevent.FieldCreatedAt: + return m.CreatedAt() + case fsevent.FieldUpdatedAt: + return m.UpdatedAt() + case fsevent.FieldDeletedAt: + return m.DeletedAt() + case fsevent.FieldEvent: + return m.Event() + case fsevent.FieldSubscriber: + return m.Subscriber() + case fsevent.FieldUserFsevent: + return m.UserFsevent() + } + 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 *FsEventMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case fsevent.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case fsevent.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case fsevent.FieldDeletedAt: + return m.OldDeletedAt(ctx) + case fsevent.FieldEvent: + return m.OldEvent(ctx) + case fsevent.FieldSubscriber: + return m.OldSubscriber(ctx) + case fsevent.FieldUserFsevent: + return m.OldUserFsevent(ctx) + } + return nil, fmt.Errorf("unknown FsEvent 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 *FsEventMutation) SetField(name string, value ent.Value) error { + switch name { + case fsevent.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case fsevent.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case fsevent.FieldDeletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDeletedAt(v) + return nil + case fsevent.FieldEvent: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEvent(v) + return nil + case fsevent.FieldSubscriber: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubscriber(v) + return nil + case fsevent.FieldUserFsevent: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserFsevent(v) + return nil + } + return fmt.Errorf("unknown FsEvent field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *FsEventMutation) 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 *FsEventMutation) 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 *FsEventMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown FsEvent numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *FsEventMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(fsevent.FieldDeletedAt) { + fields = append(fields, fsevent.FieldDeletedAt) + } + if m.FieldCleared(fsevent.FieldUserFsevent) { + fields = append(fields, fsevent.FieldUserFsevent) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *FsEventMutation) 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 *FsEventMutation) ClearField(name string) error { + switch name { + case fsevent.FieldDeletedAt: + m.ClearDeletedAt() + return nil + case fsevent.FieldUserFsevent: + m.ClearUserFsevent() + return nil + } + return fmt.Errorf("unknown FsEvent 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 *FsEventMutation) ResetField(name string) error { + switch name { + case fsevent.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case fsevent.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case fsevent.FieldDeletedAt: + m.ResetDeletedAt() + return nil + case fsevent.FieldEvent: + m.ResetEvent() + return nil + case fsevent.FieldSubscriber: + m.ResetSubscriber() + return nil + case fsevent.FieldUserFsevent: + m.ResetUserFsevent() + return nil + } + return fmt.Errorf("unknown FsEvent field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *FsEventMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.user != nil { + edges = append(edges, fsevent.EdgeUser) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *FsEventMutation) AddedIDs(name string) []ent.Value { + switch name { + case fsevent.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *FsEventMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *FsEventMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *FsEventMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.cleareduser { + edges = append(edges, fsevent.EdgeUser) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *FsEventMutation) EdgeCleared(name string) bool { + switch name { + case fsevent.EdgeUser: + return m.cleareduser + } + 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 *FsEventMutation) ClearEdge(name string) error { + switch name { + case fsevent.EdgeUser: + m.ClearUser() + return nil + } + return fmt.Errorf("unknown FsEvent 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 *FsEventMutation) ResetEdge(name string) error { + switch name { + case fsevent.EdgeUser: + m.ResetUser() + return nil + } + return fmt.Errorf("unknown FsEvent edge %s", name) +} + // GroupMutation represents an operation that mutates the Group nodes in the graph. type GroupMutation struct { config @@ -12532,6 +13241,9 @@ type UserMutation struct { tasks map[int]struct{} removedtasks map[int]struct{} clearedtasks bool + fsevents map[int]struct{} + removedfsevents map[int]struct{} + clearedfsevents bool entities map[int]struct{} removedentities map[int]struct{} clearedentities bool @@ -13465,6 +14177,60 @@ func (m *UserMutation) ResetTasks() { m.removedtasks = nil } +// AddFseventIDs adds the "fsevents" edge to the FsEvent entity by ids. +func (m *UserMutation) AddFseventIDs(ids ...int) { + if m.fsevents == nil { + m.fsevents = make(map[int]struct{}) + } + for i := range ids { + m.fsevents[ids[i]] = struct{}{} + } +} + +// ClearFsevents clears the "fsevents" edge to the FsEvent entity. +func (m *UserMutation) ClearFsevents() { + m.clearedfsevents = true +} + +// FseventsCleared reports if the "fsevents" edge to the FsEvent entity was cleared. +func (m *UserMutation) FseventsCleared() bool { + return m.clearedfsevents +} + +// RemoveFseventIDs removes the "fsevents" edge to the FsEvent entity by IDs. +func (m *UserMutation) RemoveFseventIDs(ids ...int) { + if m.removedfsevents == nil { + m.removedfsevents = make(map[int]struct{}) + } + for i := range ids { + delete(m.fsevents, ids[i]) + m.removedfsevents[ids[i]] = struct{}{} + } +} + +// RemovedFsevents returns the removed IDs of the "fsevents" edge to the FsEvent entity. +func (m *UserMutation) RemovedFseventsIDs() (ids []int) { + for id := range m.removedfsevents { + ids = append(ids, id) + } + return +} + +// FseventsIDs returns the "fsevents" edge IDs in the mutation. +func (m *UserMutation) FseventsIDs() (ids []int) { + for id := range m.fsevents { + ids = append(ids, id) + } + return +} + +// ResetFsevents resets all changes to the "fsevents" edge. +func (m *UserMutation) ResetFsevents() { + m.fsevents = nil + m.clearedfsevents = false + m.removedfsevents = nil +} + // AddEntityIDs adds the "entities" edge to the Entity entity by ids. func (m *UserMutation) AddEntityIDs(ids ...int) { if m.entities == nil { @@ -13887,7 +14653,7 @@ func (m *UserMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.group != nil { edges = append(edges, user.EdgeGroup) } @@ -13906,6 +14672,9 @@ func (m *UserMutation) AddedEdges() []string { if m.tasks != nil { edges = append(edges, user.EdgeTasks) } + if m.fsevents != nil { + edges = append(edges, user.EdgeFsevents) + } if m.entities != nil { edges = append(edges, user.EdgeEntities) } @@ -13950,6 +14719,12 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeFsevents: + ids := make([]ent.Value, 0, len(m.fsevents)) + for id := range m.fsevents { + ids = append(ids, id) + } + return ids case user.EdgeEntities: ids := make([]ent.Value, 0, len(m.entities)) for id := range m.entities { @@ -13962,7 +14737,7 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value { // 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.removedfiles != nil { edges = append(edges, user.EdgeFiles) } @@ -13978,6 +14753,9 @@ func (m *UserMutation) RemovedEdges() []string { if m.removedtasks != nil { edges = append(edges, user.EdgeTasks) } + if m.removedfsevents != nil { + edges = append(edges, user.EdgeFsevents) + } if m.removedentities != nil { edges = append(edges, user.EdgeEntities) } @@ -14018,6 +14796,12 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeFsevents: + ids := make([]ent.Value, 0, len(m.removedfsevents)) + for id := range m.removedfsevents { + ids = append(ids, id) + } + return ids case user.EdgeEntities: ids := make([]ent.Value, 0, len(m.removedentities)) for id := range m.removedentities { @@ -14030,7 +14814,7 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.clearedgroup { edges = append(edges, user.EdgeGroup) } @@ -14049,6 +14833,9 @@ func (m *UserMutation) ClearedEdges() []string { if m.clearedtasks { edges = append(edges, user.EdgeTasks) } + if m.clearedfsevents { + edges = append(edges, user.EdgeFsevents) + } if m.clearedentities { edges = append(edges, user.EdgeEntities) } @@ -14071,6 +14858,8 @@ func (m *UserMutation) EdgeCleared(name string) bool { return m.clearedpasskey case user.EdgeTasks: return m.clearedtasks + case user.EdgeFsevents: + return m.clearedfsevents case user.EdgeEntities: return m.clearedentities } @@ -14110,6 +14899,9 @@ func (m *UserMutation) ResetEdge(name string) error { case user.EdgeTasks: m.ResetTasks() return nil + case user.EdgeFsevents: + m.ResetFsevents() + return nil case user.EdgeEntities: m.ResetEntities() return nil diff --git a/ent/mutationhelper.go b/ent/mutationhelper.go index 86d1931f..1d337357 100644 --- a/ent/mutationhelper.go +++ b/ent/mutationhelper.go @@ -28,6 +28,12 @@ func (m *FileMutation) SetRawID(t int) { // SetUpdatedAt sets the "updated_at" field. +func (m *FsEventMutation) SetRawID(t int) { + m.id = &t +} + +// SetUpdatedAt sets the "updated_at" field. + func (m *GroupMutation) SetRawID(t int) { m.id = &t } diff --git a/ent/predicate/predicate.go b/ent/predicate/predicate.go index 3ee71805..9a8feb2e 100644 --- a/ent/predicate/predicate.go +++ b/ent/predicate/predicate.go @@ -18,6 +18,9 @@ type Entity func(*sql.Selector) // File is the predicate function for file builders. type File func(*sql.Selector) +// FsEvent is the predicate function for fsevent builders. +type FsEvent func(*sql.Selector) + // Group is the predicate function for group builders. type Group func(*sql.Selector) diff --git a/ent/runtime/runtime.go b/ent/runtime/runtime.go index 90100484..44655e0d 100644 --- a/ent/runtime/runtime.go +++ b/ent/runtime/runtime.go @@ -9,6 +9,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/directlink" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/metadata" "github.com/cloudreve/Cloudreve/v4/ent/node" @@ -107,6 +108,25 @@ func init() { fileDescIsSymbolic := fileFields[8].Descriptor() // file.DefaultIsSymbolic holds the default value on creation for the is_symbolic field. file.DefaultIsSymbolic = fileDescIsSymbolic.Default.(bool) + fseventMixin := schema.FsEvent{}.Mixin() + fseventMixinHooks0 := fseventMixin[0].Hooks() + fsevent.Hooks[0] = fseventMixinHooks0[0] + fseventMixinInters0 := fseventMixin[0].Interceptors() + fsevent.Interceptors[0] = fseventMixinInters0[0] + fseventMixinFields0 := fseventMixin[0].Fields() + _ = fseventMixinFields0 + fseventFields := schema.FsEvent{}.Fields() + _ = fseventFields + // fseventDescCreatedAt is the schema descriptor for created_at field. + fseventDescCreatedAt := fseventMixinFields0[0].Descriptor() + // fsevent.DefaultCreatedAt holds the default value on creation for the created_at field. + fsevent.DefaultCreatedAt = fseventDescCreatedAt.Default.(func() time.Time) + // fseventDescUpdatedAt is the schema descriptor for updated_at field. + fseventDescUpdatedAt := fseventMixinFields0[1].Descriptor() + // fsevent.DefaultUpdatedAt holds the default value on creation for the updated_at field. + fsevent.DefaultUpdatedAt = fseventDescUpdatedAt.Default.(func() time.Time) + // fsevent.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + fsevent.UpdateDefaultUpdatedAt = fseventDescUpdatedAt.UpdateDefault.(func() time.Time) groupMixin := schema.Group{}.Mixin() groupMixinHooks0 := groupMixin[0].Hooks() group.Hooks[0] = groupMixinHooks0[0] diff --git a/ent/schema/fsevent.go b/ent/schema/fsevent.go new file mode 100644 index 00000000..64bceae2 --- /dev/null +++ b/ent/schema/fsevent.go @@ -0,0 +1,38 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/gofrs/uuid" +) + +// FsEvent holds the schema definition for the FsEvent entity. +type FsEvent struct { + ent.Schema +} + +// Fields of the FsEvent. +func (FsEvent) Fields() []ent.Field { + return []ent.Field{ + field.Text("event"), + field.UUID("subscriber", uuid.Must(uuid.NewV4())), + field.Int("user_fsevent").Optional(), + } +} + +// Edges of the Task. +func (FsEvent) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("user", User.Type). + Ref("fsevents"). + Field("user_fsevent"). + Unique(), + } +} + +func (FsEvent) Mixin() []ent.Mixin { + return []ent.Mixin{ + CommonMixin{}, + } +} diff --git a/ent/schema/user.go b/ent/schema/user.go index 23cba9c7..5d473638 100644 --- a/ent/schema/user.go +++ b/ent/schema/user.go @@ -51,6 +51,7 @@ func (User) Edges() []ent.Edge { edge.To("shares", Share.Type), edge.To("passkey", Passkey.Type), edge.To("tasks", Task.Type), + edge.To("fsevents", FsEvent.Type), edge.To("entities", Entity.Type), } } diff --git a/ent/tx.go b/ent/tx.go index 98f9b540..f0e50149 100644 --- a/ent/tx.go +++ b/ent/tx.go @@ -22,6 +22,8 @@ type Tx struct { Entity *EntityClient // File is the client for interacting with the File builders. File *FileClient + // FsEvent is the client for interacting with the FsEvent builders. + FsEvent *FsEventClient // Group is the client for interacting with the Group builders. Group *GroupClient // Metadata is the client for interacting with the Metadata builders. @@ -175,6 +177,7 @@ func (tx *Tx) init() { tx.DirectLink = NewDirectLinkClient(tx.config) tx.Entity = NewEntityClient(tx.config) tx.File = NewFileClient(tx.config) + tx.FsEvent = NewFsEventClient(tx.config) tx.Group = NewGroupClient(tx.config) tx.Metadata = NewMetadataClient(tx.config) tx.Node = NewNodeClient(tx.config) diff --git a/ent/user.go b/ent/user.go index 8b361f64..2545a3ff 100644 --- a/ent/user.go +++ b/ent/user.go @@ -64,11 +64,13 @@ type UserEdges struct { Passkey []*Passkey `json:"passkey,omitempty"` // Tasks holds the value of the tasks edge. Tasks []*Task `json:"tasks,omitempty"` + // Fsevents holds the value of the fsevents edge. + Fsevents []*FsEvent `json:"fsevents,omitempty"` // Entities holds the value of the entities edge. Entities []*Entity `json:"entities,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 @@ -129,10 +131,19 @@ func (e UserEdges) TasksOrErr() ([]*Task, error) { return nil, &NotLoadedError{edge: "tasks"} } +// FseventsOrErr returns the Fsevents value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) FseventsOrErr() ([]*FsEvent, error) { + if e.loadedTypes[6] { + return e.Fsevents, nil + } + return nil, &NotLoadedError{edge: "fsevents"} +} + // EntitiesOrErr returns the Entities value or an error if the edge // was not loaded in eager-loading. func (e UserEdges) EntitiesOrErr() ([]*Entity, error) { - if e.loadedTypes[6] { + if e.loadedTypes[7] { return e.Entities, nil } return nil, &NotLoadedError{edge: "entities"} @@ -290,6 +301,11 @@ func (u *User) QueryTasks() *TaskQuery { return NewUserClient(u.config).QueryTasks(u) } +// QueryFsevents queries the "fsevents" edge of the User entity. +func (u *User) QueryFsevents() *FsEventQuery { + return NewUserClient(u.config).QueryFsevents(u) +} + // QueryEntities queries the "entities" edge of the User entity. func (u *User) QueryEntities() *EntityQuery { return NewUserClient(u.config).QueryEntities(u) @@ -393,10 +409,16 @@ func (e *User) SetTasks(v []*Task) { e.Edges.loadedTypes[5] = true } +// SetFsevents manually set the edge as loaded state. +func (e *User) SetFsevents(v []*FsEvent) { + e.Edges.Fsevents = v + e.Edges.loadedTypes[6] = true +} + // SetEntities manually set the edge as loaded state. func (e *User) SetEntities(v []*Entity) { e.Edges.Entities = v - e.Edges.loadedTypes[6] = true + e.Edges.loadedTypes[7] = true } // Users is a parsable slice of User. diff --git a/ent/user/user.go b/ent/user/user.go index 8ff28fbf..8385e63e 100644 --- a/ent/user/user.go +++ b/ent/user/user.go @@ -53,6 +53,8 @@ const ( EdgePasskey = "passkey" // EdgeTasks holds the string denoting the tasks edge name in mutations. EdgeTasks = "tasks" + // EdgeFsevents holds the string denoting the fsevents edge name in mutations. + EdgeFsevents = "fsevents" // EdgeEntities holds the string denoting the entities edge name in mutations. EdgeEntities = "entities" // Table holds the table name of the user in the database. @@ -99,6 +101,13 @@ const ( TasksInverseTable = "tasks" // TasksColumn is the table column denoting the tasks relation/edge. TasksColumn = "user_tasks" + // FseventsTable is the table that holds the fsevents relation/edge. + FseventsTable = "fs_events" + // FseventsInverseTable is the table name for the FsEvent entity. + // It exists in this package in order to avoid circular dependency with the "fsevent" package. + FseventsInverseTable = "fs_events" + // FseventsColumn is the table column denoting the fsevents relation/edge. + FseventsColumn = "user_fsevent" // EntitiesTable is the table that holds the entities relation/edge. EntitiesTable = "entities" // EntitiesInverseTable is the table name for the Entity entity. @@ -327,6 +336,20 @@ func ByTasks(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { } } +// ByFseventsCount orders the results by fsevents count. +func ByFseventsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newFseventsStep(), opts...) + } +} + +// ByFsevents orders the results by fsevents terms. +func ByFsevents(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newFseventsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + // ByEntitiesCount orders the results by entities count. func ByEntitiesCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { @@ -382,6 +405,13 @@ func newTasksStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, TasksTable, TasksColumn), ) } +func newFseventsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(FseventsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, FseventsTable, FseventsColumn), + ) +} func newEntitiesStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), diff --git a/ent/user/where.go b/ent/user/where.go index 5400662a..471f0958 100644 --- a/ent/user/where.go +++ b/ent/user/where.go @@ -818,6 +818,29 @@ func HasTasksWith(preds ...predicate.Task) predicate.User { }) } +// HasFsevents applies the HasEdge predicate on the "fsevents" edge. +func HasFsevents() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, FseventsTable, FseventsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasFseventsWith applies the HasEdge predicate on the "fsevents" edge with a given conditions (other predicates). +func HasFseventsWith(preds ...predicate.FsEvent) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newFseventsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // HasEntities applies the HasEdge predicate on the "entities" edge. func HasEntities() predicate.User { return predicate.User(func(s *sql.Selector) { diff --git a/ent/user_create.go b/ent/user_create.go index 4e5f9278..20301eb5 100644 --- a/ent/user_create.go +++ b/ent/user_create.go @@ -14,6 +14,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/davaccount" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/share" @@ -252,6 +253,21 @@ func (uc *UserCreate) AddTasks(t ...*Task) *UserCreate { return uc.AddTaskIDs(ids...) } +// AddFseventIDs adds the "fsevents" edge to the FsEvent entity by IDs. +func (uc *UserCreate) AddFseventIDs(ids ...int) *UserCreate { + uc.mutation.AddFseventIDs(ids...) + return uc +} + +// AddFsevents adds the "fsevents" edges to the FsEvent entity. +func (uc *UserCreate) AddFsevents(f ...*FsEvent) *UserCreate { + ids := make([]int, len(f)) + for i := range f { + ids[i] = f[i].ID + } + return uc.AddFseventIDs(ids...) +} + // AddEntityIDs adds the "entities" edge to the Entity entity by IDs. func (uc *UserCreate) AddEntityIDs(ids ...int) *UserCreate { uc.mutation.AddEntityIDs(ids...) @@ -549,6 +565,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := uc.mutation.FseventsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.FseventsTable, + Columns: []string{user.FseventsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } if nodes := uc.mutation.EntitiesIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/ent/user_query.go b/ent/user_query.go index bbe8eb11..2d44e1ed 100644 --- a/ent/user_query.go +++ b/ent/user_query.go @@ -14,6 +14,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/davaccount" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/predicate" @@ -35,6 +36,7 @@ type UserQuery struct { withShares *ShareQuery withPasskey *PasskeyQuery withTasks *TaskQuery + withFsevents *FsEventQuery withEntities *EntityQuery // intermediate query (i.e. traversal path). sql *sql.Selector @@ -204,6 +206,28 @@ func (uq *UserQuery) QueryTasks() *TaskQuery { return query } +// QueryFsevents chains the current query on the "fsevents" edge. +func (uq *UserQuery) QueryFsevents() *FsEventQuery { + query := (&FsEventClient{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(fsevent.Table, fsevent.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.FseventsTable, user.FseventsColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // QueryEntities chains the current query on the "entities" edge. func (uq *UserQuery) QueryEntities() *EntityQuery { query := (&EntityClient{config: uq.config}).Query() @@ -424,6 +448,7 @@ func (uq *UserQuery) Clone() *UserQuery { withShares: uq.withShares.Clone(), withPasskey: uq.withPasskey.Clone(), withTasks: uq.withTasks.Clone(), + withFsevents: uq.withFsevents.Clone(), withEntities: uq.withEntities.Clone(), // clone intermediate query. sql: uq.sql.Clone(), @@ -497,6 +522,17 @@ func (uq *UserQuery) WithTasks(opts ...func(*TaskQuery)) *UserQuery { return uq } +// WithFsevents tells the query-builder to eager-load the nodes that are connected to +// the "fsevents" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithFsevents(opts ...func(*FsEventQuery)) *UserQuery { + query := (&FsEventClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withFsevents = query + return uq +} + // WithEntities tells the query-builder to eager-load the nodes that are connected to // the "entities" edge. The optional arguments are used to configure the query builder of the edge. func (uq *UserQuery) WithEntities(opts ...func(*EntityQuery)) *UserQuery { @@ -586,13 +622,14 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e var ( nodes = []*User{} _spec = uq.querySpec() - loadedTypes = [7]bool{ + loadedTypes = [8]bool{ uq.withGroup != nil, uq.withFiles != nil, uq.withDavAccounts != nil, uq.withShares != nil, uq.withPasskey != nil, uq.withTasks != nil, + uq.withFsevents != nil, uq.withEntities != nil, } ) @@ -655,6 +692,13 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e return nil, err } } + if query := uq.withFsevents; query != nil { + if err := uq.loadFsevents(ctx, query, nodes, + func(n *User) { n.Edges.Fsevents = []*FsEvent{} }, + func(n *User, e *FsEvent) { n.Edges.Fsevents = append(n.Edges.Fsevents, e) }); err != nil { + return nil, err + } + } if query := uq.withEntities; query != nil { if err := uq.loadEntities(ctx, query, nodes, func(n *User) { n.Edges.Entities = []*Entity{} }, @@ -845,6 +889,36 @@ func (uq *UserQuery) loadTasks(ctx context.Context, query *TaskQuery, nodes []*U } return nil } +func (uq *UserQuery) loadFsevents(ctx context.Context, query *FsEventQuery, nodes []*User, init func(*User), assign func(*User, *FsEvent)) 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(fsevent.FieldUserFsevent) + } + query.Where(predicate.FsEvent(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(user.FseventsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.UserFsevent + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "user_fsevent" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (uq *UserQuery) loadEntities(ctx context.Context, query *EntityQuery, nodes []*User, init func(*User), assign func(*User, *Entity)) error { fks := make([]driver.Value, 0, len(nodes)) nodeids := make(map[int]*User) diff --git a/ent/user_update.go b/ent/user_update.go index 4d67894a..843e4918 100644 --- a/ent/user_update.go +++ b/ent/user_update.go @@ -14,6 +14,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/davaccount" "github.com/cloudreve/Cloudreve/v4/ent/entity" "github.com/cloudreve/Cloudreve/v4/ent/file" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" "github.com/cloudreve/Cloudreve/v4/ent/group" "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/predicate" @@ -297,6 +298,21 @@ func (uu *UserUpdate) AddTasks(t ...*Task) *UserUpdate { return uu.AddTaskIDs(ids...) } +// AddFseventIDs adds the "fsevents" edge to the FsEvent entity by IDs. +func (uu *UserUpdate) AddFseventIDs(ids ...int) *UserUpdate { + uu.mutation.AddFseventIDs(ids...) + return uu +} + +// AddFsevents adds the "fsevents" edges to the FsEvent entity. +func (uu *UserUpdate) AddFsevents(f ...*FsEvent) *UserUpdate { + ids := make([]int, len(f)) + for i := range f { + ids[i] = f[i].ID + } + return uu.AddFseventIDs(ids...) +} + // AddEntityIDs adds the "entities" edge to the Entity entity by IDs. func (uu *UserUpdate) AddEntityIDs(ids ...int) *UserUpdate { uu.mutation.AddEntityIDs(ids...) @@ -428,6 +444,27 @@ func (uu *UserUpdate) RemoveTasks(t ...*Task) *UserUpdate { return uu.RemoveTaskIDs(ids...) } +// ClearFsevents clears all "fsevents" edges to the FsEvent entity. +func (uu *UserUpdate) ClearFsevents() *UserUpdate { + uu.mutation.ClearFsevents() + return uu +} + +// RemoveFseventIDs removes the "fsevents" edge to FsEvent entities by IDs. +func (uu *UserUpdate) RemoveFseventIDs(ids ...int) *UserUpdate { + uu.mutation.RemoveFseventIDs(ids...) + return uu +} + +// RemoveFsevents removes "fsevents" edges to FsEvent entities. +func (uu *UserUpdate) RemoveFsevents(f ...*FsEvent) *UserUpdate { + ids := make([]int, len(f)) + for i := range f { + ids[i] = f[i].ID + } + return uu.RemoveFseventIDs(ids...) +} + // ClearEntities clears all "entities" edges to the Entity entity. func (uu *UserUpdate) ClearEntities() *UserUpdate { uu.mutation.ClearEntities() @@ -828,6 +865,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uu.mutation.FseventsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.FseventsTable, + Columns: []string{user.FseventsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedFseventsIDs(); len(nodes) > 0 && !uu.mutation.FseventsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.FseventsTable, + Columns: []string{user.FseventsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(fsevent.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.FseventsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.FseventsTable, + Columns: []string{user.FseventsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if uu.mutation.EntitiesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -1154,6 +1236,21 @@ func (uuo *UserUpdateOne) AddTasks(t ...*Task) *UserUpdateOne { return uuo.AddTaskIDs(ids...) } +// AddFseventIDs adds the "fsevents" edge to the FsEvent entity by IDs. +func (uuo *UserUpdateOne) AddFseventIDs(ids ...int) *UserUpdateOne { + uuo.mutation.AddFseventIDs(ids...) + return uuo +} + +// AddFsevents adds the "fsevents" edges to the FsEvent entity. +func (uuo *UserUpdateOne) AddFsevents(f ...*FsEvent) *UserUpdateOne { + ids := make([]int, len(f)) + for i := range f { + ids[i] = f[i].ID + } + return uuo.AddFseventIDs(ids...) +} + // AddEntityIDs adds the "entities" edge to the Entity entity by IDs. func (uuo *UserUpdateOne) AddEntityIDs(ids ...int) *UserUpdateOne { uuo.mutation.AddEntityIDs(ids...) @@ -1285,6 +1382,27 @@ func (uuo *UserUpdateOne) RemoveTasks(t ...*Task) *UserUpdateOne { return uuo.RemoveTaskIDs(ids...) } +// ClearFsevents clears all "fsevents" edges to the FsEvent entity. +func (uuo *UserUpdateOne) ClearFsevents() *UserUpdateOne { + uuo.mutation.ClearFsevents() + return uuo +} + +// RemoveFseventIDs removes the "fsevents" edge to FsEvent entities by IDs. +func (uuo *UserUpdateOne) RemoveFseventIDs(ids ...int) *UserUpdateOne { + uuo.mutation.RemoveFseventIDs(ids...) + return uuo +} + +// RemoveFsevents removes "fsevents" edges to FsEvent entities. +func (uuo *UserUpdateOne) RemoveFsevents(f ...*FsEvent) *UserUpdateOne { + ids := make([]int, len(f)) + for i := range f { + ids[i] = f[i].ID + } + return uuo.RemoveFseventIDs(ids...) +} + // ClearEntities clears all "entities" edges to the Entity entity. func (uuo *UserUpdateOne) ClearEntities() *UserUpdateOne { uuo.mutation.ClearEntities() @@ -1715,6 +1833,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uuo.mutation.FseventsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.FseventsTable, + Columns: []string{user.FseventsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedFseventsIDs(); len(nodes) > 0 && !uuo.mutation.FseventsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.FseventsTable, + Columns: []string{user.FseventsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(fsevent.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.FseventsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.FseventsTable, + Columns: []string{user.FseventsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(fsevent.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if uuo.mutation.EntitiesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/inventory/fs_event.go b/inventory/fs_event.go new file mode 100644 index 00000000..1feeecc7 --- /dev/null +++ b/inventory/fs_event.go @@ -0,0 +1,81 @@ +package inventory + +import ( + "context" + + "github.com/cloudreve/Cloudreve/v4/ent" + "github.com/cloudreve/Cloudreve/v4/ent/fsevent" + "github.com/cloudreve/Cloudreve/v4/ent/schema" + "github.com/cloudreve/Cloudreve/v4/pkg/conf" + "github.com/gofrs/uuid" + "github.com/samber/lo" +) + +type FsEventClient interface { + TxOperator + // Create a new FsEvent + Create(ctx context.Context, uid int, subscriberId uuid.UUID, events ...string) error + // Delete all FsEvents by subscriber + DeleteBySubscriber(ctx context.Context, subscriberId uuid.UUID) error + // Delete all FsEvents + DeleteAll(ctx context.Context) error + // Get all FsEvents by subscriber and user + TakeBySubscriber(ctx context.Context, subscriberId uuid.UUID, userId int) ([]*ent.FsEvent, error) +} + +func NewFsEventClient(client *ent.Client, dbType conf.DBType) FsEventClient { + return &fsEventClient{client: client, maxSQlParam: sqlParamLimit(dbType)} +} + +type fsEventClient struct { + maxSQlParam int + client *ent.Client +} + +func (c *fsEventClient) SetClient(newClient *ent.Client) TxOperator { + return &fsEventClient{client: newClient, maxSQlParam: c.maxSQlParam} +} + +func (c *fsEventClient) GetClient() *ent.Client { + return c.client +} + +func (c *fsEventClient) Create(ctx context.Context, uid int, subscriberId uuid.UUID, events ...string) error { + stms := lo.Map(events, func(event string, index int) *ent.FsEventCreate { + res := c.client.FsEvent. + Create(). + SetUserFsevent(uid). + SetEvent(event). + SetSubscriber(subscriberId).SetEvent(event) + + return res + }) + + _, err := c.client.FsEvent.CreateBulk(stms...).Save(ctx) + return err +} + +func (c *fsEventClient) DeleteBySubscriber(ctx context.Context, subscriberId uuid.UUID) error { + _, err := c.client.FsEvent.Delete().Where(fsevent.Subscriber(subscriberId)).Exec(schema.SkipSoftDelete(ctx)) + return err +} + +func (c *fsEventClient) DeleteAll(ctx context.Context) error { + _, err := c.client.FsEvent.Delete().Exec(schema.SkipSoftDelete(ctx)) + return err +} + +func (c *fsEventClient) TakeBySubscriber(ctx context.Context, subscriberId uuid.UUID, userId int) ([]*ent.FsEvent, error) { + res, err := c.client.FsEvent.Query().Where(fsevent.Subscriber(subscriberId), fsevent.UserFsevent(userId)).All(ctx) + if err != nil { + return nil, err + } + + // Delete the FsEvents + _, err = c.client.FsEvent.Delete().Where(fsevent.Subscriber(subscriberId), fsevent.UserFsevent(userId)).Exec(schema.SkipSoftDelete(ctx)) + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/middleware/common.go b/middleware/common.go index a7adab04..3c501ed5 100644 --- a/middleware/common.go +++ b/middleware/common.go @@ -103,6 +103,7 @@ func InitializeHandling(dep dependency.Dep) gin.HandlerFunc { IP: clientIp, Host: c.Request.Host, UserAgent: c.Request.UserAgent(), + ClientID: c.GetHeader(request.ClientIDHeader), } cid := uuid.FromStringOrNil(c.GetHeader(request.CorrelationHeader)) if cid == uuid.Nil { diff --git a/pkg/auth/requestinfo/requestinfo.go b/pkg/auth/requestinfo/requestinfo.go index 55c09929..a9b65a8f 100644 --- a/pkg/auth/requestinfo/requestinfo.go +++ b/pkg/auth/requestinfo/requestinfo.go @@ -22,4 +22,6 @@ type RequestInfo struct { Host string IP string UserAgent string + // ID of sync client + ClientID string } diff --git a/pkg/filemanager/eventhub/event.go b/pkg/filemanager/eventhub/event.go new file mode 100644 index 00000000..57f1cd71 --- /dev/null +++ b/pkg/filemanager/eventhub/event.go @@ -0,0 +1,26 @@ +package eventhub + +import "errors" + +type ( + Event struct { + Type EventType `json:"type"` + FileID string `json:"file_id"` + From string `json:"from"` + To string `json:"to"` + } + + EventType string +) + +const ( + EventTypeCreate = "create" + EventTypeModify = "modify" + EventTypeRename = "rename" + EventTypeDelete = "delete" +) + +var ( + // ErrEventHubClosed is returned when operations are attempted on a closed EventHub. + ErrEventHubClosed = errors.New("event hub is closed") +) diff --git a/pkg/filemanager/eventhub/eventhub.go b/pkg/filemanager/eventhub/eventhub.go new file mode 100644 index 00000000..3a1237a5 --- /dev/null +++ b/pkg/filemanager/eventhub/eventhub.go @@ -0,0 +1,199 @@ +package eventhub + +import ( + "context" + "sync" + "time" + + "github.com/cloudreve/Cloudreve/v4/inventory" + "github.com/cloudreve/Cloudreve/v4/pkg/logging" +) + +type ( + EventHub interface { + // Subscribe to a topic and return a channel to receive events. + // If a subscriber with the same ID already exists and is offline, + // it will be reactivated and any buffered events will be flushed. + Subscribe(ctx context.Context, topic int, id string) (chan *Event, bool, error) + // Unsubscribe marks the subscriber as offline instead of removing it. + // Buffered events will be kept for when the subscriber reconnects. + // Subscribers that remain offline for more than 14 days will be permanently removed. + Unsubscribe(ctx context.Context, topic int, id string) + // Get subscribers of a topic. + GetSubscribers(ctx context.Context, topic int) []Subscriber + // Close shuts down the event hub and disconnects all subscribers. + Close() + } +) + +const ( + bufSize = 16 + cleanupPeriod = 1 * time.Hour +) + +type eventHub struct { + mu sync.RWMutex + topics map[int]map[string]*subscriber + userClient inventory.UserClient + fsEventClient inventory.FsEventClient + closed bool + closeCh chan struct{} + wg sync.WaitGroup +} + +func NewEventHub(userClient inventory.UserClient, fsEventClient inventory.FsEventClient) EventHub { + e := &eventHub{ + topics: make(map[int]map[string]*subscriber), + userClient: userClient, + fsEventClient: fsEventClient, + closeCh: make(chan struct{}), + } + + // Remove all existing FsEvents + fsEventClient.DeleteAll(context.Background()) + + // Start background cleanup goroutine + e.wg.Add(1) + go e.cleanupLoop() + + return e +} + +// cleanupLoop periodically removes subscribers that have been offline for too long. +func (e *eventHub) cleanupLoop() { + defer e.wg.Done() + + ticker := time.NewTicker(cleanupPeriod) + defer ticker.Stop() + + for { + select { + case <-e.closeCh: + return + case <-ticker.C: + e.cleanupExpiredSubscribers() + } + } +} + +// cleanupExpiredSubscribers removes subscribers that have been offline for more than 14 days. +func (e *eventHub) cleanupExpiredSubscribers() { + e.mu.Lock() + defer e.mu.Unlock() + + if e.closed { + return + } + + for topic, subs := range e.topics { + for id, sub := range subs { + if sub.shouldExpire() { + sub.close() + delete(subs, id) + } + } + if len(subs) == 0 { + delete(e.topics, topic) + } + } +} + +func (e *eventHub) GetSubscribers(ctx context.Context, topic int) []Subscriber { + e.mu.RLock() + defer e.mu.RUnlock() + + subs := make([]Subscriber, 0, len(e.topics[topic])) + for _, v := range e.topics[topic] { + subs = append(subs, v) + } + return subs +} + +func (e *eventHub) Subscribe(ctx context.Context, topic int, id string) (chan *Event, bool, error) { + l := logging.FromContext(ctx) + l.Info("Subscribing to event hub for topic %d with id %s", topic, id) + + e.mu.Lock() + defer e.mu.Unlock() + + if e.closed { + return nil, false, ErrEventHubClosed + } + + subs, ok := e.topics[topic] + if !ok { + subs = make(map[string]*subscriber) + e.topics[topic] = subs + } + + // Check if subscriber already exists + if existingSub, ok := subs[id]; ok { + if existingSub.isClosed() { + // Subscriber was closed, create a new one + delete(subs, id) + } else { + // Reactivate the offline subscriber + l.Info("Reactivating offline subscriber %s for topic %d", id, topic) + existingSub.setOnline(ctx) + return existingSub.ch, true, nil + } + } + + sub, err := newSubscriber(ctx, id, e.userClient, e.fsEventClient) + if err != nil { + return nil, false, err + } + + e.topics[topic][id] = sub + return sub.ch, false, nil +} + +func (e *eventHub) Unsubscribe(ctx context.Context, topic int, id string) { + l := logging.FromContext(ctx) + l.Info("Marking subscriber offline for topic %d with id %s", topic, id) + + e.mu.Lock() + defer e.mu.Unlock() + + if e.closed { + return + } + + subs, ok := e.topics[topic] + if !ok { + return + } + + if sub, ok := subs[id]; ok { + // Stop debounce timer but keep events in buffer + sub.Stop() + // Mark as offline instead of deleting + sub.setOffline() + } +} + +// Close shuts down the event hub and disconnects all subscribers. +func (e *eventHub) Close() { + e.mu.Lock() + + if e.closed { + e.mu.Unlock() + return + } + + e.closed = true + close(e.closeCh) + + // Close all subscribers + for _, subs := range e.topics { + for _, sub := range subs { + sub.close() + } + } + e.topics = nil + + e.mu.Unlock() + + // Wait for cleanup goroutine to finish + e.wg.Wait() +} diff --git a/pkg/filemanager/eventhub/subscriber.go b/pkg/filemanager/eventhub/subscriber.go new file mode 100644 index 00000000..1ec35b54 --- /dev/null +++ b/pkg/filemanager/eventhub/subscriber.go @@ -0,0 +1,316 @@ +package eventhub + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "sync" + "time" + + "github.com/cloudreve/Cloudreve/v4/ent" + "github.com/cloudreve/Cloudreve/v4/inventory" + "github.com/cloudreve/Cloudreve/v4/pkg/logging" + "github.com/gofrs/uuid" + "github.com/samber/lo" +) + +type Subscriber interface { + ID() string + Ch() chan *Event + Publish(evt Event) + Stop() + Buffer() []*Event + // Owner returns the owner of the subscriber. + Owner() (*ent.User, error) + // Online returns whether the subscriber is online. + Online() bool + // OfflineSince returns when the subscriber went offline. + // Returns zero time if the subscriber is online. + OfflineSince() time.Time +} + +const ( + debounceDelay = 5 * time.Second + userCacheTTL = 1 * time.Hour + offlineMaxAge = 14 * 24 * time.Hour // 14 days +) + +type subscriber struct { + mu sync.Mutex + userClient inventory.UserClient + fsEventClient inventory.FsEventClient + + id string + uid int + ch chan *Event + + // Online status + online bool + offlineSince time.Time + + // Debounce buffer for pending events + buffer []*Event + timer *time.Timer + + // Owner info + ownerCached *ent.User + cachedAt time.Time + + // Close signal + closed bool + closedCh chan struct{} +} + +func newSubscriber(ctx context.Context, id string, userClient inventory.UserClient, fsEventClient inventory.FsEventClient) (*subscriber, error) { + user := inventory.UserFromContext(ctx) + if user == nil || inventory.IsAnonymousUser(user) { + return nil, errors.New("user not found") + } + + return &subscriber{ + id: id, + ch: make(chan *Event, bufSize), + userClient: userClient, + fsEventClient: fsEventClient, + ownerCached: user, + uid: user.ID, + cachedAt: time.Now(), + online: true, + closedCh: make(chan struct{}), + }, nil +} + +func (s *subscriber) ID() string { + return s.id +} + +func (s *subscriber) Ch() chan *Event { + return s.ch +} + +func (s *subscriber) Online() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.online +} + +func (s *subscriber) OfflineSince() time.Time { + s.mu.Lock() + defer s.mu.Unlock() + return s.offlineSince +} + +func (s *subscriber) Owner() (*ent.User, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if time.Since(s.cachedAt) > userCacheTTL || s.ownerCached == nil { + user, err := s.userClient.GetLoginUserByID(context.Background(), s.uid) + if err != nil { + return nil, fmt.Errorf("failed to get login user: %w", err) + } + + s.ownerCached = user + s.cachedAt = time.Now() + } + + return s.ownerCached, nil +} + +// Publish adds an event to the buffer and starts/resets the debounce timer. +// Events will be flushed to the channel after the debounce delay. +// If the subscriber is offline, events are kept in the buffer only. +func (s *subscriber) Publish(evt Event) { + s.mu.Lock() + defer s.mu.Unlock() + + if s.closed { + return + } + + s.publishLocked(evt) +} + +// publishLocked adds an event to the buffer and manages the debounce timer. +// Caller must hold s.mu. +func (s *subscriber) publishLocked(evt Event) { + // Add event to buffer + s.buffer = append(s.buffer, &evt) + + // Reset or start the debounce timer + if s.timer != nil { + s.timer.Stop() + } + s.timer = time.AfterFunc(debounceDelay, s.flush) +} + +// flush sends all buffered events to the channel. +// Called by the debounce timer. +func (s *subscriber) flush() { + s.mu.Lock() + defer s.mu.Unlock() + + s.flushLocked(context.Background()) +} + +// flushLocked sends all buffered events to the channel. +// Caller must hold s.mu. +func (s *subscriber) flushLocked(ctx context.Context) { + if len(s.buffer) == 0 || s.closed { + return + } + + if !s.online { + _ = s.fsEventClient.Create(ctx, s.ownerCached.ID, uuid.FromStringOrNil(s.id), lo.Map(s.buffer, func(item *Event, index int) string { + res, _ := json.Marshal(item) + return string(res) + })...) + } else { + // TODO: implement event merging logic here + // For now, send all buffered events individually + for _, evt := range s.buffer { + select { + case s.ch <- evt: + default: + // Non-blocking send; drop if subscriber is slow + } + } + } + + // Clear the buffer + s.buffer = nil + s.timer = nil +} + +// Stop cancels any pending debounce timer and flushes remaining events. +// Should be called before closing the subscriber. +func (s *subscriber) Stop() { + s.mu.Lock() + defer s.mu.Unlock() + + if s.timer != nil { + s.timer.Stop() + s.timer = nil + } + + // Flush any remaining events before stopping + s.flushLocked(context.Background()) +} + +// setOnline marks the subscriber as online and flushes any buffered events. +func (s *subscriber) setOnline(ctx context.Context) { + l := logging.FromContext(ctx) + s.mu.Lock() + defer s.mu.Unlock() + + if s.closed { + return + } + + s.online = true + s.ownerCached = nil + s.offlineSince = time.Time{} + + // Retrieve events from inventory + events, err := s.fsEventClient.TakeBySubscriber(ctx, uuid.FromStringOrNil(s.id), s.uid) + if err != nil { + l.Error("Failed to get events from inventory: %s", err) + return + } + + // Append events to buffer + for _, event := range events { + var eventParsed Event + err := json.Unmarshal([]byte(event.Event), &eventParsed) + if err != nil { + l.Error("Failed to unmarshal event: %s", err) + continue + } + s.buffer = append(s.buffer, &eventParsed) + } + + // Flush buffered events if any + if len(s.buffer) > 0 { + if s.timer != nil { + s.timer.Stop() + } + s.timer = time.AfterFunc(debounceDelay, s.flush) + } +} + +// setOffline marks the subscriber as offline. +func (s *subscriber) setOffline() { + s.mu.Lock() + defer s.mu.Unlock() + + if s.closed { + return + } + + s.online = false + s.offlineSince = time.Now() + + // Stop the timer, events will be kept in buffer + if s.timer != nil { + s.timer.Stop() + s.timer = nil + } + + // flush the buffer + s.flushLocked(context.Background()) +} + +// close permanently closes the subscriber. +func (s *subscriber) close() { + s.mu.Lock() + defer s.mu.Unlock() + + if s.closed { + return + } + + s.closed = true + if s.timer != nil { + s.timer.Stop() + s.timer = nil + } + + // Delete the FsEvent + s.fsEventClient.DeleteBySubscriber(context.Background(), uuid.FromStringOrNil(s.id)) + + // Signal close and close the channel + close(s.closedCh) + close(s.ch) + s.buffer = nil +} + +// isClosed returns whether the subscriber is closed. +func (s *subscriber) isClosed() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.closed +} + +// shouldExpire returns whether the subscriber should be expired (offline for too long). +func (s *subscriber) shouldExpire() bool { + s.mu.Lock() + defer s.mu.Unlock() + return !s.online && !s.offlineSince.IsZero() && time.Since(s.offlineSince) > offlineMaxAge +} + +// Buffer returns a copy of the current buffered events. +// Useful for debugging or implementing custom merging logic. +func (s *subscriber) Buffer() []*Event { + s.mu.Lock() + defer s.mu.Unlock() + + if len(s.buffer) == 0 { + return nil + } + + // Return a copy to avoid data races + buf := make([]*Event, len(s.buffer)) + copy(buf, s.buffer) + return buf +} diff --git a/pkg/filemanager/fs/dbfs/dbfs.go b/pkg/filemanager/fs/dbfs/dbfs.go index ce63ed4e..9037e281 100644 --- a/pkg/filemanager/fs/dbfs/dbfs.go +++ b/pkg/filemanager/fs/dbfs/dbfs.go @@ -15,6 +15,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/cache" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/encrypt" + "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/eventhub" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/lock" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" @@ -44,7 +45,7 @@ type ( func NewDatabaseFS(u *ent.User, fileClient inventory.FileClient, shareClient inventory.ShareClient, l logging.Logger, ls lock.LockSystem, settingClient setting.Provider, storagePolicyClient inventory.StoragePolicyClient, hasher hashid.Encoder, userClient inventory.UserClient, - cache, stateKv cache.Driver, directLinkClient inventory.DirectLinkClient, encryptorFactory encrypt.CryptorFactory) fs.FileSystem { + cache, stateKv cache.Driver, directLinkClient inventory.DirectLinkClient, encryptorFactory encrypt.CryptorFactory, eventHub eventhub.EventHub) fs.FileSystem { return &DBFS{ user: u, navigators: make(map[string]Navigator), @@ -60,6 +61,7 @@ func NewDatabaseFS(u *ent.User, fileClient inventory.FileClient, shareClient inv stateKv: stateKv, directLinkClient: directLinkClient, encryptorFactory: encryptorFactory, + eventHub: eventHub, } } @@ -79,6 +81,7 @@ type DBFS struct { stateKv cache.Driver mu sync.Mutex encryptorFactory encrypt.CryptorFactory + eventHub eventhub.EventHub } func (f *DBFS) Recycle() { @@ -643,7 +646,9 @@ func (f *DBFS) createFile(ctx context.Context, parent *File, name string, fileTy } file.SetEntities([]*ent.Entity{entity}) - return newFile(parent, file), nil + newFile := newFile(parent, file) + f.emitFileCreated(ctx, newFile) + return newFile, nil } func (f *DBFS) generateEncryptMetadata(ctx context.Context, uploadRequest *fs.UploadRequest, policy *ent.StoragePolicy) (*types.EncryptMetadata, error) { diff --git a/pkg/filemanager/fs/dbfs/events.go b/pkg/filemanager/fs/dbfs/events.go new file mode 100644 index 00000000..38f19ec8 --- /dev/null +++ b/pkg/filemanager/fs/dbfs/events.go @@ -0,0 +1,150 @@ +package dbfs + +import ( + "context" + "path" + "strings" + + "github.com/cloudreve/Cloudreve/v4/pkg/auth/requestinfo" + "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/eventhub" + "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" + "github.com/cloudreve/Cloudreve/v4/pkg/hashid" + "github.com/samber/lo" +) + +func (f *DBFS) emitFileCreated(ctx context.Context, file *File) { + subscribers := f.getEligibleSubscriber(ctx, file, true) + for _, subscriber := range subscribers { + subscriber.Publish(eventhub.Event{ + Type: eventhub.EventTypeCreate, + FileID: hashid.EncodeFileID(f.hasher, file.Model.ID), + From: subscriber.relativePath(file), + }) + } +} + +func (f *DBFS) emitFileModified(ctx context.Context, file *File) { + subscribers := f.getEligibleSubscriber(ctx, file, true) + for _, subscriber := range subscribers { + subscriber.Publish(eventhub.Event{ + Type: eventhub.EventTypeModify, + FileID: hashid.EncodeFileID(f.hasher, file.Model.ID), + From: subscriber.relativePath(file), + }) + } +} + +func (f *DBFS) emitFileRenamed(ctx context.Context, file *File, newName string) { + subscribers := f.getEligibleSubscriber(ctx, file, true) + for _, subscriber := range subscribers { + from := subscriber.relativePath(file) + to := strings.TrimSuffix(from, file.Name()) + newName + subscriber.Publish(eventhub.Event{ + Type: eventhub.EventTypeRename, + FileID: hashid.EncodeFileID(f.hasher, file.Model.ID), + From: subscriber.relativePath(file), + To: to, + }) + } +} + +func (f *DBFS) emitFileDeleted(ctx context.Context, files ...*File) { + for _, file := range files { + subscribers := f.getEligibleSubscriber(ctx, file, true) + for _, subscriber := range subscribers { + subscriber.Publish(eventhub.Event{ + Type: eventhub.EventTypeDelete, + FileID: hashid.EncodeFileID(f.hasher, file.Model.ID), + From: subscriber.relativePath(file), + }) + } + } +} + +func (f *DBFS) emitFileMoved(ctx context.Context, src, dst *File) { + srcSubMap := lo.SliceToMap(f.getEligibleSubscriber(ctx, src, true), func(subscriber foundSubscriber) (string, *foundSubscriber) { + return subscriber.ID(), &subscriber + }) + dstSubMap := lo.SliceToMap(f.getEligibleSubscriber(ctx, dst, false), func(subscriber foundSubscriber) (string, *foundSubscriber) { + return subscriber.ID(), &subscriber + }) + + for _, subscriber := range srcSubMap { + subId := subscriber.ID() + if dstSub, ok := dstSubMap[subId]; ok { + // Src and Dst subscribed by the same subscriber + subscriber.Publish(eventhub.Event{ + Type: eventhub.EventTypeRename, + FileID: hashid.EncodeFileID(f.hasher, src.Model.ID), + From: subscriber.relativePath(src), + To: path.Join(dstSub.relativePath(dst), src.Name()), + }) + delete(dstSubMap, subId) + } else { + // Only Src is subscribed by the subscriber + subscriber.Publish(eventhub.Event{ + Type: eventhub.EventTypeDelete, + FileID: hashid.EncodeFileID(f.hasher, src.Model.ID), + From: subscriber.relativePath(src), + }) + } + } + + for _, subscriber := range dstSubMap { + // Only Dst is subscribed by the subscriber + subscriber.Publish(eventhub.Event{ + Type: eventhub.EventTypeCreate, + FileID: hashid.EncodeFileID(f.hasher, src.Model.ID), + From: path.Join(subscriber.relativePath(dst), src.Name()), + }) + } + +} + +func (f *DBFS) getEligibleSubscriber(ctx context.Context, file *File, checkParentPerm bool) []foundSubscriber { + roots := file.Ancestors() + if !checkParentPerm { + // Include file itself + roots = file.AncestorsChain() + } + requestInfo := requestinfo.RequestInfoFromContext(ctx) + eligibleSubscribers := make([]foundSubscriber, 0) + + for _, root := range roots { + subscribers := f.eventHub.GetSubscribers(ctx, root.Model.ID) + subscribers = lo.Filter(subscribers, func(subscriber eventhub.Subscriber, index int) bool { + // Exlucde self from subscribers + if requestInfo != nil && subscriber.ID() == requestInfo.ClientID { + return false + } + return true + }) + eligibleSubscribers = append(eligibleSubscribers, lo.Map(subscribers, func(subscriber eventhub.Subscriber, index int) foundSubscriber { + return foundSubscriber{ + Subscriber: subscriber, + root: root, + } + })...) + } + + return eligibleSubscribers + +} + +type foundSubscriber struct { + eventhub.Subscriber + root *File +} + +func (s *foundSubscriber) relativePath(file *File) string { + res := strings.TrimPrefix(file.Uri(true).Path(), s.root.Uri(true).Path()) + if res == "" { + res = fs.Separator + } + + if res[0] != fs.Separator[0] { + res = fs.Separator + res + } + + return res +} diff --git a/pkg/filemanager/fs/dbfs/manage.go b/pkg/filemanager/fs/dbfs/manage.go index 2ffe171a..3ba84eb8 100644 --- a/pkg/filemanager/fs/dbfs/manage.go +++ b/pkg/filemanager/fs/dbfs/manage.go @@ -119,6 +119,7 @@ func (f *DBFS) Create(ctx context.Context, path *fs.URI, fileType types.FileType } ancestor = newFile(ancestor, newFolder) + f.emitFileCreated(ctx, ancestor) } else { // valide file name policy, err := f.getPreferredPolicy(ctx, ancestor) @@ -225,6 +226,8 @@ func (f *DBFS) Rename(ctx context.Context, path *fs.URI, newName string) (fs.Fil return nil, serializer.NewError(serializer.CodeDBError, "Failed to commit rename change", err) } + f.emitFileRenamed(ctx, target, newName) + return target.Replace(updated), nil } @@ -303,6 +306,8 @@ func (f *DBFS) SoftDelete(ctx context.Context, path ...*fs.URI) error { return serializer.NewError(serializer.CodeDBError, "Failed to commit soft-delete change", err) } + f.emitFileDeleted(ctx, targets...) + return ae.Aggregate() } @@ -385,7 +390,7 @@ func (f *DBFS) Delete(ctx context.Context, path []*fs.URI, opts ...fs.Option) ([ if err := inventory.CommitWithStorageDiff(ctx, tx, f.l, f.userClient); err != nil { return nil, serializer.NewError(serializer.CodeDBError, "Failed to commit delete change", err) } - + f.emitFileDeleted(ctx, targets...) return newStaleEntities, ae.Aggregate() } @@ -603,10 +608,11 @@ func (f *DBFS) MoveOrCopy(ctx context.Context, path []*fs.URI, dst *fs.URI, isCo } var ( - storageDiff inventory.StorageDiff + copiedNewTargetsMap map[int]*ent.File + storageDiff inventory.StorageDiff ) if isCopy { - _, storageDiff, err = f.copyFiles(ctx, fileNavGroup, destination, fc) + copiedNewTargetsMap, storageDiff, err = f.copyFiles(ctx, fileNavGroup, destination, fc) } else { storageDiff, err = f.moveFiles(ctx, targets, destination, fc, dstNavigator) } @@ -621,6 +627,14 @@ func (f *DBFS) MoveOrCopy(ctx context.Context, path []*fs.URI, dst *fs.URI, isCo return serializer.NewError(serializer.CodeDBError, "Failed to commit move change", err) } + for _, target := range targets { + if isCopy { + f.emitFileCreated(ctx, newFile(destination, copiedNewTargetsMap[target.ID()])) + } else { + f.emitFileMoved(ctx, target, destination) + } + } + // TODO: after move, dbfs cache should be cleared } @@ -716,6 +730,8 @@ func (f *DBFS) deleteEntity(ctx context.Context, target *File, entityId int) (in return nil, serializer.NewError(serializer.CodeDBError, "Failed to remove upload session metadata", err) } } + + f.emitFileModified(ctx, target) return diff, nil } @@ -753,6 +769,7 @@ func (f *DBFS) setCurrentVersion(ctx context.Context, target *File, versionId in return serializer.NewError(serializer.CodeDBError, "Failed to commit set current version", err) } + f.emitFileModified(ctx, target) return nil } diff --git a/pkg/filemanager/fs/dbfs/upload.go b/pkg/filemanager/fs/dbfs/upload.go index a50ae1d6..de796c31 100644 --- a/pkg/filemanager/fs/dbfs/upload.go +++ b/pkg/filemanager/fs/dbfs/upload.go @@ -355,6 +355,8 @@ func (f *DBFS) CompleteUpload(ctx context.Context, session *fs.UploadSession) (f } } + f.emitFileModified(ctx, filePrivate) + file, err = f.Get(ctx, session.Props.Uri, WithFileEntities(), WithNotRoot()) if err != nil { return nil, fmt.Errorf("failed to get updated file: %w", err) diff --git a/pkg/filemanager/manager/manager.go b/pkg/filemanager/manager/manager.go index 4ff5b8b2..4e314f7a 100644 --- a/pkg/filemanager/manager/manager.go +++ b/pkg/filemanager/manager/manager.go @@ -148,7 +148,7 @@ func NewFileManager(dep dependency.Dep, u *ent.User) FileManager { settings: dep.SettingProvider(), fs: dbfs.NewDatabaseFS(u, dep.FileClient(), dep.ShareClient(), dep.Logger(), dep.LockSystem(), dep.SettingProvider(), dep.StoragePolicyClient(), dep.HashIDEncoder(), dep.UserClient(), dep.KV(), dep.NavigatorStateKV(), - dep.DirectLinkClient(), dep.EncryptorFactory(context.TODO())), + dep.DirectLinkClient(), dep.EncryptorFactory(context.TODO()), dep.EventHub()), kv: dep.KV(), config: config, auth: dep.GeneralAuth(), diff --git a/pkg/queue/queue.go b/pkg/queue/queue.go index 2a28d838..b47a90c1 100644 --- a/pkg/queue/queue.go +++ b/pkg/queue/queue.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "runtime/debug" "sync" "sync/atomic" "time" @@ -284,6 +285,7 @@ func (q *queue) run(ctx context.Context, t Task) (task.Status, error) { // handle panic issue defer func() { if p := recover(); p != nil { + q.logger.Error("panic in queue %q: %s", q.name, debug.Stack()) panicChan <- p } }() diff --git a/pkg/request/request.go b/pkg/request/request.go index c5a8301b..afd8b063 100644 --- a/pkg/request/request.go +++ b/pkg/request/request.go @@ -30,6 +30,7 @@ const ( SiteVersionHeader = constants.CrHeaderPrefix + "Version" SiteIDHeader = constants.CrHeaderPrefix + "Site-Id" SlaveNodeIDHeader = constants.CrHeaderPrefix + "Node-Id" + ClientIDHeader = constants.CrHeaderPrefix + "Client-Id" LocalIP = "localhost" ) diff --git a/routers/controllers/file.go b/routers/controllers/file.go index 1875d178..317d23c9 100644 --- a/routers/controllers/file.go +++ b/routers/controllers/file.go @@ -426,3 +426,13 @@ func ListArchiveFiles(c *gin.Context) { Data: resp, }) } + +func HandleExplorerEventsPush(c *gin.Context) { + service := ParametersFromContext[*explorer.ExplorerEventService](c, explorer.ExplorerEventParamCtx{}) + err := service.HandleExplorerEventsPush(c) + if err != nil { + c.JSON(200, serializer.Err(c, err)) + c.Abort() + return + } +} diff --git a/routers/router.go b/routers/router.go index 18d9266c..e8256cf4 100644 --- a/routers/router.go +++ b/routers/router.go @@ -727,6 +727,13 @@ func initMasterRouter(dep dependency.Dep) *gin.Engine { controllers.FromJSON[explorer.PatchViewService](explorer.PatchViewParameterCtx{}), controllers.PatchView, ) + + // Server event push + file.GET("events", + middleware.LoginRequired(), + controllers.FromQuery[explorer.ExplorerEventService](explorer.ExplorerEventParamCtx{}), + controllers.HandleExplorerEventsPush, + ) } // 分享相关 diff --git a/service/explorer/events.go b/service/explorer/events.go new file mode 100644 index 00000000..f38e30ec --- /dev/null +++ b/service/explorer/events.go @@ -0,0 +1,99 @@ +package explorer + +import ( + "time" + + "github.com/cloudreve/Cloudreve/v4/application/dependency" + "github.com/cloudreve/Cloudreve/v4/inventory" + "github.com/cloudreve/Cloudreve/v4/pkg/auth/requestinfo" + "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/serializer" + "github.com/gin-gonic/gin" +) + +type ( + ExplorerEventService struct { + Uri string `form:"uri" binding:"required"` + } + ExplorerEventParamCtx struct{} +) + +func (s *ExplorerEventService) HandleExplorerEventsPush(c *gin.Context) error { + dep := dependency.FromContext(c) + user := inventory.UserFromContext(c) + m := manager.NewFileManager(dep, user) + l := logging.FromContext(c) + defer m.Recycle() + + uri, err := fs.NewUriFromString(s.Uri) + if err != nil { + return serializer.NewError(serializer.CodeParamErr, "Unknown uri", err) + } + + // Make sure target is a valid folder that the user can listen to + parent, _, err := m.List(c, uri, &manager.ListArgs{ + Page: 0, + PageSize: 1, + }) + if err != nil { + return serializer.NewError(serializer.CodeParamErr, "Requested uri not available", err) + } + + requestInfo := requestinfo.RequestInfoFromContext(c) + if requestInfo.ClientID == "" { + return serializer.NewError(serializer.CodeParamErr, "Client ID is required", nil) + } + + // Subscribe + eventHub := dep.EventHub() + rx, resumed, err := eventHub.Subscribe(c, parent.ID(), requestInfo.ClientID) + if err != nil { + return serializer.NewError(serializer.CodeInternalSetting, "Failed to subscribe to events", err) + } + + // SSE Headers + c.Writer.Header().Set("Content-Type", "text/event-stream") + c.Writer.Header().Set("Cache-Control", "no-cache") + c.Writer.Header().Set("Connection", "keep-alive") + c.Writer.Header().Set("X-Accel-Buffering", "no") + + keepAliveTicker := time.NewTicker(30 * time.Second) + defer keepAliveTicker.Stop() + + if resumed { + c.SSEvent("resumed", nil) + c.Writer.Flush() + } else { + c.SSEvent("subscribed", nil) + c.Writer.Flush() + } + + for { + select { + // TODO: close connection after access token expired + case <-c.Request.Context().Done(): + // Server shutdown or request cancelled + eventHub.Unsubscribe(c, parent.ID(), requestInfo.ClientID) + l.Debug("Request context done, unsubscribed from event hub") + return nil + case <-c.Writer.CloseNotify(): + eventHub.Unsubscribe(c, parent.ID(), requestInfo.ClientID) + l.Debug("Unsubscribed from event hub") + return nil + case evt, ok := <-rx: + if !ok { + // Channel closed, EventHub is shutting down + l.Debug("Event hub closed, disconnecting client") + return nil + } + c.SSEvent("event", evt) + l.Debug("Event sent: %+v", evt) + c.Writer.Flush() + case <-keepAliveTicker.C: + c.SSEvent("keep-alive", nil) + c.Writer.Flush() + } + } +}