mirror of https://github.com/cloudreve/Cloudreve
feat(ent): migrate DB settings in patches
parent
8fe2889772
commit
fec549f5ec
|
@ -3,7 +3,7 @@ package constants
|
|||
// These values will be injected at build time, DO NOT EDIT.
|
||||
|
||||
// BackendVersion 当前后端版本号
|
||||
var BackendVersion = "4.0.0-alpha.1"
|
||||
var BackendVersion = "4.1.0"
|
||||
|
||||
// IsPro 是否为Pro版本
|
||||
var IsPro = "false"
|
||||
|
|
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.23.0
|
|||
|
||||
require (
|
||||
entgo.io/ent v0.13.0
|
||||
github.com/Masterminds/semver/v3 v3.3.1
|
||||
github.com/abslant/gzip v0.0.9
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/aws/aws-sdk-go v1.31.5
|
||||
|
|
2
go.sum
2
go.sum
|
@ -76,6 +76,8 @@ github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0
|
|||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
|
|
|
@ -5,20 +5,12 @@ import (
|
|||
rawsql "database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/cloudreve/Cloudreve/v4/application/constants"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/group"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/node"
|
||||
_ "github.com/cloudreve/Cloudreve/v4/ent/runtime"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/storagepolicy"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/debug"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/boolset"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/conf"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||||
|
@ -153,302 +145,3 @@ func (d sqlite3Driver) Open(name string) (conn driver.Conn, err error) {
|
|||
func init() {
|
||||
rawsql.Register("sqlite3", sqlite3Driver{Driver: &sqlite.Driver{}})
|
||||
}
|
||||
|
||||
// needMigration exams if required schema version is satisfied.
|
||||
func needMigration(client *ent.Client, ctx context.Context, requiredDbVersion string) bool {
|
||||
c, _ := client.Setting.Query().Where(setting.NameEQ(DBVersionPrefix + requiredDbVersion)).Count(ctx)
|
||||
return c == 0
|
||||
}
|
||||
|
||||
func migrate(l logging.Logger, client *ent.Client, ctx context.Context, kv cache.Driver, requiredDbVersion string) error {
|
||||
l.Info("Start initializing database schema...")
|
||||
l.Info("Creating basic table schema...")
|
||||
if err := client.Schema.Create(ctx); err != nil {
|
||||
return fmt.Errorf("Failed creating schema resources: %w", err)
|
||||
}
|
||||
|
||||
migrateDefaultSettings(l, client, ctx, kv)
|
||||
|
||||
if err := migrateDefaultStoragePolicy(l, client, ctx); err != nil {
|
||||
return fmt.Errorf("failed migrating default storage policy: %w", err)
|
||||
}
|
||||
|
||||
if err := migrateSysGroups(l, client, ctx); err != nil {
|
||||
return fmt.Errorf("failed migrating default storage policy: %w", err)
|
||||
}
|
||||
|
||||
client.Setting.Create().SetName(DBVersionPrefix + requiredDbVersion).SetValue("installed").Save(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateDefaultSettings(l logging.Logger, client *ent.Client, ctx context.Context, kv cache.Driver) {
|
||||
// clean kv cache
|
||||
if err := kv.DeleteAll(); err != nil {
|
||||
l.Warning("Failed to remove all KV entries while schema migration: %s", err)
|
||||
}
|
||||
|
||||
// List existing settings into a map
|
||||
existingSettings := make(map[string]struct{})
|
||||
settings, err := client.Setting.Query().All(ctx)
|
||||
if err != nil {
|
||||
l.Warning("Failed to query existing settings: %s", err)
|
||||
}
|
||||
|
||||
for _, s := range settings {
|
||||
existingSettings[s.Name] = struct{}{}
|
||||
}
|
||||
|
||||
l.Info("Insert default settings...")
|
||||
for k, v := range DefaultSettings {
|
||||
if _, ok := existingSettings[k]; ok {
|
||||
l.Debug("Skip inserting setting %s, already exists.", k)
|
||||
continue
|
||||
}
|
||||
|
||||
if override, ok := os.LookupEnv(EnvDefaultOverwritePrefix + k); ok {
|
||||
l.Info("Override default setting %q with env value %q", k, override)
|
||||
v = override
|
||||
}
|
||||
|
||||
client.Setting.Create().SetName(k).SetValue(v).SaveX(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func migrateDefaultStoragePolicy(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.StoragePolicy.Query().Where(storagepolicy.ID(1)).First(ctx); err == nil {
|
||||
l.Info("Default storage policy (ID=1) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default storage policy...")
|
||||
if _, err := client.StoragePolicy.Create().
|
||||
SetName("Default storage policy").
|
||||
SetType(types.PolicyTypeLocal).
|
||||
SetDirNameRule(util.DataPath("uploads/{uid}/{path}")).
|
||||
SetFileNameRule("{uid}_{randomkey8}_{originname}").
|
||||
SetSettings(&types.PolicySetting{
|
||||
ChunkSize: 25 << 20, // 25MB
|
||||
PreAllocate: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default storage policy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateSysGroups(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if err := migrateAdminGroup(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrateUserGroup(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrateAnonymousGroup(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrateMasterNode(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateAdminGroup(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Group.Query().Where(group.ID(1)).First(ctx); err == nil {
|
||||
l.Info("Default admin group (ID=1) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default admin group...")
|
||||
permissions := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.GroupPermission]bool{
|
||||
types.GroupPermissionIsAdmin: true,
|
||||
types.GroupPermissionShare: true,
|
||||
types.GroupPermissionWebDAV: true,
|
||||
types.GroupPermissionWebDAVProxy: true,
|
||||
types.GroupPermissionArchiveDownload: true,
|
||||
types.GroupPermissionArchiveTask: true,
|
||||
types.GroupPermissionShareDownload: true,
|
||||
types.GroupPermissionRemoteDownload: true,
|
||||
types.GroupPermissionRedirectedSource: true,
|
||||
types.GroupPermissionAdvanceDelete: true,
|
||||
types.GroupPermissionIgnoreFileOwnership: true,
|
||||
// TODO: review default permission
|
||||
}, permissions)
|
||||
if _, err := client.Group.Create().
|
||||
SetName("Admin").
|
||||
SetStoragePoliciesID(1).
|
||||
SetMaxStorage(1 * constants.TB). // 1 TB default storage
|
||||
SetPermissions(permissions).
|
||||
SetSettings(&types.GroupSetting{
|
||||
SourceBatchSize: 1000,
|
||||
Aria2BatchSize: 50,
|
||||
MaxWalkedFiles: 100000,
|
||||
TrashRetention: 7 * 24 * 3600,
|
||||
RedirectedSource: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default admin group: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateUserGroup(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Group.Query().Where(group.ID(2)).First(ctx); err == nil {
|
||||
l.Info("Default user group (ID=2) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default user group...")
|
||||
permissions := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.GroupPermission]bool{
|
||||
types.GroupPermissionShare: true,
|
||||
types.GroupPermissionShareDownload: true,
|
||||
types.GroupPermissionRedirectedSource: true,
|
||||
}, permissions)
|
||||
if _, err := client.Group.Create().
|
||||
SetName("User").
|
||||
SetStoragePoliciesID(1).
|
||||
SetMaxStorage(1 * constants.GB). // 1 GB default storage
|
||||
SetPermissions(permissions).
|
||||
SetSettings(&types.GroupSetting{
|
||||
SourceBatchSize: 10,
|
||||
Aria2BatchSize: 1,
|
||||
MaxWalkedFiles: 100000,
|
||||
TrashRetention: 7 * 24 * 3600,
|
||||
RedirectedSource: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default user group: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateAnonymousGroup(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Group.Query().Where(group.ID(AnonymousGroupID)).First(ctx); err == nil {
|
||||
l.Info("Default anonymous group (ID=3) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default anonymous group...")
|
||||
permissions := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.GroupPermission]bool{
|
||||
types.GroupPermissionIsAnonymous: true,
|
||||
types.GroupPermissionShareDownload: true,
|
||||
}, permissions)
|
||||
if _, err := client.Group.Create().
|
||||
SetName("Anonymous").
|
||||
SetPermissions(permissions).
|
||||
SetSettings(&types.GroupSetting{
|
||||
MaxWalkedFiles: 100000,
|
||||
RedirectedSource: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default anonymous group: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateMasterNode(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Node.Query().Where(node.TypeEQ(node.TypeMaster)).First(ctx); err == nil {
|
||||
l.Info("Default master node already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
capabilities := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.NodeCapability]bool{
|
||||
types.NodeCapabilityCreateArchive: true,
|
||||
types.NodeCapabilityExtractArchive: true,
|
||||
types.NodeCapabilityRemoteDownload: true,
|
||||
}, capabilities)
|
||||
|
||||
stm := client.Node.Create().
|
||||
SetType(node.TypeMaster).
|
||||
SetCapabilities(capabilities).
|
||||
SetName("Master").
|
||||
SetSettings(&types.NodeSetting{
|
||||
Provider: types.DownloaderProviderAria2,
|
||||
}).
|
||||
SetStatus(node.StatusActive)
|
||||
|
||||
_, enableAria2 := os.LookupEnv(EnvEnableAria2)
|
||||
if enableAria2 {
|
||||
l.Info("Aria2 is override as enabled.")
|
||||
stm.SetSettings(&types.NodeSetting{
|
||||
Provider: types.DownloaderProviderAria2,
|
||||
Aria2Setting: &types.Aria2Setting{
|
||||
Server: "http://127.0.0.1:6800/jsonrpc",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
l.Info("Insert default master node...")
|
||||
if _, err := stm.Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default master node: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createMockData(client *ent.Client, ctx context.Context) {
|
||||
//userCount := 100
|
||||
//folderCount := 10000
|
||||
//fileCount := 25000
|
||||
//
|
||||
//// create users
|
||||
//pwdDigest, _ := digestPassword("52121225")
|
||||
//userCreates := make([]*ent.UserCreate, userCount)
|
||||
//for i := 0; i < userCount; i++ {
|
||||
// nick := uuid.Must(uuid.NewV4()).String()
|
||||
// userCreates[i] = client.User.Create().
|
||||
// SetEmail(nick + "@cloudreve.org").
|
||||
// SetNick(nick).
|
||||
// SetPassword(pwdDigest).
|
||||
// SetStatus(user.StatusActive).
|
||||
// SetGroupID(1)
|
||||
//}
|
||||
//users, err := client.User.CreateBulk(userCreates...).Save(ctx)
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//
|
||||
//// Create root folder
|
||||
//rootFolderCreates := make([]*ent.FileCreate, userCount)
|
||||
//folderIds := make([][]int, 0, folderCount*userCount+userCount)
|
||||
//for i, user := range users {
|
||||
// rootFolderCreates[i] = client.File.Create().
|
||||
// SetName(RootFolderName).
|
||||
// SetOwnerID(user.ID).
|
||||
// SetType(int(FileTypeFolder))
|
||||
//}
|
||||
//rootFolders, err := client.File.CreateBulk(rootFolderCreates...).Save(ctx)
|
||||
//for _, rootFolders := range rootFolders {
|
||||
// folderIds = append(folderIds, []int{rootFolders.ID, rootFolders.OwnerID})
|
||||
//}
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//
|
||||
//// create random folder
|
||||
//for i := 0; i < folderCount*userCount; i++ {
|
||||
// parent := lo.Sample(folderIds)
|
||||
// res := client.File.Create().
|
||||
// SetName(uuid.Must(uuid.NewV4()).String()).
|
||||
// SetType(int(FileTypeFolder)).
|
||||
// SetOwnerID(parent[1]).
|
||||
// SetFileChildren(parent[0]).
|
||||
// SaveX(ctx)
|
||||
// folderIds = append(folderIds, []int{res.ID, res.OwnerID})
|
||||
//}
|
||||
|
||||
for i := 0; i < 255; i++ {
|
||||
fmt.Printf("%d/", i)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,416 @@
|
|||
package inventory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/cloudreve/Cloudreve/v4/application/constants"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/group"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/node"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent/storagepolicy"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/boolset"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// needMigration exams if required schema version is satisfied.
|
||||
func needMigration(client *ent.Client, ctx context.Context, requiredDbVersion string) bool {
|
||||
c, _ := client.Setting.Query().Where(setting.NameEQ(DBVersionPrefix + requiredDbVersion)).Count(ctx)
|
||||
return c == 0
|
||||
}
|
||||
|
||||
func migrate(l logging.Logger, client *ent.Client, ctx context.Context, kv cache.Driver, requiredDbVersion string) error {
|
||||
l.Info("Start initializing database schema...")
|
||||
l.Info("Creating basic table schema...")
|
||||
if err := client.Schema.Create(ctx); err != nil {
|
||||
return fmt.Errorf("Failed creating schema resources: %w", err)
|
||||
}
|
||||
|
||||
migrateDefaultSettings(l, client, ctx, kv)
|
||||
|
||||
if err := migrateDefaultStoragePolicy(l, client, ctx); err != nil {
|
||||
return fmt.Errorf("failed migrating default storage policy: %w", err)
|
||||
}
|
||||
|
||||
if err := migrateSysGroups(l, client, ctx); err != nil {
|
||||
return fmt.Errorf("failed migrating default storage policy: %w", err)
|
||||
}
|
||||
|
||||
if err := applyPatches(l, client, ctx, requiredDbVersion); err != nil {
|
||||
return fmt.Errorf("failed applying schema patches: %w", err)
|
||||
}
|
||||
|
||||
client.Setting.Create().SetName(DBVersionPrefix + requiredDbVersion).SetValue("installed").Save(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateDefaultSettings(l logging.Logger, client *ent.Client, ctx context.Context, kv cache.Driver) {
|
||||
// clean kv cache
|
||||
if err := kv.DeleteAll(); err != nil {
|
||||
l.Warning("Failed to remove all KV entries while schema migration: %s", err)
|
||||
}
|
||||
|
||||
// List existing settings into a map
|
||||
existingSettings := make(map[string]struct{})
|
||||
settings, err := client.Setting.Query().All(ctx)
|
||||
if err != nil {
|
||||
l.Warning("Failed to query existing settings: %s", err)
|
||||
}
|
||||
|
||||
for _, s := range settings {
|
||||
existingSettings[s.Name] = struct{}{}
|
||||
}
|
||||
|
||||
l.Info("Insert default settings...")
|
||||
for k, v := range DefaultSettings {
|
||||
if _, ok := existingSettings[k]; ok {
|
||||
l.Debug("Skip inserting setting %s, already exists.", k)
|
||||
continue
|
||||
}
|
||||
|
||||
if override, ok := os.LookupEnv(EnvDefaultOverwritePrefix + k); ok {
|
||||
l.Info("Override default setting %q with env value %q", k, override)
|
||||
v = override
|
||||
}
|
||||
|
||||
client.Setting.Create().SetName(k).SetValue(v).SaveX(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func migrateDefaultStoragePolicy(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.StoragePolicy.Query().Where(storagepolicy.ID(1)).First(ctx); err == nil {
|
||||
l.Info("Default storage policy (ID=1) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default storage policy...")
|
||||
if _, err := client.StoragePolicy.Create().
|
||||
SetName("Default storage policy").
|
||||
SetType(types.PolicyTypeLocal).
|
||||
SetDirNameRule(util.DataPath("uploads/{uid}/{path}")).
|
||||
SetFileNameRule("{uid}_{randomkey8}_{originname}").
|
||||
SetSettings(&types.PolicySetting{
|
||||
ChunkSize: 25 << 20, // 25MB
|
||||
PreAllocate: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default storage policy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateSysGroups(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if err := migrateAdminGroup(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrateUserGroup(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrateAnonymousGroup(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrateMasterNode(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateAdminGroup(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Group.Query().Where(group.ID(1)).First(ctx); err == nil {
|
||||
l.Info("Default admin group (ID=1) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default admin group...")
|
||||
permissions := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.GroupPermission]bool{
|
||||
types.GroupPermissionIsAdmin: true,
|
||||
types.GroupPermissionShare: true,
|
||||
types.GroupPermissionWebDAV: true,
|
||||
types.GroupPermissionWebDAVProxy: true,
|
||||
types.GroupPermissionArchiveDownload: true,
|
||||
types.GroupPermissionArchiveTask: true,
|
||||
types.GroupPermissionShareDownload: true,
|
||||
types.GroupPermissionRemoteDownload: true,
|
||||
types.GroupPermissionRedirectedSource: true,
|
||||
types.GroupPermissionAdvanceDelete: true,
|
||||
types.GroupPermissionIgnoreFileOwnership: true,
|
||||
// TODO: review default permission
|
||||
}, permissions)
|
||||
if _, err := client.Group.Create().
|
||||
SetName("Admin").
|
||||
SetStoragePoliciesID(1).
|
||||
SetMaxStorage(1 * constants.TB). // 1 TB default storage
|
||||
SetPermissions(permissions).
|
||||
SetSettings(&types.GroupSetting{
|
||||
SourceBatchSize: 1000,
|
||||
Aria2BatchSize: 50,
|
||||
MaxWalkedFiles: 100000,
|
||||
TrashRetention: 7 * 24 * 3600,
|
||||
RedirectedSource: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default admin group: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateUserGroup(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Group.Query().Where(group.ID(2)).First(ctx); err == nil {
|
||||
l.Info("Default user group (ID=2) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default user group...")
|
||||
permissions := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.GroupPermission]bool{
|
||||
types.GroupPermissionShare: true,
|
||||
types.GroupPermissionShareDownload: true,
|
||||
types.GroupPermissionRedirectedSource: true,
|
||||
}, permissions)
|
||||
if _, err := client.Group.Create().
|
||||
SetName("User").
|
||||
SetStoragePoliciesID(1).
|
||||
SetMaxStorage(1 * constants.GB). // 1 GB default storage
|
||||
SetPermissions(permissions).
|
||||
SetSettings(&types.GroupSetting{
|
||||
SourceBatchSize: 10,
|
||||
Aria2BatchSize: 1,
|
||||
MaxWalkedFiles: 100000,
|
||||
TrashRetention: 7 * 24 * 3600,
|
||||
RedirectedSource: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default user group: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateAnonymousGroup(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Group.Query().Where(group.ID(AnonymousGroupID)).First(ctx); err == nil {
|
||||
l.Info("Default anonymous group (ID=3) already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Info("Insert default anonymous group...")
|
||||
permissions := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.GroupPermission]bool{
|
||||
types.GroupPermissionIsAnonymous: true,
|
||||
types.GroupPermissionShareDownload: true,
|
||||
}, permissions)
|
||||
if _, err := client.Group.Create().
|
||||
SetName("Anonymous").
|
||||
SetPermissions(permissions).
|
||||
SetSettings(&types.GroupSetting{
|
||||
MaxWalkedFiles: 100000,
|
||||
RedirectedSource: true,
|
||||
}).
|
||||
Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default anonymous group: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateMasterNode(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
if _, err := client.Node.Query().Where(node.TypeEQ(node.TypeMaster)).First(ctx); err == nil {
|
||||
l.Info("Default master node already exists, skip migrating.")
|
||||
return nil
|
||||
}
|
||||
|
||||
capabilities := &boolset.BooleanSet{}
|
||||
boolset.Sets(map[types.NodeCapability]bool{
|
||||
types.NodeCapabilityCreateArchive: true,
|
||||
types.NodeCapabilityExtractArchive: true,
|
||||
types.NodeCapabilityRemoteDownload: true,
|
||||
}, capabilities)
|
||||
|
||||
stm := client.Node.Create().
|
||||
SetType(node.TypeMaster).
|
||||
SetCapabilities(capabilities).
|
||||
SetName("Master").
|
||||
SetSettings(&types.NodeSetting{
|
||||
Provider: types.DownloaderProviderAria2,
|
||||
}).
|
||||
SetStatus(node.StatusActive)
|
||||
|
||||
_, enableAria2 := os.LookupEnv(EnvEnableAria2)
|
||||
if enableAria2 {
|
||||
l.Info("Aria2 is override as enabled.")
|
||||
stm.SetSettings(&types.NodeSetting{
|
||||
Provider: types.DownloaderProviderAria2,
|
||||
Aria2Setting: &types.Aria2Setting{
|
||||
Server: "http://127.0.0.1:6800/jsonrpc",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
l.Info("Insert default master node...")
|
||||
if _, err := stm.Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create default master node: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type (
|
||||
PatchFunc func(l logging.Logger, client *ent.Client, ctx context.Context) error
|
||||
Patch struct {
|
||||
Name string
|
||||
EndVersion string
|
||||
Func PatchFunc
|
||||
}
|
||||
)
|
||||
|
||||
var patches = []Patch{
|
||||
{
|
||||
Name: "apply_default_excalidraw_viewer",
|
||||
EndVersion: "4.1.0",
|
||||
Func: func(l logging.Logger, client *ent.Client, ctx context.Context) error {
|
||||
// 1. Apply excalidraw file icons
|
||||
// 1.1 Check if it's already applied
|
||||
iconSetting, err := client.Setting.Query().Where(setting.Name("explorer_icons")).First(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to query explorer_icons setting: %w", err)
|
||||
}
|
||||
|
||||
var icons []types.FileTypeIconSetting
|
||||
if err := json.Unmarshal([]byte(iconSetting.Value), &icons); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal explorer_icons setting: %w", err)
|
||||
}
|
||||
|
||||
iconExisted := false
|
||||
for _, icon := range icons {
|
||||
if lo.Contains(icon.Exts, "excalidraw") {
|
||||
iconExisted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 1.2 If not existed, add it
|
||||
if !iconExisted {
|
||||
// Found existing excalidraw icon default setting
|
||||
var defaultExcalidrawIcon types.FileTypeIconSetting
|
||||
for _, icon := range defaultIcons {
|
||||
if lo.Contains(icon.Exts, "excalidraw") {
|
||||
defaultExcalidrawIcon = icon
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
icons = append(icons, defaultExcalidrawIcon)
|
||||
newIconSetting, err := json.Marshal(icons)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal explorer_icons setting: %w", err)
|
||||
}
|
||||
|
||||
if _, err := client.Setting.UpdateOne(iconSetting).SetValue(string(newIconSetting)).Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to update explorer_icons setting: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Apply default file viewers
|
||||
// 2.1 Check if it's already applied
|
||||
fileViewersSetting, err := client.Setting.Query().Where(setting.Name("file_viewers")).First(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to query file_viewers setting: %w", err)
|
||||
}
|
||||
|
||||
var fileViewers []types.ViewerGroup
|
||||
if err := json.Unmarshal([]byte(fileViewersSetting.Value), &fileViewers); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal file_viewers setting: %w", err)
|
||||
}
|
||||
|
||||
fileViewerExisted := false
|
||||
for _, viewer := range fileViewers[0].Viewers {
|
||||
if viewer.ID == "excalidraw" {
|
||||
fileViewerExisted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 2.2 If not existed, add it
|
||||
if !fileViewerExisted {
|
||||
// Found existing excalidraw viewer default setting
|
||||
var defaultExcalidrawViewer types.Viewer
|
||||
for _, viewer := range defaultFileViewers[0].Viewers {
|
||||
if viewer.ID == "excalidraw" {
|
||||
defaultExcalidrawViewer = viewer
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fileViewers[0].Viewers = append(fileViewers[0].Viewers, defaultExcalidrawViewer)
|
||||
newFileViewersSetting, err := json.Marshal(fileViewers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal file_viewers setting: %w", err)
|
||||
}
|
||||
|
||||
if _, err := client.Setting.UpdateOne(fileViewersSetting).SetValue(string(newFileViewersSetting)).Save(ctx); err != nil {
|
||||
return fmt.Errorf("failed to update file_viewers setting: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func applyPatches(l logging.Logger, client *ent.Client, ctx context.Context, requiredDbVersion string) error {
|
||||
allVersionMarks, err := client.Setting.Query().Where(setting.NameHasPrefix(DBVersionPrefix)).All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
requiredDbVersion = strings.TrimSuffix(requiredDbVersion, "-pro")
|
||||
|
||||
// Find the latest applied version
|
||||
var latestAppliedVersion *semver.Version
|
||||
for _, v := range allVersionMarks {
|
||||
v.Name = strings.TrimSuffix(v.Name, "-pro")
|
||||
version, err := semver.NewVersion(strings.TrimPrefix(v.Name, DBVersionPrefix))
|
||||
if err != nil {
|
||||
l.Warning("Failed to parse past version %s: %s", v.Name, err)
|
||||
continue
|
||||
}
|
||||
if latestAppliedVersion == nil || version.Compare(latestAppliedVersion) > 0 {
|
||||
latestAppliedVersion = version
|
||||
}
|
||||
}
|
||||
|
||||
requiredVersion, err := semver.NewVersion(requiredDbVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse required version %s: %w", requiredDbVersion, err)
|
||||
}
|
||||
|
||||
if latestAppliedVersion == nil || requiredVersion.Compare(requiredVersion) > 0 {
|
||||
latestAppliedVersion = requiredVersion
|
||||
}
|
||||
|
||||
for _, patch := range patches {
|
||||
if latestAppliedVersion.Compare(semver.MustParse(patch.EndVersion)) < 0 {
|
||||
l.Info("Applying schema patch %s...", patch.Name)
|
||||
if err := patch.Func(l, client, ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -178,6 +178,14 @@ type (
|
|||
// Whether to share view setting from owner
|
||||
ShareView bool `json:"share_view,omitempty"`
|
||||
}
|
||||
|
||||
FileTypeIconSetting struct {
|
||||
Exts []string `json:"exts"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Color string `json:"color,omitempty"`
|
||||
ColorDark string `json:"color_dark,omitempty"`
|
||||
Img string `json:"img,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -250,3 +258,40 @@ const (
|
|||
DownloaderProviderAria2 = DownloaderProvider("aria2")
|
||||
DownloaderProviderQBittorrent = DownloaderProvider("qbittorrent")
|
||||
)
|
||||
|
||||
type (
|
||||
ViewerAction string
|
||||
ViewerType string
|
||||
)
|
||||
|
||||
const (
|
||||
ViewerActionView = "view"
|
||||
ViewerActionEdit = "edit"
|
||||
|
||||
ViewerTypeBuiltin = "builtin"
|
||||
ViewerTypeWopi = "wopi"
|
||||
ViewerTypeCustom = "custom"
|
||||
)
|
||||
|
||||
type Viewer struct {
|
||||
ID string `json:"id"`
|
||||
Type ViewerType `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Exts []string `json:"exts"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
WopiActions map[string]map[ViewerAction]string `json:"wopi_actions,omitempty"`
|
||||
Props map[string]string `json:"props,omitempty"`
|
||||
MaxSize int64 `json:"max_size,omitempty"`
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
Templates []NewFileTemplate `json:"templates,omitempty"`
|
||||
}
|
||||
|
||||
type ViewerGroup struct {
|
||||
Viewers []Viewer `json:"viewers"`
|
||||
}
|
||||
|
||||
type NewFileTemplate struct {
|
||||
Ext string `json:"ext"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package middleware
|
|||
|
||||
import (
|
||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/wopi"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -67,7 +67,7 @@ func ViewerSessionValidation() gin.HandlerFunc {
|
|||
|
||||
// Check if the viewer is still available
|
||||
viewers := settings.FileViewers(c)
|
||||
var v *setting.Viewer
|
||||
var v *types.Viewer
|
||||
for _, group := range viewers {
|
||||
for _, viewer := range group.Viewers {
|
||||
if viewer.ID == session.ViewerID && !viewer.Disabled {
|
||||
|
|
|
@ -54,7 +54,7 @@ type (
|
|||
// UpsertMedata update or insert metadata of given file
|
||||
PatchMedata(ctx context.Context, path []*fs.URI, data ...fs.MetadataPatch) error
|
||||
// CreateViewerSession creates a viewer session for given file
|
||||
CreateViewerSession(ctx context.Context, uri *fs.URI, version string, viewer *setting.Viewer) (*ViewerSession, error)
|
||||
CreateViewerSession(ctx context.Context, uri *fs.URI, version string, viewer *types.Viewer) (*ViewerSession, error)
|
||||
// TraverseFile traverses a file to its root file, return the file with linked root.
|
||||
TraverseFile(ctx context.Context, fileID int) (fs.File, error)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs/dbfs"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
@ -44,7 +43,7 @@ func init() {
|
|||
gob.Register(ViewerSessionCache{})
|
||||
}
|
||||
|
||||
func (m *manager) CreateViewerSession(ctx context.Context, uri *fs.URI, version string, viewer *setting.Viewer) (*ViewerSession, error) {
|
||||
func (m *manager) CreateViewerSession(ctx context.Context, uri *fs.URI, version string, viewer *types.Viewer) (*ViewerSession, error) {
|
||||
file, err := m.fs.Get(ctx, uri, dbfs.WithFileEntities(), dbfs.WithNotRoot())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -88,6 +87,6 @@ func ViewerSessionFromContext(ctx context.Context) *ViewerSessionCache {
|
|||
return ctx.Value(ViewerSessionCacheCtx{}).(*ViewerSessionCache)
|
||||
}
|
||||
|
||||
func ViewerFromContext(ctx context.Context) *setting.Viewer {
|
||||
return ctx.Value(ViewerCtx{}).(*setting.Viewer)
|
||||
func ViewerFromContext(ctx context.Context) *types.Viewer {
|
||||
return ctx.Value(ViewerCtx{}).(*types.Viewer)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -169,7 +170,7 @@ type (
|
|||
// FolderPropsCacheTTL returns the cache TTL of folder summary.
|
||||
FolderPropsCacheTTL(ctx context.Context) int
|
||||
// FileViewers returns the file viewers settings.
|
||||
FileViewers(ctx context.Context) []ViewerGroup
|
||||
FileViewers(ctx context.Context) []types.ViewerGroup
|
||||
// ViewerSessionTTL returns the TTL of viewer session.
|
||||
ViewerSessionTTL(ctx context.Context) int
|
||||
// MimeMapping returns the extension to MIME mapping settings.
|
||||
|
@ -232,11 +233,11 @@ func (s *settingProvider) Avatar(ctx context.Context) *Avatar {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *settingProvider) FileViewers(ctx context.Context) []ViewerGroup {
|
||||
func (s *settingProvider) FileViewers(ctx context.Context) []types.ViewerGroup {
|
||||
raw := s.getString(ctx, "file_viewers", "[]")
|
||||
var viewers []ViewerGroup
|
||||
var viewers []types.ViewerGroup
|
||||
if err := json.Unmarshal([]byte(raw), &viewers); err != nil {
|
||||
return []ViewerGroup{}
|
||||
return []types.ViewerGroup{}
|
||||
}
|
||||
|
||||
return viewers
|
||||
|
|
|
@ -176,42 +176,6 @@ type MapSetting struct {
|
|||
|
||||
// Viewer related
|
||||
|
||||
type (
|
||||
ViewerAction string
|
||||
ViewerType string
|
||||
)
|
||||
|
||||
const (
|
||||
ViewerActionView = "view"
|
||||
ViewerActionEdit = "edit"
|
||||
|
||||
ViewerTypeBuiltin = "builtin"
|
||||
ViewerTypeWopi = "wopi"
|
||||
)
|
||||
|
||||
type Viewer struct {
|
||||
ID string `json:"id"`
|
||||
Type ViewerType `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Exts []string `json:"exts"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
WopiActions map[string]map[ViewerAction]string `json:"wopi_actions,omitempty"`
|
||||
Props map[string]string `json:"props,omitempty"`
|
||||
MaxSize int64 `json:"max_size,omitempty"`
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
Templates []NewFileTemplate `json:"templates,omitempty"`
|
||||
}
|
||||
|
||||
type ViewerGroup struct {
|
||||
Viewers []Viewer `json:"viewers"`
|
||||
}
|
||||
|
||||
type NewFileTemplate struct {
|
||||
Ext string `json:"ext"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
|
||||
type (
|
||||
SearchCategory string
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ package wopi
|
|||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
@ -16,23 +16,23 @@ var (
|
|||
ActionEdit = ActonType("edit")
|
||||
)
|
||||
|
||||
func DiscoveryXmlToViewerGroup(xmlStr string) (*setting.ViewerGroup, error) {
|
||||
func DiscoveryXmlToViewerGroup(xmlStr string) (*types.ViewerGroup, error) {
|
||||
var discovery WopiDiscovery
|
||||
if err := xml.Unmarshal([]byte(xmlStr), &discovery); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse WOPI discovery XML: %w", err)
|
||||
}
|
||||
|
||||
group := &setting.ViewerGroup{
|
||||
Viewers: make([]setting.Viewer, 0, len(discovery.NetZone.App)),
|
||||
group := &types.ViewerGroup{
|
||||
Viewers: make([]types.Viewer, 0, len(discovery.NetZone.App)),
|
||||
}
|
||||
|
||||
for _, app := range discovery.NetZone.App {
|
||||
viewer := setting.Viewer{
|
||||
viewer := types.Viewer{
|
||||
ID: uuid.Must(uuid.NewV4()).String(),
|
||||
DisplayName: app.Name,
|
||||
Type: setting.ViewerTypeWopi,
|
||||
Type: types.ViewerTypeWopi,
|
||||
Icon: app.FavIconUrl,
|
||||
WopiActions: make(map[string]map[setting.ViewerAction]string),
|
||||
WopiActions: make(map[string]map[types.ViewerAction]string),
|
||||
}
|
||||
|
||||
for _, action := range app.Action {
|
||||
|
@ -41,21 +41,21 @@ func DiscoveryXmlToViewerGroup(xmlStr string) (*setting.ViewerGroup, error) {
|
|||
}
|
||||
|
||||
if _, ok := viewer.WopiActions[action.Ext]; !ok {
|
||||
viewer.WopiActions[action.Ext] = make(map[setting.ViewerAction]string)
|
||||
viewer.WopiActions[action.Ext] = make(map[types.ViewerAction]string)
|
||||
}
|
||||
|
||||
if action.Name == string(ActionPreview) {
|
||||
viewer.WopiActions[action.Ext][setting.ViewerActionView] = action.Urlsrc
|
||||
viewer.WopiActions[action.Ext][types.ViewerActionView] = action.Urlsrc
|
||||
} else if action.Name == string(ActionPreviewFallback) {
|
||||
viewer.WopiActions[action.Ext][setting.ViewerActionView] = action.Urlsrc
|
||||
viewer.WopiActions[action.Ext][types.ViewerActionView] = action.Urlsrc
|
||||
} else if action.Name == string(ActionEdit) {
|
||||
viewer.WopiActions[action.Ext][setting.ViewerActionEdit] = action.Urlsrc
|
||||
viewer.WopiActions[action.Ext][types.ViewerActionEdit] = action.Urlsrc
|
||||
} else if len(viewer.WopiActions[action.Ext]) == 0 {
|
||||
delete(viewer.WopiActions, action.Ext)
|
||||
}
|
||||
}
|
||||
|
||||
viewer.Exts = lo.MapToSlice(viewer.WopiActions, func(key string, value map[setting.ViewerAction]string) string {
|
||||
viewer.Exts = lo.MapToSlice(viewer.WopiActions, func(key string, value map[types.ViewerAction]string) string {
|
||||
return key
|
||||
})
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -56,7 +57,7 @@ const (
|
|||
LockDuration = time.Duration(30) * time.Minute
|
||||
)
|
||||
|
||||
func GenerateWopiSrc(ctx context.Context, action setting.ViewerAction, viewer *setting.Viewer, viewerSession *manager.ViewerSession) (*url.URL, error) {
|
||||
func GenerateWopiSrc(ctx context.Context, action types.ViewerAction, viewer *types.Viewer, viewerSession *manager.ViewerSession) (*url.URL, error) {
|
||||
dep := dependency.FromContext(ctx)
|
||||
base := dep.SettingProvider().SiteURL(setting.UseFirstSiteUrl(ctx))
|
||||
hasher := dep.HashIDEncoder()
|
||||
|
@ -69,7 +70,7 @@ func GenerateWopiSrc(ctx context.Context, action setting.ViewerAction, viewer *s
|
|||
var (
|
||||
src string
|
||||
)
|
||||
fallbackOrder := []setting.ViewerAction{action, setting.ViewerActionView, setting.ViewerActionEdit}
|
||||
fallbackOrder := []types.ViewerAction{action, types.ViewerActionView, types.ViewerActionEdit}
|
||||
for _, a := range fallbackOrder {
|
||||
if src, ok = availableActions[a]; ok {
|
||||
break
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/wopi"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-mail/mail"
|
||||
)
|
||||
|
@ -107,7 +108,7 @@ type (
|
|||
FetchWOPIDiscoveryParamCtx struct{}
|
||||
)
|
||||
|
||||
func (s *FetchWOPIDiscoveryService) Fetch(c *gin.Context) (*setting.ViewerGroup, error) {
|
||||
func (s *FetchWOPIDiscoveryService) Fetch(c *gin.Context) (*types.ViewerGroup, error) {
|
||||
dep := dependency.FromContext(c)
|
||||
requestClient := dep.RequestClient(request2.WithContext(c), request2.WithLogger(dep.Logger()))
|
||||
content, err := requestClient.Request("GET", s.Endpoint, nil).CheckHTTPResponse(http.StatusOK).GetResponse()
|
||||
|
|
|
@ -3,6 +3,7 @@ package basic
|
|||
import (
|
||||
"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/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/service/user"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -39,7 +40,7 @@ type SiteConfig struct {
|
|||
EmojiPreset string `json:"emoji_preset,omitempty"`
|
||||
MapProvider setting.MapProvider `json:"map_provider,omitempty"`
|
||||
GoogleMapTileType setting.MapGoogleTileType `json:"google_map_tile_type,omitempty"`
|
||||
FileViewers []setting.ViewerGroup `json:"file_viewers,omitempty"`
|
||||
FileViewers []types.ViewerGroup `json:"file_viewers,omitempty"`
|
||||
MaxBatchSize int `json:"max_batch_size,omitempty"`
|
||||
ThumbnailWidth int `json:"thumbnail_width,omitempty"`
|
||||
ThumbnailHeight int `json:"thumbnail_height,omitempty"`
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager/entitysource"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/wopi"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
@ -371,7 +370,7 @@ type (
|
|||
Uri string `json:"uri" form:"uri" binding:"required"`
|
||||
Version string `json:"version" form:"version"`
|
||||
ViewerID string `json:"viewer_id" form:"viewer_id" binding:"required"`
|
||||
PreferredAction setting.ViewerAction `json:"preferred_action" form:"preferred_action" binding:"required"`
|
||||
PreferredAction types.ViewerAction `json:"preferred_action" form:"preferred_action" binding:"required"`
|
||||
}
|
||||
CreateViewerSessionParamCtx struct{}
|
||||
)
|
||||
|
@ -389,7 +388,7 @@ func (s *CreateViewerSessionService) Create(c *gin.Context) (*ViewerSessionRespo
|
|||
|
||||
// Find the given viewer
|
||||
viewers := dep.SettingProvider().FileViewers(c)
|
||||
var targetViewer *setting.Viewer
|
||||
var targetViewer *types.Viewer
|
||||
for _, group := range viewers {
|
||||
for _, viewer := range group.Viewers {
|
||||
if viewer.ID == s.ViewerID && !viewer.Disabled {
|
||||
|
@ -413,7 +412,7 @@ func (s *CreateViewerSessionService) Create(c *gin.Context) (*ViewerSessionRespo
|
|||
}
|
||||
|
||||
res := &ViewerSessionResponse{Session: viewerSession}
|
||||
if targetViewer.Type == setting.ViewerTypeWopi {
|
||||
if targetViewer.Type == types.ViewerTypeWopi {
|
||||
// For WOPI viewer, generate WOPI src
|
||||
wopiSrc, err := wopi.GenerateWopiSrc(c, s.PreferredAction, targetViewer, viewerSession)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue