Init V4 community edition (#2265)

* Init V4 community edition

* Init V4 community edition
This commit is contained in:
AaronLiu
2025-04-20 17:31:25 +08:00
committed by GitHub
parent da4e44b77a
commit 21d158db07
597 changed files with 119415 additions and 41692 deletions

132
ent/schema/common.go Normal file
View File

@@ -0,0 +1,132 @@
package schema
import (
"context"
"fmt"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/mixin"
gen "github.com/cloudreve/Cloudreve/v4/ent"
"github.com/cloudreve/Cloudreve/v4/ent/hook"
"github.com/cloudreve/Cloudreve/v4/ent/intercept"
)
// CommonMixin implements the soft delete pattern for schemas and common audit features.
type CommonMixin struct {
mixin.Schema
}
// Fields of the CommonMixin.
func (CommonMixin) Fields() []ent.Field {
return commonFields()
}
type softDeleteKey struct{}
// SkipSoftDelete returns a new context that skips the soft-delete interceptor/mutators.
func SkipSoftDelete(parent context.Context) context.Context {
return context.WithValue(parent, softDeleteKey{}, true)
}
// Interceptors of the CommonMixin.
func (d CommonMixin) Interceptors() []ent.Interceptor {
return softDeleteInterceptors(d)
}
// Hooks of the CommonMixin.
func (d CommonMixin) Hooks() []ent.Hook {
return commonHooks(d)
}
// P adds a storage-level predicate to the queries and mutations.
func (d CommonMixin) P(w interface{ WhereP(...func(*sql.Selector)) }) {
p(d, w)
}
// Indexes of the CommonMixin.
func (CommonMixin) Indexes() []ent.Index {
return []ent.Index{}
}
func softDeleteInterceptors(d interface {
P(w interface {
WhereP(...func(*sql.Selector))
})
}) []ent.Interceptor {
return []ent.Interceptor{
intercept.TraverseFunc(func(ctx context.Context, q intercept.Query) error {
// Skip soft-delete, means include soft-deleted entities.
if skip, _ := ctx.Value(softDeleteKey{}).(bool); skip {
return nil
}
d.P(q)
return nil
}),
}
}
func p(d interface{ Fields() []ent.Field }, w interface{ WhereP(...func(*sql.Selector)) }) {
w.WhereP(
sql.FieldIsNull(d.Fields()[2].Descriptor().Name),
)
}
func commonFields() []ent.Field {
return []ent.Field{
field.Time("created_at").
Immutable().
Default(time.Now).
SchemaType(map[string]string{
dialect.MySQL: "datetime",
}),
field.Time("updated_at").
Default(time.Now).
UpdateDefault(time.Now).
SchemaType(map[string]string{
dialect.MySQL: "datetime",
}),
field.Time("deleted_at").
Optional().
Nillable().
SchemaType(map[string]string{
dialect.MySQL: "datetime",
}),
}
}
func commonHooks(d interface {
P(w interface {
WhereP(...func(*sql.Selector))
})
}) []ent.Hook {
return []ent.Hook{
hook.On(
func(next ent.Mutator) ent.Mutator {
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
// Skip soft-delete, means delete the entity permanently.
if skip, _ := ctx.Value(softDeleteKey{}).(bool); skip {
return next.Mutate(ctx, m)
}
mx, ok := m.(interface {
SetOp(ent.Op)
Client() *gen.Client
SetDeletedAt(time.Time)
WhereP(...func(*sql.Selector))
})
if !ok {
return nil, fmt.Errorf("unexpected mutation type in soft-delete %T", m)
}
d.P(mx)
mx.SetOp(ent.OpUpdate)
mx.SetDeletedAt(time.Now())
return mx.Client().Mutate(ctx, m)
})
},
ent.OpDeleteOne|ent.OpDelete,
),
}
}

54
ent/schema/davaccount.go Normal file
View File

@@ -0,0 +1,54 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/boolset"
)
// DavAccount holds the schema definition for the DavAccount entity.
type DavAccount struct {
ent.Schema
}
// Fields of the DavAccount.
func (DavAccount) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Text("uri"),
field.String("password").
Sensitive(),
field.Bytes("options").GoType(&boolset.BooleanSet{}),
field.JSON("props", &types.DavAccountProps{}).
Optional(),
field.Int("owner_id"),
}
}
// Edges of the DavAccount.
func (DavAccount) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type).
Ref("dav_accounts").
Field("owner_id").
Unique().
Required(),
}
}
// Indexes of the DavAccount.
func (DavAccount) Indexes() []ent.Index {
return []ent.Index{
index.Fields("owner_id", "password").
Unique(),
}
}
func (DavAccount) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

39
ent/schema/directlink.go Normal file
View File

@@ -0,0 +1,39 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)
// DirectLink holds the schema definition for the DirectLink entity.
type DirectLink struct {
ent.Schema
}
// Fields of the DirectLink.
func (DirectLink) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Int("downloads"),
field.Int("file_id"),
field.Int("speed"),
}
}
// Edges of the DirectLink.
func (DirectLink) Edges() []ent.Edge {
return []ent.Edge{
edge.From("file", File.Type).
Ref("direct_links").
Field("file_id").
Required().
Unique(),
}
}
func (DirectLink) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

