From 366ba0a9d30702037c94253ec663e43e88cf26a9 Mon Sep 17 00:00:00 2001 From: Tomas Dvorak Date: Sun, 20 Sep 2026 01:25:27 +0200 Subject: [PATCH] feat: paid share links with credit purchase and resume tickets Share owners (with the share_sell group bit) can set a points price on share links. Visitors must purchase before downloading; listing and report-abuse surfaces stay visible. Purchases debit the buyer's credit balance and credit the owner at the configurable share_score_rate, recorded atomically in the credit ledger with a unique resume ticket. - ent: share.price_points + share_purchase entity (share_id, buyer_id, points, unique ticket; unique share+buyer) - vas: PurchaseShare idempotent atomic debit/credit/record; SharePurchase / SharePurchaseByTicket lookups scoped per share - navigator: sharePaid resolved at Root (owner / share_free group / buyer / ticket); DownloadFile+GenerateThumb capabilities stripped and BeforeDownload hook returns ErrNotPurchased while unpaid - api: POST /share/purchase/:id; share info returns price/paid/ticket; file url + thumb accept purchase_ticket into navigator ctx - frontend: PaidShareGate for single-file and folder shares, price accordion in share dialog, ticket persisted to localStorage and attached to entity/thumb requests, admin share_sell/share_free group toggles + shareScoreRate VAS setting - locales: en-US/zh-CN strings - tests: purchase/idempotency/insufficient/rates/ticket-scoping inventory tests + purchase route wiring assertion Authored By: TDvorak Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ROADMAP.md | 4 +- ent/client.go | 221 ++- ent/ent.go | 2 + ent/hook/hook.go | 12 + ent/intercept/intercept.go | 30 + ent/internal/schema.go | 2 +- ent/migrate/schema.go | 46 +- ent/mutation.go | 1573 ++++++++++++++--- ent/mutationhelper.go | 6 + ent/predicate/predicate.go | 3 + ent/runtime/runtime.go | 30 + ent/schema/share.go | 5 + ent/schema/sharepurchase.go | 56 + ent/schema/user.go | 1 + ent/share.go | 37 +- ent/share/share.go | 42 + ent/share/where.go | 68 + ent/share_create.go | 122 ++ ent/share_query.go | 105 +- ent/share_update.go | 243 +++ ent/sharepurchase.go | 239 +++ ent/sharepurchase/sharepurchase.go | 163 ++ ent/sharepurchase/where.go | 427 +++++ ent/sharepurchase_create.go | 890 ++++++++++ ent/sharepurchase_delete.go | 88 + ent/sharepurchase_query.go | 680 +++++++ ent/sharepurchase_update.go | 620 +++++++ ent/tx.go | 3 + ent/user.go | 24 +- ent/user/user.go | 30 + ent/user/where.go | 23 + ent/user_create.go | 32 + ent/user_query.go | 142 +- ent/user_update.go | 163 ++ .../public/locales/en-US/application.json | 10 +- frontend/public/locales/en-US/dashboard.json | 2 + .../public/locales/zh-CN/application.json | 10 +- frontend/public/locales/zh-CN/dashboard.json | 2 + frontend/src/api/api.ts | 84 +- frontend/src/api/explorer.ts | 5 + frontend/src/api/user.ts | 2 + .../Admin/Group/EditGroup/ShareSection.tsx | 37 +- .../src/component/Admin/Settings/VAS/VAS.tsx | 12 +- .../FileManager/Dialogs/Share/ShareDialog.tsx | 1 + .../Dialogs/Share/ShareSetting.tsx | 36 + .../FileManager/Explorer/Explorer.tsx | 28 +- .../FileManager/Explorer/SingleFileView.tsx | 6 +- .../component/FileManager/PaidShareGate.tsx | 89 + frontend/src/redux/thunks/share.ts | 1 + inventory/share.go | 5 + inventory/types/types.go | 7 +- inventory/vas.go | 82 + inventory/vas_test.go | 84 + pkg/filemanager/fs/dbfs/dbfs.go | 5 +- pkg/filemanager/fs/dbfs/share_navigator.go | 62 +- pkg/filemanager/manager/manager.go | 1 + pkg/filemanager/manager/operation.go | 1 + pkg/setting/provider.go | 7 + routers/controllers/share.go | 11 + routers/router.go | 6 + routers/router_test.go | 1 + service/explorer/file.go | 14 +- service/explorer/response.go | 11 + service/share/manage.go | 7 + service/share/purchase.go | 68 + service/share/visit.go | 32 +- 66 files changed, 6523 insertions(+), 338 deletions(-) create mode 100644 ent/schema/sharepurchase.go create mode 100644 ent/sharepurchase.go create mode 100644 ent/sharepurchase/sharepurchase.go create mode 100644 ent/sharepurchase/where.go create mode 100644 ent/sharepurchase_create.go create mode 100644 ent/sharepurchase_delete.go create mode 100644 ent/sharepurchase_query.go create mode 100644 ent/sharepurchase_update.go create mode 100644 frontend/src/component/FileManager/PaidShareGate.tsx create mode 100644 service/share/purchase.go diff --git a/ROADMAP.md b/ROADMAP.md index 765a5b16..e2685cbd 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -173,7 +173,7 @@ Order = user-visible value first; each ships with backend + UI + tests. - [x] PR #140 — `allow_upload`/`allow_edit`/`preview_only`/`upload_only` props, props-derived capability sets enforced server-side (`writePermitted`), same-share move/copy, anonymous upload, drop-box listing suppression, download denial via `IsDownloadCtxKey` hooks (fixes #3555 preview-only, #3340 drop-box) - [x] File/dir ACL entity — `(subject_type ∈ user|group|anonymous|everyone, subject_id)` → R/C/U/D bitmask; Permissions dialog under More actions; enforced in share-navigator capability checks; group bit 15 gate (#181, fixes #3517) - [x] Default shares — `setting.default_symbolics` + group `default_pinned` chip-input of share IDs; materialize as share-shortcut entries on fs init (#180) - - [ ] Paid shares — `share.price_points` + gate page + purchase/resume-ticket flow; needs B.4 credits first + - [x] Paid shares — `share.price_points` + `share_purchase` (buyer debit → owner income at `share_score_rate`) + resume ticket; download/thumb gated in share navigator, listing stays visible; `share_sell` (bit 29) gates price-setting, `share_free` (bit 8) bypasses paywall; `PaidShareGate` UI + restore via `purchase_ticket` 2. **Storage policy advanced** — multiple policies per group, per-directory binding, load-balancer policy, file migration (fixes #3518, #2961, #2262). See §1.3a. - [x] PR #175 — resumable admin relocation task (entities or whole-policy scope), encryption-aware re-wrap, admin UI + per-policy migrate action (#9, #125, #136) - [x] Group→policies M:N (`allowed_policies` edge, empty = legacy single) + group-editor multi-select; per-directory `sys:preferred_policy` metadata marker with nearest-ancestor precedence (invalid marker cuts inheritance); user `preferred_policy` setting applied in own tree only; `load_balance` policy type with weighted children resolved before drivers (#182, fixes #2961) @@ -186,7 +186,7 @@ Order = user-visible value first; each ships with backend + UI + tests. - [x] `sku` entity (storage-capacity + group-upgrade types, duration, cash+points price, label, bullets); points purchase → atomic debit+grant; admin SKU tables; `/shop` page (Memberships/Storage/Redeem tabs) + nav entry - [x] `user.credits` + `credit_txn` ledger (guarded atomic adjust); Finance settings tab (balance + grants + redeem + ledger); admin manual adjust (#183) - [x] `gift_code` entity (points/storage/group × amount × duration) + `user_grant` expiring grants + `grant_expire` cron; admin generate/list/revoke + user redeem (#183) - - [ ] Paid-share `price_points` wired to the ledger + purchase/resume-ticket flow + - [x] Paid-share `price_points` wired to the ledger + purchase/resume-ticket flow 5. **System extensions** — activity/audit log, site announcements, node selection, report-abuse queue (fixes #3480, #3479 IP whitelist). See §1.3a. - [x] PR #144 — task `creator_ip` capture with CIDR-capable admin filter (#115 OSS half), group remote-download quotas per count + per volume (#16), yt-dlp downloader provider (#88), progressive image preview (#113), v3 migrator `DatabaseURL` passthrough (#42) - [x] `activity_event` entity (immutable, tx-aware, actor+IP+CID) + per-file Activity dialog + admin `/admin/event` feed + per-type enablement + retention cron (#184) diff --git a/ent/client.go b/ent/client.go index bdc0a5d7..4c0a0940 100644 --- a/ent/client.go +++ b/ent/client.go @@ -34,6 +34,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/setting" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/sku" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" @@ -86,6 +87,8 @@ type Client struct { Setting *SettingClient // Share is the client for interacting with the Share builders. Share *ShareClient + // SharePurchase is the client for interacting with the SharePurchase builders. + SharePurchase *SharePurchaseClient // Sku is the client for interacting with the Sku builders. Sku *SkuClient // StoragePolicy is the client for interacting with the StoragePolicy builders. @@ -126,6 +129,7 @@ func (c *Client) init() { c.Passkey = NewPasskeyClient(c.config) c.Setting = NewSettingClient(c.config) c.Share = NewShareClient(c.config) + c.SharePurchase = NewSharePurchaseClient(c.config) c.Sku = NewSkuClient(c.config) c.StoragePolicy = NewStoragePolicyClient(c.config) c.Task = NewTaskClient(c.config) @@ -242,6 +246,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Passkey: NewPasskeyClient(cfg), Setting: NewSettingClient(cfg), Share: NewShareClient(cfg), + SharePurchase: NewSharePurchaseClient(cfg), Sku: NewSkuClient(cfg), StoragePolicy: NewStoragePolicyClient(cfg), Task: NewTaskClient(cfg), @@ -285,6 +290,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Passkey: NewPasskeyClient(cfg), Setting: NewSettingClient(cfg), Share: NewShareClient(cfg), + SharePurchase: NewSharePurchaseClient(cfg), Sku: NewSkuClient(cfg), StoragePolicy: NewStoragePolicyClient(cfg), Task: NewTaskClient(cfg), @@ -322,7 +328,8 @@ func (c *Client) Use(hooks ...Hook) { c.AbuseReport, c.AclEntry, c.ActivityEvent, c.CreditTxn, c.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.GiftCode, c.Group, c.InvitationCode, c.Metadata, c.Node, c.OAuthClient, c.OAuthGrant, c.Passkey, - c.Setting, c.Share, c.Sku, c.StoragePolicy, c.Task, c.User, c.UserGrant, + c.Setting, c.Share, c.SharePurchase, c.Sku, c.StoragePolicy, c.Task, c.User, + c.UserGrant, } { n.Use(hooks...) } @@ -335,7 +342,8 @@ func (c *Client) Intercept(interceptors ...Interceptor) { c.AbuseReport, c.AclEntry, c.ActivityEvent, c.CreditTxn, c.DavAccount, c.DirectLink, c.Entity, c.File, c.FsEvent, c.GiftCode, c.Group, c.InvitationCode, c.Metadata, c.Node, c.OAuthClient, c.OAuthGrant, c.Passkey, - c.Setting, c.Share, c.Sku, c.StoragePolicy, c.Task, c.User, c.UserGrant, + c.Setting, c.Share, c.SharePurchase, c.Sku, c.StoragePolicy, c.Task, c.User, + c.UserGrant, } { n.Intercept(interceptors...) } @@ -382,6 +390,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.Setting.mutate(ctx, m) case *ShareMutation: return c.Share.mutate(ctx, m) + case *SharePurchaseMutation: + return c.SharePurchase.mutate(ctx, m) case *SkuMutation: return c.Sku.mutate(ctx, m) case *StoragePolicyMutation: @@ -3398,6 +3408,22 @@ func (c *ShareClient) QueryFile(s *Share) *FileQuery { return query } +// QueryPurchases queries the purchases edge of a Share. +func (c *ShareClient) QueryPurchases(s *Share) *SharePurchaseQuery { + query := (&SharePurchaseClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := s.ID + step := sqlgraph.NewStep( + sqlgraph.From(share.Table, share.FieldID, id), + sqlgraph.To(sharepurchase.Table, sharepurchase.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, share.PurchasesTable, share.PurchasesColumn), + ) + fromV = sqlgraph.Neighbors(s.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *ShareClient) Hooks() []Hook { hooks := c.hooks.Share @@ -3425,6 +3451,173 @@ func (c *ShareClient) mutate(ctx context.Context, m *ShareMutation) (Value, erro } } +// SharePurchaseClient is a client for the SharePurchase schema. +type SharePurchaseClient struct { + config +} + +// NewSharePurchaseClient returns a client for the SharePurchase from the given config. +func NewSharePurchaseClient(c config) *SharePurchaseClient { + return &SharePurchaseClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `sharepurchase.Hooks(f(g(h())))`. +func (c *SharePurchaseClient) Use(hooks ...Hook) { + c.hooks.SharePurchase = append(c.hooks.SharePurchase, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `sharepurchase.Intercept(f(g(h())))`. +func (c *SharePurchaseClient) Intercept(interceptors ...Interceptor) { + c.inters.SharePurchase = append(c.inters.SharePurchase, interceptors...) +} + +// Create returns a builder for creating a SharePurchase entity. +func (c *SharePurchaseClient) Create() *SharePurchaseCreate { + mutation := newSharePurchaseMutation(c.config, OpCreate) + return &SharePurchaseCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of SharePurchase entities. +func (c *SharePurchaseClient) CreateBulk(builders ...*SharePurchaseCreate) *SharePurchaseCreateBulk { + return &SharePurchaseCreateBulk{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 *SharePurchaseClient) MapCreateBulk(slice any, setFunc func(*SharePurchaseCreate, int)) *SharePurchaseCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &SharePurchaseCreateBulk{err: fmt.Errorf("calling to SharePurchaseClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*SharePurchaseCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &SharePurchaseCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for SharePurchase. +func (c *SharePurchaseClient) Update() *SharePurchaseUpdate { + mutation := newSharePurchaseMutation(c.config, OpUpdate) + return &SharePurchaseUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *SharePurchaseClient) UpdateOne(sp *SharePurchase) *SharePurchaseUpdateOne { + mutation := newSharePurchaseMutation(c.config, OpUpdateOne, withSharePurchase(sp)) + return &SharePurchaseUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *SharePurchaseClient) UpdateOneID(id int) *SharePurchaseUpdateOne { + mutation := newSharePurchaseMutation(c.config, OpUpdateOne, withSharePurchaseID(id)) + return &SharePurchaseUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for SharePurchase. +func (c *SharePurchaseClient) Delete() *SharePurchaseDelete { + mutation := newSharePurchaseMutation(c.config, OpDelete) + return &SharePurchaseDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *SharePurchaseClient) DeleteOne(sp *SharePurchase) *SharePurchaseDeleteOne { + return c.DeleteOneID(sp.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *SharePurchaseClient) DeleteOneID(id int) *SharePurchaseDeleteOne { + builder := c.Delete().Where(sharepurchase.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &SharePurchaseDeleteOne{builder} +} + +// Query returns a query builder for SharePurchase. +func (c *SharePurchaseClient) Query() *SharePurchaseQuery { + return &SharePurchaseQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeSharePurchase}, + inters: c.Interceptors(), + } +} + +// Get returns a SharePurchase entity by its id. +func (c *SharePurchaseClient) Get(ctx context.Context, id int) (*SharePurchase, error) { + return c.Query().Where(sharepurchase.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *SharePurchaseClient) GetX(ctx context.Context, id int) *SharePurchase { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryShare queries the share edge of a SharePurchase. +func (c *SharePurchaseClient) QueryShare(sp *SharePurchase) *ShareQuery { + query := (&ShareClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := sp.ID + step := sqlgraph.NewStep( + sqlgraph.From(sharepurchase.Table, sharepurchase.FieldID, id), + sqlgraph.To(share.Table, share.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, sharepurchase.ShareTable, sharepurchase.ShareColumn), + ) + fromV = sqlgraph.Neighbors(sp.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryBuyer queries the buyer edge of a SharePurchase. +func (c *SharePurchaseClient) QueryBuyer(sp *SharePurchase) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := sp.ID + step := sqlgraph.NewStep( + sqlgraph.From(sharepurchase.Table, sharepurchase.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, sharepurchase.BuyerTable, sharepurchase.BuyerColumn), + ) + fromV = sqlgraph.Neighbors(sp.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *SharePurchaseClient) Hooks() []Hook { + hooks := c.hooks.SharePurchase + return append(hooks[:len(hooks):len(hooks)], sharepurchase.Hooks[:]...) +} + +// Interceptors returns the client interceptors. +func (c *SharePurchaseClient) Interceptors() []Interceptor { + inters := c.inters.SharePurchase + return append(inters[:len(inters):len(inters)], sharepurchase.Interceptors[:]...) +} + +func (c *SharePurchaseClient) mutate(ctx context.Context, m *SharePurchaseMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&SharePurchaseCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&SharePurchaseUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&SharePurchaseUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&SharePurchaseDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown SharePurchase mutation op: %q", m.Op()) + } +} + // SkuClient is a client for the Sku schema. type SkuClient struct { config @@ -4226,6 +4419,22 @@ func (c *UserClient) QueryGrants(u *User) *UserGrantQuery { return query } +// QuerySharePurchases queries the share_purchases edge of a User. +func (c *UserClient) QuerySharePurchases(u *User) *SharePurchaseQuery { + query := (&SharePurchaseClient{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(sharepurchase.Table, sharepurchase.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.SharePurchasesTable, user.SharePurchasesColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *UserClient) Hooks() []Hook { hooks := c.hooks.User @@ -4409,14 +4618,14 @@ type ( hooks struct { AbuseReport, AclEntry, ActivityEvent, CreditTxn, DavAccount, DirectLink, Entity, File, FsEvent, GiftCode, Group, InvitationCode, Metadata, Node, OAuthClient, - OAuthGrant, Passkey, Setting, Share, Sku, StoragePolicy, Task, User, - UserGrant []ent.Hook + OAuthGrant, Passkey, Setting, Share, SharePurchase, Sku, StoragePolicy, Task, + User, UserGrant []ent.Hook } inters struct { AbuseReport, AclEntry, ActivityEvent, CreditTxn, DavAccount, DirectLink, Entity, File, FsEvent, GiftCode, Group, InvitationCode, Metadata, Node, OAuthClient, - OAuthGrant, Passkey, Setting, Share, Sku, StoragePolicy, Task, User, - UserGrant []ent.Interceptor + OAuthGrant, Passkey, Setting, Share, SharePurchase, Sku, StoragePolicy, Task, + User, UserGrant []ent.Interceptor } ) diff --git a/ent/ent.go b/ent/ent.go index fe12586b..d508f642 100644 --- a/ent/ent.go +++ b/ent/ent.go @@ -31,6 +31,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/setting" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/sku" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" @@ -115,6 +116,7 @@ func checkColumn(table, column string) error { passkey.Table: passkey.ValidColumn, setting.Table: setting.ValidColumn, share.Table: share.ValidColumn, + sharepurchase.Table: sharepurchase.ValidColumn, sku.Table: sku.ValidColumn, storagepolicy.Table: storagepolicy.ValidColumn, task.Table: task.ValidColumn, diff --git a/ent/hook/hook.go b/ent/hook/hook.go index 6a4118d9..e596a4e4 100644 --- a/ent/hook/hook.go +++ b/ent/hook/hook.go @@ -237,6 +237,18 @@ func (f ShareFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ShareMutation", m) } +// The SharePurchaseFunc type is an adapter to allow the use of ordinary +// function as SharePurchase mutator. +type SharePurchaseFunc func(context.Context, *ent.SharePurchaseMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f SharePurchaseFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.SharePurchaseMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SharePurchaseMutation", m) +} + // The SkuFunc type is an adapter to allow the use of ordinary // function as Sku mutator. type SkuFunc func(context.Context, *ent.SkuMutation) (ent.Value, error) diff --git a/ent/intercept/intercept.go b/ent/intercept/intercept.go index 7237abb6..741de4a6 100644 --- a/ent/intercept/intercept.go +++ b/ent/intercept/intercept.go @@ -28,6 +28,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/setting" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/sku" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" @@ -604,6 +605,33 @@ func (f TraverseShare) Traverse(ctx context.Context, q ent.Query) error { return fmt.Errorf("unexpected query type %T. expect *ent.ShareQuery", q) } +// The SharePurchaseFunc type is an adapter to allow the use of ordinary function as a Querier. +type SharePurchaseFunc func(context.Context, *ent.SharePurchaseQuery) (ent.Value, error) + +// Query calls f(ctx, q). +func (f SharePurchaseFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) { + if q, ok := q.(*ent.SharePurchaseQuery); ok { + return f(ctx, q) + } + return nil, fmt.Errorf("unexpected query type %T. expect *ent.SharePurchaseQuery", q) +} + +// The TraverseSharePurchase type is an adapter to allow the use of ordinary function as Traverser. +type TraverseSharePurchase func(context.Context, *ent.SharePurchaseQuery) error + +// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline. +func (f TraverseSharePurchase) Intercept(next ent.Querier) ent.Querier { + return next +} + +// Traverse calls f(ctx, q). +func (f TraverseSharePurchase) Traverse(ctx context.Context, q ent.Query) error { + if q, ok := q.(*ent.SharePurchaseQuery); ok { + return f(ctx, q) + } + return fmt.Errorf("unexpected query type %T. expect *ent.SharePurchaseQuery", q) +} + // The SkuFunc type is an adapter to allow the use of ordinary function as a Querier. type SkuFunc func(context.Context, *ent.SkuQuery) (ent.Value, error) @@ -780,6 +808,8 @@ func NewQuery(q ent.Query) (Query, error) { return &query[*ent.SettingQuery, predicate.Setting, setting.OrderOption]{typ: ent.TypeSetting, tq: q}, nil case *ent.ShareQuery: return &query[*ent.ShareQuery, predicate.Share, share.OrderOption]{typ: ent.TypeShare, tq: q}, nil + case *ent.SharePurchaseQuery: + return &query[*ent.SharePurchaseQuery, predicate.SharePurchase, sharepurchase.OrderOption]{typ: ent.TypeSharePurchase, tq: q}, nil case *ent.SkuQuery: return &query[*ent.SkuQuery, predicate.Sku, sku.OrderOption]{typ: ent.TypeSku, tq: q}, nil case *ent.StoragePolicyQuery: diff --git a/ent/internal/schema.go b/ent/internal/schema.go index e9f067c2..11422325 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\":\"AbuseReport\",\"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\":\"reporter_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"reporter_email\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"target_type\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"target_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"reason\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"open\",\"default_kind\":24,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"admin_note\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"status\",\"created_at\"]},{\"fields\":[\"target_type\",\"target_id\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"AclEntry\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"field\":\"file_id\",\"ref_name\":\"acl_entries\",\"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\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"subject_type\",\"type\":{\"Type\":6,\"Ident\":\"aclentry.SubjectType\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"user\",\"V\":\"user\"},{\"N\":\"group\",\"V\":\"group\"},{\"N\":\"anonymous\",\"V\":\"anonymous\"},{\"N\":\"everyone\",\"V\":\"everyone\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"subject_id\",\"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}}],\"indexes\":[{\"unique\":true,\"fields\":[\"file_id\",\"subject_type\",\"subject_id\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"ActivityEvent\",\"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\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"actor_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ip\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"cid\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"share_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"extra\",\"type\":{\"Type\":3,\"Ident\":\"map[string]interface {}\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"map[string]interface {}\",\"Kind\":21,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"created_at\"]},{\"fields\":[\"type\",\"created_at\"]},{\"fields\":[\"file_id\",\"created_at\"]},{\"fields\":[\"actor_id\",\"created_at\"]},{\"fields\":[\"share_id\",\"created_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"CreditTxn\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"credit_txns\",\"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\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"credittxn.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"purchase\",\"V\":\"purchase\"},{\"N\":\"gift\",\"V\":\"gift\"},{\"N\":\"share_income\",\"V\":\"share_income\"},{\"N\":\"adjust\",\"V\":\"adjust\"}],\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ref\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"des\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"user_id\",\"created_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"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\":\"acl_entries\",\"type\":\"AclEntry\"},{\"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\",\"created_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\":\"GiftCode\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"redeemer\",\"type\":\"User\",\"field\":\"used_by_id\",\"ref_name\":\"redeemed_codes\",\"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\":\"code\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"giftcode.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"points\",\"V\":\"points\"},{\"N\":\"storage\",\"V\":\"storage\"},{\"N\":\"group\",\"V\":\"group\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"duration\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"used_by_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"used_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"des\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"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},{\"name\":\"allowed_policies\",\"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\":\"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\":\"InvitationCode\",\"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\":\"code\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":64,\"unique\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"group_id\",\"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\":\"max_uses\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"used_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"expires_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"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\":\"OAuthClient\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"grants\",\"type\":\"OAuthGrant\"}],\"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\":\"guid\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":255,\"unique\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"secret\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":255,\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":255,\"validators\":1,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"homepage_url\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2048,\"optional\":true,\"validators\":1,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"redirect_uris\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.OAuthClientProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"OAuthClientProps\",\"Ident\":\"types.OAuthClientProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_enabled\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":true,\"default_kind\":1,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"OAuthGrant\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"oauth_grants\",\"unique\":true,\"inverse\":true,\"required\":true},{\"name\":\"client\",\"type\":\"OAuthClient\",\"field\":\"client_id\",\"ref_name\":\"grants\",\"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\":\"client_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"last_used_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"client_id\"]}],\"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\":\"Sku\",\"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},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"sku.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"storage\",\"V\":\"storage\"},{\"N\":\"group\",\"V\":\"group\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"duration\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"price\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"points\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"label\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"des\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"enabled\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":true,\"default_kind\":1,\"position\":{\"Index\":8,\"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\":9,\"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\":\"allowed_groups\",\"type\":\"Group\",\"ref_name\":\"allowed_policies\",\"inverse\":true},{\"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\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"storagepolicy.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"suspended\",\"V\":\"suspended\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"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\":\"bucket_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_private\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"access_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"secret_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"max_size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"dir_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"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\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"node_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":12,\"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}},{\"name\":\"creator_ip\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":45,\"optional\":true,\"validators\":1,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"hidden\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":7,\"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\"},{\"name\":\"oauth_grants\",\"type\":\"OAuthGrant\"},{\"name\":\"credit_txns\",\"type\":\"CreditTxn\"},{\"name\":\"redeemed_codes\",\"type\":\"GiftCode\"},{\"name\":\"grants\",\"type\":\"UserGrant\"}],\"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\":\"ban_expires\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ban_reason\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"last_login\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Time of the last successful sign-in\"},{\"name\":\"storage\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"credits\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"two_factor_secret\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"avatar\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"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\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"group_users\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"UserGrant\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"grants\",\"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\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"usergrant.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"storage\",\"V\":\"storage\"},{\"N\":\"group\",\"V\":\"group\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"prev_group_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"expires_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"user_id\",\"type\"]},{\"fields\":[\"expires_at\"]}],\"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\":\"AbuseReport\",\"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\":\"reporter_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"reporter_email\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"target_type\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"target_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"reason\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"open\",\"default_kind\":24,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"admin_note\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"status\",\"created_at\"]},{\"fields\":[\"target_type\",\"target_id\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"AclEntry\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"file\",\"type\":\"File\",\"field\":\"file_id\",\"ref_name\":\"acl_entries\",\"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\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"subject_type\",\"type\":{\"Type\":6,\"Ident\":\"aclentry.SubjectType\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"user\",\"V\":\"user\"},{\"N\":\"group\",\"V\":\"group\"},{\"N\":\"anonymous\",\"V\":\"anonymous\"},{\"N\":\"everyone\",\"V\":\"everyone\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"subject_id\",\"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}}],\"indexes\":[{\"unique\":true,\"fields\":[\"file_id\",\"subject_type\",\"subject_id\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"ActivityEvent\",\"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\":\"type\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"actor_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ip\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"cid\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"share_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"extra\",\"type\":{\"Type\":3,\"Ident\":\"map[string]interface {}\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"map[string]interface {}\",\"Kind\":21,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"created_at\"]},{\"fields\":[\"type\",\"created_at\"]},{\"fields\":[\"file_id\",\"created_at\"]},{\"fields\":[\"actor_id\",\"created_at\"]},{\"fields\":[\"share_id\",\"created_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"CreditTxn\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"credit_txns\",\"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\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"credittxn.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"purchase\",\"V\":\"purchase\"},{\"N\":\"gift\",\"V\":\"gift\"},{\"N\":\"share_income\",\"V\":\"share_income\"},{\"N\":\"adjust\",\"V\":\"adjust\"}],\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ref\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"des\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"user_id\",\"created_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"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\":\"acl_entries\",\"type\":\"AclEntry\"},{\"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\",\"created_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\":\"GiftCode\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"redeemer\",\"type\":\"User\",\"field\":\"used_by_id\",\"ref_name\":\"redeemed_codes\",\"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\":\"code\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"giftcode.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"points\",\"V\":\"points\"},{\"N\":\"storage\",\"V\":\"storage\"},{\"N\":\"group\",\"V\":\"group\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"duration\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"used_by_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"used_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"des\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"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},{\"name\":\"allowed_policies\",\"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\":\"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\":\"InvitationCode\",\"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\":\"code\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":64,\"unique\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"group_id\",\"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\":\"max_uses\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"used_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"expires_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"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\":\"OAuthClient\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"grants\",\"type\":\"OAuthGrant\"}],\"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\":\"guid\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":255,\"unique\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"secret\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":255,\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":255,\"validators\":1,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"homepage_url\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2048,\"optional\":true,\"validators\":1,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"redirect_uris\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"props\",\"type\":{\"Type\":3,\"Ident\":\"*types.OAuthClientProps\",\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"PkgName\":\"types\",\"Nillable\":true,\"RType\":{\"Name\":\"OAuthClientProps\",\"Ident\":\"types.OAuthClientProps\",\"Kind\":22,\"PkgPath\":\"github.com/cloudreve/Cloudreve/v4/inventory/types\",\"Methods\":{}}},\"default\":true,\"default_value\":{},\"default_kind\":22,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_enabled\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":true,\"default_kind\":1,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"OAuthGrant\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"oauth_grants\",\"unique\":true,\"inverse\":true,\"required\":true},{\"name\":\"client\",\"type\":\"OAuthClient\",\"field\":\"client_id\",\"ref_name\":\"grants\",\"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\":\"client_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"last_used_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"datetime\"}}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"client_id\"]}],\"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},{\"name\":\"purchases\",\"type\":\"SharePurchase\"}],\"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\":\"price_points\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"validators\":1,\"position\":{\"Index\":5,\"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\":6,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"SharePurchase\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"share\",\"type\":\"Share\",\"field\":\"share_id\",\"ref_name\":\"purchases\",\"unique\":true,\"inverse\":true,\"required\":true},{\"name\":\"buyer\",\"type\":\"User\",\"field\":\"buyer_id\",\"ref_name\":\"share_purchases\",\"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\":\"share_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"buyer_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"points\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ticket\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"validators\":1,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"share_id\",\"buyer_id\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"Sku\",\"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},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"sku.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"storage\",\"V\":\"storage\"},{\"N\":\"group\",\"V\":\"group\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"duration\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"price\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"points\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"label\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"des\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"enabled\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":true,\"default_kind\":1,\"position\":{\"Index\":8,\"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\":9,\"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\":\"allowed_groups\",\"type\":\"Group\",\"ref_name\":\"allowed_policies\",\"inverse\":true},{\"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\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"storagepolicy.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"suspended\",\"V\":\"suspended\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"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\":\"bucket_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_private\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"access_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"secret_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"max_size\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"dir_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"file_name_rule\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"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\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"node_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":12,\"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}},{\"name\":\"creator_ip\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":45,\"optional\":true,\"validators\":1,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"hidden\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":7,\"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\"},{\"name\":\"oauth_grants\",\"type\":\"OAuthGrant\"},{\"name\":\"credit_txns\",\"type\":\"CreditTxn\"},{\"name\":\"redeemed_codes\",\"type\":\"GiftCode\"},{\"name\":\"grants\",\"type\":\"UserGrant\"},{\"name\":\"share_purchases\",\"type\":\"SharePurchase\"}],\"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\":\"ban_expires\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ban_reason\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":2147483647,\"optional\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"last_login\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Time of the last successful sign-in\"},{\"name\":\"storage\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"credits\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"two_factor_secret\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"avatar\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"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\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"group_users\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0}]},{\"name\":\"UserGrant\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"grants\",\"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\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"usergrant.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"storage\",\"V\":\"storage\"},{\"N\":\"group\",\"V\":\"group\"}],\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"amount\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"prev_group_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"expires_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"user_id\",\"type\"]},{\"fields\":[\"expires_at\"]}],\"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 43abc9b9..d365397e 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -578,6 +578,7 @@ var ( {Name: "downloads", Type: field.TypeInt, Default: 0}, {Name: "expires", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"mysql": "datetime"}}, {Name: "remain_downloads", Type: field.TypeInt, Nullable: true}, + {Name: "price_points", Type: field.TypeInt, Default: 0}, {Name: "props", Type: field.TypeJSON, Nullable: true}, {Name: "file_shares", Type: field.TypeInt, Nullable: true}, {Name: "user_shares", Type: field.TypeInt, Nullable: true}, @@ -590,18 +591,56 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "shares_files_shares", - Columns: []*schema.Column{SharesColumns[10]}, + Columns: []*schema.Column{SharesColumns[11]}, RefColumns: []*schema.Column{FilesColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "shares_users_shares", - Columns: []*schema.Column{SharesColumns[11]}, + Columns: []*schema.Column{SharesColumns[12]}, RefColumns: []*schema.Column{UsersColumns[0]}, OnDelete: schema.SetNull, }, }, } + // SharePurchasesColumns holds the columns for the "share_purchases" table. + SharePurchasesColumns = []*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: "points", Type: field.TypeInt}, + {Name: "ticket", Type: field.TypeString, Unique: true}, + {Name: "share_id", Type: field.TypeInt}, + {Name: "buyer_id", Type: field.TypeInt}, + } + // SharePurchasesTable holds the schema information for the "share_purchases" table. + SharePurchasesTable = &schema.Table{ + Name: "share_purchases", + Columns: SharePurchasesColumns, + PrimaryKey: []*schema.Column{SharePurchasesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "share_purchases_shares_purchases", + Columns: []*schema.Column{SharePurchasesColumns[6]}, + RefColumns: []*schema.Column{SharesColumns[0]}, + OnDelete: schema.NoAction, + }, + { + Symbol: "share_purchases_users_share_purchases", + Columns: []*schema.Column{SharePurchasesColumns[7]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + Indexes: []*schema.Index{ + { + Name: "sharepurchase_share_id_buyer_id", + Unique: true, + Columns: []*schema.Column{SharePurchasesColumns[6], SharePurchasesColumns[7]}, + }, + }, + } // SkusColumns holds the columns for the "skus" table. SkusColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, @@ -831,6 +870,7 @@ var ( PasskeysTable, SettingsTable, SharesTable, + SharePurchasesTable, SkusTable, StoragePoliciesTable, TasksTable, @@ -860,6 +900,8 @@ func init() { PasskeysTable.ForeignKeys[0].RefTable = UsersTable SharesTable.ForeignKeys[0].RefTable = FilesTable SharesTable.ForeignKeys[1].RefTable = UsersTable + SharePurchasesTable.ForeignKeys[0].RefTable = SharesTable + SharePurchasesTable.ForeignKeys[1].RefTable = UsersTable StoragePoliciesTable.ForeignKeys[0].RefTable = NodesTable TasksTable.ForeignKeys[0].RefTable = UsersTable UsersTable.ForeignKeys[0].RefTable = GroupsTable diff --git a/ent/mutation.go b/ent/mutation.go index e8944012..141943e7 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -31,6 +31,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/setting" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/sku" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" @@ -70,6 +71,7 @@ const ( TypePasskey = "Passkey" TypeSetting = "Setting" TypeShare = "Share" + TypeSharePurchase = "SharePurchase" TypeSku = "Sku" TypeStoragePolicy = "StoragePolicy" TypeTask = "Task" @@ -17305,12 +17307,17 @@ type ShareMutation struct { expires *time.Time remain_downloads *int addremain_downloads *int + price_points *int + addprice_points *int props **types.ShareProps clearedFields map[string]struct{} user *int cleareduser bool file *int clearedfile bool + purchases map[int]struct{} + removedpurchases map[int]struct{} + clearedpurchases bool done bool oldValue func(context.Context) (*Share, error) predicates []predicate.Share @@ -17815,6 +17822,62 @@ func (m *ShareMutation) ResetRemainDownloads() { delete(m.clearedFields, share.FieldRemainDownloads) } +// SetPricePoints sets the "price_points" field. +func (m *ShareMutation) SetPricePoints(i int) { + m.price_points = &i + m.addprice_points = nil +} + +// PricePoints returns the value of the "price_points" field in the mutation. +func (m *ShareMutation) PricePoints() (r int, exists bool) { + v := m.price_points + if v == nil { + return + } + return *v, true +} + +// OldPricePoints returns the old "price_points" field's value of the Share entity. +// If the Share 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 *ShareMutation) OldPricePoints(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPricePoints is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPricePoints requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPricePoints: %w", err) + } + return oldValue.PricePoints, nil +} + +// AddPricePoints adds i to the "price_points" field. +func (m *ShareMutation) AddPricePoints(i int) { + if m.addprice_points != nil { + *m.addprice_points += i + } else { + m.addprice_points = &i + } +} + +// AddedPricePoints returns the value that was added to the "price_points" field in this mutation. +func (m *ShareMutation) AddedPricePoints() (r int, exists bool) { + v := m.addprice_points + if v == nil { + return + } + return *v, true +} + +// ResetPricePoints resets all changes to the "price_points" field. +func (m *ShareMutation) ResetPricePoints() { + m.price_points = nil + m.addprice_points = nil +} + // SetProps sets the "props" field. func (m *ShareMutation) SetProps(tp *types.ShareProps) { m.props = &tp @@ -17942,6 +18005,60 @@ func (m *ShareMutation) ResetFile() { m.clearedfile = false } +// AddPurchaseIDs adds the "purchases" edge to the SharePurchase entity by ids. +func (m *ShareMutation) AddPurchaseIDs(ids ...int) { + if m.purchases == nil { + m.purchases = make(map[int]struct{}) + } + for i := range ids { + m.purchases[ids[i]] = struct{}{} + } +} + +// ClearPurchases clears the "purchases" edge to the SharePurchase entity. +func (m *ShareMutation) ClearPurchases() { + m.clearedpurchases = true +} + +// PurchasesCleared reports if the "purchases" edge to the SharePurchase entity was cleared. +func (m *ShareMutation) PurchasesCleared() bool { + return m.clearedpurchases +} + +// RemovePurchaseIDs removes the "purchases" edge to the SharePurchase entity by IDs. +func (m *ShareMutation) RemovePurchaseIDs(ids ...int) { + if m.removedpurchases == nil { + m.removedpurchases = make(map[int]struct{}) + } + for i := range ids { + delete(m.purchases, ids[i]) + m.removedpurchases[ids[i]] = struct{}{} + } +} + +// RemovedPurchases returns the removed IDs of the "purchases" edge to the SharePurchase entity. +func (m *ShareMutation) RemovedPurchasesIDs() (ids []int) { + for id := range m.removedpurchases { + ids = append(ids, id) + } + return +} + +// PurchasesIDs returns the "purchases" edge IDs in the mutation. +func (m *ShareMutation) PurchasesIDs() (ids []int) { + for id := range m.purchases { + ids = append(ids, id) + } + return +} + +// ResetPurchases resets all changes to the "purchases" edge. +func (m *ShareMutation) ResetPurchases() { + m.purchases = nil + m.clearedpurchases = false + m.removedpurchases = nil +} + // Where appends a list predicates to the ShareMutation builder. func (m *ShareMutation) Where(ps ...predicate.Share) { m.predicates = append(m.predicates, ps...) @@ -17976,7 +18093,7 @@ func (m *ShareMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ShareMutation) Fields() []string { - fields := make([]string, 0, 9) + fields := make([]string, 0, 10) if m.created_at != nil { fields = append(fields, share.FieldCreatedAt) } @@ -17986,23 +18103,964 @@ func (m *ShareMutation) Fields() []string { if m.deleted_at != nil { fields = append(fields, share.FieldDeletedAt) } - if m.password != nil { - fields = append(fields, share.FieldPassword) + if m.password != nil { + fields = append(fields, share.FieldPassword) + } + if m.views != nil { + fields = append(fields, share.FieldViews) + } + if m.downloads != nil { + fields = append(fields, share.FieldDownloads) + } + if m.expires != nil { + fields = append(fields, share.FieldExpires) + } + if m.remain_downloads != nil { + fields = append(fields, share.FieldRemainDownloads) + } + if m.price_points != nil { + fields = append(fields, share.FieldPricePoints) + } + if m.props != nil { + fields = append(fields, share.FieldProps) + } + 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 *ShareMutation) Field(name string) (ent.Value, bool) { + switch name { + case share.FieldCreatedAt: + return m.CreatedAt() + case share.FieldUpdatedAt: + return m.UpdatedAt() + case share.FieldDeletedAt: + return m.DeletedAt() + case share.FieldPassword: + return m.Password() + case share.FieldViews: + return m.Views() + case share.FieldDownloads: + return m.Downloads() + case share.FieldExpires: + return m.Expires() + case share.FieldRemainDownloads: + return m.RemainDownloads() + case share.FieldPricePoints: + return m.PricePoints() + case share.FieldProps: + return m.Props() + } + 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 *ShareMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case share.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case share.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case share.FieldDeletedAt: + return m.OldDeletedAt(ctx) + case share.FieldPassword: + return m.OldPassword(ctx) + case share.FieldViews: + return m.OldViews(ctx) + case share.FieldDownloads: + return m.OldDownloads(ctx) + case share.FieldExpires: + return m.OldExpires(ctx) + case share.FieldRemainDownloads: + return m.OldRemainDownloads(ctx) + case share.FieldPricePoints: + return m.OldPricePoints(ctx) + case share.FieldProps: + return m.OldProps(ctx) + } + return nil, fmt.Errorf("unknown Share 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 *ShareMutation) SetField(name string, value ent.Value) error { + switch name { + case share.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 share.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 share.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 share.FieldPassword: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPassword(v) + return nil + case share.FieldViews: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetViews(v) + return nil + case share.FieldDownloads: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDownloads(v) + return nil + case share.FieldExpires: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpires(v) + return nil + case share.FieldRemainDownloads: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRemainDownloads(v) + return nil + case share.FieldPricePoints: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPricePoints(v) + return nil + case share.FieldProps: + v, ok := value.(*types.ShareProps) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProps(v) + return nil + } + return fmt.Errorf("unknown Share field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ShareMutation) AddedFields() []string { + var fields []string + if m.addviews != nil { + fields = append(fields, share.FieldViews) + } + if m.adddownloads != nil { + fields = append(fields, share.FieldDownloads) + } + if m.addremain_downloads != nil { + fields = append(fields, share.FieldRemainDownloads) + } + if m.addprice_points != nil { + fields = append(fields, share.FieldPricePoints) + } + 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 *ShareMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case share.FieldViews: + return m.AddedViews() + case share.FieldDownloads: + return m.AddedDownloads() + case share.FieldRemainDownloads: + return m.AddedRemainDownloads() + case share.FieldPricePoints: + return m.AddedPricePoints() + } + 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 *ShareMutation) AddField(name string, value ent.Value) error { + switch name { + case share.FieldViews: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddViews(v) + return nil + case share.FieldDownloads: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddDownloads(v) + return nil + case share.FieldRemainDownloads: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddRemainDownloads(v) + return nil + case share.FieldPricePoints: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddPricePoints(v) + return nil + } + return fmt.Errorf("unknown Share numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ShareMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(share.FieldDeletedAt) { + fields = append(fields, share.FieldDeletedAt) + } + if m.FieldCleared(share.FieldPassword) { + fields = append(fields, share.FieldPassword) + } + if m.FieldCleared(share.FieldExpires) { + fields = append(fields, share.FieldExpires) + } + if m.FieldCleared(share.FieldRemainDownloads) { + fields = append(fields, share.FieldRemainDownloads) + } + if m.FieldCleared(share.FieldProps) { + fields = append(fields, share.FieldProps) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ShareMutation) 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 *ShareMutation) ClearField(name string) error { + switch name { + case share.FieldDeletedAt: + m.ClearDeletedAt() + return nil + case share.FieldPassword: + m.ClearPassword() + return nil + case share.FieldExpires: + m.ClearExpires() + return nil + case share.FieldRemainDownloads: + m.ClearRemainDownloads() + return nil + case share.FieldProps: + m.ClearProps() + return nil + } + return fmt.Errorf("unknown Share 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 *ShareMutation) ResetField(name string) error { + switch name { + case share.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case share.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case share.FieldDeletedAt: + m.ResetDeletedAt() + return nil + case share.FieldPassword: + m.ResetPassword() + return nil + case share.FieldViews: + m.ResetViews() + return nil + case share.FieldDownloads: + m.ResetDownloads() + return nil + case share.FieldExpires: + m.ResetExpires() + return nil + case share.FieldRemainDownloads: + m.ResetRemainDownloads() + return nil + case share.FieldPricePoints: + m.ResetPricePoints() + return nil + case share.FieldProps: + m.ResetProps() + return nil + } + return fmt.Errorf("unknown Share field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ShareMutation) AddedEdges() []string { + edges := make([]string, 0, 3) + if m.user != nil { + edges = append(edges, share.EdgeUser) + } + if m.file != nil { + edges = append(edges, share.EdgeFile) + } + if m.purchases != nil { + edges = append(edges, share.EdgePurchases) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ShareMutation) AddedIDs(name string) []ent.Value { + switch name { + case share.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + case share.EdgeFile: + if id := m.file; id != nil { + return []ent.Value{*id} + } + case share.EdgePurchases: + ids := make([]ent.Value, 0, len(m.purchases)) + for id := range m.purchases { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ShareMutation) RemovedEdges() []string { + edges := make([]string, 0, 3) + if m.removedpurchases != nil { + edges = append(edges, share.EdgePurchases) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ShareMutation) RemovedIDs(name string) []ent.Value { + switch name { + case share.EdgePurchases: + ids := make([]ent.Value, 0, len(m.removedpurchases)) + for id := range m.removedpurchases { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ShareMutation) ClearedEdges() []string { + edges := make([]string, 0, 3) + if m.cleareduser { + edges = append(edges, share.EdgeUser) + } + if m.clearedfile { + edges = append(edges, share.EdgeFile) + } + if m.clearedpurchases { + edges = append(edges, share.EdgePurchases) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ShareMutation) EdgeCleared(name string) bool { + switch name { + case share.EdgeUser: + return m.cleareduser + case share.EdgeFile: + return m.clearedfile + case share.EdgePurchases: + return m.clearedpurchases + } + 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 *ShareMutation) ClearEdge(name string) error { + switch name { + case share.EdgeUser: + m.ClearUser() + return nil + case share.EdgeFile: + m.ClearFile() + return nil + } + return fmt.Errorf("unknown Share 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 *ShareMutation) ResetEdge(name string) error { + switch name { + case share.EdgeUser: + m.ResetUser() + return nil + case share.EdgeFile: + m.ResetFile() + return nil + case share.EdgePurchases: + m.ResetPurchases() + return nil + } + return fmt.Errorf("unknown Share edge %s", name) +} + +// SharePurchaseMutation represents an operation that mutates the SharePurchase nodes in the graph. +type SharePurchaseMutation struct { + config + op Op + typ string + id *int + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + points *int + addpoints *int + ticket *string + clearedFields map[string]struct{} + share *int + clearedshare bool + buyer *int + clearedbuyer bool + done bool + oldValue func(context.Context) (*SharePurchase, error) + predicates []predicate.SharePurchase +} + +var _ ent.Mutation = (*SharePurchaseMutation)(nil) + +// sharepurchaseOption allows management of the mutation configuration using functional options. +type sharepurchaseOption func(*SharePurchaseMutation) + +// newSharePurchaseMutation creates new mutation for the SharePurchase entity. +func newSharePurchaseMutation(c config, op Op, opts ...sharepurchaseOption) *SharePurchaseMutation { + m := &SharePurchaseMutation{ + config: c, + op: op, + typ: TypeSharePurchase, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withSharePurchaseID sets the ID field of the mutation. +func withSharePurchaseID(id int) sharepurchaseOption { + return func(m *SharePurchaseMutation) { + var ( + err error + once sync.Once + value *SharePurchase + ) + m.oldValue = func(ctx context.Context) (*SharePurchase, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().SharePurchase.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withSharePurchase sets the old SharePurchase of the mutation. +func withSharePurchase(node *SharePurchase) sharepurchaseOption { + return func(m *SharePurchaseMutation) { + m.oldValue = func(context.Context) (*SharePurchase, 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 SharePurchaseMutation) 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 SharePurchaseMutation) 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 *SharePurchaseMutation) 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 *SharePurchaseMutation) 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().SharePurchase.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 *SharePurchaseMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *SharePurchaseMutation) 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 SharePurchase entity. +// If the SharePurchase 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 *SharePurchaseMutation) 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 *SharePurchaseMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *SharePurchaseMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *SharePurchaseMutation) 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 SharePurchase entity. +// If the SharePurchase 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 *SharePurchaseMutation) 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 *SharePurchaseMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetDeletedAt sets the "deleted_at" field. +func (m *SharePurchaseMutation) SetDeletedAt(t time.Time) { + m.deleted_at = &t +} + +// DeletedAt returns the value of the "deleted_at" field in the mutation. +func (m *SharePurchaseMutation) 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 SharePurchase entity. +// If the SharePurchase 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 *SharePurchaseMutation) 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 *SharePurchaseMutation) ClearDeletedAt() { + m.deleted_at = nil + m.clearedFields[sharepurchase.FieldDeletedAt] = struct{}{} +} + +// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation. +func (m *SharePurchaseMutation) DeletedAtCleared() bool { + _, ok := m.clearedFields[sharepurchase.FieldDeletedAt] + return ok +} + +// ResetDeletedAt resets all changes to the "deleted_at" field. +func (m *SharePurchaseMutation) ResetDeletedAt() { + m.deleted_at = nil + delete(m.clearedFields, sharepurchase.FieldDeletedAt) +} + +// SetShareID sets the "share_id" field. +func (m *SharePurchaseMutation) SetShareID(i int) { + m.share = &i +} + +// ShareID returns the value of the "share_id" field in the mutation. +func (m *SharePurchaseMutation) ShareID() (r int, exists bool) { + v := m.share + if v == nil { + return + } + return *v, true +} + +// OldShareID returns the old "share_id" field's value of the SharePurchase entity. +// If the SharePurchase 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 *SharePurchaseMutation) OldShareID(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldShareID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldShareID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldShareID: %w", err) + } + return oldValue.ShareID, nil +} + +// ResetShareID resets all changes to the "share_id" field. +func (m *SharePurchaseMutation) ResetShareID() { + m.share = nil +} + +// SetBuyerID sets the "buyer_id" field. +func (m *SharePurchaseMutation) SetBuyerID(i int) { + m.buyer = &i +} + +// BuyerID returns the value of the "buyer_id" field in the mutation. +func (m *SharePurchaseMutation) BuyerID() (r int, exists bool) { + v := m.buyer + if v == nil { + return + } + return *v, true +} + +// OldBuyerID returns the old "buyer_id" field's value of the SharePurchase entity. +// If the SharePurchase 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 *SharePurchaseMutation) OldBuyerID(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBuyerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBuyerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBuyerID: %w", err) + } + return oldValue.BuyerID, nil +} + +// ResetBuyerID resets all changes to the "buyer_id" field. +func (m *SharePurchaseMutation) ResetBuyerID() { + m.buyer = nil +} + +// SetPoints sets the "points" field. +func (m *SharePurchaseMutation) SetPoints(i int) { + m.points = &i + m.addpoints = nil +} + +// Points returns the value of the "points" field in the mutation. +func (m *SharePurchaseMutation) Points() (r int, exists bool) { + v := m.points + if v == nil { + return + } + return *v, true +} + +// OldPoints returns the old "points" field's value of the SharePurchase entity. +// If the SharePurchase 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 *SharePurchaseMutation) OldPoints(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPoints is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPoints requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPoints: %w", err) + } + return oldValue.Points, nil +} + +// AddPoints adds i to the "points" field. +func (m *SharePurchaseMutation) AddPoints(i int) { + if m.addpoints != nil { + *m.addpoints += i + } else { + m.addpoints = &i + } +} + +// AddedPoints returns the value that was added to the "points" field in this mutation. +func (m *SharePurchaseMutation) AddedPoints() (r int, exists bool) { + v := m.addpoints + if v == nil { + return + } + return *v, true +} + +// ResetPoints resets all changes to the "points" field. +func (m *SharePurchaseMutation) ResetPoints() { + m.points = nil + m.addpoints = nil +} + +// SetTicket sets the "ticket" field. +func (m *SharePurchaseMutation) SetTicket(s string) { + m.ticket = &s +} + +// Ticket returns the value of the "ticket" field in the mutation. +func (m *SharePurchaseMutation) Ticket() (r string, exists bool) { + v := m.ticket + if v == nil { + return + } + return *v, true +} + +// OldTicket returns the old "ticket" field's value of the SharePurchase entity. +// If the SharePurchase 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 *SharePurchaseMutation) OldTicket(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTicket is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTicket requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTicket: %w", err) + } + return oldValue.Ticket, nil +} + +// ResetTicket resets all changes to the "ticket" field. +func (m *SharePurchaseMutation) ResetTicket() { + m.ticket = nil +} + +// ClearShare clears the "share" edge to the Share entity. +func (m *SharePurchaseMutation) ClearShare() { + m.clearedshare = true + m.clearedFields[sharepurchase.FieldShareID] = struct{}{} +} + +// ShareCleared reports if the "share" edge to the Share entity was cleared. +func (m *SharePurchaseMutation) ShareCleared() bool { + return m.clearedshare +} + +// ShareIDs returns the "share" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ShareID instead. It exists only for internal usage by the builders. +func (m *SharePurchaseMutation) ShareIDs() (ids []int) { + if id := m.share; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetShare resets all changes to the "share" edge. +func (m *SharePurchaseMutation) ResetShare() { + m.share = nil + m.clearedshare = false +} + +// ClearBuyer clears the "buyer" edge to the User entity. +func (m *SharePurchaseMutation) ClearBuyer() { + m.clearedbuyer = true + m.clearedFields[sharepurchase.FieldBuyerID] = struct{}{} +} + +// BuyerCleared reports if the "buyer" edge to the User entity was cleared. +func (m *SharePurchaseMutation) BuyerCleared() bool { + return m.clearedbuyer +} + +// BuyerIDs returns the "buyer" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// BuyerID instead. It exists only for internal usage by the builders. +func (m *SharePurchaseMutation) BuyerIDs() (ids []int) { + if id := m.buyer; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetBuyer resets all changes to the "buyer" edge. +func (m *SharePurchaseMutation) ResetBuyer() { + m.buyer = nil + m.clearedbuyer = false +} + +// Where appends a list predicates to the SharePurchaseMutation builder. +func (m *SharePurchaseMutation) Where(ps ...predicate.SharePurchase) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the SharePurchaseMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *SharePurchaseMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.SharePurchase, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *SharePurchaseMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *SharePurchaseMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (SharePurchase). +func (m *SharePurchaseMutation) 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 *SharePurchaseMutation) Fields() []string { + fields := make([]string, 0, 7) + if m.created_at != nil { + fields = append(fields, sharepurchase.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, sharepurchase.FieldUpdatedAt) } - if m.views != nil { - fields = append(fields, share.FieldViews) + if m.deleted_at != nil { + fields = append(fields, sharepurchase.FieldDeletedAt) } - if m.downloads != nil { - fields = append(fields, share.FieldDownloads) + if m.share != nil { + fields = append(fields, sharepurchase.FieldShareID) } - if m.expires != nil { - fields = append(fields, share.FieldExpires) + if m.buyer != nil { + fields = append(fields, sharepurchase.FieldBuyerID) } - if m.remain_downloads != nil { - fields = append(fields, share.FieldRemainDownloads) + if m.points != nil { + fields = append(fields, sharepurchase.FieldPoints) } - if m.props != nil { - fields = append(fields, share.FieldProps) + if m.ticket != nil { + fields = append(fields, sharepurchase.FieldTicket) } return fields } @@ -18010,26 +19068,22 @@ func (m *ShareMutation) Fields() []string { // 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 *ShareMutation) Field(name string) (ent.Value, bool) { +func (m *SharePurchaseMutation) Field(name string) (ent.Value, bool) { switch name { - case share.FieldCreatedAt: + case sharepurchase.FieldCreatedAt: return m.CreatedAt() - case share.FieldUpdatedAt: + case sharepurchase.FieldUpdatedAt: return m.UpdatedAt() - case share.FieldDeletedAt: + case sharepurchase.FieldDeletedAt: return m.DeletedAt() - case share.FieldPassword: - return m.Password() - case share.FieldViews: - return m.Views() - case share.FieldDownloads: - return m.Downloads() - case share.FieldExpires: - return m.Expires() - case share.FieldRemainDownloads: - return m.RemainDownloads() - case share.FieldProps: - return m.Props() + case sharepurchase.FieldShareID: + return m.ShareID() + case sharepurchase.FieldBuyerID: + return m.BuyerID() + case sharepurchase.FieldPoints: + return m.Points() + case sharepurchase.FieldTicket: + return m.Ticket() } return nil, false } @@ -18037,114 +19091,90 @@ func (m *ShareMutation) Field(name string) (ent.Value, bool) { // 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 *ShareMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *SharePurchaseMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case share.FieldCreatedAt: + case sharepurchase.FieldCreatedAt: return m.OldCreatedAt(ctx) - case share.FieldUpdatedAt: + case sharepurchase.FieldUpdatedAt: return m.OldUpdatedAt(ctx) - case share.FieldDeletedAt: + case sharepurchase.FieldDeletedAt: return m.OldDeletedAt(ctx) - case share.FieldPassword: - return m.OldPassword(ctx) - case share.FieldViews: - return m.OldViews(ctx) - case share.FieldDownloads: - return m.OldDownloads(ctx) - case share.FieldExpires: - return m.OldExpires(ctx) - case share.FieldRemainDownloads: - return m.OldRemainDownloads(ctx) - case share.FieldProps: - return m.OldProps(ctx) + case sharepurchase.FieldShareID: + return m.OldShareID(ctx) + case sharepurchase.FieldBuyerID: + return m.OldBuyerID(ctx) + case sharepurchase.FieldPoints: + return m.OldPoints(ctx) + case sharepurchase.FieldTicket: + return m.OldTicket(ctx) } - return nil, fmt.Errorf("unknown Share field %s", name) + return nil, fmt.Errorf("unknown SharePurchase 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 *ShareMutation) SetField(name string, value ent.Value) error { +func (m *SharePurchaseMutation) SetField(name string, value ent.Value) error { switch name { - case share.FieldCreatedAt: + case sharepurchase.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 share.FieldUpdatedAt: + case sharepurchase.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 share.FieldDeletedAt: + case sharepurchase.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 share.FieldPassword: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetPassword(v) - return nil - case share.FieldViews: + case sharepurchase.FieldShareID: v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetViews(v) + m.SetShareID(v) return nil - case share.FieldDownloads: + case sharepurchase.FieldBuyerID: v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDownloads(v) - return nil - case share.FieldExpires: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetExpires(v) + m.SetBuyerID(v) return nil - case share.FieldRemainDownloads: + case sharepurchase.FieldPoints: v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetRemainDownloads(v) + m.SetPoints(v) return nil - case share.FieldProps: - v, ok := value.(*types.ShareProps) + case sharepurchase.FieldTicket: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetProps(v) + m.SetTicket(v) return nil } - return fmt.Errorf("unknown Share field %s", name) + return fmt.Errorf("unknown SharePurchase field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *ShareMutation) AddedFields() []string { +func (m *SharePurchaseMutation) AddedFields() []string { var fields []string - if m.addviews != nil { - fields = append(fields, share.FieldViews) - } - if m.adddownloads != nil { - fields = append(fields, share.FieldDownloads) - } - if m.addremain_downloads != nil { - fields = append(fields, share.FieldRemainDownloads) + if m.addpoints != nil { + fields = append(fields, sharepurchase.FieldPoints) } return fields } @@ -18152,14 +19182,10 @@ func (m *ShareMutation) AddedFields() []string { // 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 *ShareMutation) AddedField(name string) (ent.Value, bool) { +func (m *SharePurchaseMutation) AddedField(name string) (ent.Value, bool) { switch name { - case share.FieldViews: - return m.AddedViews() - case share.FieldDownloads: - return m.AddedDownloads() - case share.FieldRemainDownloads: - return m.AddedRemainDownloads() + case sharepurchase.FieldPoints: + return m.AddedPoints() } return nil, false } @@ -18167,142 +19193,98 @@ func (m *ShareMutation) AddedField(name string) (ent.Value, bool) { // 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 *ShareMutation) AddField(name string, value ent.Value) error { +func (m *SharePurchaseMutation) AddField(name string, value ent.Value) error { switch name { - case share.FieldViews: - v, ok := value.(int) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.AddViews(v) - return nil - case share.FieldDownloads: - v, ok := value.(int) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.AddDownloads(v) - return nil - case share.FieldRemainDownloads: + case sharepurchase.FieldPoints: v, ok := value.(int) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.AddRemainDownloads(v) + m.AddPoints(v) return nil } - return fmt.Errorf("unknown Share numeric field %s", name) + return fmt.Errorf("unknown SharePurchase numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *ShareMutation) ClearedFields() []string { +func (m *SharePurchaseMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(share.FieldDeletedAt) { - fields = append(fields, share.FieldDeletedAt) - } - if m.FieldCleared(share.FieldPassword) { - fields = append(fields, share.FieldPassword) - } - if m.FieldCleared(share.FieldExpires) { - fields = append(fields, share.FieldExpires) - } - if m.FieldCleared(share.FieldRemainDownloads) { - fields = append(fields, share.FieldRemainDownloads) - } - if m.FieldCleared(share.FieldProps) { - fields = append(fields, share.FieldProps) + if m.FieldCleared(sharepurchase.FieldDeletedAt) { + fields = append(fields, sharepurchase.FieldDeletedAt) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *ShareMutation) FieldCleared(name string) bool { +func (m *SharePurchaseMutation) 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 *ShareMutation) ClearField(name string) error { +func (m *SharePurchaseMutation) ClearField(name string) error { switch name { - case share.FieldDeletedAt: + case sharepurchase.FieldDeletedAt: m.ClearDeletedAt() return nil - case share.FieldPassword: - m.ClearPassword() - return nil - case share.FieldExpires: - m.ClearExpires() - return nil - case share.FieldRemainDownloads: - m.ClearRemainDownloads() - return nil - case share.FieldProps: - m.ClearProps() - return nil } - return fmt.Errorf("unknown Share nullable field %s", name) + return fmt.Errorf("unknown SharePurchase 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 *ShareMutation) ResetField(name string) error { +func (m *SharePurchaseMutation) ResetField(name string) error { switch name { - case share.FieldCreatedAt: + case sharepurchase.FieldCreatedAt: m.ResetCreatedAt() return nil - case share.FieldUpdatedAt: + case sharepurchase.FieldUpdatedAt: m.ResetUpdatedAt() return nil - case share.FieldDeletedAt: + case sharepurchase.FieldDeletedAt: m.ResetDeletedAt() return nil - case share.FieldPassword: - m.ResetPassword() - return nil - case share.FieldViews: - m.ResetViews() - return nil - case share.FieldDownloads: - m.ResetDownloads() + case sharepurchase.FieldShareID: + m.ResetShareID() return nil - case share.FieldExpires: - m.ResetExpires() + case sharepurchase.FieldBuyerID: + m.ResetBuyerID() return nil - case share.FieldRemainDownloads: - m.ResetRemainDownloads() + case sharepurchase.FieldPoints: + m.ResetPoints() return nil - case share.FieldProps: - m.ResetProps() + case sharepurchase.FieldTicket: + m.ResetTicket() return nil } - return fmt.Errorf("unknown Share field %s", name) + return fmt.Errorf("unknown SharePurchase field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *ShareMutation) AddedEdges() []string { +func (m *SharePurchaseMutation) AddedEdges() []string { edges := make([]string, 0, 2) - if m.user != nil { - edges = append(edges, share.EdgeUser) + if m.share != nil { + edges = append(edges, sharepurchase.EdgeShare) } - if m.file != nil { - edges = append(edges, share.EdgeFile) + if m.buyer != nil { + edges = append(edges, sharepurchase.EdgeBuyer) } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *ShareMutation) AddedIDs(name string) []ent.Value { +func (m *SharePurchaseMutation) AddedIDs(name string) []ent.Value { switch name { - case share.EdgeUser: - if id := m.user; id != nil { + case sharepurchase.EdgeShare: + if id := m.share; id != nil { return []ent.Value{*id} } - case share.EdgeFile: - if id := m.file; id != nil { + case sharepurchase.EdgeBuyer: + if id := m.buyer; id != nil { return []ent.Value{*id} } } @@ -18310,67 +19292,67 @@ func (m *ShareMutation) AddedIDs(name string) []ent.Value { } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *ShareMutation) RemovedEdges() []string { +func (m *SharePurchaseMutation) RemovedEdges() []string { edges := make([]string, 0, 2) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *ShareMutation) RemovedIDs(name string) []ent.Value { +func (m *SharePurchaseMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *ShareMutation) ClearedEdges() []string { +func (m *SharePurchaseMutation) ClearedEdges() []string { edges := make([]string, 0, 2) - if m.cleareduser { - edges = append(edges, share.EdgeUser) + if m.clearedshare { + edges = append(edges, sharepurchase.EdgeShare) } - if m.clearedfile { - edges = append(edges, share.EdgeFile) + if m.clearedbuyer { + edges = append(edges, sharepurchase.EdgeBuyer) } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *ShareMutation) EdgeCleared(name string) bool { +func (m *SharePurchaseMutation) EdgeCleared(name string) bool { switch name { - case share.EdgeUser: - return m.cleareduser - case share.EdgeFile: - return m.clearedfile + case sharepurchase.EdgeShare: + return m.clearedshare + case sharepurchase.EdgeBuyer: + return m.clearedbuyer } 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 *ShareMutation) ClearEdge(name string) error { +func (m *SharePurchaseMutation) ClearEdge(name string) error { switch name { - case share.EdgeUser: - m.ClearUser() + case sharepurchase.EdgeShare: + m.ClearShare() return nil - case share.EdgeFile: - m.ClearFile() + case sharepurchase.EdgeBuyer: + m.ClearBuyer() return nil } - return fmt.Errorf("unknown Share unique edge %s", name) + return fmt.Errorf("unknown SharePurchase 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 *ShareMutation) ResetEdge(name string) error { +func (m *SharePurchaseMutation) ResetEdge(name string) error { switch name { - case share.EdgeUser: - m.ResetUser() + case sharepurchase.EdgeShare: + m.ResetShare() return nil - case share.EdgeFile: - m.ResetFile() + case sharepurchase.EdgeBuyer: + m.ResetBuyer() return nil } - return fmt.Errorf("unknown Share edge %s", name) + return fmt.Errorf("unknown SharePurchase edge %s", name) } // SkuMutation represents an operation that mutates the Sku nodes in the graph. @@ -22445,65 +23427,68 @@ func (m *TaskMutation) ResetEdge(name string) error { // UserMutation represents an operation that mutates the User nodes in the graph. type UserMutation struct { config - op Op - typ string - id *int - created_at *time.Time - updated_at *time.Time - deleted_at *time.Time - email *string - nick *string - password *string - status *user.Status - ban_expires *time.Time - ban_reason *string - last_login *time.Time - storage *int64 - addstorage *int64 - credits *int64 - addcredits *int64 - two_factor_secret *string - avatar *string - settings **types.UserSetting - clearedFields map[string]struct{} - group *int - clearedgroup bool - files map[int]struct{} - removedfiles map[int]struct{} - clearedfiles bool - dav_accounts map[int]struct{} - removeddav_accounts map[int]struct{} - cleareddav_accounts bool - shares map[int]struct{} - removedshares map[int]struct{} - clearedshares bool - passkey map[int]struct{} - removedpasskey map[int]struct{} - clearedpasskey bool - 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 - oauth_grants map[int]struct{} - removedoauth_grants map[int]struct{} - clearedoauth_grants bool - credit_txns map[int]struct{} - removedcredit_txns map[int]struct{} - clearedcredit_txns bool - redeemed_codes map[int]struct{} - removedredeemed_codes map[int]struct{} - clearedredeemed_codes bool - grants map[int]struct{} - removedgrants map[int]struct{} - clearedgrants bool - done bool - oldValue func(context.Context) (*User, error) - predicates []predicate.User + op Op + typ string + id *int + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + email *string + nick *string + password *string + status *user.Status + ban_expires *time.Time + ban_reason *string + last_login *time.Time + storage *int64 + addstorage *int64 + credits *int64 + addcredits *int64 + two_factor_secret *string + avatar *string + settings **types.UserSetting + clearedFields map[string]struct{} + group *int + clearedgroup bool + files map[int]struct{} + removedfiles map[int]struct{} + clearedfiles bool + dav_accounts map[int]struct{} + removeddav_accounts map[int]struct{} + cleareddav_accounts bool + shares map[int]struct{} + removedshares map[int]struct{} + clearedshares bool + passkey map[int]struct{} + removedpasskey map[int]struct{} + clearedpasskey bool + 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 + oauth_grants map[int]struct{} + removedoauth_grants map[int]struct{} + clearedoauth_grants bool + credit_txns map[int]struct{} + removedcredit_txns map[int]struct{} + clearedcredit_txns bool + redeemed_codes map[int]struct{} + removedredeemed_codes map[int]struct{} + clearedredeemed_codes bool + grants map[int]struct{} + removedgrants map[int]struct{} + clearedgrants bool + share_purchases map[int]struct{} + removedshare_purchases map[int]struct{} + clearedshare_purchases bool + done bool + oldValue func(context.Context) (*User, error) + predicates []predicate.User } var _ ent.Mutation = (*UserMutation)(nil) @@ -23958,6 +24943,60 @@ func (m *UserMutation) ResetGrants() { m.removedgrants = nil } +// AddSharePurchaseIDs adds the "share_purchases" edge to the SharePurchase entity by ids. +func (m *UserMutation) AddSharePurchaseIDs(ids ...int) { + if m.share_purchases == nil { + m.share_purchases = make(map[int]struct{}) + } + for i := range ids { + m.share_purchases[ids[i]] = struct{}{} + } +} + +// ClearSharePurchases clears the "share_purchases" edge to the SharePurchase entity. +func (m *UserMutation) ClearSharePurchases() { + m.clearedshare_purchases = true +} + +// SharePurchasesCleared reports if the "share_purchases" edge to the SharePurchase entity was cleared. +func (m *UserMutation) SharePurchasesCleared() bool { + return m.clearedshare_purchases +} + +// RemoveSharePurchaseIDs removes the "share_purchases" edge to the SharePurchase entity by IDs. +func (m *UserMutation) RemoveSharePurchaseIDs(ids ...int) { + if m.removedshare_purchases == nil { + m.removedshare_purchases = make(map[int]struct{}) + } + for i := range ids { + delete(m.share_purchases, ids[i]) + m.removedshare_purchases[ids[i]] = struct{}{} + } +} + +// RemovedSharePurchases returns the removed IDs of the "share_purchases" edge to the SharePurchase entity. +func (m *UserMutation) RemovedSharePurchasesIDs() (ids []int) { + for id := range m.removedshare_purchases { + ids = append(ids, id) + } + return +} + +// SharePurchasesIDs returns the "share_purchases" edge IDs in the mutation. +func (m *UserMutation) SharePurchasesIDs() (ids []int) { + for id := range m.share_purchases { + ids = append(ids, id) + } + return +} + +// ResetSharePurchases resets all changes to the "share_purchases" edge. +func (m *UserMutation) ResetSharePurchases() { + m.share_purchases = nil + m.clearedshare_purchases = false + m.removedshare_purchases = nil +} + // Where appends a list predicates to the UserMutation builder. func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) @@ -24424,7 +25463,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, 12) + edges := make([]string, 0, 13) if m.group != nil { edges = append(edges, user.EdgeGroup) } @@ -24461,6 +25500,9 @@ func (m *UserMutation) AddedEdges() []string { if m.grants != nil { edges = append(edges, user.EdgeGrants) } + if m.share_purchases != nil { + edges = append(edges, user.EdgeSharePurchases) + } return edges } @@ -24538,13 +25580,19 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeSharePurchases: + ids := make([]ent.Value, 0, len(m.share_purchases)) + for id := range m.share_purchases { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 12) + edges := make([]string, 0, 13) if m.removedfiles != nil { edges = append(edges, user.EdgeFiles) } @@ -24578,6 +25626,9 @@ func (m *UserMutation) RemovedEdges() []string { if m.removedgrants != nil { edges = append(edges, user.EdgeGrants) } + if m.removedshare_purchases != nil { + edges = append(edges, user.EdgeSharePurchases) + } return edges } @@ -24651,13 +25702,19 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeSharePurchases: + ids := make([]ent.Value, 0, len(m.removedshare_purchases)) + for id := range m.removedshare_purchases { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 12) + edges := make([]string, 0, 13) if m.clearedgroup { edges = append(edges, user.EdgeGroup) } @@ -24694,6 +25751,9 @@ func (m *UserMutation) ClearedEdges() []string { if m.clearedgrants { edges = append(edges, user.EdgeGrants) } + if m.clearedshare_purchases { + edges = append(edges, user.EdgeSharePurchases) + } return edges } @@ -24725,6 +25785,8 @@ func (m *UserMutation) EdgeCleared(name string) bool { return m.clearedredeemed_codes case user.EdgeGrants: return m.clearedgrants + case user.EdgeSharePurchases: + return m.clearedshare_purchases } return false } @@ -24780,6 +25842,9 @@ func (m *UserMutation) ResetEdge(name string) error { case user.EdgeGrants: m.ResetGrants() return nil + case user.EdgeSharePurchases: + m.ResetSharePurchases() + return nil } return fmt.Errorf("unknown User edge %s", name) } diff --git a/ent/mutationhelper.go b/ent/mutationhelper.go index 5d8da380..867d4525 100644 --- a/ent/mutationhelper.go +++ b/ent/mutationhelper.go @@ -118,6 +118,12 @@ func (m *ShareMutation) SetRawID(t int) { // SetUpdatedAt sets the "updated_at" field. +func (m *SharePurchaseMutation) SetRawID(t int) { + m.id = &t +} + +// SetUpdatedAt sets the "updated_at" field. + func (m *SkuMutation) SetRawID(t int) { m.id = &t } diff --git a/ent/predicate/predicate.go b/ent/predicate/predicate.go index 97226947..c72b6ec4 100644 --- a/ent/predicate/predicate.go +++ b/ent/predicate/predicate.go @@ -63,6 +63,9 @@ type Setting func(*sql.Selector) // Share is the predicate function for share builders. type Share func(*sql.Selector) +// SharePurchase is the predicate function for sharepurchase builders. +type SharePurchase func(*sql.Selector) + // Sku is the predicate function for sku builders. type Sku func(*sql.Selector) diff --git a/ent/runtime/runtime.go b/ent/runtime/runtime.go index 09964c5b..3acb4555 100644 --- a/ent/runtime/runtime.go +++ b/ent/runtime/runtime.go @@ -25,6 +25,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/schema" "github.com/cloudreve/Cloudreve/v4/ent/setting" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/sku" "github.com/cloudreve/Cloudreve/v4/ent/storagepolicy" "github.com/cloudreve/Cloudreve/v4/ent/task" @@ -483,6 +484,35 @@ func init() { shareDescDownloads := shareFields[2].Descriptor() // share.DefaultDownloads holds the default value on creation for the downloads field. share.DefaultDownloads = shareDescDownloads.Default.(int) + // shareDescPricePoints is the schema descriptor for price_points field. + shareDescPricePoints := shareFields[5].Descriptor() + // share.DefaultPricePoints holds the default value on creation for the price_points field. + share.DefaultPricePoints = shareDescPricePoints.Default.(int) + // share.PricePointsValidator is a validator for the "price_points" field. It is called by the builders before save. + share.PricePointsValidator = shareDescPricePoints.Validators[0].(func(int) error) + sharepurchaseMixin := schema.SharePurchase{}.Mixin() + sharepurchaseMixinHooks0 := sharepurchaseMixin[0].Hooks() + sharepurchase.Hooks[0] = sharepurchaseMixinHooks0[0] + sharepurchaseMixinInters0 := sharepurchaseMixin[0].Interceptors() + sharepurchase.Interceptors[0] = sharepurchaseMixinInters0[0] + sharepurchaseMixinFields0 := sharepurchaseMixin[0].Fields() + _ = sharepurchaseMixinFields0 + sharepurchaseFields := schema.SharePurchase{}.Fields() + _ = sharepurchaseFields + // sharepurchaseDescCreatedAt is the schema descriptor for created_at field. + sharepurchaseDescCreatedAt := sharepurchaseMixinFields0[0].Descriptor() + // sharepurchase.DefaultCreatedAt holds the default value on creation for the created_at field. + sharepurchase.DefaultCreatedAt = sharepurchaseDescCreatedAt.Default.(func() time.Time) + // sharepurchaseDescUpdatedAt is the schema descriptor for updated_at field. + sharepurchaseDescUpdatedAt := sharepurchaseMixinFields0[1].Descriptor() + // sharepurchase.DefaultUpdatedAt holds the default value on creation for the updated_at field. + sharepurchase.DefaultUpdatedAt = sharepurchaseDescUpdatedAt.Default.(func() time.Time) + // sharepurchase.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + sharepurchase.UpdateDefaultUpdatedAt = sharepurchaseDescUpdatedAt.UpdateDefault.(func() time.Time) + // sharepurchaseDescTicket is the schema descriptor for ticket field. + sharepurchaseDescTicket := sharepurchaseFields[3].Descriptor() + // sharepurchase.TicketValidator is a validator for the "ticket" field. It is called by the builders before save. + sharepurchase.TicketValidator = sharepurchaseDescTicket.Validators[0].(func(string) error) skuMixin := schema.Sku{}.Mixin() skuMixinHooks0 := skuMixin[0].Hooks() sku.Hooks[0] = skuMixinHooks0[0] diff --git a/ent/schema/share.go b/ent/schema/share.go index d5fdf989..d503ff21 100644 --- a/ent/schema/share.go +++ b/ent/schema/share.go @@ -31,6 +31,10 @@ func (Share) Fields() []ent.Field { field.Int("remain_downloads"). Nillable(). Optional(), + // Points price visitors must pay before downloading. 0 = free share. + field.Int("price_points"). + Default(0). + NonNegative(), field.JSON("props", &types.ShareProps{}).Optional(), } } @@ -42,6 +46,7 @@ func (Share) Edges() []ent.Edge { Ref("shares").Unique(), edge.From("file", File.Type). Ref("shares").Unique(), + edge.To("purchases", SharePurchase.Type), } } diff --git a/ent/schema/sharepurchase.go b/ent/schema/sharepurchase.go new file mode 100644 index 00000000..52bf59ad --- /dev/null +++ b/ent/schema/sharepurchase.go @@ -0,0 +1,56 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" +) + +// SharePurchase records a points purchase of a paid share. One row per +// (share, buyer); the ticket acts as a bearer credential to restore access +// after session loss. +type SharePurchase struct { + ent.Schema +} + +// Fields of the SharePurchase. +func (SharePurchase) Fields() []ent.Field { + return []ent.Field{ + field.Int("share_id"), + field.Int("buyer_id"), + field.Int("points"), + field.String("ticket"). + Unique(). + NotEmpty(), + } +} + +// Edges of the SharePurchase. +func (SharePurchase) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("share", Share.Type). + Ref("purchases"). + Field("share_id"). + Unique(). + Required(), + edge.From("buyer", User.Type). + Ref("share_purchases"). + Field("buyer_id"). + Unique(). + Required(), + } +} + +func (SharePurchase) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("share_id", "buyer_id"). + Unique(), + } +} + +func (SharePurchase) Mixin() []ent.Mixin { + return []ent.Mixin{ + CommonMixin{}, + } +} diff --git a/ent/schema/user.go b/ent/schema/user.go index def70c4a..de6a487d 100644 --- a/ent/schema/user.go +++ b/ent/schema/user.go @@ -72,6 +72,7 @@ func (User) Edges() []ent.Edge { edge.To("credit_txns", CreditTxn.Type), edge.To("redeemed_codes", GiftCode.Type), edge.To("grants", UserGrant.Type), + edge.To("share_purchases", SharePurchase.Type), } } diff --git a/ent/share.go b/ent/share.go index ee650476..ea55a0b1 100644 --- a/ent/share.go +++ b/ent/share.go @@ -37,6 +37,8 @@ type Share struct { Expires *time.Time `json:"expires,omitempty"` // RemainDownloads holds the value of the "remain_downloads" field. RemainDownloads *int `json:"remain_downloads,omitempty"` + // PricePoints holds the value of the "price_points" field. + PricePoints int `json:"price_points,omitempty"` // Props holds the value of the "props" field. Props *types.ShareProps `json:"props,omitempty"` // Edges holds the relations/edges for other nodes in the graph. @@ -53,9 +55,11 @@ type ShareEdges struct { User *User `json:"user,omitempty"` // File holds the value of the file edge. File *File `json:"file,omitempty"` + // Purchases holds the value of the purchases edge. + Purchases []*SharePurchase `json:"purchases,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [2]bool + loadedTypes [3]bool } // UserOrErr returns the User value or an error if the edge @@ -84,6 +88,15 @@ func (e ShareEdges) FileOrErr() (*File, error) { return nil, &NotLoadedError{edge: "file"} } +// PurchasesOrErr returns the Purchases value or an error if the edge +// was not loaded in eager-loading. +func (e ShareEdges) PurchasesOrErr() ([]*SharePurchase, error) { + if e.loadedTypes[2] { + return e.Purchases, nil + } + return nil, &NotLoadedError{edge: "purchases"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*Share) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -91,7 +104,7 @@ func (*Share) scanValues(columns []string) ([]any, error) { switch columns[i] { case share.FieldProps: values[i] = new([]byte) - case share.FieldID, share.FieldViews, share.FieldDownloads, share.FieldRemainDownloads: + case share.FieldID, share.FieldViews, share.FieldDownloads, share.FieldRemainDownloads, share.FieldPricePoints: values[i] = new(sql.NullInt64) case share.FieldPassword: values[i] = new(sql.NullString) @@ -173,6 +186,12 @@ func (s *Share) assignValues(columns []string, values []any) error { s.RemainDownloads = new(int) *s.RemainDownloads = int(value.Int64) } + case share.FieldPricePoints: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field price_points", values[i]) + } else if value.Valid { + s.PricePoints = int(value.Int64) + } case share.FieldProps: if value, ok := values[i].(*[]byte); !ok { return fmt.Errorf("unexpected type %T for field props", values[i]) @@ -218,6 +237,11 @@ func (s *Share) QueryFile() *FileQuery { return NewShareClient(s.config).QueryFile(s) } +// QueryPurchases queries the "purchases" edge of the Share entity. +func (s *Share) QueryPurchases() *SharePurchaseQuery { + return NewShareClient(s.config).QueryPurchases(s) +} + // Update returns a builder for updating this Share. // Note that you need to call Share.Unwrap() before calling this method if this Share // was returned from a transaction, and the transaction was committed or rolled back. @@ -271,6 +295,9 @@ func (s *Share) String() string { builder.WriteString(fmt.Sprintf("%v", *v)) } builder.WriteString(", ") + builder.WriteString("price_points=") + builder.WriteString(fmt.Sprintf("%v", s.PricePoints)) + builder.WriteString(", ") builder.WriteString("props=") builder.WriteString(fmt.Sprintf("%v", s.Props)) builder.WriteByte(')') @@ -289,5 +316,11 @@ func (e *Share) SetFile(v *File) { e.Edges.loadedTypes[1] = true } +// SetPurchases manually set the edge as loaded state. +func (e *Share) SetPurchases(v []*SharePurchase) { + e.Edges.Purchases = v + e.Edges.loadedTypes[2] = true +} + // Shares is a parsable slice of Share. type Shares []*Share diff --git a/ent/share/share.go b/ent/share/share.go index 631f7c89..ce4db752 100644 --- a/ent/share/share.go +++ b/ent/share/share.go @@ -31,12 +31,16 @@ const ( FieldExpires = "expires" // FieldRemainDownloads holds the string denoting the remain_downloads field in the database. FieldRemainDownloads = "remain_downloads" + // FieldPricePoints holds the string denoting the price_points field in the database. + FieldPricePoints = "price_points" // FieldProps holds the string denoting the props field in the database. FieldProps = "props" // EdgeUser holds the string denoting the user edge name in mutations. EdgeUser = "user" // EdgeFile holds the string denoting the file edge name in mutations. EdgeFile = "file" + // EdgePurchases holds the string denoting the purchases edge name in mutations. + EdgePurchases = "purchases" // Table holds the table name of the share in the database. Table = "shares" // UserTable is the table that holds the user relation/edge. @@ -53,6 +57,13 @@ const ( FileInverseTable = "files" // FileColumn is the table column denoting the file relation/edge. FileColumn = "file_shares" + // PurchasesTable is the table that holds the purchases relation/edge. + PurchasesTable = "share_purchases" + // PurchasesInverseTable is the table name for the SharePurchase entity. + // It exists in this package in order to avoid circular dependency with the "sharepurchase" package. + PurchasesInverseTable = "share_purchases" + // PurchasesColumn is the table column denoting the purchases relation/edge. + PurchasesColumn = "share_id" ) // Columns holds all SQL columns for share fields. @@ -66,6 +77,7 @@ var Columns = []string{ FieldDownloads, FieldExpires, FieldRemainDownloads, + FieldPricePoints, FieldProps, } @@ -109,6 +121,10 @@ var ( DefaultViews int // DefaultDownloads holds the default value on creation for the "downloads" field. DefaultDownloads int + // DefaultPricePoints holds the default value on creation for the "price_points" field. + DefaultPricePoints int + // PricePointsValidator is a validator for the "price_points" field. It is called by the builders before save. + PricePointsValidator func(int) error ) // OrderOption defines the ordering options for the Share queries. @@ -159,6 +175,11 @@ func ByRemainDownloads(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldRemainDownloads, opts...).ToFunc() } +// ByPricePoints orders the results by the price_points field. +func ByPricePoints(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPricePoints, opts...).ToFunc() +} + // ByUserField orders the results by user field. func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { @@ -172,6 +193,20 @@ func ByFileField(field string, opts ...sql.OrderTermOption) OrderOption { sqlgraph.OrderByNeighborTerms(s, newFileStep(), sql.OrderByField(field, opts...)) } } + +// ByPurchasesCount orders the results by purchases count. +func ByPurchasesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newPurchasesStep(), opts...) + } +} + +// ByPurchases orders the results by purchases terms. +func ByPurchases(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newPurchasesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newUserStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -186,3 +221,10 @@ func newFileStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, FileTable, FileColumn), ) } +func newPurchasesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(PurchasesInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, PurchasesTable, PurchasesColumn), + ) +} diff --git a/ent/share/where.go b/ent/share/where.go index b2bbde9c..89e0a5a3 100644 --- a/ent/share/where.go +++ b/ent/share/where.go @@ -95,6 +95,11 @@ func RemainDownloads(v int) predicate.Share { return predicate.Share(sql.FieldEQ(FieldRemainDownloads, v)) } +// PricePoints applies equality check predicate on the "price_points" field. It's identical to PricePointsEQ. +func PricePoints(v int) predicate.Share { + return predicate.Share(sql.FieldEQ(FieldPricePoints, v)) +} + // CreatedAtEQ applies the EQ predicate on the "created_at" field. func CreatedAtEQ(v time.Time) predicate.Share { return predicate.Share(sql.FieldEQ(FieldCreatedAt, v)) @@ -480,6 +485,46 @@ func RemainDownloadsNotNil() predicate.Share { return predicate.Share(sql.FieldNotNull(FieldRemainDownloads)) } +// PricePointsEQ applies the EQ predicate on the "price_points" field. +func PricePointsEQ(v int) predicate.Share { + return predicate.Share(sql.FieldEQ(FieldPricePoints, v)) +} + +// PricePointsNEQ applies the NEQ predicate on the "price_points" field. +func PricePointsNEQ(v int) predicate.Share { + return predicate.Share(sql.FieldNEQ(FieldPricePoints, v)) +} + +// PricePointsIn applies the In predicate on the "price_points" field. +func PricePointsIn(vs ...int) predicate.Share { + return predicate.Share(sql.FieldIn(FieldPricePoints, vs...)) +} + +// PricePointsNotIn applies the NotIn predicate on the "price_points" field. +func PricePointsNotIn(vs ...int) predicate.Share { + return predicate.Share(sql.FieldNotIn(FieldPricePoints, vs...)) +} + +// PricePointsGT applies the GT predicate on the "price_points" field. +func PricePointsGT(v int) predicate.Share { + return predicate.Share(sql.FieldGT(FieldPricePoints, v)) +} + +// PricePointsGTE applies the GTE predicate on the "price_points" field. +func PricePointsGTE(v int) predicate.Share { + return predicate.Share(sql.FieldGTE(FieldPricePoints, v)) +} + +// PricePointsLT applies the LT predicate on the "price_points" field. +func PricePointsLT(v int) predicate.Share { + return predicate.Share(sql.FieldLT(FieldPricePoints, v)) +} + +// PricePointsLTE applies the LTE predicate on the "price_points" field. +func PricePointsLTE(v int) predicate.Share { + return predicate.Share(sql.FieldLTE(FieldPricePoints, v)) +} + // PropsIsNil applies the IsNil predicate on the "props" field. func PropsIsNil() predicate.Share { return predicate.Share(sql.FieldIsNull(FieldProps)) @@ -536,6 +581,29 @@ func HasFileWith(preds ...predicate.File) predicate.Share { }) } +// HasPurchases applies the HasEdge predicate on the "purchases" edge. +func HasPurchases() predicate.Share { + return predicate.Share(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, PurchasesTable, PurchasesColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasPurchasesWith applies the HasEdge predicate on the "purchases" edge with a given conditions (other predicates). +func HasPurchasesWith(preds ...predicate.SharePurchase) predicate.Share { + return predicate.Share(func(s *sql.Selector) { + step := newPurchasesStep() + 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.Share) predicate.Share { return predicate.Share(sql.AndPredicates(predicates...)) diff --git a/ent/share_create.go b/ent/share_create.go index 9380d2ba..c6f641b8 100644 --- a/ent/share_create.go +++ b/ent/share_create.go @@ -13,6 +13,7 @@ import ( "entgo.io/ent/schema/field" "github.com/cloudreve/Cloudreve/v4/ent/file" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/inventory/types" ) @@ -137,6 +138,20 @@ func (sc *ShareCreate) SetNillableRemainDownloads(i *int) *ShareCreate { return sc } +// SetPricePoints sets the "price_points" field. +func (sc *ShareCreate) SetPricePoints(i int) *ShareCreate { + sc.mutation.SetPricePoints(i) + return sc +} + +// SetNillablePricePoints sets the "price_points" field if the given value is not nil. +func (sc *ShareCreate) SetNillablePricePoints(i *int) *ShareCreate { + if i != nil { + sc.SetPricePoints(*i) + } + return sc +} + // SetProps sets the "props" field. func (sc *ShareCreate) SetProps(tp *types.ShareProps) *ShareCreate { sc.mutation.SetProps(tp) @@ -181,6 +196,21 @@ func (sc *ShareCreate) SetFile(f *File) *ShareCreate { return sc.SetFileID(f.ID) } +// AddPurchaseIDs adds the "purchases" edge to the SharePurchase entity by IDs. +func (sc *ShareCreate) AddPurchaseIDs(ids ...int) *ShareCreate { + sc.mutation.AddPurchaseIDs(ids...) + return sc +} + +// AddPurchases adds the "purchases" edges to the SharePurchase entity. +func (sc *ShareCreate) AddPurchases(s ...*SharePurchase) *ShareCreate { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return sc.AddPurchaseIDs(ids...) +} + // Mutation returns the ShareMutation object of the builder. func (sc *ShareCreate) Mutation() *ShareMutation { return sc.mutation @@ -240,6 +270,10 @@ func (sc *ShareCreate) defaults() error { v := share.DefaultDownloads sc.mutation.SetDownloads(v) } + if _, ok := sc.mutation.PricePoints(); !ok { + v := share.DefaultPricePoints + sc.mutation.SetPricePoints(v) + } return nil } @@ -257,6 +291,14 @@ func (sc *ShareCreate) check() error { if _, ok := sc.mutation.Downloads(); !ok { return &ValidationError{Name: "downloads", err: errors.New(`ent: missing required field "Share.downloads"`)} } + if _, ok := sc.mutation.PricePoints(); !ok { + return &ValidationError{Name: "price_points", err: errors.New(`ent: missing required field "Share.price_points"`)} + } + if v, ok := sc.mutation.PricePoints(); ok { + if err := share.PricePointsValidator(v); err != nil { + return &ValidationError{Name: "price_points", err: fmt.Errorf(`ent: validator failed for field "Share.price_points": %w`, err)} + } + } return nil } @@ -323,6 +365,10 @@ func (sc *ShareCreate) createSpec() (*Share, *sqlgraph.CreateSpec) { _spec.SetField(share.FieldRemainDownloads, field.TypeInt, value) _node.RemainDownloads = &value } + if value, ok := sc.mutation.PricePoints(); ok { + _spec.SetField(share.FieldPricePoints, field.TypeInt, value) + _node.PricePoints = value + } if value, ok := sc.mutation.Props(); ok { _spec.SetField(share.FieldProps, field.TypeJSON, value) _node.Props = value @@ -361,6 +407,22 @@ func (sc *ShareCreate) createSpec() (*Share, *sqlgraph.CreateSpec) { _node.file_shares = &nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := sc.mutation.PurchasesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: share.PurchasesTable, + Columns: []string{share.PurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } @@ -539,6 +601,24 @@ func (u *ShareUpsert) ClearRemainDownloads() *ShareUpsert { return u } +// SetPricePoints sets the "price_points" field. +func (u *ShareUpsert) SetPricePoints(v int) *ShareUpsert { + u.Set(share.FieldPricePoints, v) + return u +} + +// UpdatePricePoints sets the "price_points" field to the value that was provided on create. +func (u *ShareUpsert) UpdatePricePoints() *ShareUpsert { + u.SetExcluded(share.FieldPricePoints) + return u +} + +// AddPricePoints adds v to the "price_points" field. +func (u *ShareUpsert) AddPricePoints(v int) *ShareUpsert { + u.Add(share.FieldPricePoints, v) + return u +} + // SetProps sets the "props" field. func (u *ShareUpsert) SetProps(v *types.ShareProps) *ShareUpsert { u.Set(share.FieldProps, v) @@ -749,6 +829,27 @@ func (u *ShareUpsertOne) ClearRemainDownloads() *ShareUpsertOne { }) } +// SetPricePoints sets the "price_points" field. +func (u *ShareUpsertOne) SetPricePoints(v int) *ShareUpsertOne { + return u.Update(func(s *ShareUpsert) { + s.SetPricePoints(v) + }) +} + +// AddPricePoints adds v to the "price_points" field. +func (u *ShareUpsertOne) AddPricePoints(v int) *ShareUpsertOne { + return u.Update(func(s *ShareUpsert) { + s.AddPricePoints(v) + }) +} + +// UpdatePricePoints sets the "price_points" field to the value that was provided on create. +func (u *ShareUpsertOne) UpdatePricePoints() *ShareUpsertOne { + return u.Update(func(s *ShareUpsert) { + s.UpdatePricePoints() + }) +} + // SetProps sets the "props" field. func (u *ShareUpsertOne) SetProps(v *types.ShareProps) *ShareUpsertOne { return u.Update(func(s *ShareUpsert) { @@ -1133,6 +1234,27 @@ func (u *ShareUpsertBulk) ClearRemainDownloads() *ShareUpsertBulk { }) } +// SetPricePoints sets the "price_points" field. +func (u *ShareUpsertBulk) SetPricePoints(v int) *ShareUpsertBulk { + return u.Update(func(s *ShareUpsert) { + s.SetPricePoints(v) + }) +} + +// AddPricePoints adds v to the "price_points" field. +func (u *ShareUpsertBulk) AddPricePoints(v int) *ShareUpsertBulk { + return u.Update(func(s *ShareUpsert) { + s.AddPricePoints(v) + }) +} + +// UpdatePricePoints sets the "price_points" field to the value that was provided on create. +func (u *ShareUpsertBulk) UpdatePricePoints() *ShareUpsertBulk { + return u.Update(func(s *ShareUpsert) { + s.UpdatePricePoints() + }) +} + // SetProps sets the "props" field. func (u *ShareUpsertBulk) SetProps(v *types.ShareProps) *ShareUpsertBulk { return u.Update(func(s *ShareUpsert) { diff --git a/ent/share_query.go b/ent/share_query.go index b3bc6f53..531df0a7 100644 --- a/ent/share_query.go +++ b/ent/share_query.go @@ -4,6 +4,7 @@ package ent import ( "context" + "database/sql/driver" "fmt" "math" @@ -13,19 +14,21 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/file" "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/user" ) // ShareQuery is the builder for querying Share entities. type ShareQuery struct { config - ctx *QueryContext - order []share.OrderOption - inters []Interceptor - predicates []predicate.Share - withUser *UserQuery - withFile *FileQuery - withFKs bool + ctx *QueryContext + order []share.OrderOption + inters []Interceptor + predicates []predicate.Share + withUser *UserQuery + withFile *FileQuery + withPurchases *SharePurchaseQuery + withFKs bool // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -106,6 +109,28 @@ func (sq *ShareQuery) QueryFile() *FileQuery { return query } +// QueryPurchases chains the current query on the "purchases" edge. +func (sq *ShareQuery) QueryPurchases() *SharePurchaseQuery { + query := (&SharePurchaseClient{config: sq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := sq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := sq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(share.Table, share.FieldID, selector), + sqlgraph.To(sharepurchase.Table, sharepurchase.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, share.PurchasesTable, share.PurchasesColumn), + ) + fromU = sqlgraph.SetNeighbors(sq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first Share entity from the query. // Returns a *NotFoundError when no Share was found. func (sq *ShareQuery) First(ctx context.Context) (*Share, error) { @@ -293,13 +318,14 @@ func (sq *ShareQuery) Clone() *ShareQuery { return nil } return &ShareQuery{ - config: sq.config, - ctx: sq.ctx.Clone(), - order: append([]share.OrderOption{}, sq.order...), - inters: append([]Interceptor{}, sq.inters...), - predicates: append([]predicate.Share{}, sq.predicates...), - withUser: sq.withUser.Clone(), - withFile: sq.withFile.Clone(), + config: sq.config, + ctx: sq.ctx.Clone(), + order: append([]share.OrderOption{}, sq.order...), + inters: append([]Interceptor{}, sq.inters...), + predicates: append([]predicate.Share{}, sq.predicates...), + withUser: sq.withUser.Clone(), + withFile: sq.withFile.Clone(), + withPurchases: sq.withPurchases.Clone(), // clone intermediate query. sql: sq.sql.Clone(), path: sq.path, @@ -328,6 +354,17 @@ func (sq *ShareQuery) WithFile(opts ...func(*FileQuery)) *ShareQuery { return sq } +// WithPurchases tells the query-builder to eager-load the nodes that are connected to +// the "purchases" edge. The optional arguments are used to configure the query builder of the edge. +func (sq *ShareQuery) WithPurchases(opts ...func(*SharePurchaseQuery)) *ShareQuery { + query := (&SharePurchaseClient{config: sq.config}).Query() + for _, opt := range opts { + opt(query) + } + sq.withPurchases = query + return sq +} + // 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. // @@ -407,9 +444,10 @@ func (sq *ShareQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Share, nodes = []*Share{} withFKs = sq.withFKs _spec = sq.querySpec() - loadedTypes = [2]bool{ + loadedTypes = [3]bool{ sq.withUser != nil, sq.withFile != nil, + sq.withPurchases != nil, } ) if sq.withUser != nil || sq.withFile != nil { @@ -448,6 +486,13 @@ func (sq *ShareQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Share, return nil, err } } + if query := sq.withPurchases; query != nil { + if err := sq.loadPurchases(ctx, query, nodes, + func(n *Share) { n.Edges.Purchases = []*SharePurchase{} }, + func(n *Share, e *SharePurchase) { n.Edges.Purchases = append(n.Edges.Purchases, e) }); err != nil { + return nil, err + } + } return nodes, nil } @@ -515,6 +560,36 @@ func (sq *ShareQuery) loadFile(ctx context.Context, query *FileQuery, nodes []*S } return nil } +func (sq *ShareQuery) loadPurchases(ctx context.Context, query *SharePurchaseQuery, nodes []*Share, init func(*Share), assign func(*Share, *SharePurchase)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*Share) + 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(sharepurchase.FieldShareID) + } + query.Where(predicate.SharePurchase(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(share.PurchasesColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.ShareID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "share_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (sq *ShareQuery) sqlCount(ctx context.Context) (int, error) { _spec := sq.querySpec() diff --git a/ent/share_update.go b/ent/share_update.go index 7cdbd1b9..05d56d78 100644 --- a/ent/share_update.go +++ b/ent/share_update.go @@ -14,6 +14,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/file" "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/inventory/types" ) @@ -166,6 +167,27 @@ func (su *ShareUpdate) ClearRemainDownloads() *ShareUpdate { return su } +// SetPricePoints sets the "price_points" field. +func (su *ShareUpdate) SetPricePoints(i int) *ShareUpdate { + su.mutation.ResetPricePoints() + su.mutation.SetPricePoints(i) + return su +} + +// SetNillablePricePoints sets the "price_points" field if the given value is not nil. +func (su *ShareUpdate) SetNillablePricePoints(i *int) *ShareUpdate { + if i != nil { + su.SetPricePoints(*i) + } + return su +} + +// AddPricePoints adds i to the "price_points" field. +func (su *ShareUpdate) AddPricePoints(i int) *ShareUpdate { + su.mutation.AddPricePoints(i) + return su +} + // SetProps sets the "props" field. func (su *ShareUpdate) SetProps(tp *types.ShareProps) *ShareUpdate { su.mutation.SetProps(tp) @@ -216,6 +238,21 @@ func (su *ShareUpdate) SetFile(f *File) *ShareUpdate { return su.SetFileID(f.ID) } +// AddPurchaseIDs adds the "purchases" edge to the SharePurchase entity by IDs. +func (su *ShareUpdate) AddPurchaseIDs(ids ...int) *ShareUpdate { + su.mutation.AddPurchaseIDs(ids...) + return su +} + +// AddPurchases adds the "purchases" edges to the SharePurchase entity. +func (su *ShareUpdate) AddPurchases(s ...*SharePurchase) *ShareUpdate { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return su.AddPurchaseIDs(ids...) +} + // Mutation returns the ShareMutation object of the builder. func (su *ShareUpdate) Mutation() *ShareMutation { return su.mutation @@ -233,6 +270,27 @@ func (su *ShareUpdate) ClearFile() *ShareUpdate { return su } +// ClearPurchases clears all "purchases" edges to the SharePurchase entity. +func (su *ShareUpdate) ClearPurchases() *ShareUpdate { + su.mutation.ClearPurchases() + return su +} + +// RemovePurchaseIDs removes the "purchases" edge to SharePurchase entities by IDs. +func (su *ShareUpdate) RemovePurchaseIDs(ids ...int) *ShareUpdate { + su.mutation.RemovePurchaseIDs(ids...) + return su +} + +// RemovePurchases removes "purchases" edges to SharePurchase entities. +func (su *ShareUpdate) RemovePurchases(s ...*SharePurchase) *ShareUpdate { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return su.RemovePurchaseIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (su *ShareUpdate) Save(ctx context.Context) (int, error) { if err := su.defaults(); err != nil { @@ -275,7 +333,20 @@ func (su *ShareUpdate) defaults() error { return nil } +// check runs all checks and user-defined validators on the builder. +func (su *ShareUpdate) check() error { + if v, ok := su.mutation.PricePoints(); ok { + if err := share.PricePointsValidator(v); err != nil { + return &ValidationError{Name: "price_points", err: fmt.Errorf(`ent: validator failed for field "Share.price_points": %w`, err)} + } + } + return nil +} + func (su *ShareUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := su.check(); err != nil { + return n, err + } _spec := sqlgraph.NewUpdateSpec(share.Table, share.Columns, sqlgraph.NewFieldSpec(share.FieldID, field.TypeInt)) if ps := su.mutation.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { @@ -326,6 +397,12 @@ func (su *ShareUpdate) sqlSave(ctx context.Context) (n int, err error) { if su.mutation.RemainDownloadsCleared() { _spec.ClearField(share.FieldRemainDownloads, field.TypeInt) } + if value, ok := su.mutation.PricePoints(); ok { + _spec.SetField(share.FieldPricePoints, field.TypeInt, value) + } + if value, ok := su.mutation.AddedPricePoints(); ok { + _spec.AddField(share.FieldPricePoints, field.TypeInt, value) + } if value, ok := su.mutation.Props(); ok { _spec.SetField(share.FieldProps, field.TypeJSON, value) } @@ -390,6 +467,51 @@ func (su *ShareUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if su.mutation.PurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: share.PurchasesTable, + Columns: []string{share.PurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := su.mutation.RemovedPurchasesIDs(); len(nodes) > 0 && !su.mutation.PurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: share.PurchasesTable, + Columns: []string{share.PurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.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 := su.mutation.PurchasesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: share.PurchasesTable, + Columns: []string{share.PurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.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, su.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{share.Label} @@ -545,6 +667,27 @@ func (suo *ShareUpdateOne) ClearRemainDownloads() *ShareUpdateOne { return suo } +// SetPricePoints sets the "price_points" field. +func (suo *ShareUpdateOne) SetPricePoints(i int) *ShareUpdateOne { + suo.mutation.ResetPricePoints() + suo.mutation.SetPricePoints(i) + return suo +} + +// SetNillablePricePoints sets the "price_points" field if the given value is not nil. +func (suo *ShareUpdateOne) SetNillablePricePoints(i *int) *ShareUpdateOne { + if i != nil { + suo.SetPricePoints(*i) + } + return suo +} + +// AddPricePoints adds i to the "price_points" field. +func (suo *ShareUpdateOne) AddPricePoints(i int) *ShareUpdateOne { + suo.mutation.AddPricePoints(i) + return suo +} + // SetProps sets the "props" field. func (suo *ShareUpdateOne) SetProps(tp *types.ShareProps) *ShareUpdateOne { suo.mutation.SetProps(tp) @@ -595,6 +738,21 @@ func (suo *ShareUpdateOne) SetFile(f *File) *ShareUpdateOne { return suo.SetFileID(f.ID) } +// AddPurchaseIDs adds the "purchases" edge to the SharePurchase entity by IDs. +func (suo *ShareUpdateOne) AddPurchaseIDs(ids ...int) *ShareUpdateOne { + suo.mutation.AddPurchaseIDs(ids...) + return suo +} + +// AddPurchases adds the "purchases" edges to the SharePurchase entity. +func (suo *ShareUpdateOne) AddPurchases(s ...*SharePurchase) *ShareUpdateOne { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return suo.AddPurchaseIDs(ids...) +} + // Mutation returns the ShareMutation object of the builder. func (suo *ShareUpdateOne) Mutation() *ShareMutation { return suo.mutation @@ -612,6 +770,27 @@ func (suo *ShareUpdateOne) ClearFile() *ShareUpdateOne { return suo } +// ClearPurchases clears all "purchases" edges to the SharePurchase entity. +func (suo *ShareUpdateOne) ClearPurchases() *ShareUpdateOne { + suo.mutation.ClearPurchases() + return suo +} + +// RemovePurchaseIDs removes the "purchases" edge to SharePurchase entities by IDs. +func (suo *ShareUpdateOne) RemovePurchaseIDs(ids ...int) *ShareUpdateOne { + suo.mutation.RemovePurchaseIDs(ids...) + return suo +} + +// RemovePurchases removes "purchases" edges to SharePurchase entities. +func (suo *ShareUpdateOne) RemovePurchases(s ...*SharePurchase) *ShareUpdateOne { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return suo.RemovePurchaseIDs(ids...) +} + // Where appends a list predicates to the ShareUpdate builder. func (suo *ShareUpdateOne) Where(ps ...predicate.Share) *ShareUpdateOne { suo.mutation.Where(ps...) @@ -667,7 +846,20 @@ func (suo *ShareUpdateOne) defaults() error { return nil } +// check runs all checks and user-defined validators on the builder. +func (suo *ShareUpdateOne) check() error { + if v, ok := suo.mutation.PricePoints(); ok { + if err := share.PricePointsValidator(v); err != nil { + return &ValidationError{Name: "price_points", err: fmt.Errorf(`ent: validator failed for field "Share.price_points": %w`, err)} + } + } + return nil +} + func (suo *ShareUpdateOne) sqlSave(ctx context.Context) (_node *Share, err error) { + if err := suo.check(); err != nil { + return _node, err + } _spec := sqlgraph.NewUpdateSpec(share.Table, share.Columns, sqlgraph.NewFieldSpec(share.FieldID, field.TypeInt)) id, ok := suo.mutation.ID() if !ok { @@ -735,6 +927,12 @@ func (suo *ShareUpdateOne) sqlSave(ctx context.Context) (_node *Share, err error if suo.mutation.RemainDownloadsCleared() { _spec.ClearField(share.FieldRemainDownloads, field.TypeInt) } + if value, ok := suo.mutation.PricePoints(); ok { + _spec.SetField(share.FieldPricePoints, field.TypeInt, value) + } + if value, ok := suo.mutation.AddedPricePoints(); ok { + _spec.AddField(share.FieldPricePoints, field.TypeInt, value) + } if value, ok := suo.mutation.Props(); ok { _spec.SetField(share.FieldProps, field.TypeJSON, value) } @@ -799,6 +997,51 @@ func (suo *ShareUpdateOne) sqlSave(ctx context.Context) (_node *Share, err error } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if suo.mutation.PurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: share.PurchasesTable, + Columns: []string{share.PurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := suo.mutation.RemovedPurchasesIDs(); len(nodes) > 0 && !suo.mutation.PurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: share.PurchasesTable, + Columns: []string{share.PurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.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 := suo.mutation.PurchasesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: share.PurchasesTable, + Columns: []string{share.PurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &Share{config: suo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/ent/sharepurchase.go b/ent/sharepurchase.go new file mode 100644 index 00000000..b0ea885a --- /dev/null +++ b/ent/sharepurchase.go @@ -0,0 +1,239 @@ +// 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/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" + "github.com/cloudreve/Cloudreve/v4/ent/user" +) + +// SharePurchase is the model entity for the SharePurchase schema. +type SharePurchase 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"` + // ShareID holds the value of the "share_id" field. + ShareID int `json:"share_id,omitempty"` + // BuyerID holds the value of the "buyer_id" field. + BuyerID int `json:"buyer_id,omitempty"` + // Points holds the value of the "points" field. + Points int `json:"points,omitempty"` + // Ticket holds the value of the "ticket" field. + Ticket string `json:"ticket,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the SharePurchaseQuery when eager-loading is set. + Edges SharePurchaseEdges `json:"edges"` + selectValues sql.SelectValues +} + +// SharePurchaseEdges holds the relations/edges for other nodes in the graph. +type SharePurchaseEdges struct { + // Share holds the value of the share edge. + Share *Share `json:"share,omitempty"` + // Buyer holds the value of the buyer edge. + Buyer *User `json:"buyer,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// ShareOrErr returns the Share value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e SharePurchaseEdges) ShareOrErr() (*Share, error) { + if e.loadedTypes[0] { + if e.Share == nil { + // Edge was loaded but was not found. + return nil, &NotFoundError{label: share.Label} + } + return e.Share, nil + } + return nil, &NotLoadedError{edge: "share"} +} + +// BuyerOrErr returns the Buyer value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e SharePurchaseEdges) BuyerOrErr() (*User, error) { + if e.loadedTypes[1] { + if e.Buyer == nil { + // Edge was loaded but was not found. + return nil, &NotFoundError{label: user.Label} + } + return e.Buyer, nil + } + return nil, &NotLoadedError{edge: "buyer"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*SharePurchase) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case sharepurchase.FieldID, sharepurchase.FieldShareID, sharepurchase.FieldBuyerID, sharepurchase.FieldPoints: + values[i] = new(sql.NullInt64) + case sharepurchase.FieldTicket: + values[i] = new(sql.NullString) + case sharepurchase.FieldCreatedAt, sharepurchase.FieldUpdatedAt, sharepurchase.FieldDeletedAt: + values[i] = new(sql.NullTime) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the SharePurchase fields. +func (sp *SharePurchase) 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 sharepurchase.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + sp.ID = int(value.Int64) + case sharepurchase.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 { + sp.CreatedAt = value.Time + } + case sharepurchase.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 { + sp.UpdatedAt = value.Time + } + case sharepurchase.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 { + sp.DeletedAt = new(time.Time) + *sp.DeletedAt = value.Time + } + case sharepurchase.FieldShareID: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field share_id", values[i]) + } else if value.Valid { + sp.ShareID = int(value.Int64) + } + case sharepurchase.FieldBuyerID: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field buyer_id", values[i]) + } else if value.Valid { + sp.BuyerID = int(value.Int64) + } + case sharepurchase.FieldPoints: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field points", values[i]) + } else if value.Valid { + sp.Points = int(value.Int64) + } + case sharepurchase.FieldTicket: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field ticket", values[i]) + } else if value.Valid { + sp.Ticket = value.String + } + default: + sp.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the SharePurchase. +// This includes values selected through modifiers, order, etc. +func (sp *SharePurchase) Value(name string) (ent.Value, error) { + return sp.selectValues.Get(name) +} + +// QueryShare queries the "share" edge of the SharePurchase entity. +func (sp *SharePurchase) QueryShare() *ShareQuery { + return NewSharePurchaseClient(sp.config).QueryShare(sp) +} + +// QueryBuyer queries the "buyer" edge of the SharePurchase entity. +func (sp *SharePurchase) QueryBuyer() *UserQuery { + return NewSharePurchaseClient(sp.config).QueryBuyer(sp) +} + +// Update returns a builder for updating this SharePurchase. +// Note that you need to call SharePurchase.Unwrap() before calling this method if this SharePurchase +// was returned from a transaction, and the transaction was committed or rolled back. +func (sp *SharePurchase) Update() *SharePurchaseUpdateOne { + return NewSharePurchaseClient(sp.config).UpdateOne(sp) +} + +// Unwrap unwraps the SharePurchase 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 (sp *SharePurchase) Unwrap() *SharePurchase { + _tx, ok := sp.config.driver.(*txDriver) + if !ok { + panic("ent: SharePurchase is not a transactional entity") + } + sp.config.driver = _tx.drv + return sp +} + +// String implements the fmt.Stringer. +func (sp *SharePurchase) String() string { + var builder strings.Builder + builder.WriteString("SharePurchase(") + builder.WriteString(fmt.Sprintf("id=%v, ", sp.ID)) + builder.WriteString("created_at=") + builder.WriteString(sp.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(sp.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + if v := sp.DeletedAt; v != nil { + builder.WriteString("deleted_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("share_id=") + builder.WriteString(fmt.Sprintf("%v", sp.ShareID)) + builder.WriteString(", ") + builder.WriteString("buyer_id=") + builder.WriteString(fmt.Sprintf("%v", sp.BuyerID)) + builder.WriteString(", ") + builder.WriteString("points=") + builder.WriteString(fmt.Sprintf("%v", sp.Points)) + builder.WriteString(", ") + builder.WriteString("ticket=") + builder.WriteString(sp.Ticket) + builder.WriteByte(')') + return builder.String() +} + +// SetShare manually set the edge as loaded state. +func (e *SharePurchase) SetShare(v *Share) { + e.Edges.Share = v + e.Edges.loadedTypes[0] = true +} + +// SetBuyer manually set the edge as loaded state. +func (e *SharePurchase) SetBuyer(v *User) { + e.Edges.Buyer = v + e.Edges.loadedTypes[1] = true +} + +// SharePurchases is a parsable slice of SharePurchase. +type SharePurchases []*SharePurchase diff --git a/ent/sharepurchase/sharepurchase.go b/ent/sharepurchase/sharepurchase.go new file mode 100644 index 00000000..a13833ed --- /dev/null +++ b/ent/sharepurchase/sharepurchase.go @@ -0,0 +1,163 @@ +// Code generated by ent, DO NOT EDIT. + +package sharepurchase + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the sharepurchase type in the database. + Label = "share_purchase" + // 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" + // FieldShareID holds the string denoting the share_id field in the database. + FieldShareID = "share_id" + // FieldBuyerID holds the string denoting the buyer_id field in the database. + FieldBuyerID = "buyer_id" + // FieldPoints holds the string denoting the points field in the database. + FieldPoints = "points" + // FieldTicket holds the string denoting the ticket field in the database. + FieldTicket = "ticket" + // EdgeShare holds the string denoting the share edge name in mutations. + EdgeShare = "share" + // EdgeBuyer holds the string denoting the buyer edge name in mutations. + EdgeBuyer = "buyer" + // Table holds the table name of the sharepurchase in the database. + Table = "share_purchases" + // ShareTable is the table that holds the share relation/edge. + ShareTable = "share_purchases" + // ShareInverseTable is the table name for the Share entity. + // It exists in this package in order to avoid circular dependency with the "share" package. + ShareInverseTable = "shares" + // ShareColumn is the table column denoting the share relation/edge. + ShareColumn = "share_id" + // BuyerTable is the table that holds the buyer relation/edge. + BuyerTable = "share_purchases" + // BuyerInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + BuyerInverseTable = "users" + // BuyerColumn is the table column denoting the buyer relation/edge. + BuyerColumn = "buyer_id" +) + +// Columns holds all SQL columns for sharepurchase fields. +var Columns = []string{ + FieldID, + FieldCreatedAt, + FieldUpdatedAt, + FieldDeletedAt, + FieldShareID, + FieldBuyerID, + FieldPoints, + FieldTicket, +} + +// 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 + // TicketValidator is a validator for the "ticket" field. It is called by the builders before save. + TicketValidator func(string) error +) + +// OrderOption defines the ordering options for the SharePurchase 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() +} + +// ByShareID orders the results by the share_id field. +func ByShareID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldShareID, opts...).ToFunc() +} + +// ByBuyerID orders the results by the buyer_id field. +func ByBuyerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBuyerID, opts...).ToFunc() +} + +// ByPoints orders the results by the points field. +func ByPoints(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPoints, opts...).ToFunc() +} + +// ByTicket orders the results by the ticket field. +func ByTicket(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTicket, opts...).ToFunc() +} + +// ByShareField orders the results by share field. +func ByShareField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newShareStep(), sql.OrderByField(field, opts...)) + } +} + +// ByBuyerField orders the results by buyer field. +func ByBuyerField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newBuyerStep(), sql.OrderByField(field, opts...)) + } +} +func newShareStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ShareInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ShareTable, ShareColumn), + ) +} +func newBuyerStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(BuyerInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, BuyerTable, BuyerColumn), + ) +} diff --git a/ent/sharepurchase/where.go b/ent/sharepurchase/where.go new file mode 100644 index 00000000..391297e9 --- /dev/null +++ b/ent/sharepurchase/where.go @@ -0,0 +1,427 @@ +// Code generated by ent, DO NOT EDIT. + +package sharepurchase + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.SharePurchase { + return predicate.SharePurchase(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.SharePurchase { + return predicate.SharePurchase(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.SharePurchase { + return predicate.SharePurchase(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.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldDeletedAt, v)) +} + +// ShareID applies equality check predicate on the "share_id" field. It's identical to ShareIDEQ. +func ShareID(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldShareID, v)) +} + +// BuyerID applies equality check predicate on the "buyer_id" field. It's identical to BuyerIDEQ. +func BuyerID(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldBuyerID, v)) +} + +// Points applies equality check predicate on the "points" field. It's identical to PointsEQ. +func Points(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldPoints, v)) +} + +// Ticket applies equality check predicate on the "ticket" field. It's identical to TicketEQ. +func Ticket(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldTicket, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// DeletedAtEQ applies the EQ predicate on the "deleted_at" field. +func DeletedAtEQ(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldDeletedAt, v)) +} + +// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field. +func DeletedAtNEQ(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldDeletedAt, v)) +} + +// DeletedAtIn applies the In predicate on the "deleted_at" field. +func DeletedAtIn(vs ...time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldDeletedAt, vs...)) +} + +// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field. +func DeletedAtNotIn(vs ...time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldDeletedAt, vs...)) +} + +// DeletedAtGT applies the GT predicate on the "deleted_at" field. +func DeletedAtGT(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGT(FieldDeletedAt, v)) +} + +// DeletedAtGTE applies the GTE predicate on the "deleted_at" field. +func DeletedAtGTE(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGTE(FieldDeletedAt, v)) +} + +// DeletedAtLT applies the LT predicate on the "deleted_at" field. +func DeletedAtLT(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLT(FieldDeletedAt, v)) +} + +// DeletedAtLTE applies the LTE predicate on the "deleted_at" field. +func DeletedAtLTE(v time.Time) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLTE(FieldDeletedAt, v)) +} + +// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field. +func DeletedAtIsNil() predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIsNull(FieldDeletedAt)) +} + +// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field. +func DeletedAtNotNil() predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotNull(FieldDeletedAt)) +} + +// ShareIDEQ applies the EQ predicate on the "share_id" field. +func ShareIDEQ(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldShareID, v)) +} + +// ShareIDNEQ applies the NEQ predicate on the "share_id" field. +func ShareIDNEQ(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldShareID, v)) +} + +// ShareIDIn applies the In predicate on the "share_id" field. +func ShareIDIn(vs ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldShareID, vs...)) +} + +// ShareIDNotIn applies the NotIn predicate on the "share_id" field. +func ShareIDNotIn(vs ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldShareID, vs...)) +} + +// BuyerIDEQ applies the EQ predicate on the "buyer_id" field. +func BuyerIDEQ(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldBuyerID, v)) +} + +// BuyerIDNEQ applies the NEQ predicate on the "buyer_id" field. +func BuyerIDNEQ(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldBuyerID, v)) +} + +// BuyerIDIn applies the In predicate on the "buyer_id" field. +func BuyerIDIn(vs ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldBuyerID, vs...)) +} + +// BuyerIDNotIn applies the NotIn predicate on the "buyer_id" field. +func BuyerIDNotIn(vs ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldBuyerID, vs...)) +} + +// PointsEQ applies the EQ predicate on the "points" field. +func PointsEQ(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldPoints, v)) +} + +// PointsNEQ applies the NEQ predicate on the "points" field. +func PointsNEQ(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldPoints, v)) +} + +// PointsIn applies the In predicate on the "points" field. +func PointsIn(vs ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldPoints, vs...)) +} + +// PointsNotIn applies the NotIn predicate on the "points" field. +func PointsNotIn(vs ...int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldPoints, vs...)) +} + +// PointsGT applies the GT predicate on the "points" field. +func PointsGT(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGT(FieldPoints, v)) +} + +// PointsGTE applies the GTE predicate on the "points" field. +func PointsGTE(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGTE(FieldPoints, v)) +} + +// PointsLT applies the LT predicate on the "points" field. +func PointsLT(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLT(FieldPoints, v)) +} + +// PointsLTE applies the LTE predicate on the "points" field. +func PointsLTE(v int) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLTE(FieldPoints, v)) +} + +// TicketEQ applies the EQ predicate on the "ticket" field. +func TicketEQ(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEQ(FieldTicket, v)) +} + +// TicketNEQ applies the NEQ predicate on the "ticket" field. +func TicketNEQ(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNEQ(FieldTicket, v)) +} + +// TicketIn applies the In predicate on the "ticket" field. +func TicketIn(vs ...string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldIn(FieldTicket, vs...)) +} + +// TicketNotIn applies the NotIn predicate on the "ticket" field. +func TicketNotIn(vs ...string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldNotIn(FieldTicket, vs...)) +} + +// TicketGT applies the GT predicate on the "ticket" field. +func TicketGT(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGT(FieldTicket, v)) +} + +// TicketGTE applies the GTE predicate on the "ticket" field. +func TicketGTE(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldGTE(FieldTicket, v)) +} + +// TicketLT applies the LT predicate on the "ticket" field. +func TicketLT(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLT(FieldTicket, v)) +} + +// TicketLTE applies the LTE predicate on the "ticket" field. +func TicketLTE(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldLTE(FieldTicket, v)) +} + +// TicketContains applies the Contains predicate on the "ticket" field. +func TicketContains(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldContains(FieldTicket, v)) +} + +// TicketHasPrefix applies the HasPrefix predicate on the "ticket" field. +func TicketHasPrefix(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldHasPrefix(FieldTicket, v)) +} + +// TicketHasSuffix applies the HasSuffix predicate on the "ticket" field. +func TicketHasSuffix(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldHasSuffix(FieldTicket, v)) +} + +// TicketEqualFold applies the EqualFold predicate on the "ticket" field. +func TicketEqualFold(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldEqualFold(FieldTicket, v)) +} + +// TicketContainsFold applies the ContainsFold predicate on the "ticket" field. +func TicketContainsFold(v string) predicate.SharePurchase { + return predicate.SharePurchase(sql.FieldContainsFold(FieldTicket, v)) +} + +// HasShare applies the HasEdge predicate on the "share" edge. +func HasShare() predicate.SharePurchase { + return predicate.SharePurchase(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ShareTable, ShareColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasShareWith applies the HasEdge predicate on the "share" edge with a given conditions (other predicates). +func HasShareWith(preds ...predicate.Share) predicate.SharePurchase { + return predicate.SharePurchase(func(s *sql.Selector) { + step := newShareStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasBuyer applies the HasEdge predicate on the "buyer" edge. +func HasBuyer() predicate.SharePurchase { + return predicate.SharePurchase(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, BuyerTable, BuyerColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasBuyerWith applies the HasEdge predicate on the "buyer" edge with a given conditions (other predicates). +func HasBuyerWith(preds ...predicate.User) predicate.SharePurchase { + return predicate.SharePurchase(func(s *sql.Selector) { + step := newBuyerStep() + 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.SharePurchase) predicate.SharePurchase { + return predicate.SharePurchase(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.SharePurchase) predicate.SharePurchase { + return predicate.SharePurchase(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.SharePurchase) predicate.SharePurchase { + return predicate.SharePurchase(sql.NotPredicates(p)) +} diff --git a/ent/sharepurchase_create.go b/ent/sharepurchase_create.go new file mode 100644 index 00000000..d52501d1 --- /dev/null +++ b/ent/sharepurchase_create.go @@ -0,0 +1,890 @@ +// 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/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" + "github.com/cloudreve/Cloudreve/v4/ent/user" +) + +// SharePurchaseCreate is the builder for creating a SharePurchase entity. +type SharePurchaseCreate struct { + config + mutation *SharePurchaseMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetCreatedAt sets the "created_at" field. +func (spc *SharePurchaseCreate) SetCreatedAt(t time.Time) *SharePurchaseCreate { + spc.mutation.SetCreatedAt(t) + return spc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (spc *SharePurchaseCreate) SetNillableCreatedAt(t *time.Time) *SharePurchaseCreate { + if t != nil { + spc.SetCreatedAt(*t) + } + return spc +} + +// SetUpdatedAt sets the "updated_at" field. +func (spc *SharePurchaseCreate) SetUpdatedAt(t time.Time) *SharePurchaseCreate { + spc.mutation.SetUpdatedAt(t) + return spc +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (spc *SharePurchaseCreate) SetNillableUpdatedAt(t *time.Time) *SharePurchaseCreate { + if t != nil { + spc.SetUpdatedAt(*t) + } + return spc +} + +// SetDeletedAt sets the "deleted_at" field. +func (spc *SharePurchaseCreate) SetDeletedAt(t time.Time) *SharePurchaseCreate { + spc.mutation.SetDeletedAt(t) + return spc +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (spc *SharePurchaseCreate) SetNillableDeletedAt(t *time.Time) *SharePurchaseCreate { + if t != nil { + spc.SetDeletedAt(*t) + } + return spc +} + +// SetShareID sets the "share_id" field. +func (spc *SharePurchaseCreate) SetShareID(i int) *SharePurchaseCreate { + spc.mutation.SetShareID(i) + return spc +} + +// SetBuyerID sets the "buyer_id" field. +func (spc *SharePurchaseCreate) SetBuyerID(i int) *SharePurchaseCreate { + spc.mutation.SetBuyerID(i) + return spc +} + +// SetPoints sets the "points" field. +func (spc *SharePurchaseCreate) SetPoints(i int) *SharePurchaseCreate { + spc.mutation.SetPoints(i) + return spc +} + +// SetTicket sets the "ticket" field. +func (spc *SharePurchaseCreate) SetTicket(s string) *SharePurchaseCreate { + spc.mutation.SetTicket(s) + return spc +} + +// SetShare sets the "share" edge to the Share entity. +func (spc *SharePurchaseCreate) SetShare(s *Share) *SharePurchaseCreate { + return spc.SetShareID(s.ID) +} + +// SetBuyer sets the "buyer" edge to the User entity. +func (spc *SharePurchaseCreate) SetBuyer(u *User) *SharePurchaseCreate { + return spc.SetBuyerID(u.ID) +} + +// Mutation returns the SharePurchaseMutation object of the builder. +func (spc *SharePurchaseCreate) Mutation() *SharePurchaseMutation { + return spc.mutation +} + +// Save creates the SharePurchase in the database. +func (spc *SharePurchaseCreate) Save(ctx context.Context) (*SharePurchase, error) { + if err := spc.defaults(); err != nil { + return nil, err + } + return withHooks(ctx, spc.sqlSave, spc.mutation, spc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (spc *SharePurchaseCreate) SaveX(ctx context.Context) *SharePurchase { + v, err := spc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (spc *SharePurchaseCreate) Exec(ctx context.Context) error { + _, err := spc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (spc *SharePurchaseCreate) ExecX(ctx context.Context) { + if err := spc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (spc *SharePurchaseCreate) defaults() error { + if _, ok := spc.mutation.CreatedAt(); !ok { + if sharepurchase.DefaultCreatedAt == nil { + return fmt.Errorf("ent: uninitialized sharepurchase.DefaultCreatedAt (forgotten import ent/runtime?)") + } + v := sharepurchase.DefaultCreatedAt() + spc.mutation.SetCreatedAt(v) + } + if _, ok := spc.mutation.UpdatedAt(); !ok { + if sharepurchase.DefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized sharepurchase.DefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := sharepurchase.DefaultUpdatedAt() + spc.mutation.SetUpdatedAt(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (spc *SharePurchaseCreate) check() error { + if _, ok := spc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "SharePurchase.created_at"`)} + } + if _, ok := spc.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "SharePurchase.updated_at"`)} + } + if _, ok := spc.mutation.ShareID(); !ok { + return &ValidationError{Name: "share_id", err: errors.New(`ent: missing required field "SharePurchase.share_id"`)} + } + if _, ok := spc.mutation.BuyerID(); !ok { + return &ValidationError{Name: "buyer_id", err: errors.New(`ent: missing required field "SharePurchase.buyer_id"`)} + } + if _, ok := spc.mutation.Points(); !ok { + return &ValidationError{Name: "points", err: errors.New(`ent: missing required field "SharePurchase.points"`)} + } + if _, ok := spc.mutation.Ticket(); !ok { + return &ValidationError{Name: "ticket", err: errors.New(`ent: missing required field "SharePurchase.ticket"`)} + } + if v, ok := spc.mutation.Ticket(); ok { + if err := sharepurchase.TicketValidator(v); err != nil { + return &ValidationError{Name: "ticket", err: fmt.Errorf(`ent: validator failed for field "SharePurchase.ticket": %w`, err)} + } + } + if _, ok := spc.mutation.ShareID(); !ok { + return &ValidationError{Name: "share", err: errors.New(`ent: missing required edge "SharePurchase.share"`)} + } + if _, ok := spc.mutation.BuyerID(); !ok { + return &ValidationError{Name: "buyer", err: errors.New(`ent: missing required edge "SharePurchase.buyer"`)} + } + return nil +} + +func (spc *SharePurchaseCreate) sqlSave(ctx context.Context) (*SharePurchase, error) { + if err := spc.check(); err != nil { + return nil, err + } + _node, _spec := spc.createSpec() + if err := sqlgraph.CreateNode(ctx, spc.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) + spc.mutation.id = &_node.ID + spc.mutation.done = true + return _node, nil +} + +func (spc *SharePurchaseCreate) createSpec() (*SharePurchase, *sqlgraph.CreateSpec) { + var ( + _node = &SharePurchase{config: spc.config} + _spec = sqlgraph.NewCreateSpec(sharepurchase.Table, sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt)) + ) + + if id, ok := spc.mutation.ID(); ok { + _node.ID = id + id64 := int64(id) + _spec.ID.Value = id64 + } + + _spec.OnConflict = spc.conflict + if value, ok := spc.mutation.CreatedAt(); ok { + _spec.SetField(sharepurchase.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := spc.mutation.UpdatedAt(); ok { + _spec.SetField(sharepurchase.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := spc.mutation.DeletedAt(); ok { + _spec.SetField(sharepurchase.FieldDeletedAt, field.TypeTime, value) + _node.DeletedAt = &value + } + if value, ok := spc.mutation.Points(); ok { + _spec.SetField(sharepurchase.FieldPoints, field.TypeInt, value) + _node.Points = value + } + if value, ok := spc.mutation.Ticket(); ok { + _spec.SetField(sharepurchase.FieldTicket, field.TypeString, value) + _node.Ticket = value + } + if nodes := spc.mutation.ShareIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.ShareTable, + Columns: []string{sharepurchase.ShareColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(share.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.ShareID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := spc.mutation.BuyerIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.BuyerTable, + Columns: []string{sharepurchase.BuyerColumn}, + 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.BuyerID = 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.SharePurchase.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.SharePurchaseUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (spc *SharePurchaseCreate) OnConflict(opts ...sql.ConflictOption) *SharePurchaseUpsertOne { + spc.conflict = opts + return &SharePurchaseUpsertOne{ + create: spc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.SharePurchase.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (spc *SharePurchaseCreate) OnConflictColumns(columns ...string) *SharePurchaseUpsertOne { + spc.conflict = append(spc.conflict, sql.ConflictColumns(columns...)) + return &SharePurchaseUpsertOne{ + create: spc, + } +} + +type ( + // SharePurchaseUpsertOne is the builder for "upsert"-ing + // one SharePurchase node. + SharePurchaseUpsertOne struct { + create *SharePurchaseCreate + } + + // SharePurchaseUpsert is the "OnConflict" setter. + SharePurchaseUpsert struct { + *sql.UpdateSet + } +) + +// SetUpdatedAt sets the "updated_at" field. +func (u *SharePurchaseUpsert) SetUpdatedAt(v time.Time) *SharePurchaseUpsert { + u.Set(sharepurchase.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *SharePurchaseUpsert) UpdateUpdatedAt() *SharePurchaseUpsert { + u.SetExcluded(sharepurchase.FieldUpdatedAt) + return u +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *SharePurchaseUpsert) SetDeletedAt(v time.Time) *SharePurchaseUpsert { + u.Set(sharepurchase.FieldDeletedAt, v) + return u +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *SharePurchaseUpsert) UpdateDeletedAt() *SharePurchaseUpsert { + u.SetExcluded(sharepurchase.FieldDeletedAt) + return u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *SharePurchaseUpsert) ClearDeletedAt() *SharePurchaseUpsert { + u.SetNull(sharepurchase.FieldDeletedAt) + return u +} + +// SetShareID sets the "share_id" field. +func (u *SharePurchaseUpsert) SetShareID(v int) *SharePurchaseUpsert { + u.Set(sharepurchase.FieldShareID, v) + return u +} + +// UpdateShareID sets the "share_id" field to the value that was provided on create. +func (u *SharePurchaseUpsert) UpdateShareID() *SharePurchaseUpsert { + u.SetExcluded(sharepurchase.FieldShareID) + return u +} + +// SetBuyerID sets the "buyer_id" field. +func (u *SharePurchaseUpsert) SetBuyerID(v int) *SharePurchaseUpsert { + u.Set(sharepurchase.FieldBuyerID, v) + return u +} + +// UpdateBuyerID sets the "buyer_id" field to the value that was provided on create. +func (u *SharePurchaseUpsert) UpdateBuyerID() *SharePurchaseUpsert { + u.SetExcluded(sharepurchase.FieldBuyerID) + return u +} + +// SetPoints sets the "points" field. +func (u *SharePurchaseUpsert) SetPoints(v int) *SharePurchaseUpsert { + u.Set(sharepurchase.FieldPoints, v) + return u +} + +// UpdatePoints sets the "points" field to the value that was provided on create. +func (u *SharePurchaseUpsert) UpdatePoints() *SharePurchaseUpsert { + u.SetExcluded(sharepurchase.FieldPoints) + return u +} + +// AddPoints adds v to the "points" field. +func (u *SharePurchaseUpsert) AddPoints(v int) *SharePurchaseUpsert { + u.Add(sharepurchase.FieldPoints, v) + return u +} + +// SetTicket sets the "ticket" field. +func (u *SharePurchaseUpsert) SetTicket(v string) *SharePurchaseUpsert { + u.Set(sharepurchase.FieldTicket, v) + return u +} + +// UpdateTicket sets the "ticket" field to the value that was provided on create. +func (u *SharePurchaseUpsert) UpdateTicket() *SharePurchaseUpsert { + u.SetExcluded(sharepurchase.FieldTicket) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create. +// Using this option is equivalent to using: +// +// client.SharePurchase.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *SharePurchaseUpsertOne) UpdateNewValues() *SharePurchaseUpsertOne { + 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(sharepurchase.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.SharePurchase.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *SharePurchaseUpsertOne) Ignore() *SharePurchaseUpsertOne { + 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 *SharePurchaseUpsertOne) DoNothing() *SharePurchaseUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the SharePurchaseCreate.OnConflict +// documentation for more info. +func (u *SharePurchaseUpsertOne) Update(set func(*SharePurchaseUpsert)) *SharePurchaseUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&SharePurchaseUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *SharePurchaseUpsertOne) SetUpdatedAt(v time.Time) *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *SharePurchaseUpsertOne) UpdateUpdatedAt() *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *SharePurchaseUpsertOne) SetDeletedAt(v time.Time) *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *SharePurchaseUpsertOne) UpdateDeletedAt() *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *SharePurchaseUpsertOne) ClearDeletedAt() *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.ClearDeletedAt() + }) +} + +// SetShareID sets the "share_id" field. +func (u *SharePurchaseUpsertOne) SetShareID(v int) *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetShareID(v) + }) +} + +// UpdateShareID sets the "share_id" field to the value that was provided on create. +func (u *SharePurchaseUpsertOne) UpdateShareID() *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateShareID() + }) +} + +// SetBuyerID sets the "buyer_id" field. +func (u *SharePurchaseUpsertOne) SetBuyerID(v int) *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetBuyerID(v) + }) +} + +// UpdateBuyerID sets the "buyer_id" field to the value that was provided on create. +func (u *SharePurchaseUpsertOne) UpdateBuyerID() *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateBuyerID() + }) +} + +// SetPoints sets the "points" field. +func (u *SharePurchaseUpsertOne) SetPoints(v int) *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetPoints(v) + }) +} + +// AddPoints adds v to the "points" field. +func (u *SharePurchaseUpsertOne) AddPoints(v int) *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.AddPoints(v) + }) +} + +// UpdatePoints sets the "points" field to the value that was provided on create. +func (u *SharePurchaseUpsertOne) UpdatePoints() *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdatePoints() + }) +} + +// SetTicket sets the "ticket" field. +func (u *SharePurchaseUpsertOne) SetTicket(v string) *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetTicket(v) + }) +} + +// UpdateTicket sets the "ticket" field to the value that was provided on create. +func (u *SharePurchaseUpsertOne) UpdateTicket() *SharePurchaseUpsertOne { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateTicket() + }) +} + +// Exec executes the query. +func (u *SharePurchaseUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for SharePurchaseCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *SharePurchaseUpsertOne) 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 *SharePurchaseUpsertOne) 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 *SharePurchaseUpsertOne) IDX(ctx context.Context) int { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +func (m *SharePurchaseCreate) SetRawID(t int) *SharePurchaseCreate { + m.mutation.SetRawID(t) + return m +} + +// SharePurchaseCreateBulk is the builder for creating many SharePurchase entities in bulk. +type SharePurchaseCreateBulk struct { + config + err error + builders []*SharePurchaseCreate + conflict []sql.ConflictOption +} + +// Save creates the SharePurchase entities in the database. +func (spcb *SharePurchaseCreateBulk) Save(ctx context.Context) ([]*SharePurchase, error) { + if spcb.err != nil { + return nil, spcb.err + } + specs := make([]*sqlgraph.CreateSpec, len(spcb.builders)) + nodes := make([]*SharePurchase, len(spcb.builders)) + mutators := make([]Mutator, len(spcb.builders)) + for i := range spcb.builders { + func(i int, root context.Context) { + builder := spcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*SharePurchaseMutation) + 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, spcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = spcb.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, spcb.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, spcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (spcb *SharePurchaseCreateBulk) SaveX(ctx context.Context) []*SharePurchase { + v, err := spcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (spcb *SharePurchaseCreateBulk) Exec(ctx context.Context) error { + _, err := spcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (spcb *SharePurchaseCreateBulk) ExecX(ctx context.Context) { + if err := spcb.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.SharePurchase.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.SharePurchaseUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (spcb *SharePurchaseCreateBulk) OnConflict(opts ...sql.ConflictOption) *SharePurchaseUpsertBulk { + spcb.conflict = opts + return &SharePurchaseUpsertBulk{ + create: spcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.SharePurchase.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (spcb *SharePurchaseCreateBulk) OnConflictColumns(columns ...string) *SharePurchaseUpsertBulk { + spcb.conflict = append(spcb.conflict, sql.ConflictColumns(columns...)) + return &SharePurchaseUpsertBulk{ + create: spcb, + } +} + +// SharePurchaseUpsertBulk is the builder for "upsert"-ing +// a bulk of SharePurchase nodes. +type SharePurchaseUpsertBulk struct { + create *SharePurchaseCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.SharePurchase.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *SharePurchaseUpsertBulk) UpdateNewValues() *SharePurchaseUpsertBulk { + 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(sharepurchase.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.SharePurchase.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *SharePurchaseUpsertBulk) Ignore() *SharePurchaseUpsertBulk { + 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 *SharePurchaseUpsertBulk) DoNothing() *SharePurchaseUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the SharePurchaseCreateBulk.OnConflict +// documentation for more info. +func (u *SharePurchaseUpsertBulk) Update(set func(*SharePurchaseUpsert)) *SharePurchaseUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&SharePurchaseUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *SharePurchaseUpsertBulk) SetUpdatedAt(v time.Time) *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *SharePurchaseUpsertBulk) UpdateUpdatedAt() *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *SharePurchaseUpsertBulk) SetDeletedAt(v time.Time) *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *SharePurchaseUpsertBulk) UpdateDeletedAt() *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *SharePurchaseUpsertBulk) ClearDeletedAt() *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.ClearDeletedAt() + }) +} + +// SetShareID sets the "share_id" field. +func (u *SharePurchaseUpsertBulk) SetShareID(v int) *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetShareID(v) + }) +} + +// UpdateShareID sets the "share_id" field to the value that was provided on create. +func (u *SharePurchaseUpsertBulk) UpdateShareID() *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateShareID() + }) +} + +// SetBuyerID sets the "buyer_id" field. +func (u *SharePurchaseUpsertBulk) SetBuyerID(v int) *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetBuyerID(v) + }) +} + +// UpdateBuyerID sets the "buyer_id" field to the value that was provided on create. +func (u *SharePurchaseUpsertBulk) UpdateBuyerID() *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateBuyerID() + }) +} + +// SetPoints sets the "points" field. +func (u *SharePurchaseUpsertBulk) SetPoints(v int) *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetPoints(v) + }) +} + +// AddPoints adds v to the "points" field. +func (u *SharePurchaseUpsertBulk) AddPoints(v int) *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.AddPoints(v) + }) +} + +// UpdatePoints sets the "points" field to the value that was provided on create. +func (u *SharePurchaseUpsertBulk) UpdatePoints() *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdatePoints() + }) +} + +// SetTicket sets the "ticket" field. +func (u *SharePurchaseUpsertBulk) SetTicket(v string) *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.SetTicket(v) + }) +} + +// UpdateTicket sets the "ticket" field to the value that was provided on create. +func (u *SharePurchaseUpsertBulk) UpdateTicket() *SharePurchaseUpsertBulk { + return u.Update(func(s *SharePurchaseUpsert) { + s.UpdateTicket() + }) +} + +// Exec executes the query. +func (u *SharePurchaseUpsertBulk) 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 SharePurchaseCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for SharePurchaseCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *SharePurchaseUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/sharepurchase_delete.go b/ent/sharepurchase_delete.go new file mode 100644 index 00000000..851c6f76 --- /dev/null +++ b/ent/sharepurchase_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" +) + +// SharePurchaseDelete is the builder for deleting a SharePurchase entity. +type SharePurchaseDelete struct { + config + hooks []Hook + mutation *SharePurchaseMutation +} + +// Where appends a list predicates to the SharePurchaseDelete builder. +func (spd *SharePurchaseDelete) Where(ps ...predicate.SharePurchase) *SharePurchaseDelete { + spd.mutation.Where(ps...) + return spd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (spd *SharePurchaseDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, spd.sqlExec, spd.mutation, spd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (spd *SharePurchaseDelete) ExecX(ctx context.Context) int { + n, err := spd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (spd *SharePurchaseDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(sharepurchase.Table, sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt)) + if ps := spd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, spd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + spd.mutation.done = true + return affected, err +} + +// SharePurchaseDeleteOne is the builder for deleting a single SharePurchase entity. +type SharePurchaseDeleteOne struct { + spd *SharePurchaseDelete +} + +// Where appends a list predicates to the SharePurchaseDelete builder. +func (spdo *SharePurchaseDeleteOne) Where(ps ...predicate.SharePurchase) *SharePurchaseDeleteOne { + spdo.spd.mutation.Where(ps...) + return spdo +} + +// Exec executes the deletion query. +func (spdo *SharePurchaseDeleteOne) Exec(ctx context.Context) error { + n, err := spdo.spd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{sharepurchase.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (spdo *SharePurchaseDeleteOne) ExecX(ctx context.Context) { + if err := spdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/sharepurchase_query.go b/ent/sharepurchase_query.go new file mode 100644 index 00000000..7f0df5ed --- /dev/null +++ b/ent/sharepurchase_query.go @@ -0,0 +1,680 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/cloudreve/Cloudreve/v4/ent/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" + "github.com/cloudreve/Cloudreve/v4/ent/user" +) + +// SharePurchaseQuery is the builder for querying SharePurchase entities. +type SharePurchaseQuery struct { + config + ctx *QueryContext + order []sharepurchase.OrderOption + inters []Interceptor + predicates []predicate.SharePurchase + withShare *ShareQuery + withBuyer *UserQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the SharePurchaseQuery builder. +func (spq *SharePurchaseQuery) Where(ps ...predicate.SharePurchase) *SharePurchaseQuery { + spq.predicates = append(spq.predicates, ps...) + return spq +} + +// Limit the number of records to be returned by this query. +func (spq *SharePurchaseQuery) Limit(limit int) *SharePurchaseQuery { + spq.ctx.Limit = &limit + return spq +} + +// Offset to start from. +func (spq *SharePurchaseQuery) Offset(offset int) *SharePurchaseQuery { + spq.ctx.Offset = &offset + return spq +} + +// 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 (spq *SharePurchaseQuery) Unique(unique bool) *SharePurchaseQuery { + spq.ctx.Unique = &unique + return spq +} + +// Order specifies how the records should be ordered. +func (spq *SharePurchaseQuery) Order(o ...sharepurchase.OrderOption) *SharePurchaseQuery { + spq.order = append(spq.order, o...) + return spq +} + +// QueryShare chains the current query on the "share" edge. +func (spq *SharePurchaseQuery) QueryShare() *ShareQuery { + query := (&ShareClient{config: spq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := spq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := spq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(sharepurchase.Table, sharepurchase.FieldID, selector), + sqlgraph.To(share.Table, share.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, sharepurchase.ShareTable, sharepurchase.ShareColumn), + ) + fromU = sqlgraph.SetNeighbors(spq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryBuyer chains the current query on the "buyer" edge. +func (spq *SharePurchaseQuery) QueryBuyer() *UserQuery { + query := (&UserClient{config: spq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := spq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := spq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(sharepurchase.Table, sharepurchase.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, sharepurchase.BuyerTable, sharepurchase.BuyerColumn), + ) + fromU = sqlgraph.SetNeighbors(spq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first SharePurchase entity from the query. +// Returns a *NotFoundError when no SharePurchase was found. +func (spq *SharePurchaseQuery) First(ctx context.Context) (*SharePurchase, error) { + nodes, err := spq.Limit(1).All(setContextOp(ctx, spq.ctx, "First")) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{sharepurchase.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (spq *SharePurchaseQuery) FirstX(ctx context.Context) *SharePurchase { + node, err := spq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first SharePurchase ID from the query. +// Returns a *NotFoundError when no SharePurchase ID was found. +func (spq *SharePurchaseQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = spq.Limit(1).IDs(setContextOp(ctx, spq.ctx, "FirstID")); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{sharepurchase.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (spq *SharePurchaseQuery) FirstIDX(ctx context.Context) int { + id, err := spq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single SharePurchase entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one SharePurchase entity is found. +// Returns a *NotFoundError when no SharePurchase entities are found. +func (spq *SharePurchaseQuery) Only(ctx context.Context) (*SharePurchase, error) { + nodes, err := spq.Limit(2).All(setContextOp(ctx, spq.ctx, "Only")) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{sharepurchase.Label} + default: + return nil, &NotSingularError{sharepurchase.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (spq *SharePurchaseQuery) OnlyX(ctx context.Context) *SharePurchase { + node, err := spq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only SharePurchase ID in the query. +// Returns a *NotSingularError when more than one SharePurchase ID is found. +// Returns a *NotFoundError when no entities are found. +func (spq *SharePurchaseQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = spq.Limit(2).IDs(setContextOp(ctx, spq.ctx, "OnlyID")); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{sharepurchase.Label} + default: + err = &NotSingularError{sharepurchase.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (spq *SharePurchaseQuery) OnlyIDX(ctx context.Context) int { + id, err := spq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of SharePurchases. +func (spq *SharePurchaseQuery) All(ctx context.Context) ([]*SharePurchase, error) { + ctx = setContextOp(ctx, spq.ctx, "All") + if err := spq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*SharePurchase, *SharePurchaseQuery]() + return withInterceptors[[]*SharePurchase](ctx, spq, qr, spq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (spq *SharePurchaseQuery) AllX(ctx context.Context) []*SharePurchase { + nodes, err := spq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of SharePurchase IDs. +func (spq *SharePurchaseQuery) IDs(ctx context.Context) (ids []int, err error) { + if spq.ctx.Unique == nil && spq.path != nil { + spq.Unique(true) + } + ctx = setContextOp(ctx, spq.ctx, "IDs") + if err = spq.Select(sharepurchase.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (spq *SharePurchaseQuery) IDsX(ctx context.Context) []int { + ids, err := spq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (spq *SharePurchaseQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, spq.ctx, "Count") + if err := spq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, spq, querierCount[*SharePurchaseQuery](), spq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (spq *SharePurchaseQuery) CountX(ctx context.Context) int { + count, err := spq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (spq *SharePurchaseQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, spq.ctx, "Exist") + switch _, err := spq.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 (spq *SharePurchaseQuery) ExistX(ctx context.Context) bool { + exist, err := spq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the SharePurchaseQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (spq *SharePurchaseQuery) Clone() *SharePurchaseQuery { + if spq == nil { + return nil + } + return &SharePurchaseQuery{ + config: spq.config, + ctx: spq.ctx.Clone(), + order: append([]sharepurchase.OrderOption{}, spq.order...), + inters: append([]Interceptor{}, spq.inters...), + predicates: append([]predicate.SharePurchase{}, spq.predicates...), + withShare: spq.withShare.Clone(), + withBuyer: spq.withBuyer.Clone(), + // clone intermediate query. + sql: spq.sql.Clone(), + path: spq.path, + } +} + +// WithShare tells the query-builder to eager-load the nodes that are connected to +// the "share" edge. The optional arguments are used to configure the query builder of the edge. +func (spq *SharePurchaseQuery) WithShare(opts ...func(*ShareQuery)) *SharePurchaseQuery { + query := (&ShareClient{config: spq.config}).Query() + for _, opt := range opts { + opt(query) + } + spq.withShare = query + return spq +} + +// WithBuyer tells the query-builder to eager-load the nodes that are connected to +// the "buyer" edge. The optional arguments are used to configure the query builder of the edge. +func (spq *SharePurchaseQuery) WithBuyer(opts ...func(*UserQuery)) *SharePurchaseQuery { + query := (&UserClient{config: spq.config}).Query() + for _, opt := range opts { + opt(query) + } + spq.withBuyer = query + return spq +} + +// 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.SharePurchase.Query(). +// GroupBy(sharepurchase.FieldCreatedAt). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (spq *SharePurchaseQuery) GroupBy(field string, fields ...string) *SharePurchaseGroupBy { + spq.ctx.Fields = append([]string{field}, fields...) + grbuild := &SharePurchaseGroupBy{build: spq} + grbuild.flds = &spq.ctx.Fields + grbuild.label = sharepurchase.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.SharePurchase.Query(). +// Select(sharepurchase.FieldCreatedAt). +// Scan(ctx, &v) +func (spq *SharePurchaseQuery) Select(fields ...string) *SharePurchaseSelect { + spq.ctx.Fields = append(spq.ctx.Fields, fields...) + sbuild := &SharePurchaseSelect{SharePurchaseQuery: spq} + sbuild.label = sharepurchase.Label + sbuild.flds, sbuild.scan = &spq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a SharePurchaseSelect configured with the given aggregations. +func (spq *SharePurchaseQuery) Aggregate(fns ...AggregateFunc) *SharePurchaseSelect { + return spq.Select().Aggregate(fns...) +} + +func (spq *SharePurchaseQuery) prepareQuery(ctx context.Context) error { + for _, inter := range spq.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, spq); err != nil { + return err + } + } + } + for _, f := range spq.ctx.Fields { + if !sharepurchase.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if spq.path != nil { + prev, err := spq.path(ctx) + if err != nil { + return err + } + spq.sql = prev + } + return nil +} + +func (spq *SharePurchaseQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*SharePurchase, error) { + var ( + nodes = []*SharePurchase{} + _spec = spq.querySpec() + loadedTypes = [2]bool{ + spq.withShare != nil, + spq.withBuyer != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*SharePurchase).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &SharePurchase{config: spq.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, spq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := spq.withShare; query != nil { + if err := spq.loadShare(ctx, query, nodes, nil, + func(n *SharePurchase, e *Share) { n.Edges.Share = e }); err != nil { + return nil, err + } + } + if query := spq.withBuyer; query != nil { + if err := spq.loadBuyer(ctx, query, nodes, nil, + func(n *SharePurchase, e *User) { n.Edges.Buyer = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (spq *SharePurchaseQuery) loadShare(ctx context.Context, query *ShareQuery, nodes []*SharePurchase, init func(*SharePurchase), assign func(*SharePurchase, *Share)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*SharePurchase) + for i := range nodes { + fk := nodes[i].ShareID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(share.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 "share_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (spq *SharePurchaseQuery) loadBuyer(ctx context.Context, query *UserQuery, nodes []*SharePurchase, init func(*SharePurchase), assign func(*SharePurchase, *User)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*SharePurchase) + for i := range nodes { + fk := nodes[i].BuyerID + 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 "buyer_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (spq *SharePurchaseQuery) sqlCount(ctx context.Context) (int, error) { + _spec := spq.querySpec() + _spec.Node.Columns = spq.ctx.Fields + if len(spq.ctx.Fields) > 0 { + _spec.Unique = spq.ctx.Unique != nil && *spq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, spq.driver, _spec) +} + +func (spq *SharePurchaseQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(sharepurchase.Table, sharepurchase.Columns, sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt)) + _spec.From = spq.sql + if unique := spq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if spq.path != nil { + _spec.Unique = true + } + if fields := spq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, sharepurchase.FieldID) + for i := range fields { + if fields[i] != sharepurchase.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if spq.withShare != nil { + _spec.Node.AddColumnOnce(sharepurchase.FieldShareID) + } + if spq.withBuyer != nil { + _spec.Node.AddColumnOnce(sharepurchase.FieldBuyerID) + } + } + if ps := spq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := spq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := spq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := spq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (spq *SharePurchaseQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(spq.driver.Dialect()) + t1 := builder.Table(sharepurchase.Table) + columns := spq.ctx.Fields + if len(columns) == 0 { + columns = sharepurchase.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if spq.sql != nil { + selector = spq.sql + selector.Select(selector.Columns(columns...)...) + } + if spq.ctx.Unique != nil && *spq.ctx.Unique { + selector.Distinct() + } + for _, p := range spq.predicates { + p(selector) + } + for _, p := range spq.order { + p(selector) + } + if offset := spq.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 := spq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// SharePurchaseGroupBy is the group-by builder for SharePurchase entities. +type SharePurchaseGroupBy struct { + selector + build *SharePurchaseQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (spgb *SharePurchaseGroupBy) Aggregate(fns ...AggregateFunc) *SharePurchaseGroupBy { + spgb.fns = append(spgb.fns, fns...) + return spgb +} + +// Scan applies the selector query and scans the result into the given value. +func (spgb *SharePurchaseGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, spgb.build.ctx, "GroupBy") + if err := spgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*SharePurchaseQuery, *SharePurchaseGroupBy](ctx, spgb.build, spgb, spgb.build.inters, v) +} + +func (spgb *SharePurchaseGroupBy) sqlScan(ctx context.Context, root *SharePurchaseQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(spgb.fns)) + for _, fn := range spgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*spgb.flds)+len(spgb.fns)) + for _, f := range *spgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*spgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := spgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// SharePurchaseSelect is the builder for selecting fields of SharePurchase entities. +type SharePurchaseSelect struct { + *SharePurchaseQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (sps *SharePurchaseSelect) Aggregate(fns ...AggregateFunc) *SharePurchaseSelect { + sps.fns = append(sps.fns, fns...) + return sps +} + +// Scan applies the selector query and scans the result into the given value. +func (sps *SharePurchaseSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, sps.ctx, "Select") + if err := sps.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*SharePurchaseQuery, *SharePurchaseSelect](ctx, sps.SharePurchaseQuery, sps, sps.inters, v) +} + +func (sps *SharePurchaseSelect) sqlScan(ctx context.Context, root *SharePurchaseQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(sps.fns)) + for _, fn := range sps.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*sps.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 := sps.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/sharepurchase_update.go b/ent/sharepurchase_update.go new file mode 100644 index 00000000..f3e2eaea --- /dev/null +++ b/ent/sharepurchase_update.go @@ -0,0 +1,620 @@ +// 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/predicate" + "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" + "github.com/cloudreve/Cloudreve/v4/ent/user" +) + +// SharePurchaseUpdate is the builder for updating SharePurchase entities. +type SharePurchaseUpdate struct { + config + hooks []Hook + mutation *SharePurchaseMutation +} + +// Where appends a list predicates to the SharePurchaseUpdate builder. +func (spu *SharePurchaseUpdate) Where(ps ...predicate.SharePurchase) *SharePurchaseUpdate { + spu.mutation.Where(ps...) + return spu +} + +// SetUpdatedAt sets the "updated_at" field. +func (spu *SharePurchaseUpdate) SetUpdatedAt(t time.Time) *SharePurchaseUpdate { + spu.mutation.SetUpdatedAt(t) + return spu +} + +// SetDeletedAt sets the "deleted_at" field. +func (spu *SharePurchaseUpdate) SetDeletedAt(t time.Time) *SharePurchaseUpdate { + spu.mutation.SetDeletedAt(t) + return spu +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (spu *SharePurchaseUpdate) SetNillableDeletedAt(t *time.Time) *SharePurchaseUpdate { + if t != nil { + spu.SetDeletedAt(*t) + } + return spu +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (spu *SharePurchaseUpdate) ClearDeletedAt() *SharePurchaseUpdate { + spu.mutation.ClearDeletedAt() + return spu +} + +// SetShareID sets the "share_id" field. +func (spu *SharePurchaseUpdate) SetShareID(i int) *SharePurchaseUpdate { + spu.mutation.SetShareID(i) + return spu +} + +// SetNillableShareID sets the "share_id" field if the given value is not nil. +func (spu *SharePurchaseUpdate) SetNillableShareID(i *int) *SharePurchaseUpdate { + if i != nil { + spu.SetShareID(*i) + } + return spu +} + +// SetBuyerID sets the "buyer_id" field. +func (spu *SharePurchaseUpdate) SetBuyerID(i int) *SharePurchaseUpdate { + spu.mutation.SetBuyerID(i) + return spu +} + +// SetNillableBuyerID sets the "buyer_id" field if the given value is not nil. +func (spu *SharePurchaseUpdate) SetNillableBuyerID(i *int) *SharePurchaseUpdate { + if i != nil { + spu.SetBuyerID(*i) + } + return spu +} + +// SetPoints sets the "points" field. +func (spu *SharePurchaseUpdate) SetPoints(i int) *SharePurchaseUpdate { + spu.mutation.ResetPoints() + spu.mutation.SetPoints(i) + return spu +} + +// SetNillablePoints sets the "points" field if the given value is not nil. +func (spu *SharePurchaseUpdate) SetNillablePoints(i *int) *SharePurchaseUpdate { + if i != nil { + spu.SetPoints(*i) + } + return spu +} + +// AddPoints adds i to the "points" field. +func (spu *SharePurchaseUpdate) AddPoints(i int) *SharePurchaseUpdate { + spu.mutation.AddPoints(i) + return spu +} + +// SetTicket sets the "ticket" field. +func (spu *SharePurchaseUpdate) SetTicket(s string) *SharePurchaseUpdate { + spu.mutation.SetTicket(s) + return spu +} + +// SetNillableTicket sets the "ticket" field if the given value is not nil. +func (spu *SharePurchaseUpdate) SetNillableTicket(s *string) *SharePurchaseUpdate { + if s != nil { + spu.SetTicket(*s) + } + return spu +} + +// SetShare sets the "share" edge to the Share entity. +func (spu *SharePurchaseUpdate) SetShare(s *Share) *SharePurchaseUpdate { + return spu.SetShareID(s.ID) +} + +// SetBuyer sets the "buyer" edge to the User entity. +func (spu *SharePurchaseUpdate) SetBuyer(u *User) *SharePurchaseUpdate { + return spu.SetBuyerID(u.ID) +} + +// Mutation returns the SharePurchaseMutation object of the builder. +func (spu *SharePurchaseUpdate) Mutation() *SharePurchaseMutation { + return spu.mutation +} + +// ClearShare clears the "share" edge to the Share entity. +func (spu *SharePurchaseUpdate) ClearShare() *SharePurchaseUpdate { + spu.mutation.ClearShare() + return spu +} + +// ClearBuyer clears the "buyer" edge to the User entity. +func (spu *SharePurchaseUpdate) ClearBuyer() *SharePurchaseUpdate { + spu.mutation.ClearBuyer() + return spu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (spu *SharePurchaseUpdate) Save(ctx context.Context) (int, error) { + if err := spu.defaults(); err != nil { + return 0, err + } + return withHooks(ctx, spu.sqlSave, spu.mutation, spu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (spu *SharePurchaseUpdate) SaveX(ctx context.Context) int { + affected, err := spu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (spu *SharePurchaseUpdate) Exec(ctx context.Context) error { + _, err := spu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (spu *SharePurchaseUpdate) ExecX(ctx context.Context) { + if err := spu.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (spu *SharePurchaseUpdate) defaults() error { + if _, ok := spu.mutation.UpdatedAt(); !ok { + if sharepurchase.UpdateDefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized sharepurchase.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := sharepurchase.UpdateDefaultUpdatedAt() + spu.mutation.SetUpdatedAt(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (spu *SharePurchaseUpdate) check() error { + if v, ok := spu.mutation.Ticket(); ok { + if err := sharepurchase.TicketValidator(v); err != nil { + return &ValidationError{Name: "ticket", err: fmt.Errorf(`ent: validator failed for field "SharePurchase.ticket": %w`, err)} + } + } + if _, ok := spu.mutation.ShareID(); spu.mutation.ShareCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "SharePurchase.share"`) + } + if _, ok := spu.mutation.BuyerID(); spu.mutation.BuyerCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "SharePurchase.buyer"`) + } + return nil +} + +func (spu *SharePurchaseUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := spu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(sharepurchase.Table, sharepurchase.Columns, sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt)) + if ps := spu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := spu.mutation.UpdatedAt(); ok { + _spec.SetField(sharepurchase.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := spu.mutation.DeletedAt(); ok { + _spec.SetField(sharepurchase.FieldDeletedAt, field.TypeTime, value) + } + if spu.mutation.DeletedAtCleared() { + _spec.ClearField(sharepurchase.FieldDeletedAt, field.TypeTime) + } + if value, ok := spu.mutation.Points(); ok { + _spec.SetField(sharepurchase.FieldPoints, field.TypeInt, value) + } + if value, ok := spu.mutation.AddedPoints(); ok { + _spec.AddField(sharepurchase.FieldPoints, field.TypeInt, value) + } + if value, ok := spu.mutation.Ticket(); ok { + _spec.SetField(sharepurchase.FieldTicket, field.TypeString, value) + } + if spu.mutation.ShareCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.ShareTable, + Columns: []string{sharepurchase.ShareColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(share.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := spu.mutation.ShareIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.ShareTable, + Columns: []string{sharepurchase.ShareColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(share.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if spu.mutation.BuyerCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.BuyerTable, + Columns: []string{sharepurchase.BuyerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := spu.mutation.BuyerIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.BuyerTable, + Columns: []string{sharepurchase.BuyerColumn}, + 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, spu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{sharepurchase.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + spu.mutation.done = true + return n, nil +} + +// SharePurchaseUpdateOne is the builder for updating a single SharePurchase entity. +type SharePurchaseUpdateOne struct { + config + fields []string + hooks []Hook + mutation *SharePurchaseMutation +} + +// SetUpdatedAt sets the "updated_at" field. +func (spuo *SharePurchaseUpdateOne) SetUpdatedAt(t time.Time) *SharePurchaseUpdateOne { + spuo.mutation.SetUpdatedAt(t) + return spuo +} + +// SetDeletedAt sets the "deleted_at" field. +func (spuo *SharePurchaseUpdateOne) SetDeletedAt(t time.Time) *SharePurchaseUpdateOne { + spuo.mutation.SetDeletedAt(t) + return spuo +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (spuo *SharePurchaseUpdateOne) SetNillableDeletedAt(t *time.Time) *SharePurchaseUpdateOne { + if t != nil { + spuo.SetDeletedAt(*t) + } + return spuo +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (spuo *SharePurchaseUpdateOne) ClearDeletedAt() *SharePurchaseUpdateOne { + spuo.mutation.ClearDeletedAt() + return spuo +} + +// SetShareID sets the "share_id" field. +func (spuo *SharePurchaseUpdateOne) SetShareID(i int) *SharePurchaseUpdateOne { + spuo.mutation.SetShareID(i) + return spuo +} + +// SetNillableShareID sets the "share_id" field if the given value is not nil. +func (spuo *SharePurchaseUpdateOne) SetNillableShareID(i *int) *SharePurchaseUpdateOne { + if i != nil { + spuo.SetShareID(*i) + } + return spuo +} + +// SetBuyerID sets the "buyer_id" field. +func (spuo *SharePurchaseUpdateOne) SetBuyerID(i int) *SharePurchaseUpdateOne { + spuo.mutation.SetBuyerID(i) + return spuo +} + +// SetNillableBuyerID sets the "buyer_id" field if the given value is not nil. +func (spuo *SharePurchaseUpdateOne) SetNillableBuyerID(i *int) *SharePurchaseUpdateOne { + if i != nil { + spuo.SetBuyerID(*i) + } + return spuo +} + +// SetPoints sets the "points" field. +func (spuo *SharePurchaseUpdateOne) SetPoints(i int) *SharePurchaseUpdateOne { + spuo.mutation.ResetPoints() + spuo.mutation.SetPoints(i) + return spuo +} + +// SetNillablePoints sets the "points" field if the given value is not nil. +func (spuo *SharePurchaseUpdateOne) SetNillablePoints(i *int) *SharePurchaseUpdateOne { + if i != nil { + spuo.SetPoints(*i) + } + return spuo +} + +// AddPoints adds i to the "points" field. +func (spuo *SharePurchaseUpdateOne) AddPoints(i int) *SharePurchaseUpdateOne { + spuo.mutation.AddPoints(i) + return spuo +} + +// SetTicket sets the "ticket" field. +func (spuo *SharePurchaseUpdateOne) SetTicket(s string) *SharePurchaseUpdateOne { + spuo.mutation.SetTicket(s) + return spuo +} + +// SetNillableTicket sets the "ticket" field if the given value is not nil. +func (spuo *SharePurchaseUpdateOne) SetNillableTicket(s *string) *SharePurchaseUpdateOne { + if s != nil { + spuo.SetTicket(*s) + } + return spuo +} + +// SetShare sets the "share" edge to the Share entity. +func (spuo *SharePurchaseUpdateOne) SetShare(s *Share) *SharePurchaseUpdateOne { + return spuo.SetShareID(s.ID) +} + +// SetBuyer sets the "buyer" edge to the User entity. +func (spuo *SharePurchaseUpdateOne) SetBuyer(u *User) *SharePurchaseUpdateOne { + return spuo.SetBuyerID(u.ID) +} + +// Mutation returns the SharePurchaseMutation object of the builder. +func (spuo *SharePurchaseUpdateOne) Mutation() *SharePurchaseMutation { + return spuo.mutation +} + +// ClearShare clears the "share" edge to the Share entity. +func (spuo *SharePurchaseUpdateOne) ClearShare() *SharePurchaseUpdateOne { + spuo.mutation.ClearShare() + return spuo +} + +// ClearBuyer clears the "buyer" edge to the User entity. +func (spuo *SharePurchaseUpdateOne) ClearBuyer() *SharePurchaseUpdateOne { + spuo.mutation.ClearBuyer() + return spuo +} + +// Where appends a list predicates to the SharePurchaseUpdate builder. +func (spuo *SharePurchaseUpdateOne) Where(ps ...predicate.SharePurchase) *SharePurchaseUpdateOne { + spuo.mutation.Where(ps...) + return spuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (spuo *SharePurchaseUpdateOne) Select(field string, fields ...string) *SharePurchaseUpdateOne { + spuo.fields = append([]string{field}, fields...) + return spuo +} + +// Save executes the query and returns the updated SharePurchase entity. +func (spuo *SharePurchaseUpdateOne) Save(ctx context.Context) (*SharePurchase, error) { + if err := spuo.defaults(); err != nil { + return nil, err + } + return withHooks(ctx, spuo.sqlSave, spuo.mutation, spuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (spuo *SharePurchaseUpdateOne) SaveX(ctx context.Context) *SharePurchase { + node, err := spuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (spuo *SharePurchaseUpdateOne) Exec(ctx context.Context) error { + _, err := spuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (spuo *SharePurchaseUpdateOne) ExecX(ctx context.Context) { + if err := spuo.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (spuo *SharePurchaseUpdateOne) defaults() error { + if _, ok := spuo.mutation.UpdatedAt(); !ok { + if sharepurchase.UpdateDefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized sharepurchase.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := sharepurchase.UpdateDefaultUpdatedAt() + spuo.mutation.SetUpdatedAt(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (spuo *SharePurchaseUpdateOne) check() error { + if v, ok := spuo.mutation.Ticket(); ok { + if err := sharepurchase.TicketValidator(v); err != nil { + return &ValidationError{Name: "ticket", err: fmt.Errorf(`ent: validator failed for field "SharePurchase.ticket": %w`, err)} + } + } + if _, ok := spuo.mutation.ShareID(); spuo.mutation.ShareCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "SharePurchase.share"`) + } + if _, ok := spuo.mutation.BuyerID(); spuo.mutation.BuyerCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "SharePurchase.buyer"`) + } + return nil +} + +func (spuo *SharePurchaseUpdateOne) sqlSave(ctx context.Context) (_node *SharePurchase, err error) { + if err := spuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(sharepurchase.Table, sharepurchase.Columns, sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt)) + id, ok := spuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "SharePurchase.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := spuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, sharepurchase.FieldID) + for _, f := range fields { + if !sharepurchase.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != sharepurchase.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := spuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := spuo.mutation.UpdatedAt(); ok { + _spec.SetField(sharepurchase.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := spuo.mutation.DeletedAt(); ok { + _spec.SetField(sharepurchase.FieldDeletedAt, field.TypeTime, value) + } + if spuo.mutation.DeletedAtCleared() { + _spec.ClearField(sharepurchase.FieldDeletedAt, field.TypeTime) + } + if value, ok := spuo.mutation.Points(); ok { + _spec.SetField(sharepurchase.FieldPoints, field.TypeInt, value) + } + if value, ok := spuo.mutation.AddedPoints(); ok { + _spec.AddField(sharepurchase.FieldPoints, field.TypeInt, value) + } + if value, ok := spuo.mutation.Ticket(); ok { + _spec.SetField(sharepurchase.FieldTicket, field.TypeString, value) + } + if spuo.mutation.ShareCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.ShareTable, + Columns: []string{sharepurchase.ShareColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(share.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := spuo.mutation.ShareIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.ShareTable, + Columns: []string{sharepurchase.ShareColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(share.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if spuo.mutation.BuyerCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.BuyerTable, + Columns: []string{sharepurchase.BuyerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := spuo.mutation.BuyerIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: sharepurchase.BuyerTable, + Columns: []string{sharepurchase.BuyerColumn}, + 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 = &SharePurchase{config: spuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, spuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{sharepurchase.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + spuo.mutation.done = true + return _node, nil +} diff --git a/ent/tx.go b/ent/tx.go index 7ec81c18..02120c97 100644 --- a/ent/tx.go +++ b/ent/tx.go @@ -52,6 +52,8 @@ type Tx struct { Setting *SettingClient // Share is the client for interacting with the Share builders. Share *ShareClient + // SharePurchase is the client for interacting with the SharePurchase builders. + SharePurchase *SharePurchaseClient // Sku is the client for interacting with the Sku builders. Sku *SkuClient // StoragePolicy is the client for interacting with the StoragePolicy builders. @@ -212,6 +214,7 @@ func (tx *Tx) init() { tx.Passkey = NewPasskeyClient(tx.config) tx.Setting = NewSettingClient(tx.config) tx.Share = NewShareClient(tx.config) + tx.SharePurchase = NewSharePurchaseClient(tx.config) tx.Sku = NewSkuClient(tx.config) tx.StoragePolicy = NewStoragePolicyClient(tx.config) tx.Task = NewTaskClient(tx.config) diff --git a/ent/user.go b/ent/user.go index d2b7334b..03734db4 100644 --- a/ent/user.go +++ b/ent/user.go @@ -84,9 +84,11 @@ type UserEdges struct { RedeemedCodes []*GiftCode `json:"redeemed_codes,omitempty"` // Grants holds the value of the grants edge. Grants []*UserGrant `json:"grants,omitempty"` + // SharePurchases holds the value of the share_purchases edge. + SharePurchases []*SharePurchase `json:"share_purchases,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [12]bool + loadedTypes [13]bool } // GroupOrErr returns the Group value or an error if the edge @@ -201,6 +203,15 @@ func (e UserEdges) GrantsOrErr() ([]*UserGrant, error) { return nil, &NotLoadedError{edge: "grants"} } +// SharePurchasesOrErr returns the SharePurchases value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) SharePurchasesOrErr() ([]*SharePurchase, error) { + if e.loadedTypes[12] { + return e.SharePurchases, nil + } + return nil, &NotLoadedError{edge: "share_purchases"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -409,6 +420,11 @@ func (u *User) QueryGrants() *UserGrantQuery { return NewUserClient(u.config).QueryGrants(u) } +// QuerySharePurchases queries the "share_purchases" edge of the User entity. +func (u *User) QuerySharePurchases() *SharePurchaseQuery { + return NewUserClient(u.config).QuerySharePurchases(u) +} + // Update returns a builder for updating this User. // Note that you need to call User.Unwrap() before calling this method if this User // was returned from a transaction, and the transaction was committed or rolled back. @@ -559,5 +575,11 @@ func (e *User) SetGrants(v []*UserGrant) { e.Edges.loadedTypes[11] = true } +// SetSharePurchases manually set the edge as loaded state. +func (e *User) SetSharePurchases(v []*SharePurchase) { + e.Edges.SharePurchases = v + e.Edges.loadedTypes[12] = true +} + // Users is a parsable slice of User. type Users []*User diff --git a/ent/user/user.go b/ent/user/user.go index 7629ca1e..bb88d66e 100644 --- a/ent/user/user.go +++ b/ent/user/user.go @@ -73,6 +73,8 @@ const ( EdgeRedeemedCodes = "redeemed_codes" // EdgeGrants holds the string denoting the grants edge name in mutations. EdgeGrants = "grants" + // EdgeSharePurchases holds the string denoting the share_purchases edge name in mutations. + EdgeSharePurchases = "share_purchases" // Table holds the table name of the user in the database. Table = "users" // GroupTable is the table that holds the group relation/edge. @@ -159,6 +161,13 @@ const ( GrantsInverseTable = "user_grants" // GrantsColumn is the table column denoting the grants relation/edge. GrantsColumn = "user_id" + // SharePurchasesTable is the table that holds the share_purchases relation/edge. + SharePurchasesTable = "share_purchases" + // SharePurchasesInverseTable is the table name for the SharePurchase entity. + // It exists in this package in order to avoid circular dependency with the "sharepurchase" package. + SharePurchasesInverseTable = "share_purchases" + // SharePurchasesColumn is the table column denoting the share_purchases relation/edge. + SharePurchasesColumn = "buyer_id" ) // Columns holds all SQL columns for user fields. @@ -489,6 +498,20 @@ func ByGrants(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { sqlgraph.OrderByNeighborTerms(s, newGrantsStep(), append([]sql.OrderTerm{term}, terms...)...) } } + +// BySharePurchasesCount orders the results by share_purchases count. +func BySharePurchasesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newSharePurchasesStep(), opts...) + } +} + +// BySharePurchases orders the results by share_purchases terms. +func BySharePurchases(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newSharePurchasesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newGroupStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -573,3 +596,10 @@ func newGrantsStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, GrantsTable, GrantsColumn), ) } +func newSharePurchasesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(SharePurchasesInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, SharePurchasesTable, SharePurchasesColumn), + ) +} diff --git a/ent/user/where.go b/ent/user/where.go index 59695b10..f908fc65 100644 --- a/ent/user/where.go +++ b/ent/user/where.go @@ -1191,6 +1191,29 @@ func HasGrantsWith(preds ...predicate.UserGrant) predicate.User { }) } +// HasSharePurchases applies the HasEdge predicate on the "share_purchases" edge. +func HasSharePurchases() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, SharePurchasesTable, SharePurchasesColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasSharePurchasesWith applies the HasEdge predicate on the "share_purchases" edge with a given conditions (other predicates). +func HasSharePurchasesWith(preds ...predicate.SharePurchase) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newSharePurchasesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User(sql.AndPredicates(predicates...)) diff --git a/ent/user_create.go b/ent/user_create.go index 279ad510..76c00469 100644 --- a/ent/user_create.go +++ b/ent/user_create.go @@ -21,6 +21,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/oauthgrant" "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/ent/usergrant" @@ -403,6 +404,21 @@ func (uc *UserCreate) AddGrants(u ...*UserGrant) *UserCreate { return uc.AddGrantIDs(ids...) } +// AddSharePurchaseIDs adds the "share_purchases" edge to the SharePurchase entity by IDs. +func (uc *UserCreate) AddSharePurchaseIDs(ids ...int) *UserCreate { + uc.mutation.AddSharePurchaseIDs(ids...) + return uc +} + +// AddSharePurchases adds the "share_purchases" edges to the SharePurchase entity. +func (uc *UserCreate) AddSharePurchases(s ...*SharePurchase) *UserCreate { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return uc.AddSharePurchaseIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uc *UserCreate) Mutation() *UserMutation { return uc.mutation @@ -804,6 +820,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := uc.mutation.SharePurchasesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.SharePurchasesTable, + Columns: []string{user.SharePurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/ent/user_query.go b/ent/user_query.go index 745e5735..a6c45738 100644 --- a/ent/user_query.go +++ b/ent/user_query.go @@ -22,6 +22,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/ent/usergrant" @@ -30,22 +31,23 @@ import ( // UserQuery is the builder for querying User entities. type UserQuery struct { config - ctx *QueryContext - order []user.OrderOption - inters []Interceptor - predicates []predicate.User - withGroup *GroupQuery - withFiles *FileQuery - withDavAccounts *DavAccountQuery - withShares *ShareQuery - withPasskey *PasskeyQuery - withTasks *TaskQuery - withFsevents *FsEventQuery - withEntities *EntityQuery - withOauthGrants *OAuthGrantQuery - withCreditTxns *CreditTxnQuery - withRedeemedCodes *GiftCodeQuery - withGrants *UserGrantQuery + ctx *QueryContext + order []user.OrderOption + inters []Interceptor + predicates []predicate.User + withGroup *GroupQuery + withFiles *FileQuery + withDavAccounts *DavAccountQuery + withShares *ShareQuery + withPasskey *PasskeyQuery + withTasks *TaskQuery + withFsevents *FsEventQuery + withEntities *EntityQuery + withOauthGrants *OAuthGrantQuery + withCreditTxns *CreditTxnQuery + withRedeemedCodes *GiftCodeQuery + withGrants *UserGrantQuery + withSharePurchases *SharePurchaseQuery // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -346,6 +348,28 @@ func (uq *UserQuery) QueryGrants() *UserGrantQuery { return query } +// QuerySharePurchases chains the current query on the "share_purchases" edge. +func (uq *UserQuery) QuerySharePurchases() *SharePurchaseQuery { + query := (&SharePurchaseClient{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(sharepurchase.Table, sharepurchase.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.SharePurchasesTable, user.SharePurchasesColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first User entity from the query. // Returns a *NotFoundError when no User was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { @@ -533,23 +557,24 @@ func (uq *UserQuery) Clone() *UserQuery { return nil } return &UserQuery{ - config: uq.config, - ctx: uq.ctx.Clone(), - order: append([]user.OrderOption{}, uq.order...), - inters: append([]Interceptor{}, uq.inters...), - predicates: append([]predicate.User{}, uq.predicates...), - withGroup: uq.withGroup.Clone(), - withFiles: uq.withFiles.Clone(), - withDavAccounts: uq.withDavAccounts.Clone(), - withShares: uq.withShares.Clone(), - withPasskey: uq.withPasskey.Clone(), - withTasks: uq.withTasks.Clone(), - withFsevents: uq.withFsevents.Clone(), - withEntities: uq.withEntities.Clone(), - withOauthGrants: uq.withOauthGrants.Clone(), - withCreditTxns: uq.withCreditTxns.Clone(), - withRedeemedCodes: uq.withRedeemedCodes.Clone(), - withGrants: uq.withGrants.Clone(), + config: uq.config, + ctx: uq.ctx.Clone(), + order: append([]user.OrderOption{}, uq.order...), + inters: append([]Interceptor{}, uq.inters...), + predicates: append([]predicate.User{}, uq.predicates...), + withGroup: uq.withGroup.Clone(), + withFiles: uq.withFiles.Clone(), + withDavAccounts: uq.withDavAccounts.Clone(), + withShares: uq.withShares.Clone(), + withPasskey: uq.withPasskey.Clone(), + withTasks: uq.withTasks.Clone(), + withFsevents: uq.withFsevents.Clone(), + withEntities: uq.withEntities.Clone(), + withOauthGrants: uq.withOauthGrants.Clone(), + withCreditTxns: uq.withCreditTxns.Clone(), + withRedeemedCodes: uq.withRedeemedCodes.Clone(), + withGrants: uq.withGrants.Clone(), + withSharePurchases: uq.withSharePurchases.Clone(), // clone intermediate query. sql: uq.sql.Clone(), path: uq.path, @@ -688,6 +713,17 @@ func (uq *UserQuery) WithGrants(opts ...func(*UserGrantQuery)) *UserQuery { return uq } +// WithSharePurchases tells the query-builder to eager-load the nodes that are connected to +// the "share_purchases" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithSharePurchases(opts ...func(*SharePurchaseQuery)) *UserQuery { + query := (&SharePurchaseClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withSharePurchases = query + return uq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -766,7 +802,7 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e var ( nodes = []*User{} _spec = uq.querySpec() - loadedTypes = [12]bool{ + loadedTypes = [13]bool{ uq.withGroup != nil, uq.withFiles != nil, uq.withDavAccounts != nil, @@ -779,6 +815,7 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e uq.withCreditTxns != nil, uq.withRedeemedCodes != nil, uq.withGrants != nil, + uq.withSharePurchases != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -882,6 +919,13 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e return nil, err } } + if query := uq.withSharePurchases; query != nil { + if err := uq.loadSharePurchases(ctx, query, nodes, + func(n *User) { n.Edges.SharePurchases = []*SharePurchase{} }, + func(n *User, e *SharePurchase) { n.Edges.SharePurchases = append(n.Edges.SharePurchases, e) }); err != nil { + return nil, err + } + } return nodes, nil } @@ -1245,6 +1289,36 @@ func (uq *UserQuery) loadGrants(ctx context.Context, query *UserGrantQuery, node } return nil } +func (uq *UserQuery) loadSharePurchases(ctx context.Context, query *SharePurchaseQuery, nodes []*User, init func(*User), assign func(*User, *SharePurchase)) 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(sharepurchase.FieldBuyerID) + } + query.Where(predicate.SharePurchase(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(user.SharePurchasesColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.BuyerID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "buyer_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { _spec := uq.querySpec() diff --git a/ent/user_update.go b/ent/user_update.go index 6b907a10..f395fdb6 100644 --- a/ent/user_update.go +++ b/ent/user_update.go @@ -22,6 +22,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/passkey" "github.com/cloudreve/Cloudreve/v4/ent/predicate" "github.com/cloudreve/Cloudreve/v4/ent/share" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/ent/usergrant" @@ -473,6 +474,21 @@ func (uu *UserUpdate) AddGrants(u ...*UserGrant) *UserUpdate { return uu.AddGrantIDs(ids...) } +// AddSharePurchaseIDs adds the "share_purchases" edge to the SharePurchase entity by IDs. +func (uu *UserUpdate) AddSharePurchaseIDs(ids ...int) *UserUpdate { + uu.mutation.AddSharePurchaseIDs(ids...) + return uu +} + +// AddSharePurchases adds the "share_purchases" edges to the SharePurchase entity. +func (uu *UserUpdate) AddSharePurchases(s ...*SharePurchase) *UserUpdate { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return uu.AddSharePurchaseIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uu *UserUpdate) Mutation() *UserMutation { return uu.mutation @@ -715,6 +731,27 @@ func (uu *UserUpdate) RemoveGrants(u ...*UserGrant) *UserUpdate { return uu.RemoveGrantIDs(ids...) } +// ClearSharePurchases clears all "share_purchases" edges to the SharePurchase entity. +func (uu *UserUpdate) ClearSharePurchases() *UserUpdate { + uu.mutation.ClearSharePurchases() + return uu +} + +// RemoveSharePurchaseIDs removes the "share_purchases" edge to SharePurchase entities by IDs. +func (uu *UserUpdate) RemoveSharePurchaseIDs(ids ...int) *UserUpdate { + uu.mutation.RemoveSharePurchaseIDs(ids...) + return uu +} + +// RemoveSharePurchases removes "share_purchases" edges to SharePurchase entities. +func (uu *UserUpdate) RemoveSharePurchases(s ...*SharePurchase) *UserUpdate { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return uu.RemoveSharePurchaseIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { if err := uu.defaults(); err != nil { @@ -1388,6 +1425,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uu.mutation.SharePurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.SharePurchasesTable, + Columns: []string{user.SharePurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedSharePurchasesIDs(); len(nodes) > 0 && !uu.mutation.SharePurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.SharePurchasesTable, + Columns: []string{user.SharePurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.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.SharePurchasesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.SharePurchasesTable, + Columns: []string{user.SharePurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{user.Label} @@ -1840,6 +1922,21 @@ func (uuo *UserUpdateOne) AddGrants(u ...*UserGrant) *UserUpdateOne { return uuo.AddGrantIDs(ids...) } +// AddSharePurchaseIDs adds the "share_purchases" edge to the SharePurchase entity by IDs. +func (uuo *UserUpdateOne) AddSharePurchaseIDs(ids ...int) *UserUpdateOne { + uuo.mutation.AddSharePurchaseIDs(ids...) + return uuo +} + +// AddSharePurchases adds the "share_purchases" edges to the SharePurchase entity. +func (uuo *UserUpdateOne) AddSharePurchases(s ...*SharePurchase) *UserUpdateOne { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return uuo.AddSharePurchaseIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uuo *UserUpdateOne) Mutation() *UserMutation { return uuo.mutation @@ -2082,6 +2179,27 @@ func (uuo *UserUpdateOne) RemoveGrants(u ...*UserGrant) *UserUpdateOne { return uuo.RemoveGrantIDs(ids...) } +// ClearSharePurchases clears all "share_purchases" edges to the SharePurchase entity. +func (uuo *UserUpdateOne) ClearSharePurchases() *UserUpdateOne { + uuo.mutation.ClearSharePurchases() + return uuo +} + +// RemoveSharePurchaseIDs removes the "share_purchases" edge to SharePurchase entities by IDs. +func (uuo *UserUpdateOne) RemoveSharePurchaseIDs(ids ...int) *UserUpdateOne { + uuo.mutation.RemoveSharePurchaseIDs(ids...) + return uuo +} + +// RemoveSharePurchases removes "share_purchases" edges to SharePurchase entities. +func (uuo *UserUpdateOne) RemoveSharePurchases(s ...*SharePurchase) *UserUpdateOne { + ids := make([]int, len(s)) + for i := range s { + ids[i] = s[i].ID + } + return uuo.RemoveSharePurchaseIDs(ids...) +} + // Where appends a list predicates to the UserUpdate builder. func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne { uuo.mutation.Where(ps...) @@ -2785,6 +2903,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uuo.mutation.SharePurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.SharePurchasesTable, + Columns: []string{user.SharePurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedSharePurchasesIDs(); len(nodes) > 0 && !uuo.mutation.SharePurchasesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.SharePurchasesTable, + Columns: []string{user.SharePurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.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.SharePurchasesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.SharePurchasesTable, + Columns: []string{user.SharePurchasesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(sharepurchase.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &User{config: uuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/frontend/public/locales/en-US/application.json b/frontend/public/locales/en-US/application.json index 258ee73e..246ea265 100644 --- a/frontend/public/locales/en-US/application.json +++ b/frontend/public/locales/en-US/application.json @@ -502,6 +502,10 @@ "policyGroupDefault": "Group default" }, "modals": { + "paidShare": "Paid share", + "paidSharePrice": "{{price}} points", + "paidShareDes": "Visitors must purchase this share with credit points before downloading. Set 0 for a free share.", + "paidSharePriceLabel": "Price (points)", "includePasswordInShareLink": "Include password in share link", "includePasswordInShareLinkDes": "If selected, password will be included in the share link, and no password is required when accessing the share link.", "showFileName": "Show file name", @@ -806,7 +810,11 @@ "preview": "Preview", "incorrectPassword": "Password incorrect.", "shareNotExist": "Share link is invalid or expired.", - "copyLinkToClipboard": "Copy link to clipboard" + "copyLinkToClipboard": "Copy link to clipboard", + "paidShareDes": "This is a paid share. Purchasing costs {{price}} points and unlocks downloads.", + "purchaseForPoints": "Purchase for {{price}} pts", + "signInToPurchase": "Sign in to purchase", + "yourBalance": "Balance: {{credits}} points" }, "download": { "noFilesFound": "No files found", diff --git a/frontend/public/locales/en-US/dashboard.json b/frontend/public/locales/en-US/dashboard.json index 151d2296..83b9e2ae 100644 --- a/frontend/public/locales/en-US/dashboard.json +++ b/frontend/public/locales/en-US/dashboard.json @@ -1316,6 +1316,8 @@ "allowCreateShareLinkDes": "If disabled, users cannot create sharing links.", "shareFree": "Free share link", "shareFreeDes": "When enabled, users can access all paid sharing links without purchasing.", + "shareSell": "Sell share links", + "shareSellDes": "When enabled, users can set a points price on share links they create.", "fileManagement": "File management", "allowWabDAV": "WebDAV", "allowWabDAVDes": "If disabled, users cannot connect to the storage via the WebDAV protocol", diff --git a/frontend/public/locales/zh-CN/application.json b/frontend/public/locales/zh-CN/application.json index eca26b8b..65812cea 100644 --- a/frontend/public/locales/zh-CN/application.json +++ b/frontend/public/locales/zh-CN/application.json @@ -502,6 +502,10 @@ "policyGroupDefault": "用户组默认" }, "modals": { + "paidShare": "付费分享", + "paidSharePrice": "{{price}} 积分", + "paidShareDes": "访客需使用积分购买此分享后才能下载。设置为 0 表示免费分享。", + "paidSharePriceLabel": "价格(积分)", "includePasswordInShareLink": "在链接中包含密码", "includePasswordInShareLinkDes": "勾选后,分享链接中会包含密码,通过此链接访问时不需要再输入密码。", "showFileName": "显示文件名", @@ -806,7 +810,11 @@ "preview": "预览", "incorrectPassword": "密码不正确", "shareNotExist": "分享不存在或已过期", - "copyLinkToClipboard": "复制链接到剪切板" + "copyLinkToClipboard": "复制链接到剪切板", + "paidShareDes": "此分享为付费分享,需支付 {{price}} 积分购买后才能下载。", + "purchaseForPoints": "支付 {{price}} 积分购买", + "signInToPurchase": "登录后购买", + "yourBalance": "余额:{{credits}} 积分" }, "download": { "noFilesFound": "没有找到任何文件", diff --git a/frontend/public/locales/zh-CN/dashboard.json b/frontend/public/locales/zh-CN/dashboard.json index a0beaa07..2f2477fb 100644 --- a/frontend/public/locales/zh-CN/dashboard.json +++ b/frontend/public/locales/zh-CN/dashboard.json @@ -1316,6 +1316,8 @@ "allowCreateShareLinkDes": "关闭后,用户无法创建分享链接。", "shareFree": "无需购买分享链接", "shareFreeDes": "开启后,用户无需购买即可访问所有付费分享链接。", + "shareSell": "出售分享链接", + "shareSellDes": "开启后,用户可以为其创建的分享链接设置积分价格。", "fileManagement": "文件管理", "allowWabDAV": "WebDAV", "allowWabDAVDes": "关闭后,用户无法通过 WebDAV 协议连接至网盘。", diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index c0ca6969..71a32e44 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -120,6 +120,7 @@ import { User, UserSettings, } from "./user.ts"; +import CrUri, { Filesystem } from "../util/uri.ts"; import { ArchiveWorkflowService, DownloadWorkflowService, @@ -319,11 +320,23 @@ export function getFileList(req: ListFileService, skipSnackbar = true): ThunkRes export function getFileThumb(path: string, contextHint?: string): ThunkResponse { return async (dispatch, _getState) => { + const params: Record = { uri: path }; + try { + const uri = new CrUri(path); + if (uri.fs() == Filesystem.share) { + const ticket = getSharePurchaseTicket(uri.id()); + if (ticket) { + params.purchase_ticket = ticket; + } + } + } catch { + // non-CrUri inputs fall through unchanged + } return await dispatch( send( "/file/thumb", { - params: { uri: path }, + params, method: "GET", headers: contextHint ? { @@ -675,6 +688,48 @@ export function sendDeleteShares(ids: string[]): ThunkResponse { }; } +const shareTicketKey = (shareId: string) => `cloudreve.share_ticket.${shareId}`; + +export const getSharePurchaseTicket = (shareId: string): string | undefined => { + try { + return localStorage.getItem(shareTicketKey(shareId)) ?? undefined; + } catch { + return undefined; + } +}; + +export const setSharePurchaseTicket = (shareId: string, ticket: string) => { + try { + localStorage.setItem(shareTicketKey(shareId), ticket); + } catch { + // storage unavailable; resume simply won't survive a reload + } +}; + +export interface SharePurchaseResponse { + ticket: string; +} + +export function purchaseShare(id: string): ThunkResponse { + return async (dispatch, _getState) => { + const res = await dispatch( + send( + "/share/purchase/" + id, + { + method: "POST", + }, + { + ...defaultOpts, + }, + ), + ); + if (res?.ticket) { + setSharePurchaseTicket(id, res.ticket); + } + return res; + }; +} + export function getShareInfo( id: string, password?: string, @@ -687,6 +742,10 @@ export function getShareInfo( if (password && password != "") { query.set("password", password); } + const purchaseTicket = getSharePurchaseTicket(id); + if (purchaseTicket) { + query.set("purchase_ticket", purchaseTicket); + } if (count_views) { query.set("count_views", "true"); } @@ -696,7 +755,7 @@ export function getShareInfo( if (query.toString() != "") { uri += "?" + query.toString(); } - return await dispatch( + const res = await dispatch( send( uri, { @@ -708,6 +767,12 @@ export function getShareInfo( }, ), ); + // Persist the server-issued resume ticket so entity/thumb downloads keep + // working for buyers who purchased on another device or browser. + if (res?.purchase_ticket) { + setSharePurchaseTicket(id, res.purchase_ticket); + } + return res; }; } @@ -730,6 +795,21 @@ export function sendCreateFile(req: CreateFileService): ThunkResponse { return async (dispatch, _getState) => { + // Attach the stored purchase ticket for share URIs so paid-share + // downloads resume without a session. + if (!req.purchase_ticket && req.uris.length > 0) { + try { + const uri = new CrUri(req.uris[0]); + if (uri.fs() == Filesystem.share) { + const ticket = getSharePurchaseTicket(uri.id()); + if (ticket) { + req = { ...req, purchase_ticket: ticket }; + } + } + } catch { + // non-CrUri inputs fall through unchanged + } + } return await dispatch( send( "/file/url", diff --git a/frontend/src/api/explorer.ts b/frontend/src/api/explorer.ts index add3c502..7f797bd2 100644 --- a/frontend/src/api/explorer.ts +++ b/frontend/src/api/explorer.ts @@ -95,6 +95,9 @@ export interface Share { preview_only?: boolean; upload_only?: boolean; note?: string; + price?: number; + paid?: boolean; + purchase_ticket?: string; } export enum PolicyType { @@ -347,6 +350,7 @@ export interface ShareCreateService { preview_only?: boolean; upload_only?: boolean; note?: string; + price_points?: number; } export interface CreateFileService { @@ -366,6 +370,7 @@ export interface FileURLService extends MultipleUriService { skip_error?: boolean; use_primary_site_url?: boolean; archive?: boolean; + purchase_ticket?: string; } export interface FileURLResponse { diff --git a/frontend/src/api/user.ts b/frontend/src/api/user.ts index 71589784..563e050a 100644 --- a/frontend/src/api/user.ts +++ b/frontend/src/api/user.ts @@ -93,6 +93,7 @@ export const GroupPermission = { archive_task: 5, webdav_proxy: 6, share_download: 7, + share_free: 8, remote_download: 9, redirected_source: 11, advance_delete: 12, @@ -109,6 +110,7 @@ export const GroupPermission = { admin_payment: 26, admin_events: 27, admin_reports: 28, + share_sell: 29, }; // Delegated admin section bits — is_admin implies all of them. diff --git a/frontend/src/component/Admin/Group/EditGroup/ShareSection.tsx b/frontend/src/component/Admin/Group/EditGroup/ShareSection.tsx index 60a42a24..7dc23a43 100644 --- a/frontend/src/component/Admin/Group/EditGroup/ShareSection.tsx +++ b/frontend/src/component/Admin/Group/EditGroup/ShareSection.tsx @@ -37,6 +37,26 @@ const ShareSection = () => { [setGroup], ); + const onShareSellChange = useCallback( + (e: React.ChangeEvent) => { + setGroup((p: GroupEnt) => ({ + ...p, + permissions: new Boolset(p.permissions).set(GroupPermission.share_sell, e.target.checked).toString(), + })); + }, + [setGroup], + ); + + const onShareFreeChange = useCallback( + (e: React.ChangeEvent) => { + setGroup((p: GroupEnt) => ({ + ...p, + permissions: new Boolset(p.permissions).set(GroupPermission.share_free, e.target.checked).toString(), + })); + }, + [setGroup], + ); + const onSetExplicitUserChange = useCallback( (e: React.ChangeEvent) => { setGroup((p: GroupEnt) => ({ @@ -68,10 +88,25 @@ const ShareSection = () => { )} + {values?.id != AnonymousGroupID && ( + + + + } + label={t("group.shareSell")} + /> + {t("group.shareSellDes")} + + + )} } + control={ + + } label={ {t("group.shareFree")} diff --git a/frontend/src/component/Admin/Settings/VAS/VAS.tsx b/frontend/src/component/Admin/Settings/VAS/VAS.tsx index ac6da986..f7159e6d 100644 --- a/frontend/src/component/Admin/Settings/VAS/VAS.tsx +++ b/frontend/src/component/Admin/Settings/VAS/VAS.tsx @@ -22,7 +22,7 @@ import PaymentProviders from "./PaymentProviders.tsx"; import SkuTable from "./SkuTable.tsx"; const VAS = () => { const { t } = useTranslation("dashboard"); - const { formRef } = useContext(SettingContext); + const { formRef, setSettings, values } = useContext(SettingContext); const currencyPopupState = usePopupState({ variant: "popover", popupId: "currencySelector", @@ -52,7 +52,15 @@ const VAS = () => { - + { + const v = Math.max(0, Math.min(100, Math.floor(Number(e.target.value) || 0))); + setSettings({ share_score_rate: v.toString() }); + }} + /> {t("settings.shareScoreRateDes")} diff --git a/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx b/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx index 8a457921..33bd3b28 100644 --- a/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx +++ b/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx @@ -66,6 +66,7 @@ const shareToSetting = (share: ShareModel, t: TFunction): ShareSetting => { preview_only: share.preview_only, upload_only: share.upload_only, note: share.note, + price_points: share.price && share.price > 0 ? share.price : undefined, downloads: share.remain_downloads != undefined && share.remain_downloads > 0, expires_val: expireOptions[2], diff --git a/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx b/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx index 1c99e2d7..3dba2788 100644 --- a/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx +++ b/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx @@ -24,6 +24,7 @@ import { Code } from "../../../Common/Code.tsx"; import { FilledTextField, SmallFormControlLabel } from "../../../Common/StyledComponents.tsx"; import BookInformation from "../../../Icons/BookInformation.tsx"; import ClockArrowDownload from "../../../Icons/ClockArrowDownload.tsx"; +import CoinStack from "../../../Icons/CoinStack.tsx"; import Edit from "../../../Icons/Edit.tsx"; import Eye from "../../../Icons/Eye.tsx"; import EyeOff from "../../../Icons/EyeOff.tsx"; @@ -91,6 +92,7 @@ export interface ShareSetting { note?: string; downloads?: boolean; expires?: boolean; + price_points?: number; downloads_val: valueOption; expires_val: valueOption; @@ -243,6 +245,40 @@ const ShareSettingContent = ({ setting, file, editing, onSettingChange }: ShareS + + + + + + + + + + + + {t("application:modals.paidShareDes")} + + + { + const v = Math.max(0, Math.floor(Number(e.target.value) || 0)); + onSettingChange({ ...setting, price_points: v > 0 ? v : undefined }); + }} + /> + + + diff --git a/frontend/src/component/FileManager/Explorer/Explorer.tsx b/frontend/src/component/FileManager/Explorer/Explorer.tsx index dc11cd39..5c30b751 100644 --- a/frontend/src/component/FileManager/Explorer/Explorer.tsx +++ b/frontend/src/component/FileManager/Explorer/Explorer.tsx @@ -1,19 +1,22 @@ import { Box, useMediaQuery, useTheme } from "@mui/material"; -import React, { RefCallback, useCallback, useContext, useEffect, useMemo } from "react"; +import React, { RefCallback, useCallback, useContext, useEffect, useMemo, useState } from "react"; import { CSSTransition, SwitchTransition } from "react-transition-group"; import { useAreaSelection } from "../../../hooks/areaSelection.ts"; import { useAppDispatch, useAppSelector } from "../../../redux/hooks.ts"; import { ConfigLoadState } from "../../../redux/siteConfigSlice.ts"; import { openEmptyContextMenu } from "../../../redux/thunks/filemanager.ts"; import { loadSiteConfig } from "../../../redux/thunks/site.ts"; +import { queueLoadShareInfo } from "../../../redux/thunks/share.ts"; +import { Share } from "../../../api/explorer.ts"; import CircularProgress from "../../Common/CircularProgress.tsx"; import "../../Common/FadeTransition.css"; import { RadiusFrame } from "../../Frame/RadiusFrame.tsx"; +import PaidShareGate from "../PaidShareGate.tsx"; import ExplorerError from "./ExplorerError.tsx"; import GridView, { FmFile } from "./GridView/GridView.tsx"; import { Layouts } from "../../../redux/fileManagerSlice.ts"; -import { SearchParam } from "../../../util/uri.ts"; +import CrUri, { Filesystem, SearchParam } from "../../../util/uri.ts"; import { FileManagerIndex } from "../FileManager.tsx"; import { FmIndexContext } from "../FmIndexContext.tsx"; import EmptyFileList, { SearchLimitReached } from "./EmptyFileList.tsx"; @@ -58,6 +61,22 @@ const Explorer = () => { const files = useAppSelector((state) => state.fileManager[fmIndex].list?.files); const recursion_limit_reached = useAppSelector((state) => state.fileManager[fmIndex].list?.recursion_limit_reached); const layout = useAppSelector((state) => state.fileManager[fmIndex].layout); + const currentFs = useAppSelector((state) => state.fileManager[fmIndex].current_fs); + const purePath = useAppSelector((state) => state.fileManager[fmIndex].pure_path); + const [shareInfo, setShareInfo] = useState(null); + + useEffect(() => { + setShareInfo(null); + if (currentFs == Filesystem.share && purePath) { + try { + dispatch(queueLoadShareInfo(new CrUri(purePath))) + .then(setShareInfo) + .catch(() => setShareInfo(null)); + } catch { + setShareInfo(null); + } + } + }, [currentFs, purePath, dispatch]); const selectContainerRef = React.useRef(null); @@ -119,6 +138,11 @@ const Explorer = () => { onMouseUp={isMobile || isTouch ? undefined : handleMouseUp} onMouseMove={isMobile || isTouch ? undefined : handleMouseMove} > + {fmIndex == FileManagerIndex.main && shareInfo && (shareInfo.price ?? 0) > 0 && !shareInfo.paid && ( + + window.location.reload()} /> + + )} ) => { )} + window.location.reload()} /> ) => { {t("application:fileManager.save")} )} - {displayOpt.showOpen && file && ( + {displayOpt.showOpen && file && !(shareInfo.price && !shareInfo.paid) && ( dispatch(openViewers(0, file))} @@ -262,7 +264,7 @@ const SingleFileView = forwardRef((_props, ref: React.Ref) => { )} - {!shareInfo.preview_only && ( + {!shareInfo.preview_only && !(shareInfo.price && !shareInfo.paid) && ( diff --git a/frontend/src/component/FileManager/PaidShareGate.tsx b/frontend/src/component/FileManager/PaidShareGate.tsx new file mode 100644 index 00000000..9793bdb7 --- /dev/null +++ b/frontend/src/component/FileManager/PaidShareGate.tsx @@ -0,0 +1,89 @@ +import { Alert, Box, Button, Typography } from "@mui/material"; +import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import { getCredit, purchaseShare } from "../../api/api.ts"; +import { CreditInfo } from "../../api/user.ts"; +import { Share } from "../../api/explorer.ts"; +import { useAppDispatch } from "../../redux/hooks.ts"; +import SessionManager from "../../session/index.ts"; +import CoinStack from "../Icons/CoinStack.tsx"; + +interface PaidShareGateProps { + shareInfo: Share; + // Called after a successful purchase; the caller typically reloads state. + onPurchased?: () => void; +} + +// PaidShareGate renders the purchase CTA for a priced share the visitor has +// not paid for. Anonymous visitors get a sign-in prompt; buyers resume via +// the ticket stored by purchaseShare. +const PaidShareGate = ({ shareInfo, onPurchased }: PaidShareGateProps) => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const [loading, setLoading] = useState(false); + const [credit, setCredit] = useState(null); + + const user = SessionManager.currentLoginOrNull(); + const price = shareInfo.price ?? 0; + const paid = shareInfo.paid ?? false; + + useEffect(() => { + if (user && price > 0 && !paid) { + dispatch(getCredit()) + .then(setCredit) + .catch(() => setCredit(null)); + } + }, [user, price, paid, dispatch]); + + const purchase = useCallback(async () => { + setLoading(true); + try { + await dispatch(purchaseShare(shareInfo.id)); + onPurchased?.(); + } catch (_e) { + // snackbar already reported by the request layer + } finally { + setLoading(false); + } + }, [dispatch, shareInfo.id, onPurchased]); + + if (price <= 0 || paid || (user && shareInfo.owner.id == user.user.id)) { + return null; + } + + return ( + } + sx={{ alignItems: "center" }} + action={ + user ? ( + + ) : ( + + ) + } + > + + {t("application:share.paidShareDes", { price })} + {user && credit != null && ( + + {t("application:share.yourBalance", { credits: credit.credits })} + + )} + + + ); +}; + +export default PaidShareGate; diff --git a/frontend/src/redux/thunks/share.ts b/frontend/src/redux/thunks/share.ts index 05c526fe..600c4ca3 100644 --- a/frontend/src/redux/thunks/share.ts +++ b/frontend/src/redux/thunks/share.ts @@ -35,6 +35,7 @@ export function createOrUpdateShareLink( preview_only: setting.preview_only, upload_only: setting.upload_only, note: setting.note?.trim() || undefined, + price_points: setting.price_points ?? undefined, downloads: setting.downloads && setting.downloads_val.value > 0 ? setting.downloads_val.value : undefined, expire: setting.expires && setting.expires_val.value > 0 ? setting.expires_val.value : undefined, }; diff --git a/inventory/share.go b/inventory/share.go index 771493ae..f3b71108 100644 --- a/inventory/share.go +++ b/inventory/share.go @@ -69,6 +69,7 @@ type ( OwnerID int FileID int Props *types.ShareProps + PricePoints int } ListShareArgs struct { @@ -133,6 +134,7 @@ func (c *shareClient) Upsert(ctx context.Context, params *CreateShareParams) (*e createQuery.SetProps(params.Props) } + createQuery.SetPricePoints(params.PricePoints) return createQuery.Save(ctx) } @@ -152,6 +154,9 @@ func (c *shareClient) Upsert(ctx context.Context, params *CreateShareParams) (*e if params.Props != nil { query.SetProps(params.Props) } + if params.PricePoints > 0 { + query.SetPricePoints(params.PricePoints) + } return query.Save(ctx) } diff --git a/inventory/types/types.go b/inventory/types/types.go index 4aa23e9a..a3204be8 100644 --- a/inventory/types/types.go +++ b/inventory/types/types.go @@ -324,7 +324,9 @@ const ( GroupPermissionArchiveTask GroupPermissionWebDAVProxy GroupPermissionShareDownload - GroupPermission_CommunityPlaceholder1 + // GroupPermissionShareFree lets members access paid shares without + // purchasing (e.g. staff or VIP groups). + GroupPermissionShareFree GroupPermissionRemoteDownload GroupPermission_CommunityPlaceholder2 GroupPermissionRedirectedSource // not used @@ -354,6 +356,9 @@ const ( GroupPermissionAdminPayment GroupPermissionAdminEvents GroupPermissionAdminReports + // GroupPermissionShareSell allows members to set a points price on + // shares they create (paid shares). + GroupPermissionShareSell ) // AclPermission is a bit position in an ACL entry's permission bitmask. diff --git a/inventory/vas.go b/inventory/vas.go index 69a9d751..d875e664 100644 --- a/inventory/vas.go +++ b/inventory/vas.go @@ -10,6 +10,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/credittxn" "github.com/cloudreve/Cloudreve/v4/ent/giftcode" "github.com/cloudreve/Cloudreve/v4/ent/schema" + "github.com/cloudreve/Cloudreve/v4/ent/sharepurchase" "github.com/cloudreve/Cloudreve/v4/ent/sku" "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/ent/usergrant" @@ -66,6 +67,17 @@ type ( // grant. Fails with ErrInsufficientPoints when the balance cannot // cover the price, leaving the grant unapplied. PurchaseSku(ctx context.Context, userID int, sku *ent.Sku) error + // PurchaseShare atomically debits the share's points price from the + // buyer, credits the owner income (price times scoreRate fraction), + // and records the purchase row. Idempotent: an existing purchase for + // the same (share, buyer) is returned without debiting again. + PurchaseShare(ctx context.Context, share *ent.Share, buyerID int, scoreRate float64) (*ent.SharePurchase, error) + // SharePurchase returns the purchase row for (share, buyer), or + // ent.NotFound when the buyer has not purchased. + SharePurchase(ctx context.Context, shareID, buyerID int) (*ent.SharePurchase, error) + // SharePurchaseByTicket resolves a resume ticket to its purchase row, + // scoped to the given share so tickets cannot cross shares. + SharePurchaseByTicket(ctx context.Context, shareID int, ticket string) (*ent.SharePurchase, error) } CreateGiftCodeParams struct { @@ -417,6 +429,76 @@ func (c *vasClient) PurchaseSku(ctx context.Context, userID int, s *ent.Sku) err return Commit(tx) } +func (c *vasClient) PurchaseShare(ctx context.Context, s *ent.Share, buyerID int, scoreRate float64) (*ent.SharePurchase, error) { + if existing, err := c.SharePurchase(ctx, s.ID, buyerID); err == nil { + return existing, nil + } else if !ent.IsNotFound(err) { + return nil, err + } + + price := int64(s.PricePoints) + if scoreRate < 0 { + scoreRate = 0 + } + if scoreRate > 1 { + scoreRate = 1 + } + income := int64(float64(price) * scoreRate) + + txVc, tx, txCtx, err := WithTx(ctx, c) + if err != nil { + return nil, err + } + + if err := txVc.CreditAdjust(txCtx, buyerID, -price, credittxn.TypePurchase, + strconv.Itoa(s.ID), "share purchase"); err != nil { + _ = Rollback(tx) + return nil, err + } + + purchase, err := txVc.client.SharePurchase.Create(). + SetShareID(s.ID). + SetBuyerID(buyerID). + SetPoints(s.PricePoints). + SetTicket(newGiftCodeString()). + Save(txCtx) + if err != nil { + _ = Rollback(tx) + // Concurrent buyer already committed — their purchase wins. + if existing, qErr := c.SharePurchase(ctx, s.ID, buyerID); qErr == nil { + return existing, nil + } + return nil, err + } + + if income > 0 && s.Edges.User != nil && s.Edges.User.ID != buyerID { + if err := txVc.CreditAdjust(txCtx, s.Edges.User.ID, income, credittxn.TypeShareIncome, + strconv.Itoa(s.ID), "share sale"); err != nil { + return nil, Rollback(tx) + } + } + + if err := Commit(tx); err != nil { + return nil, err + } + return purchase, nil +} + +func (c *vasClient) SharePurchase(ctx context.Context, shareID, buyerID int) (*ent.SharePurchase, error) { + return c.client.SharePurchase.Query(). + Where(sharepurchase.ShareID(shareID), sharepurchase.BuyerID(buyerID)). + Only(ctx) +} + +func (c *vasClient) SharePurchaseByTicket(ctx context.Context, shareID int, ticket string) (*ent.SharePurchase, error) { + if ticket == "" { + return nil, &ent.NotFoundError{} + } + return c.client.SharePurchase.Query(). + Where(sharepurchase.ShareID(shareID), sharepurchase.Ticket(ticket)). + Only(ctx) +} + func newGiftCodeString() string { return uuid.Must(uuid.NewV4()).String() } diff --git a/inventory/vas_test.go b/inventory/vas_test.go index 7d809b83..837a09b6 100644 --- a/inventory/vas_test.go +++ b/inventory/vas_test.go @@ -2,6 +2,7 @@ package inventory import ( "context" + "fmt" "sync" "testing" "time" @@ -12,6 +13,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/giftcode" "github.com/cloudreve/Cloudreve/v4/ent/sku" "github.com/cloudreve/Cloudreve/v4/ent/usergrant" + "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" "github.com/stretchr/testify/require" ) @@ -239,3 +241,85 @@ func TestPurchaseSku(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(1024), bonus) } + +func paidShareFixture(t *testing.T, client *ent.Client, price int) (*ent.User, *ent.User, *ent.Share) { + return paidShareFixtureN(t, client, price, 0) +} + +func paidShareFixtureN(t *testing.T, client *ent.Client, price, n int) (*ent.User, *ent.User, *ent.Share) { + ctx := context.Background() + suffix := fmt.Sprintf("%s-%d", t.Name(), n) + group := client.Group.Create().SetName("g" + suffix).SetPermissions(&boolset.BooleanSet{}).SaveX(ctx) + owner := client.User.Create().SetEmail("owner-" + suffix + "@example.com").SetNick("o" + suffix).SetGroup(group).SaveX(ctx) + buyer := client.User.Create().SetEmail("buyer-" + suffix + "@example.com").SetNick("b" + suffix).SetGroup(group).SaveX(ctx) + root := client.File.Create().SetName(RootFolderName).SetType(int(types.FileTypeFolder)).SetOwner(owner).SaveX(ctx) + file := client.File.Create().SetName("paid" + suffix + ".txt").SetType(int(types.FileTypeFile)).SetOwner(owner).SetParent(root).SaveX(ctx) + share := client.Share.Create().SetUser(owner).SetFile(file).SetPricePoints(price).SaveX(ctx) + share.Edges.User = owner + return owner, buyer, share +} + +func TestPurchaseShare(t *testing.T) { + ctx := context.Background() + client, c := newVasClient(t) + owner, buyer, share := paidShareFixture(t, client, 100) + require.NoError(t, c.CreditAdjust(ctx, buyer.ID, 250, credittxn.TypeAdjust, "", "grant")) + + // 80% commission: buyer pays 100, owner earns 80. + purchase, err := c.PurchaseShare(ctx, share, buyer.ID, 0.8) + require.NoError(t, err) + require.NotEmpty(t, purchase.Ticket) + require.Equal(t, 100, purchase.Points) + require.Equal(t, int64(150), client.User.GetX(ctx, buyer.ID).Credits) + require.Equal(t, int64(80), client.User.GetX(ctx, owner.ID).Credits) + + // Idempotent: second purchase returns the same row without re-debiting. + again, err := c.PurchaseShare(ctx, share, buyer.ID, 0.8) + require.NoError(t, err) + require.Equal(t, purchase.ID, again.ID) + require.Equal(t, purchase.Ticket, again.Ticket) + require.Equal(t, int64(150), client.User.GetX(ctx, buyer.ID).Credits) + + // Ticket is scoped to its own share. + _, err = c.SharePurchaseByTicket(ctx, share.ID, purchase.Ticket) + require.NoError(t, err) + _, _, otherShare := paidShareFixtureN(t, client, 100, 1) + _, err = c.SharePurchaseByTicket(ctx, otherShare.ID, purchase.Ticket) + require.Error(t, err) + _, err = c.SharePurchaseByTicket(ctx, share.ID, "bogus-ticket") + require.Error(t, err) +} + +func TestPurchaseShareInsufficient(t *testing.T) { + ctx := context.Background() + client, c := newVasClient(t) + owner, buyer, share := paidShareFixture(t, client, 100) + require.NoError(t, c.CreditAdjust(ctx, buyer.ID, 50, credittxn.TypeAdjust, "", "grant")) + + _, err := c.PurchaseShare(ctx, share, buyer.ID, 1) + require.ErrorIs(t, err, ErrInsufficientPoints) + require.Equal(t, int64(50), client.User.GetX(ctx, buyer.ID).Credits) + require.Equal(t, int64(0), client.User.GetX(ctx, owner.ID).Credits) + _, err = c.SharePurchase(ctx, share.ID, buyer.ID) + require.Error(t, err) +} + +func TestPurchaseShareIncomeRates(t *testing.T) { + ctx := context.Background() + client, c := newVasClient(t) + + // Zero commission: owner earns nothing, purchase still recorded. + owner, buyer, share := paidShareFixture(t, client, 40) + require.NoError(t, c.CreditAdjust(ctx, buyer.ID, 100, credittxn.TypeAdjust, "", "grant")) + p, err := c.PurchaseShare(ctx, share, buyer.ID, 0) + require.NoError(t, err) + require.NotNil(t, p) + require.Equal(t, int64(0), client.User.GetX(ctx, owner.ID).Credits) + + // Rate above 1 clamps to full price. + owner2, buyer2, share2 := paidShareFixtureN(t, client, 30, 1) + require.NoError(t, c.CreditAdjust(ctx, buyer2.ID, 100, credittxn.TypeAdjust, "", "grant")) + _, err = c.PurchaseShare(ctx, share2, buyer2.ID, 1.5) + require.NoError(t, err) + require.Equal(t, int64(30), client.User.GetX(ctx, owner2.ID).Credits) +} diff --git a/pkg/filemanager/fs/dbfs/dbfs.go b/pkg/filemanager/fs/dbfs/dbfs.go index cd9cb638..bf2e0420 100644 --- a/pkg/filemanager/fs/dbfs/dbfs.go +++ b/pkg/filemanager/fs/dbfs/dbfs.go @@ -43,6 +43,9 @@ type ( // IsDownloadCtxKey marks the request as an explicit file download (as // opposed to an inline preview fetch). Navigator hooks consult it. IsDownloadCtxKey struct{} + // PurchaseTicketCtxKey carries a paid-share resume ticket used to + // restore download access after session loss. + PurchaseTicketCtxKey struct{} // ExpectedSourceIDsCtxKey carries source-file identity preconditions // for move/copy/rename: a []int positionally aligned with the source // URI list. An entry of 0 disables the check for that position (#3565). @@ -947,7 +950,7 @@ func (f *DBFS) getNavigator(ctx context.Context, path *fs.URI, requiredCapabilit case constants.FileSystemMy: n = NewMyNavigator(f.user, f.fileClient, f.userClient, f.l, config, f.hasher) case constants.FileSystemShare: - n = NewShareNavigator(f.user, f.fileClient, f.shareClient, f.aclClient, f.l, config, f.hasher) + n = NewShareNavigator(f.user, f.fileClient, f.shareClient, f.aclClient, f.vasClient, f.l, config, f.hasher) case constants.FileSystemTrash: n = NewTrashNavigator(f.user, f.fileClient, f.l, config, f.hasher) case constants.FileSystemSharedWithMe: diff --git a/pkg/filemanager/fs/dbfs/share_navigator.go b/pkg/filemanager/fs/dbfs/share_navigator.go index 586f642d..962dd75b 100644 --- a/pkg/filemanager/fs/dbfs/share_navigator.go +++ b/pkg/filemanager/fs/dbfs/share_navigator.go @@ -38,13 +38,14 @@ var shareNavigatorCapability = &boolset.BooleanSet{} // NewShareNavigator creates a navigator for user's "shared" file system. func NewShareNavigator(u *ent.User, fileClient inventory.FileClient, shareClient inventory.ShareClient, - aclClient inventory.AclClient, l logging.Logger, config *setting.DBFS, hasher hashid.Encoder) Navigator { + aclClient inventory.AclClient, vasClient inventory.VasClient, l logging.Logger, config *setting.DBFS, hasher hashid.Encoder) Navigator { n := &shareNavigator{ user: u, l: l, fileClient: fileClient, shareClient: shareClient, aclClient: aclClient, + vasClient: vasClient, config: config, } n.baseNavigator = newBaseNavigator(fileClient, defaultFilter, u, hasher, config) @@ -70,8 +71,12 @@ type ( // shared file matching the acting user. nil means no entry matched — // capabilities then fall back to share props. aclCaps *boolset.BooleanSet + vasClient inventory.VasClient disableRecycle bool persist func() + // sharePaid is resolved at Root() and persisted with the navigator + // state: free shares, the owner, and buyers/ticket holders are true. + sharePaid bool } shareNavigatorState struct { @@ -81,6 +86,7 @@ type ( Share *ent.Share Owner *ent.User AclCaps *boolset.BooleanSet + SharePaid bool } ) @@ -94,6 +100,7 @@ func (n *shareNavigator) PersistState(kv cache.Driver, key string) { Share: n.share, Owner: n.owner, AclCaps: n.aclCaps, + SharePaid: n.sharePaid, }, ContextHintTTL) } } @@ -107,6 +114,7 @@ func (n *shareNavigator) RestoreState(s State) error { n.share = state.Share n.aclCaps = state.AclCaps n.owner = state.Owner + n.sharePaid = state.SharePaid return nil } @@ -150,6 +158,7 @@ func (n *shareNavigator) Root(ctx context.Context, path *fs.URI) (*File, error) // Share must be assigned before capabilities are derived from its props. n.share = share + n.sharePaid = n.checkSharePaid(ctx, share) // Resolve per-file ACL entries for non-owner visitors; matched rows // replace the share-props capability set for this user. @@ -316,7 +325,9 @@ func (n *shareNavigator) shareCapabilities() *boolset.BooleanSet { // Matched ACL entries fully define a non-owner visitor's capabilities; // when no entry matched (nil) share props apply as the link default. if n.aclCaps != nil && n.owner != nil && n.user.ID != n.owner.ID { - return aclPermsToCapabilities(n.aclCaps) + bs := aclPermsToCapabilities(n.aclCaps) + n.stripUnpaid(bs) + return bs } bs := &boolset.BooleanSet{} @@ -365,9 +376,19 @@ func (n *shareNavigator) shareCapabilities() *boolset.BooleanSet { boolset.Set(int(NavigatorCapabilitySoftDelete), true, bs) } + n.stripUnpaid(bs) return bs } +// stripUnpaid removes download/thumbnail capabilities for visitors who have +// not paid for a priced share; listing stays so the paywall can render. +func (n *shareNavigator) stripUnpaid(bs *boolset.BooleanSet) { + if n.share != nil && n.share.PricePoints > 0 && !n.sharePaid { + boolset.Set(int(NavigatorCapabilityDownloadFile), false, bs) + boolset.Set(int(NavigatorCapabilityGenerateThumb), false, bs) + } +} + // aclPermsToCapabilities maps ACL permission bits (read/create/update/delete) // to the navigator capability set granted through a share link. func aclPermsToCapabilities(perms *boolset.BooleanSet) *boolset.BooleanSet { @@ -401,7 +422,7 @@ func aclPermsToCapabilities(perms *boolset.BooleanSet) *boolset.BooleanSet { if perms.Enabled(int(types.AclPermDelete)) { boolset.Sets(map[NavigatorCapability]bool{ NavigatorCapabilityDeleteFile: true, - NavigatorCapabilitySoftDelete: true, + NavigatorCapabilitySoftDelete: true, }, bs) } return bs @@ -431,6 +452,11 @@ func (n *shareNavigator) FollowTx(ctx context.Context) (func(), error) { func (n *shareNavigator) ExecuteHook(ctx context.Context, hookType fs.HookType, file *File) error { switch hookType { case fs.HookTypeBeforeDownload: + // Priced shares deny every entity fetch — previews included — until + // the visitor holds a purchase or a valid resume ticket. + if n.share != nil && n.share.PricePoints > 0 && !n.sharePaid { + return ErrNotPurchased + } // Preview-only shares deny explicit downloads but still allow // entity fetches for inline viewers. if n.share != nil && n.share.Props != nil && n.share.Props.PreviewOnly { @@ -445,6 +471,36 @@ func (n *shareNavigator) ExecuteHook(ctx context.Context, hookType fs.HookType, return nil } +// checkSharePaid resolves whether the acting user may fetch entities of a +// priced share. Free shares, the owner, existing buyers, and holders of a +// valid resume ticket pass; everyone else — including anonymous users +// without a ticket — is denied. +func (n *shareNavigator) checkSharePaid(ctx context.Context, share *ent.Share) bool { + if share.PricePoints <= 0 || n.user.ID == share.Edges.User.ID { + return true + } + // Groups with the share-free bit (staff/VIP) bypass the paywall. + if n.user.Edges.Group != nil && + n.user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionShareFree)) { + return true + } + if n.vasClient == nil { + return false + } + if ticket, ok := ctx.Value(PurchaseTicketCtxKey{}).(string); ok && ticket != "" { + if _, err := n.vasClient.SharePurchaseByTicket(ctx, share.ID, ticket); err == nil { + return true + } + } + if inventory.IsAnonymousUser(n.user) { + return false + } + if _, err := n.vasClient.SharePurchase(ctx, share.ID, n.user.ID); err == nil { + return true + } + return false +} + func (n *shareNavigator) Walk(ctx context.Context, levelFiles []*File, limit, depth int, f WalkFunc) error { return n.baseNavigator.walk(ctx, levelFiles, limit, depth, f) } diff --git a/pkg/filemanager/manager/manager.go b/pkg/filemanager/manager/manager.go index 19e963bc..91a2bf28 100644 --- a/pkg/filemanager/manager/manager.go +++ b/pkg/filemanager/manager/manager.go @@ -128,6 +128,7 @@ type ( PreviewOnly bool UploadOnly bool Note string + PricePoints int } FullTextSearchResults struct { diff --git a/pkg/filemanager/manager/operation.go b/pkg/filemanager/manager/operation.go index 7e2ce99f..45a8fb33 100644 --- a/pkg/filemanager/manager/operation.go +++ b/pkg/filemanager/manager/operation.go @@ -366,6 +366,7 @@ func (l *manager) CreateOrUpdateShare(ctx context.Context, path *fs.URI, args *C RemainDownloads: args.RemainDownloads, Existed: existed, Props: props, + PricePoints: args.PricePoints, }) if err != nil { diff --git a/pkg/setting/provider.go b/pkg/setting/provider.go index cc09e3f7..1071ea25 100644 --- a/pkg/setting/provider.go +++ b/pkg/setting/provider.go @@ -268,6 +268,9 @@ type ( // Announcement returns the site-wide markdown announcement shown to // logged-in users. "" disables the announcement modal. Announcement(ctx context.Context) string + // ShareScoreRate returns the fraction (0..1) of a paid share's + // points price the owner receives as income. + ShareScoreRate(ctx context.Context) float64 } UseFirstSiteUrlCtxKey = struct{} ) @@ -932,6 +935,10 @@ func (s *settingProvider) AbuseCaptchaEnabled(ctx context.Context) bool { return s.getBoolean(ctx, "abuse_captcha", true) } +func (s *settingProvider) ShareScoreRate(ctx context.Context) float64 { + return s.getFloat64(ctx, "share_score_rate", 100) / 100 +} + func (s *settingProvider) AuthnEnabled(ctx context.Context) bool { return s.getBoolean(ctx, "authn_enabled", false) } diff --git a/routers/controllers/share.go b/routers/controllers/share.go index eac89258..e37445da 100644 --- a/routers/controllers/share.go +++ b/routers/controllers/share.go @@ -42,6 +42,17 @@ func GetShare(c *gin.Context) { c.JSON(200, serializer.Response{Data: info}) } +// PurchaseShare purchases a paid share with the caller's credit balance. +func PurchaseShare(c *gin.Context) { + service := ParametersFromContext[*share.SharePurchaseService](c, share.SharePurchaseParamCtx{}) + res, err := service.Purchase(c) + if respondErr(c, err) { + return + } + + c.JSON(200, serializer.Response{Data: res}) +} + // ListShare 列出分享 func ListShare(c *gin.Context) { service := ParametersFromContext[*share.ListShareService](c, share.ListShareParamCtx{}) diff --git a/routers/router.go b/routers/router.go index b237815e..293cbe45 100644 --- a/routers/router.go +++ b/routers/router.go @@ -939,6 +939,12 @@ func initMasterRouter(dep dependency.Dep) *gin.Engine { controllers.FromQuery[sharesvc.ShareInfoService](sharesvc.ShareInfoParamCtx{}), controllers.GetShare, ) + // Purchase a paid share with credits + share.POST("purchase/:id", + middleware.LoginRequired(), + middleware.HashID(hashid.ShareID), + controllers.PurchaseShare, + ) // List my shares share.GET("", middleware.LoginRequired(), diff --git a/routers/router_test.go b/routers/router_test.go index a8397dc5..5c0b6408 100644 --- a/routers/router_test.go +++ b/routers/router_test.go @@ -51,6 +51,7 @@ func TestMasterRouteWiring(t *testing.T) { "GET /api/v4/user/info/:id", "PUT /api/v4/file/upload", "POST /api/v4/file/upload/:sessionId/:index", + "POST /api/v4/share/purchase/:id", "GET /f/:id/:name", } for _, e := range expected { diff --git a/service/explorer/file.go b/service/explorer/file.go index 479c2f1e..c558b0b0 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -415,6 +415,8 @@ type ( SkipError bool `json:"skip_error"` Archive bool `json:"archive"` NoCache bool `json:"no_cache"` + // PurchaseTicket restores paid-share access after session loss. + PurchaseTicket string `json:"purchase_ticket"` } FileURLResponse struct { Urls []manager.EntityUrl `json:"urls"` @@ -503,6 +505,9 @@ func (s *FileURLService) Get(c *gin.Context) (*FileURLResponse, error) { if s.UsePrimarySiteURL { ctx = setting.UseFirstSiteUrl(ctx) } + if s.PurchaseTicket != "" { + ctx = context.WithValue(ctx, dbfs.PurchaseTicketCtxKey{}, s.PurchaseTicket) + } res, earliestExpire, err := m.GetEntityUrls(ctx, urlReq, fs.WithDownloadSpeed(int64(user.Edges.Group.SpeedLimit)), @@ -541,7 +546,8 @@ func (s *FileURLService) Get(c *gin.Context) (*FileURLResponse, error) { type ( FileThumbParameterCtx struct{} FileThumbService struct { - Uri string `form:"uri" binding:"required"` + Uri string `form:"uri" binding:"required"` + PurchaseTicket string `form:"purchase_ticket"` } FileThumbResponse struct { Url string `json:"url"` @@ -562,7 +568,11 @@ func (s *FileThumbService) Get(c *gin.Context) (*FileThumbResponse, error) { } // Get thumbnail - thumb, err := m.Thumbnail(c, uri) + var ctx context.Context = c + if s.PurchaseTicket != "" { + ctx = context.WithValue(c, dbfs.PurchaseTicketCtxKey{}, s.PurchaseTicket) + } + thumb, err := m.Thumbnail(ctx, uri) if err != nil { return nil, fmt.Errorf("failed to get thumbnail: %w", err) } diff --git a/service/explorer/response.go b/service/explorer/response.go index fd94a473..f5f1d951 100644 --- a/service/explorer/response.go +++ b/service/explorer/response.go @@ -346,6 +346,16 @@ type Share struct { // Only viewable if explicitly unlocked by owner SourceUri string `json:"source_uri,omitempty"` + + // Points price for paid shares. Always visible so visitors can render + // the purchase gate. + Price int `json:"price,omitempty"` + // Paid is true when the requester is the owner, has purchased the + // share, or presented a valid resume ticket. Omitted for free shares. + Paid *bool `json:"paid,omitempty"` + // PurchaseTicket is the requester's resume credential; only set for + // the purchasing user, never for the owner or other visitors. + PurchaseTicket string `json:"purchase_ticket,omitempty"` } func BuildShare(ctx context.Context, s *ent.Share, base *url.URL, hasher hashid.Encoder, requester *ent.User, owner *ent.User, @@ -365,6 +375,7 @@ func BuildShare(ctx context.Context, s *ent.Share, base *url.URL, hasher hashid. Visited: s.Views, SourceType: util.ToPtr(t), PasswordProtected: s.Password != "", + Price: s.PricePoints, } if unlocked { diff --git a/service/share/manage.go b/service/share/manage.go index 1e7482d2..a9b675cd 100644 --- a/service/share/manage.go +++ b/service/share/manage.go @@ -34,6 +34,8 @@ type ( UploadOnly bool `json:"upload_only"` // Optional owner-defined note shown on My Shares (#3570). Note string `json:"note" binding:"omitempty,max=255"` + // Points price visitors must pay before downloading. 0 = free share. + PricePoints int `json:"price_points" binding:"omitempty,min=0"` } ShareCreateParamCtx struct{} @@ -81,6 +83,10 @@ func (service *ShareCreateService) Upsert(c *gin.Context, existed int) (string, return "", serializer.NewError(serializer.CodeGroupNotAllowed, "Group permission denied", nil) } + if service.PricePoints > 0 && !user.Edges.Group.Permissions.Enabled(int(types.GroupPermissionShareSell)) { + return "", serializer.NewError(serializer.CodeGroupNotAllowed, "Group permission denied for paid share", nil) + } + uri, err := fs.NewUriFromString(service.Uri) if err != nil { return "", serializer.NewError(serializer.CodeParamErr, "unknown uri", err) @@ -105,6 +111,7 @@ func (service *ShareCreateService) Upsert(c *gin.Context, existed int) (string, PreviewOnly: service.PreviewOnly, UploadOnly: service.UploadOnly, Note: service.Note, + PricePoints: service.PricePoints, }) if err != nil { return "", err diff --git a/service/share/purchase.go b/service/share/purchase.go new file mode 100644 index 00000000..df16e197 --- /dev/null +++ b/service/share/purchase.go @@ -0,0 +1,68 @@ +package share + +import ( + "context" + "errors" + + "github.com/cloudreve/Cloudreve/v4/application/dependency" + "github.com/cloudreve/Cloudreve/v4/inventory" + "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/activity" + "github.com/cloudreve/Cloudreve/v4/pkg/hashid" + "github.com/cloudreve/Cloudreve/v4/pkg/serializer" + "github.com/gin-gonic/gin" +) + +type ( + // SharePurchaseService purchases a paid share with the caller's credit + // balance and returns the resume ticket. + SharePurchaseService struct{} + SharePurchaseParamCtx struct{} + SharePurchaseResponse struct { + Ticket string `json:"ticket"` + } +) + +func (s *SharePurchaseService) Purchase(c *gin.Context) (*SharePurchaseResponse, error) { + dep := dependency.FromContext(c) + u := inventory.UserFromContext(c) + + ctx := context.WithValue(c, inventory.LoadShareUser{}, true) + ctx = context.WithValue(ctx, inventory.LoadShareFile{}, true) + share, err := dep.ShareClient().GetByID(ctx, hashid.FromContext(c)) + if err != nil { + return nil, serializer.NewError(serializer.CodeNotFound, "Share not found", err) + } + + if err := inventory.IsValidShare(share); err != nil { + return nil, serializer.NewError(serializer.CodeNotFound, "Share link expired", err) + } + if share.PricePoints <= 0 { + return nil, serializer.NewError(serializer.CodeParamErr, "Share is free", nil) + } + if share.Edges.User.ID == u.ID { + return nil, serializer.NewError(serializer.CodeParamErr, "Cannot purchase own share", nil) + } + if !u.Edges.Group.Permissions.Enabled(int(types.GroupPermissionShareDownload)) { + return nil, serializer.NewError(serializer.CodeNoPermissionErr, "You don't have permission to access share links", nil) + } + + purchase, err := dep.VasClient().PurchaseShare(ctx, share, u.ID, dep.SettingProvider().ShareScoreRate(c)) + if err != nil { + switch { + case errors.Is(err, inventory.ErrInsufficientPoints): + return nil, serializer.NewError(serializer.CodeParamErr, "Insufficient credit balance", err) + default: + return nil, serializer.NewError(serializer.CodeDBError, "Failed to purchase share", err) + } + } + + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventPaymentFulfilled, + activity.Share(share.ID), activity.File(share.Edges.File.ID), + activity.Extra(map[string]any{ + "points": purchase.Points, + "seller": share.Edges.User.ID, + })) + + return &SharePurchaseResponse{Ticket: purchase.Ticket}, nil +} diff --git a/service/share/visit.go b/service/share/visit.go index 70a48696..1b253bcf 100644 --- a/service/share/visit.go +++ b/service/share/visit.go @@ -51,9 +51,10 @@ func (s *ShortLinkRedirectService) RedirectTo(c *gin.Context) string { type ( ShareInfoService struct { - Password string `form:"password"` - CountViews bool `form:"count_views"` - OwnerExtended bool `form:"owner_extended"` + Password string `form:"password"` + CountViews bool `form:"count_views"` + OwnerExtended bool `form:"owner_extended"` + PurchaseTicket string `form:"purchase_ticket"` } ShareInfoParamCtx struct{} ) @@ -93,6 +94,31 @@ func (s *ShareInfoService) Get(c *gin.Context) (*explorer.Share, error) { res := explorer.BuildShare(c, share, base, dep.HashIDEncoder(), u, share.Edges.User, share.Edges.File.Name, types.FileType(share.Edges.File.Type), unlocked, false) + // Priced shares resolve the requester's payment state: owner, existing + // buyer, or bearer of a valid resume ticket. + if share.PricePoints > 0 { + paid := share.Edges.User.ID == u.ID || + (u.Edges.Group != nil && u.Edges.Group.Permissions.Enabled(int(types.GroupPermissionShareFree))) + purchaseTicket := "" + if !paid { + vasClient := dep.VasClient() + if s.PurchaseTicket != "" { + if p, err := vasClient.SharePurchaseByTicket(ctx, share.ID, s.PurchaseTicket); err == nil { + paid = true + purchaseTicket = p.Ticket + } + } + if !paid && !inventory.IsAnonymousUser(u) { + if p, err := vasClient.SharePurchase(ctx, share.ID, u.ID); err == nil { + paid = true + purchaseTicket = p.Ticket + } + } + } + res.Paid = &paid + res.PurchaseTicket = purchaseTicket + } + if s.OwnerExtended && share.Edges.User.ID == u.ID { // Add more information about the shared file m := manager.NewFileManager(dep, u)