54
ent/schema/entity.go Normal file
View File

@@ -0,0 +1,54 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/gofrs/uuid"
)
// Entity holds the schema definition for the Entity.
type Entity struct {
ent.Schema
}
// Fields of the Entity.
func (Entity) Fields() []ent.Field {
return []ent.Field{
field.Int("type"),
field.Text("source"),
field.Int64("size"),
field.Int("reference_count").Default(1),
field.Int("storage_policy_entities"),
field.Int("created_by").Optional(),
field.UUID("upload_session_id", uuid.Must(uuid.NewV4())).
Optional().
Nillable(),
field.JSON("recycle_options", &types.EntityRecycleOption{}).
Optional(),
}
}
// Edges of the Entity.
func (Entity) Edges() []ent.Edge {
return []ent.Edge{
edge.From("file", File.Type).
Ref("entities"),
edge.From("user", User.Type).
Field("created_by").
Unique().
Ref("entities"),
edge.From("storage_policy", StoragePolicy.Type).
Ref("entities").
Field("storage_policy_entities").
Unique().
Required(),
}
}
func (Entity) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

73
ent/schema/file.go Normal file
View File

@@ -0,0 +1,73 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
)
// File holds the schema definition for the File entity.
type File struct {
ent.Schema
}
// Fields of the File.
func (File) Fields() []ent.Field {
return []ent.Field{
field.Int("type"),
field.String("name"),
field.Int("owner_id"),
field.Int64("size").
Default(0),
field.Int("primary_entity").
Optional(),
field.Int("file_children").
Optional(),
field.Bool("is_symbolic").
Default(false),
field.JSON("props", &types.FileProps{}).Optional(),
field.Int("storage_policy_files").
Optional(),
}
}
// Edges of the File.
func (File) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type).
Ref("files").
Field("owner_id").
Unique().
Required(),
edge.From("storage_policies", StoragePolicy.Type).
Ref("files").
Field("storage_policy_files").
Unique(),
edge.To("children", File.Type).
From("parent").
Field("file_children").
Unique(),
edge.To("metadata", Metadata.Type),
edge.To("entities", Entity.Type),
edge.To("shares", Share.Type),
edge.To("direct_links", DirectLink.Type),
}
}
// Indexes of the File.
func (File) Indexes() []ent.Index {
return []ent.Index{
index.Fields("file_children", "name").
Unique(),
index.Fields("file_children", "type", "updated_at"),
index.Fields("file_children", "type", "size"),
}
}
func (File) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

45
ent/schema/group.go Normal file
View File

@@ -0,0 +1,45 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/boolset"
)
// Group holds the schema definition for the Group entity.
type Group struct {
ent.Schema
}
func (Group) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Int64("max_storage").
Optional(),
field.Int("speed_limit").
Optional(),
field.Bytes("permissions").GoType(&boolset.BooleanSet{}),
field.JSON("settings", &types.GroupSetting{}).
Default(&types.GroupSetting{}).
Optional(),
field.Int("storage_policy_id").Optional(),
}
}
func (Group) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}
func (Group) Edges() []ent.Edge {
return []ent.Edge{
edge.To("users", User.Type),
edge.From("storage_policies", StoragePolicy.Type).
Ref("groups").
Field("storage_policy_id").
Unique(),
}
}

48
ent/schema/metadata.go Normal file
View File

@@ -0,0 +1,48 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// Metadata holds the schema definition for the Metadata entity.
type Metadata struct {
ent.Schema
}
// Fields of the Metadata.
func (Metadata) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Text("value"),
field.Int("file_id"),
field.Bool("is_public").
Default(false),
}
}
// Edges of the Metadata.
func (Metadata) Edges() []ent.Edge {
return []ent.Edge{
edge.From("file", File.Type).
Ref("metadata").
Field("file_id").
Required().
Unique(),
}
}
func (Metadata) Indexes() []ent.Index {
return []ent.Index{
index.Fields("file_id", "name").
Unique(),
}
}
func (Metadata) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

46
ent/schema/node.go Normal file
View File

@@ -0,0 +1,46 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/boolset"
)
// Node holds the schema definition for the Node entity.
type Node struct {
ent.Schema
}
// Fields of the Node.
func (Node) Fields() []ent.Field {
return []ent.Field{
field.Enum("status").
Values("active", "suspended"),
field.String("name"),
field.Enum("type").
Values("master", "slave"),
field.String("server").
Optional(),
field.String("slave_key").Optional(),
field.Bytes("capabilities").GoType(&boolset.BooleanSet{}),
field.JSON("settings", &types.NodeSetting{}).
Default(&types.NodeSetting{}).
Optional(),
field.Int("weight").Default(0),
}
}
// Edges of the Node.
func (Node) Edges() []ent.Edge {
return []ent.Edge{
edge.To("storage_policy", StoragePolicy.Type),
}
}
func (Node) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

55
ent/schema/passkey.go Normal file
View File

@@ -0,0 +1,55 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"github.com/go-webauthn/webauthn/webauthn"
)
// Passkey holds the schema definition for the Passkey entity.
type Passkey struct {
ent.Schema
}
// Fields of the Passkey.
func (Passkey) Fields() []ent.Field {
return []ent.Field{
field.Int("user_id"),
field.String("credential_id"),
field.String("name"),
field.JSON("credential", &webauthn.Credential{}).
Sensitive(),
field.Time("used_at").
Optional().
Nillable().
SchemaType(map[string]string{
dialect.MySQL: "datetime",
}),
}
}
// Edges of the Passkey.
func (Passkey) Edges() []ent.Edge {
return []ent.Edge{
edge.From("user", User.Type).
Field("user_id").
Ref("passkey").
Unique().
Required(),
}
}
func (Passkey) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}
func (Passkey) Indexes() []ent.Index {
return []ent.Index{
index.Fields("user_id", "credential_id").Unique(),
}
}

59
ent/schema/policy.go Normal file
View File

@@ -0,0 +1,59 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
)
// StoragePolicy holds the schema definition for the storage policy entity.
type StoragePolicy struct {
ent.Schema
}
func (StoragePolicy) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.String("type"),
field.String("server").
Optional(),
field.String("bucket_name").
Optional(),
field.Bool("is_private").
Optional(),
field.Text("access_key").
Optional(),
field.Text("secret_key").
Optional(),
field.Int64("max_size").
Optional(),
field.String("dir_name_rule").
Optional(),
field.String("file_name_rule").
Optional(),
field.JSON("settings", &types.PolicySetting{}).
Default(&types.PolicySetting{}).
Optional(),
field.Int("node_id").Optional(),
}
}
func (StoragePolicy) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}
func (StoragePolicy) Edges() []ent.Edge {
return []ent.Edge{
edge.To("users", User.Type),
edge.To("groups", Group.Type),
edge.To("files", File.Type),
edge.To("entities", Entity.Type),
edge.From("node", Node.Type).
Ref("storage_policy").
Field("node_id").
Unique(),
}
}

30
ent/schema/setting.go Normal file
View File

@@ -0,0 +1,30 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// Setting holds the schema definition for key-value setting entity.
type Setting struct {
ent.Schema
}
func (Setting) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Unique(),
field.Text("value").
Optional(),
}
}
func (Setting) Edges() []ent.Edge {
return nil
}
func (Setting) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

50
ent/schema/share.go Normal file
View File

@@ -0,0 +1,50 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)
// Share holds the schema definition for the Share entity.
type Share struct {
ent.Schema
}
// Fields of the Share.
func (Share) Fields() []ent.Field {
return []ent.Field{
field.String("password").
Optional(),
field.Int("views").
Default(0),
field.Int("downloads").
Default(0),
field.Time("expires").
Nillable().
Optional().
SchemaType(map[string]string{
dialect.MySQL: "datetime",
}),
field.Int("remain_downloads").
Nillable().
Optional(),
}
}
// Edges of the Share.
func (Share) Edges() []ent.Edge {
return []ent.Edge{
edge.From("user", User.Type).
Ref("shares").Unique(),
edge.From("file", File.Type).
Ref("shares").Unique(),
}
}
func (Share) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

46
ent/schema/task.go Normal file
View File

@@ -0,0 +1,46 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/gofrs/uuid"
)
// Task holds the schema definition for the Task entity.
type Task struct {
ent.Schema
}
// Fields of the Task.
func (Task) Fields() []ent.Field {
return []ent.Field{
field.String("type"),
field.Enum("status").
Values("queued", "processing", "suspending", "error", "canceled", "completed").
Default("queued"),
field.JSON("public_state", &types.TaskPublicState{}),
field.Text("private_state").Optional(),
field.UUID("correlation_id", uuid.Must(uuid.NewV4())).
Optional().
Immutable(),
field.Int("user_tasks").Optional(),
}
}
// Edges of the Task.
func (Task) Edges() []ent.Edge {
return []ent.Edge{
edge.From("user", User.Type).
Ref("tasks").
Field("user_tasks").
Unique(),
}
}
func (Task) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}

62
ent/schema/user.go Normal file
View File

@@ -0,0 +1,62 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("email").
MaxLen(100).
Unique(),
field.String("nick").
MaxLen(100),
field.String("password").
Optional().
Sensitive(),
field.Enum("status").
Values("active", "inactive", "manual_banned", "sys_banned").
Default("active"),
field.Int64("storage").
Default(0),
field.String("two_factor_secret").
Sensitive().
Optional(),
field.String("avatar").
Optional(),
field.JSON("settings", &types.UserSetting{}).
Default(&types.UserSetting{}).
Optional(),
field.Int("group_users"),
}
}
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.From("group", Group.Type).
Ref("users").
Field("group_users").
Unique().
Required(),
edge.To("files", File.Type),
edge.To("dav_accounts", DavAccount.Type),
edge.To("shares", Share.Type),
edge.To("passkey", Passkey.Type),
edge.To("tasks", Task.Type),
edge.To("entities", Entity.Type),
}
}
func (User) Mixin() []ent.Mixin {
return []ent.Mixin{
CommonMixin{},
}
}