1923 lines
50 KiB
Go
1923 lines
50 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
"voltaserve/cache"
|
|
"voltaserve/client"
|
|
"voltaserve/config"
|
|
"voltaserve/errorpkg"
|
|
"voltaserve/guard"
|
|
"voltaserve/helper"
|
|
"voltaserve/infra"
|
|
"voltaserve/model"
|
|
"voltaserve/repo"
|
|
"voltaserve/search"
|
|
|
|
"github.com/reactivex/rxgo/v2"
|
|
)
|
|
|
|
type File struct {
|
|
ID string `json:"id"`
|
|
WorkspaceID string `json:"workspaceId"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
ParentID *string `json:"parentId,omitempty"`
|
|
Version *int64 `json:"version,omitempty"`
|
|
Original *Download `json:"original,omitempty"`
|
|
Preview *Download `json:"preview,omitempty"`
|
|
Thumbnail *Thumbnail `json:"thumbnail,omitempty"`
|
|
Status string `json:"status,omitempty"`
|
|
Snapshots []*Snapshot `json:"snapshots,omitempty"`
|
|
Permission string `json:"permission"`
|
|
IsShared *bool `json:"isShared,omitempty"`
|
|
CreateTime string `json:"createTime"`
|
|
UpdateTime *string `json:"updateTime,omitempty"`
|
|
}
|
|
|
|
type FileList struct {
|
|
Data []*File `json:"data"`
|
|
TotalPages uint `json:"totalPages"`
|
|
TotalElements uint `json:"totalElements"`
|
|
Page uint `json:"page"`
|
|
Size uint `json:"size"`
|
|
Query *FileQuery `json:"query,omitempty"`
|
|
}
|
|
|
|
type FileListOptions struct {
|
|
Page uint
|
|
Size uint
|
|
SortBy string
|
|
SortOrder string
|
|
Query *FileQuery
|
|
}
|
|
|
|
type FileQuery struct {
|
|
Text string `json:"text" validate:"required"`
|
|
Type *string `json:"type,omitempty" validate:"omitempty,oneof=file folder"`
|
|
CreateTimeAfter *int64 `json:"createTimeAfter,omitempty"`
|
|
CreateTimeBefore *int64 `json:"createTimeBefore,omitempty"`
|
|
UpdateTimeAfter *int64 `json:"updateTimeAfter,omitempty"`
|
|
UpdateTimeBefore *int64 `json:"updateTimeBefore,omitempty"`
|
|
}
|
|
|
|
type FileCreateOptions struct {
|
|
WorkspaceID string `json:"workspaceId" validate:"required"`
|
|
Name string `json:"name" validate:"required,max=255"`
|
|
Type string `json:"type" validate:"required,oneof=file folder"`
|
|
ParentID *string `json:"parentId" validate:"required"`
|
|
}
|
|
|
|
type FileCreateFolderOptions struct {
|
|
WorkspaceID string `json:"workspaceId" validate:"required"`
|
|
Name string `json:"name" validate:"required,max=255"`
|
|
ParentID *string `json:"parentId"`
|
|
}
|
|
|
|
type FileCopyOptions struct {
|
|
IDs []string `json:"ids" validate:"required"`
|
|
}
|
|
|
|
type FileBatchDeleteOptions struct {
|
|
IDs []string `json:"ids" validate:"required"`
|
|
}
|
|
|
|
type FileBatchGetOptions struct {
|
|
IDs []string `json:"ids" validate:"required"`
|
|
}
|
|
|
|
type FileGrantUserPermissionOptions struct {
|
|
UserID string `json:"userId" validate:"required"`
|
|
IDs []string `json:"ids" validate:"required"`
|
|
Permission string `json:"permission" validate:"required,oneof=viewer editor owner"`
|
|
}
|
|
|
|
type FileRevokeUserPermissionOptions struct {
|
|
IDs []string `json:"ids" validate:"required"`
|
|
UserID string `json:"userId" validate:"required"`
|
|
}
|
|
|
|
type FileGrantGroupPermissionOptions struct {
|
|
GroupID string `json:"groupId" validate:"required"`
|
|
IDs []string `json:"ids" validate:"required"`
|
|
Permission string `json:"permission" validate:"required,oneof=viewer editor owner"`
|
|
}
|
|
|
|
type FileRevokeGroupPermissionOptions struct {
|
|
IDs []string `json:"ids" validate:"required"`
|
|
GroupID string `json:"groupId" validate:"required"`
|
|
}
|
|
|
|
type FileMoveOptions struct {
|
|
IDs []string `json:"ids" validate:"required"`
|
|
}
|
|
|
|
type FileRenameOptions struct {
|
|
Name string `json:"name" validate:"required,max=255"`
|
|
}
|
|
|
|
type FileUpdateOCRLanguageOptions struct {
|
|
ID string `json:"id" validate:"required"`
|
|
}
|
|
|
|
type Snapshot struct {
|
|
ID string `json:"id"`
|
|
Version int64 `json:"version"`
|
|
Original *Download `json:"original,omitempty"`
|
|
Preview *Download `json:"preview,omitempty"`
|
|
OCR *Download `json:"ocr,omitempty"`
|
|
Thumbnail *Thumbnail `json:"thumbnail,omitempty"`
|
|
Language *string `json:"language,omitempty"`
|
|
Status string `json:"status,omitempty"`
|
|
}
|
|
|
|
type ImageProps struct {
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
}
|
|
|
|
type Thumbnail struct {
|
|
Base64 string `json:"base64"`
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
}
|
|
|
|
type Download struct {
|
|
Extension string `json:"extension"`
|
|
Size int64 `json:"size"`
|
|
Image *ImageProps `json:"image,omitempty"`
|
|
Language *string `json:"language,omitempty"`
|
|
}
|
|
|
|
type UserPermission struct {
|
|
ID string `json:"id"`
|
|
User *User `json:"user"`
|
|
Permission string `json:"permission"`
|
|
}
|
|
|
|
type GroupPermission struct {
|
|
ID string `json:"id"`
|
|
Group *Group `json:"group"`
|
|
Permission string `json:"permission"`
|
|
}
|
|
|
|
type SnapshotUpdateOptions struct {
|
|
Options client.PipelineRunOptions `json:"options"`
|
|
Original *model.S3Object `json:"original,omitempty"`
|
|
Preview *model.S3Object `json:"preview,omitempty"`
|
|
Text *model.S3Object `json:"text,omitempty"`
|
|
OCR *model.S3Object `json:"ocr,omitempty"`
|
|
Thumbnail *model.Thumbnail `json:"thumbnail,omitempty"`
|
|
Status string `json:"status,omitempty"`
|
|
}
|
|
|
|
type FileService struct {
|
|
fileRepo repo.FileRepo
|
|
fileSearch *search.FileSearch
|
|
fileGuard *guard.FileGuard
|
|
fileMapper *FileMapper
|
|
fileCache *cache.FileCache
|
|
workspaceCache *cache.WorkspaceCache
|
|
workspaceRepo repo.WorkspaceRepo
|
|
workspaceGuard *guard.WorkspaceGuard
|
|
workspaceSvc *WorkspaceService
|
|
snapshotRepo repo.SnapshotRepo
|
|
userRepo repo.UserRepo
|
|
userMapper *userMapper
|
|
groupCache *cache.GroupCache
|
|
groupGuard *guard.GroupGuard
|
|
groupMapper *groupMapper
|
|
permissionRepo repo.PermissionRepo
|
|
fileIdent *infra.FileIdentifier
|
|
s3 *infra.S3Manager
|
|
conversionClient *client.ConversionClient
|
|
config config.Config
|
|
}
|
|
|
|
func NewFileService() *FileService {
|
|
return &FileService{
|
|
fileRepo: repo.NewFileRepo(),
|
|
fileCache: cache.NewFileCache(),
|
|
fileSearch: search.NewFileSearch(),
|
|
fileGuard: guard.NewFileGuard(),
|
|
fileMapper: NewFileMapper(),
|
|
workspaceGuard: guard.NewWorkspaceGuard(),
|
|
workspaceCache: cache.NewWorkspaceCache(),
|
|
workspaceRepo: repo.NewWorkspaceRepo(),
|
|
workspaceSvc: NewWorkspaceService(),
|
|
snapshotRepo: repo.NewSnapshotRepo(),
|
|
userRepo: repo.NewUserRepo(),
|
|
userMapper: newUserMapper(),
|
|
groupCache: cache.NewGroupCache(),
|
|
groupGuard: guard.NewGroupGuard(),
|
|
groupMapper: newGroupMapper(),
|
|
permissionRepo: repo.NewPermissionRepo(),
|
|
fileIdent: infra.NewFileIdentifier(),
|
|
s3: infra.NewS3Manager(),
|
|
conversionClient: client.NewConversionClient(),
|
|
config: config.GetConfig(),
|
|
}
|
|
}
|
|
|
|
func (svc *FileService) Create(opts FileCreateOptions, userID string) (*File, error) {
|
|
var components []string
|
|
for _, component := range strings.Split(opts.Name, "/") {
|
|
if component != "" {
|
|
components = append(components, component)
|
|
}
|
|
}
|
|
parentID := opts.ParentID
|
|
if len(components) > 1 {
|
|
for _, component := range components[:len(components)-1] {
|
|
existing, err := svc.getChildWithName(*parentID, component)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if existing != nil {
|
|
parentID = new(string)
|
|
*parentID = existing.GetID()
|
|
} else {
|
|
res, err := svc.create(FileCreateOptions{
|
|
Name: component,
|
|
Type: model.FileTypeFolder,
|
|
ParentID: parentID,
|
|
WorkspaceID: opts.WorkspaceID,
|
|
}, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
parentID = &res.ID
|
|
}
|
|
}
|
|
}
|
|
name := components[len(components)-1]
|
|
return svc.create(FileCreateOptions{
|
|
WorkspaceID: opts.WorkspaceID,
|
|
Name: name,
|
|
Type: opts.Type,
|
|
ParentID: parentID,
|
|
}, userID)
|
|
}
|
|
|
|
func (svc *FileService) create(opts FileCreateOptions, userID string) (*File, error) {
|
|
if len(*opts.ParentID) > 0 {
|
|
if err := svc.validateParent(*opts.ParentID, userID); err != nil {
|
|
return nil, err
|
|
}
|
|
existing, err := svc.getChildWithName(*opts.ParentID, opts.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if existing != nil {
|
|
return nil, errorpkg.NewFileWithSimilarNameExistsError()
|
|
}
|
|
}
|
|
file, err := svc.fileRepo.Insert(repo.FileInsertOptions{
|
|
Name: opts.Name,
|
|
WorkspaceID: opts.WorkspaceID,
|
|
ParentID: opts.ParentID,
|
|
Type: opts.Type,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := svc.fileRepo.GrantUserPermission(file.GetID(), userID, model.PermissionOwner); err != nil {
|
|
return nil, err
|
|
}
|
|
file, err = svc.fileRepo.Find(file.GetID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileSearch.Index([]model.File{file}); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileCache.Set(file); err != nil {
|
|
return nil, err
|
|
}
|
|
res, err := svc.fileMapper.mapOne(file, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) validateParent(id string, userID string) error {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionEditor); err != nil {
|
|
return err
|
|
}
|
|
if file.GetType() != model.FileTypeFolder {
|
|
return errorpkg.NewFileIsNotAFolderError(file)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc *FileService) Store(fileID string, filePath string, userID string) (*File, error) {
|
|
file, err := svc.fileRepo.Find(fileID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileCache.Set(file); err != nil {
|
|
return nil, err
|
|
}
|
|
workspace, err := svc.workspaceCache.Get(file.GetWorkspaceID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
latestVersion, err := svc.snapshotRepo.GetLatestVersionForFile(fileID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
snapshotID := helper.NewID()
|
|
snapshot := repo.NewSnapshot()
|
|
snapshot.SetID(snapshotID)
|
|
snapshot.SetVersion(latestVersion)
|
|
snapshot.SetStatus(model.SnapshotStatusNew)
|
|
if err = svc.snapshotRepo.Save(snapshot); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.snapshotRepo.MapWithFile(snapshotID, fileID); err != nil {
|
|
return nil, err
|
|
}
|
|
stat, err := os.Stat(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
original := model.S3Object{
|
|
Bucket: workspace.GetBucket(),
|
|
Key: fileID + "/" + snapshotID + "/original" + strings.ToLower(filepath.Ext(filePath)),
|
|
Size: stat.Size(),
|
|
}
|
|
if err = svc.s3.PutFile(original.Key, filePath, infra.DetectMimeFromFile(filePath), workspace.GetBucket()); err != nil {
|
|
return nil, err
|
|
}
|
|
snapshot.SetOriginal(&original)
|
|
if err := svc.snapshotRepo.Save(snapshot); err != nil {
|
|
return nil, err
|
|
}
|
|
file, err = svc.fileCache.Refresh(file.GetID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res, err := svc.fileMapper.mapOne(file, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := svc.conversionClient.RunPipeline(&client.PipelineRunOptions{
|
|
FileID: file.GetID(),
|
|
SnapshotID: snapshot.GetID(),
|
|
Bucket: original.Bucket,
|
|
Key: original.Key,
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) UpdateSnapshot(opts SnapshotUpdateOptions, apiKey string) error {
|
|
if apiKey != svc.config.Security.APIKey {
|
|
return errorpkg.NewInvalidAPIKeyError()
|
|
}
|
|
if err := svc.snapshotRepo.Update(opts.Options.SnapshotID, repo.SnapshotUpdateOptions{
|
|
Thumbnail: opts.Thumbnail,
|
|
Original: opts.Original,
|
|
Preview: opts.Preview,
|
|
Text: opts.Text,
|
|
Status: opts.Status,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
file, err := svc.fileCache.Refresh(opts.Options.FileID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = svc.fileSearch.Update([]model.File{file}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc *FileService) DownloadOriginalBuffer(id string, userID string) (*bytes.Buffer, model.File, model.Snapshot, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
snapshots := file.GetSnapshots()
|
|
if len(snapshots) == 0 {
|
|
return nil, nil, nil, errorpkg.NewSnapshotNotFoundError(nil)
|
|
}
|
|
latestSnapshot := snapshots[len(snapshots)-1]
|
|
if latestSnapshot.HasOriginal() {
|
|
original := latestSnapshot.GetOriginal()
|
|
buf, err := svc.s3.GetObject(original.Key, original.Bucket)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return buf, file, latestSnapshot, nil
|
|
} else {
|
|
return nil, nil, nil, errorpkg.NewS3ObjectNotFoundError(nil)
|
|
}
|
|
}
|
|
|
|
func (svc *FileService) DownloadPreviewBuffer(id string, userID string) (*bytes.Buffer, model.File, model.Snapshot, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
snapshots := file.GetSnapshots()
|
|
if len(snapshots) == 0 {
|
|
return nil, nil, nil, errorpkg.NewSnapshotNotFoundError(nil)
|
|
}
|
|
latestSnapshot := snapshots[len(snapshots)-1]
|
|
if latestSnapshot.HasPreview() {
|
|
preview := latestSnapshot.GetPreview()
|
|
buf, err := svc.s3.GetObject(preview.Key, preview.Bucket)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return buf, file, latestSnapshot, nil
|
|
} else {
|
|
return nil, nil, nil, errorpkg.NewS3ObjectNotFoundError(nil)
|
|
}
|
|
}
|
|
|
|
func (svc *FileService) FindByID(ids []string, userID string) ([]*File, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var res []*File
|
|
for _, id := range ids {
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return nil, err
|
|
}
|
|
f, err := svc.fileMapper.mapOne(file, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = append(res, f)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) FindByPath(path string, userID string) (*File, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if path == "/" {
|
|
return &File{
|
|
ID: user.GetID(),
|
|
WorkspaceID: "",
|
|
Name: "/",
|
|
Type: model.FileTypeFolder,
|
|
Permission: "owner",
|
|
CreateTime: user.GetCreateTime(),
|
|
UpdateTime: nil,
|
|
}, nil
|
|
}
|
|
components := []string{}
|
|
for _, v := range strings.Split(path, "/") {
|
|
if v != "" {
|
|
components = append(components, v)
|
|
}
|
|
}
|
|
if len(components) == 0 || components[0] == "" {
|
|
return nil, errorpkg.NewInvalidPathError(fmt.Errorf("invalid path '%s'", path))
|
|
}
|
|
workspace, err := svc.workspaceSvc.Find(helper.WorkspaceIDFromSlug(components[0]), userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(components) == 1 {
|
|
return &File{
|
|
ID: workspace.RootID,
|
|
WorkspaceID: workspace.ID,
|
|
Name: helper.SlugFromWorkspace(workspace.ID, workspace.Name),
|
|
Type: model.FileTypeFolder,
|
|
Permission: workspace.Permission,
|
|
CreateTime: workspace.CreateTime,
|
|
UpdateTime: workspace.UpdateTime,
|
|
}, nil
|
|
}
|
|
currentID := workspace.RootID
|
|
components = components[1:]
|
|
for _, component := range components {
|
|
ids, err := svc.fileRepo.GetChildrenIDs(currentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authorized, err := svc.doAuthorizationByIDs(ids, user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var filtered []model.File
|
|
for _, f := range authorized {
|
|
if f.GetName() == component {
|
|
filtered = append(filtered, f)
|
|
}
|
|
}
|
|
if len(filtered) > 0 {
|
|
item := filtered[0]
|
|
currentID = item.GetID()
|
|
if item.GetType() == model.FileTypeFolder {
|
|
continue
|
|
} else if item.GetType() == model.FileTypeFile {
|
|
break
|
|
}
|
|
} else {
|
|
return nil, errorpkg.NewFileNotFoundError(fmt.Errorf("component not found '%s'", component))
|
|
}
|
|
}
|
|
result, err := svc.FindByID([]string{currentID}, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(result) == 0 {
|
|
return nil, errorpkg.NewFileNotFoundError(fmt.Errorf("item not found '%s'", currentID))
|
|
}
|
|
return result[0], nil
|
|
}
|
|
|
|
func (svc *FileService) ListByPath(path string, userID string) ([]*File, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if path == "/" {
|
|
workspaces, err := svc.workspaceSvc.findAll(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := []*File{}
|
|
for _, w := range workspaces {
|
|
result = append(result, &File{
|
|
ID: w.RootID,
|
|
WorkspaceID: w.ID,
|
|
Name: helper.SlugFromWorkspace(w.ID, w.Name),
|
|
Type: model.FileTypeFolder,
|
|
Permission: w.Permission,
|
|
CreateTime: w.CreateTime,
|
|
UpdateTime: w.UpdateTime,
|
|
})
|
|
}
|
|
return result, nil
|
|
}
|
|
components := []string{}
|
|
for _, v := range strings.Split(path, "/") {
|
|
if v != "" {
|
|
components = append(components, v)
|
|
}
|
|
}
|
|
if len(components) == 0 || components[0] == "" {
|
|
return nil, errorpkg.NewInvalidPathError(fmt.Errorf("invalid path '%s'", path))
|
|
}
|
|
workspace, err := svc.workspaceRepo.Find(helper.WorkspaceIDFromSlug(components[0]))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
currentID := workspace.GetRootID()
|
|
currentType := model.FileTypeFolder
|
|
components = components[1:]
|
|
for _, component := range components {
|
|
ids, err := svc.fileRepo.GetChildrenIDs(currentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authorized, err := svc.doAuthorizationByIDs(ids, user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var filtered []model.File
|
|
for _, f := range authorized {
|
|
if f.GetName() == component {
|
|
filtered = append(filtered, f)
|
|
}
|
|
}
|
|
if len(filtered) > 0 {
|
|
item := filtered[0]
|
|
currentID = item.GetID()
|
|
currentType = item.GetType()
|
|
if item.GetType() == model.FileTypeFolder {
|
|
continue
|
|
} else if item.GetType() == model.FileTypeFile {
|
|
break
|
|
}
|
|
} else {
|
|
return nil, errorpkg.NewFileNotFoundError(fmt.Errorf("component not found '%s'", component))
|
|
}
|
|
}
|
|
if currentType == model.FileTypeFolder {
|
|
ids, err := svc.fileRepo.GetChildrenIDs(currentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authorized, err := svc.doAuthorizationByIDs(ids, user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := svc.fileMapper.mapMany(authorized, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
} else if currentType == model.FileTypeFile {
|
|
result, err := svc.FindByID([]string{currentID}, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
} else {
|
|
return nil, errorpkg.NewInternalServerError(fmt.Errorf("invalid file type %s", currentType))
|
|
}
|
|
}
|
|
|
|
func (svc *FileService) List(id string, opts FileListOptions, userID string) (*FileList, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return nil, err
|
|
}
|
|
if opts.Page < 1 {
|
|
return nil, errorpkg.NewInvalidPageParameterError()
|
|
}
|
|
if opts.Size < 1 {
|
|
return nil, errorpkg.NewInvalidSizeParameterError()
|
|
}
|
|
ids, err := svc.fileRepo.GetChildrenIDs(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var data []model.File
|
|
for _, id := range ids {
|
|
var f model.File
|
|
f, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data = append(data, f)
|
|
}
|
|
var filtered []model.File
|
|
for _, f := range data {
|
|
if opts.Query == nil || *opts.Query.Type == "" || f.GetType() == *opts.Query.Type {
|
|
filtered = append(filtered, f)
|
|
}
|
|
}
|
|
authorized, err := svc.doAuthorization(filtered, user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sorted := svc.doSorting(authorized, opts.SortBy, opts.SortOrder, userID)
|
|
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
|
mapped, err := svc.fileMapper.mapMany(paged, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &FileList{
|
|
Data: mapped,
|
|
TotalElements: totalElements,
|
|
TotalPages: totalPages,
|
|
Page: opts.Page,
|
|
Size: opts.Size,
|
|
}, nil
|
|
}
|
|
|
|
func (svc *FileService) Search(id string, opts FileListOptions, userID string) (*FileList, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
parent, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
workspace, err := svc.workspaceRepo.Find(parent.GetWorkspaceID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := svc.workspaceGuard.Authorize(user, workspace, model.PermissionViewer); err != nil {
|
|
return nil, err
|
|
}
|
|
data, err := svc.fileSearch.Query(opts.Query.Text)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
filtered, err := svc.doQueryFiltering(data, *opts.Query, parent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authorized, err := svc.doAuthorization(filtered, user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sorted := svc.doSorting(authorized, opts.SortBy, opts.SortOrder, userID)
|
|
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
|
v, err := svc.fileMapper.mapMany(paged, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res := &FileList{
|
|
Data: v,
|
|
TotalElements: totalElements,
|
|
TotalPages: totalPages,
|
|
Page: opts.Page,
|
|
Size: opts.Size,
|
|
Query: opts.Query,
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) GetPath(id string, userID string) ([]*File, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return nil, err
|
|
}
|
|
path, err := svc.fileRepo.FindPath(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res := []*File{}
|
|
for _, f := range path {
|
|
v, err := svc.fileMapper.mapOne(f, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = append([]*File{v}, res...)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) GetIDs(id string, userID string) ([]string, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return nil, err
|
|
}
|
|
ids, err := svc.fileRepo.GetChildrenIDs(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (svc *FileService) Copy(targetID string, sourceIDs []string, userID string) (copiedFiles []*File, err error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
target, err := svc.fileCache.Get(targetID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
/* Do checks */
|
|
for _, sourceID := range sourceIDs {
|
|
var source model.File
|
|
if source, err = svc.fileCache.Get(sourceID); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, target, model.PermissionEditor); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, source, model.PermissionEditor); err != nil {
|
|
return nil, err
|
|
}
|
|
if source.GetID() == target.GetID() {
|
|
return nil, errorpkg.NewFileCannotBeCopiedIntoIselfError(source)
|
|
}
|
|
if target.GetType() != model.FileTypeFolder {
|
|
return nil, errorpkg.NewFileIsNotAFolderError(target)
|
|
}
|
|
if yes, _ := svc.fileRepo.IsGrandChildOf(target.GetID(), source.GetID()); yes {
|
|
return nil, errorpkg.NewFileCannotBeCopiedIntoOwnSubtreeError(source)
|
|
}
|
|
}
|
|
|
|
/* Do copying */
|
|
allClones := []model.File{}
|
|
for _, sourceID := range sourceIDs {
|
|
/* Get original tree */
|
|
var sourceTree []model.File
|
|
if sourceTree, err = svc.fileRepo.FindTree(sourceID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
/* Clone source tree */
|
|
var rootCloneIndex int
|
|
var cloneIDs = make(map[string]string)
|
|
var originalIDs = make(map[string]string)
|
|
var clones []model.File
|
|
var permissions []*repo.UserPermission
|
|
for i, o := range sourceTree {
|
|
c := repo.NewFile()
|
|
c.SetID(helper.NewID())
|
|
c.SetParentID(o.GetParentID())
|
|
c.SetWorkspaceID(o.GetWorkspaceID())
|
|
c.SetType(o.GetType())
|
|
c.SetName(o.GetName())
|
|
c.SetCreateTime(time.Now().UTC().Format(time.RFC3339))
|
|
if o.GetID() == sourceID {
|
|
rootCloneIndex = i
|
|
}
|
|
cloneIDs[o.GetID()] = c.GetID()
|
|
originalIDs[c.GetID()] = o.GetID()
|
|
clones = append(clones, c)
|
|
permissions = append(permissions, &repo.UserPermission{
|
|
ID: helper.NewID(),
|
|
UserID: userID,
|
|
ResourceID: c.GetID(),
|
|
Permission: model.PermissionOwner,
|
|
CreateTime: time.Now().UTC().Format(time.RFC3339),
|
|
})
|
|
}
|
|
|
|
/* Set parent IDs of clones */
|
|
for i, c := range clones {
|
|
id := cloneIDs[*c.GetParentID()]
|
|
clones[i].SetParentID(&id)
|
|
}
|
|
|
|
rootClone := clones[rootCloneIndex]
|
|
|
|
/* Parent ID of root clone is target ID */
|
|
if clones != nil {
|
|
rootClone.SetParentID(&targetID)
|
|
}
|
|
|
|
/* If there is a file with similar name, append a prefix */
|
|
existing, err := svc.getChildWithName(targetID, rootClone.GetName())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if existing != nil {
|
|
rootClone.SetName(fmt.Sprintf("Copy of %s", rootClone.GetName()))
|
|
}
|
|
|
|
/* Persist clones */
|
|
if err = svc.fileRepo.BulkInsert(clones, 100); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
/* Persist permissions */
|
|
if err = svc.fileRepo.BulkInsertPermissions(permissions, 100); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
/* Assign snapshots to clones */
|
|
for _, c := range clones {
|
|
if err := svc.fileRepo.AssignSnapshots(c.GetID(), originalIDs[c.GetID()]); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
/* Index clones for search */
|
|
if err := svc.fileSearch.Index(clones); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
/* Create cache for clones */
|
|
for _, c := range clones {
|
|
if _, err := svc.fileCache.Refresh(c.GetID()); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
allClones = append(allClones, clones...)
|
|
}
|
|
|
|
/* Refresh updateTime on target */
|
|
timeNow := time.Now().UTC().Format(time.RFC3339)
|
|
target.SetUpdateTime(&timeNow)
|
|
if err := svc.fileRepo.Save(target); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
copiedFiles, err = svc.fileMapper.mapMany(allClones, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return copiedFiles, nil
|
|
}
|
|
|
|
func (svc *FileService) Move(targetID string, sourceIDs []string, userID string) (parentIDs []string, err error) {
|
|
parentIDs = []string{}
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
target, err := svc.fileCache.Get(targetID)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
/* Do checks */
|
|
for _, id := range sourceIDs {
|
|
source, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
if source.GetParentID() != nil {
|
|
existing, err := svc.getChildWithName(targetID, source.GetName())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if existing != nil {
|
|
return nil, errorpkg.NewFileWithSimilarNameExistsError()
|
|
}
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, target, model.PermissionEditor); err != nil {
|
|
return []string{}, err
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, source, model.PermissionEditor); err != nil {
|
|
return []string{}, err
|
|
}
|
|
if source.GetParentID() != nil && *source.GetParentID() == target.GetID() {
|
|
return []string{}, errorpkg.NewFileAlreadyChildOfDestinationError(source, target)
|
|
}
|
|
if target.GetID() == source.GetID() {
|
|
return []string{}, errorpkg.NewFileCannotBeMovedIntoItselfError(source)
|
|
}
|
|
if target.GetType() != model.FileTypeFolder {
|
|
return []string{}, errorpkg.NewFileIsNotAFolderError(target)
|
|
}
|
|
targetIsGrandChildOfSource, _ := svc.fileRepo.IsGrandChildOf(target.GetID(), source.GetID())
|
|
if targetIsGrandChildOfSource {
|
|
return []string{}, errorpkg.NewTargetIsGrandChildOfSourceError(source)
|
|
}
|
|
}
|
|
|
|
/* Do moving */
|
|
for _, id := range sourceIDs {
|
|
source, _ := svc.fileCache.Get(id)
|
|
|
|
/* Add old parent */
|
|
parentIDs = append(parentIDs, *source.GetParentID())
|
|
|
|
/* Move source into target */
|
|
if err := svc.fileRepo.MoveSourceIntoTarget(target.GetID(), source.GetID()); err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
/* Get updated source */
|
|
source, err = svc.fileRepo.Find(source.GetID())
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
// Add new parent
|
|
parentIDs = append(parentIDs, *source.GetParentID())
|
|
|
|
/* Refresh updateTime on source and target */
|
|
timeNow := time.Now().UTC().Format(time.RFC3339)
|
|
source.SetUpdateTime(&timeNow)
|
|
if err := svc.fileRepo.Save(source); err != nil {
|
|
return []string{}, err
|
|
}
|
|
target.SetUpdateTime(&timeNow)
|
|
if err := svc.fileRepo.Save(target); err != nil {
|
|
return []string{}, err
|
|
}
|
|
if err := svc.fileSearch.Update([]model.File{source}); err != nil {
|
|
return []string{}, err
|
|
}
|
|
sourceTree, err := svc.fileRepo.FindTree(source.GetID())
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
for _, f := range sourceTree {
|
|
if err := svc.fileCache.Set(f); err != nil {
|
|
return []string{}, err
|
|
}
|
|
}
|
|
}
|
|
return parentIDs, nil
|
|
}
|
|
|
|
func (svc *FileService) Rename(id string, name string, userID string) (*File, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if file.GetParentID() != nil {
|
|
existing, err := svc.getChildWithName(*file.GetParentID(), name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if existing != nil {
|
|
return nil, errorpkg.NewFileWithSimilarNameExistsError()
|
|
}
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionEditor); err != nil {
|
|
return nil, err
|
|
}
|
|
file.SetName(name)
|
|
if err = svc.fileRepo.Save(file); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.fileSearch.Update([]model.File{file}); err != nil {
|
|
return nil, err
|
|
}
|
|
err = svc.fileCache.Set(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res, err := svc.fileMapper.mapOne(file, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) Delete(ids []string, userID string) ([]string, error) {
|
|
var res []string
|
|
for _, id := range ids {
|
|
var user model.User
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if file.GetParentID() == nil {
|
|
workspace, err := svc.workspaceCache.Get(file.GetWorkspaceID())
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
return nil, errorpkg.NewCannotDeleteWorkspaceRootError(file, workspace)
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionOwner); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Add parent
|
|
res = append(res, *file.GetParentID())
|
|
|
|
var tree []model.File
|
|
tree, err = svc.fileRepo.FindTree(file.GetID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var treeIDs []string
|
|
for _, f := range tree {
|
|
treeIDs = append(treeIDs, f.GetID())
|
|
}
|
|
if err := svc.fileSearch.Delete(treeIDs); err != nil {
|
|
// Here we don't return an error or panic but we just print the error
|
|
fmt.Println(err)
|
|
}
|
|
for _, f := range tree {
|
|
if err = svc.fileCache.Delete(f.GetID()); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for _, f := range tree {
|
|
if err = svc.fileRepo.Delete(f.GetID()); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = svc.snapshotRepo.DeleteMappingsForFile(f.GetID()); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
var danglingSnapshots []model.Snapshot
|
|
danglingSnapshots, err = svc.snapshotRepo.FindAllDangling()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, s := range danglingSnapshots {
|
|
if s.HasOriginal() {
|
|
if err = svc.s3.RemoveObject(s.GetOriginal().Key, s.GetOriginal().Bucket); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if s.HasPreview() {
|
|
if err = svc.s3.RemoveObject(s.GetPreview().Key, s.GetPreview().Bucket); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if s.HasText() {
|
|
if err = svc.s3.RemoveObject(s.GetText().Key, s.GetText().Bucket); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
if err = svc.snapshotRepo.DeleteAllDangling(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) GetSize(id string, userID string) (int64, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return -1, err
|
|
}
|
|
res, err := svc.fileRepo.GetSize(id)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) GetItemCount(id string, userID string) (int64, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
|
return 0, err
|
|
}
|
|
res, err := svc.fileRepo.GetItemCount(id)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) GrantUserPermission(ids []string, assigneeID string, permission string, userID string) error {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, id := range ids {
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionOwner); err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.userRepo.Find(assigneeID); err != nil {
|
|
return err
|
|
}
|
|
if err = svc.fileRepo.GrantUserPermission(id, assigneeID, permission); err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.fileCache.Refresh(file.GetID()); err != nil {
|
|
return err
|
|
}
|
|
workspace, err := svc.workspaceRepo.Find(file.GetWorkspaceID())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = svc.workspaceCache.Set(workspace); err != nil {
|
|
return err
|
|
}
|
|
path, err := svc.fileRepo.FindPath(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, f := range path {
|
|
if err := svc.fileCache.Set(f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
tree, err := svc.fileRepo.FindTree(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, f := range tree {
|
|
if err := svc.fileCache.Set(f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc *FileService) RevokeUserPermission(ids []string, assigneeID string, userID string) error {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, id := range ids {
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, file, model.PermissionOwner); err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.userRepo.Find(assigneeID); err != nil {
|
|
return err
|
|
}
|
|
if err := svc.fileRepo.RevokeUserPermission(id, assigneeID); err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.fileCache.Refresh(file.GetID()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc *FileService) GrantGroupPermission(ids []string, groupID string, permission string, userID string) error {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, id := range ids {
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = svc.fileGuard.Authorize(user, file, model.PermissionOwner); err != nil {
|
|
return err
|
|
}
|
|
group, err := svc.groupCache.Get(groupID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := svc.groupGuard.Authorize(user, group, model.PermissionViewer); err != nil {
|
|
return err
|
|
}
|
|
if err = svc.fileRepo.GrantGroupPermission(id, groupID, permission); err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.fileCache.Refresh(file.GetID()); err != nil {
|
|
return err
|
|
}
|
|
workspace, err := svc.workspaceRepo.Find(file.GetWorkspaceID())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = svc.workspaceCache.Set(workspace); err != nil {
|
|
return err
|
|
}
|
|
path, err := svc.fileRepo.FindPath(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, f := range path {
|
|
if err := svc.fileCache.Set(f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
tree, err := svc.fileRepo.FindTree(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, f := range tree {
|
|
if err := svc.fileCache.Set(f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc *FileService) RevokeGroupPermission(ids []string, groupID string, userID string) error {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, id := range ids {
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, file, model.PermissionOwner); err != nil {
|
|
return err
|
|
}
|
|
group, err := svc.groupCache.Get(groupID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := svc.groupGuard.Authorize(user, group, model.PermissionViewer); err != nil {
|
|
return err
|
|
}
|
|
if err := svc.fileRepo.RevokeGroupPermission(id, groupID); err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.fileCache.Refresh(file.GetID()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc *FileService) GetUserPermissions(id string, userID string) ([]*UserPermission, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, file, model.PermissionOwner); err != nil {
|
|
return nil, err
|
|
}
|
|
permissions, err := svc.permissionRepo.GetUserPermissions(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res := make([]*UserPermission, 0)
|
|
for _, p := range permissions {
|
|
if p.UserID == userID {
|
|
continue
|
|
}
|
|
u, err := svc.userRepo.Find(p.UserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = append(res, &UserPermission{
|
|
ID: p.ID,
|
|
User: svc.userMapper.mapOne(u),
|
|
Permission: p.Permission,
|
|
})
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) GetGroupPermissions(id string, userID string) ([]*GroupPermission, error) {
|
|
user, err := svc.userRepo.Find(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := svc.fileGuard.Authorize(user, file, model.PermissionOwner); err != nil {
|
|
return nil, err
|
|
}
|
|
permissions, err := svc.permissionRepo.GetGroupPermissions(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res := make([]*GroupPermission, 0)
|
|
for _, p := range permissions {
|
|
m, err := svc.groupCache.Get(p.GroupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
g, err := svc.groupMapper.mapOne(m, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = append(res, &GroupPermission{
|
|
ID: p.ID,
|
|
Group: g,
|
|
Permission: p.Permission,
|
|
})
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) doAuthorization(data []model.File, user model.User) ([]model.File, error) {
|
|
var res []model.File
|
|
for _, f := range data {
|
|
if svc.fileGuard.IsAuthorized(user, f, model.PermissionViewer) {
|
|
res = append(res, f)
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) doAuthorizationByIDs(ids []string, user model.User) ([]model.File, error) {
|
|
var res []model.File
|
|
for _, id := range ids {
|
|
var f model.File
|
|
f, err := svc.fileCache.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if svc.fileGuard.IsAuthorized(user, f, model.PermissionViewer) {
|
|
res = append(res, f)
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) doSorting(data []model.File, sortBy string, sortOrder string, userID string) []model.File {
|
|
if sortBy == SortByName {
|
|
sort.Slice(data, func(i, j int) bool {
|
|
if sortOrder == SortOrderDesc {
|
|
return data[i].GetName() > data[j].GetName()
|
|
} else {
|
|
return data[i].GetName() < data[j].GetName()
|
|
}
|
|
})
|
|
return data
|
|
} else if sortBy == SortBySize {
|
|
sort.Slice(data, func(i, j int) bool {
|
|
fileA, err := svc.fileMapper.mapOne(data[i], userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
fileB, err := svc.fileMapper.mapOne(data[j], userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
var sizeA int64 = 0
|
|
if fileA.Original != nil {
|
|
sizeA = int64(fileA.Original.Size)
|
|
}
|
|
var sizeB int64 = 0
|
|
if fileB.Original != nil {
|
|
sizeB = int64(fileB.Original.Size)
|
|
}
|
|
if sortOrder == SortOrderDesc {
|
|
return sizeA > sizeB
|
|
} else {
|
|
return sizeA < sizeB
|
|
}
|
|
})
|
|
return data
|
|
} else if sortBy == SortByDateCreated {
|
|
sort.Slice(data, func(i, j int) bool {
|
|
a, _ := time.Parse(time.RFC3339, data[i].GetCreateTime())
|
|
b, _ := time.Parse(time.RFC3339, data[j].GetCreateTime())
|
|
if sortOrder == SortOrderDesc {
|
|
return a.UnixMilli() > b.UnixMilli()
|
|
} else {
|
|
return a.UnixMilli() < b.UnixMilli()
|
|
}
|
|
})
|
|
return data
|
|
} else if sortBy == SortByDateModified {
|
|
sort.Slice(data, func(i, j int) bool {
|
|
if data[i].GetUpdateTime() != nil && data[j].GetUpdateTime() != nil {
|
|
a, _ := time.Parse(time.RFC3339, *data[i].GetUpdateTime())
|
|
b, _ := time.Parse(time.RFC3339, *data[j].GetUpdateTime())
|
|
if sortOrder == SortOrderDesc {
|
|
return a.UnixMilli() > b.UnixMilli()
|
|
} else {
|
|
return a.UnixMilli() < b.UnixMilli()
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
})
|
|
return data
|
|
} else if sortBy == SortByKind {
|
|
folders, _ := rxgo.Just(data)().
|
|
Filter(func(v interface{}) bool {
|
|
return v.(model.File).GetType() == model.FileTypeFolder
|
|
}).
|
|
ToSlice(0)
|
|
files, _ := rxgo.Just(data)().
|
|
Filter(func(v interface{}) bool {
|
|
return v.(model.File).GetType() == model.FileTypeFile
|
|
}).
|
|
ToSlice(0)
|
|
images, _ := rxgo.Just(files)().
|
|
Filter(func(v interface{}) bool {
|
|
f, err := svc.fileMapper.mapOne(v.(model.File), userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if f.Original == nil {
|
|
return false
|
|
}
|
|
if svc.fileIdent.IsImage(f.Original.Extension) {
|
|
return true
|
|
}
|
|
return false
|
|
}).
|
|
ToSlice(0)
|
|
pdfs, _ := rxgo.Just(files)().
|
|
Filter(func(v interface{}) bool {
|
|
f, err := svc.fileMapper.mapOne(v.(model.File), userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if f.Original == nil {
|
|
return false
|
|
}
|
|
if svc.fileIdent.IsPDF(f.Original.Extension) {
|
|
return true
|
|
}
|
|
return false
|
|
}).
|
|
ToSlice(0)
|
|
documents, _ := rxgo.Just(files)().
|
|
Filter(func(v interface{}) bool {
|
|
f, err := svc.fileMapper.mapOne(v.(model.File), userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if f.Original == nil {
|
|
return false
|
|
}
|
|
if svc.fileIdent.IsOffice(f.Original.Extension) {
|
|
return true
|
|
}
|
|
return false
|
|
}).
|
|
ToSlice(0)
|
|
videos, _ := rxgo.Just(files)().
|
|
Filter(func(v interface{}) bool {
|
|
f, err := svc.fileMapper.mapOne(v.(model.File), userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if f.Original == nil {
|
|
return false
|
|
}
|
|
if svc.fileIdent.IsVideo(f.Original.Extension) {
|
|
return true
|
|
}
|
|
return false
|
|
}).
|
|
ToSlice(0)
|
|
texts, _ := rxgo.Just(files)().
|
|
Filter(func(v interface{}) bool {
|
|
f, err := svc.fileMapper.mapOne(v.(model.File), userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if f.Original == nil {
|
|
return false
|
|
}
|
|
if svc.fileIdent.IsPlainText(f.Original.Extension) {
|
|
return true
|
|
}
|
|
return false
|
|
}).
|
|
ToSlice(0)
|
|
others, _ := rxgo.Just(files)().
|
|
Filter(func(v interface{}) bool {
|
|
f, err := svc.fileMapper.mapOne(v.(model.File), userID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if f.Original == nil {
|
|
return false
|
|
}
|
|
if !svc.fileIdent.IsImage(f.Original.Extension) &&
|
|
!svc.fileIdent.IsPDF(f.Original.Extension) &&
|
|
!svc.fileIdent.IsOffice(f.Original.Extension) &&
|
|
!svc.fileIdent.IsVideo(f.Original.Extension) &&
|
|
!svc.fileIdent.IsPlainText(f.Original.Extension) {
|
|
return true
|
|
}
|
|
return false
|
|
}).
|
|
ToSlice(0)
|
|
var res []model.File
|
|
for _, v := range folders {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return data
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
for _, v := range images {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return data
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
for _, v := range pdfs {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return data
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
for _, v := range documents {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return data
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
for _, v := range videos {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return data
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
for _, v := range texts {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return data
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
for _, v := range others {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return data
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
return res
|
|
}
|
|
return data
|
|
}
|
|
|
|
func (svc *FileService) doPagination(data []model.File, page, size uint) ([]model.File, uint, uint) {
|
|
totalElements := uint(len(data))
|
|
totalPages := (totalElements + size - 1) / size
|
|
if page > totalPages {
|
|
return nil, totalElements, totalPages
|
|
}
|
|
startIndex := (page - 1) * size
|
|
endIndex := startIndex + size
|
|
if endIndex > totalElements {
|
|
endIndex = totalElements
|
|
}
|
|
pageData := data[startIndex:endIndex]
|
|
return pageData, totalElements, totalPages
|
|
}
|
|
|
|
func (svc *FileService) doQueryFiltering(data []model.File, opts FileQuery, parent model.File) ([]model.File, error) {
|
|
filtered, _ := rxgo.Just(data)().
|
|
Filter(func(v interface{}) bool {
|
|
return v.(model.File).GetWorkspaceID() == parent.GetWorkspaceID()
|
|
}).
|
|
Filter(func(v interface{}) bool {
|
|
if opts.Type != nil {
|
|
return v.(model.File).GetType() == *opts.Type
|
|
} else {
|
|
return true
|
|
}
|
|
}).
|
|
Filter(func(v interface{}) bool {
|
|
file := v.(model.File)
|
|
res, err := svc.fileRepo.IsGrandChildOf(file.GetID(), parent.GetID())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return res
|
|
}).
|
|
Filter(func(v interface{}) bool {
|
|
if opts.CreateTimeBefore != nil {
|
|
t, _ := time.Parse(time.RFC3339, v.(model.File).GetCreateTime())
|
|
return t.UnixMilli() >= *opts.CreateTimeAfter
|
|
} else {
|
|
return true
|
|
}
|
|
}).
|
|
Filter(func(v interface{}) bool {
|
|
if opts.CreateTimeBefore != nil {
|
|
t, _ := time.Parse(time.RFC3339, v.(model.File).GetCreateTime())
|
|
return t.UnixMilli() <= *opts.CreateTimeBefore
|
|
} else {
|
|
return true
|
|
}
|
|
}).
|
|
Filter(func(v interface{}) bool {
|
|
if opts.UpdateTimeAfter != nil {
|
|
file := v.(model.File)
|
|
t, _ := time.Parse(time.RFC3339, v.(model.File).GetCreateTime())
|
|
return file.GetUpdateTime() != nil && t.UnixMilli() >= *opts.UpdateTimeAfter
|
|
} else {
|
|
return true
|
|
}
|
|
}).
|
|
Filter(func(v interface{}) bool {
|
|
if opts.UpdateTimeBefore != nil {
|
|
file := v.(model.File)
|
|
t, _ := time.Parse(time.RFC3339, v.(model.File).GetCreateTime())
|
|
return file.GetUpdateTime() != nil && t.UnixMilli() <= *opts.UpdateTimeBefore
|
|
} else {
|
|
return true
|
|
}
|
|
}).
|
|
ToSlice(0)
|
|
var res []model.File
|
|
for _, v := range filtered {
|
|
var file model.File
|
|
file, err := svc.fileCache.Get(v.(model.File).GetID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = append(res, file)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc *FileService) getChildWithName(id string, name string) (model.File, error) {
|
|
children, err := svc.fileRepo.FindChildren(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, child := range children {
|
|
if child.GetName() == name {
|
|
return child, nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
type FileMapper struct {
|
|
groupCache *cache.GroupCache
|
|
config config.Config
|
|
}
|
|
|
|
func NewFileMapper() *FileMapper {
|
|
return &FileMapper{
|
|
groupCache: cache.NewGroupCache(),
|
|
config: config.GetConfig(),
|
|
}
|
|
}
|
|
|
|
func (mp *FileMapper) mapOne(m model.File, userID string) (*File, error) {
|
|
snapshots := m.GetSnapshots()
|
|
res := &File{
|
|
ID: m.GetID(),
|
|
WorkspaceID: m.GetWorkspaceID(),
|
|
Name: m.GetName(),
|
|
Type: m.GetType(),
|
|
ParentID: m.GetParentID(),
|
|
Snapshots: mp.mapSnapshots(snapshots),
|
|
CreateTime: m.GetCreateTime(),
|
|
UpdateTime: m.GetUpdateTime(),
|
|
}
|
|
if len(snapshots) > 0 {
|
|
latest := mp.mapSnapshot(snapshots[len(snapshots)-1])
|
|
res.Version = &latest.Version
|
|
res.Original = latest.Original
|
|
res.Preview = latest.Preview
|
|
res.Thumbnail = latest.Thumbnail
|
|
res.Status = latest.Status
|
|
}
|
|
res.Permission = ""
|
|
for _, p := range m.GetUserPermissions() {
|
|
if p.GetUserID() == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
|
res.Permission = p.GetValue()
|
|
}
|
|
}
|
|
for _, p := range m.GetGroupPermissions() {
|
|
g, err := mp.groupCache.Get(p.GetGroupID())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, u := range g.GetUsers() {
|
|
if u == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
|
res.Permission = p.GetValue()
|
|
}
|
|
}
|
|
}
|
|
shareCount := 0
|
|
for _, p := range m.GetUserPermissions() {
|
|
if p.GetUserID() != userID {
|
|
shareCount++
|
|
}
|
|
}
|
|
if res.Permission == model.PermissionOwner {
|
|
shareCount += len(m.GetGroupPermissions())
|
|
res.IsShared = new(bool)
|
|
if shareCount > 0 {
|
|
*res.IsShared = true
|
|
} else {
|
|
*res.IsShared = false
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (mp *FileMapper) mapMany(data []model.File, userID string) ([]*File, error) {
|
|
res := make([]*File, 0)
|
|
for _, f := range data {
|
|
v, err := mp.mapOne(f, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = append(res, v)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (mp *FileMapper) mapSnapshot(m model.Snapshot) *Snapshot {
|
|
s := &Snapshot{
|
|
ID: m.GetID(),
|
|
Version: m.GetVersion(),
|
|
Status: m.GetStatus(),
|
|
}
|
|
if m.HasOriginal() {
|
|
s.Original = mp.mapOriginal(m.GetOriginal())
|
|
}
|
|
if m.HasPreview() {
|
|
s.Preview = mp.mapPreview(m.GetPreview())
|
|
}
|
|
if m.HasThumbnail() {
|
|
s.Thumbnail = mp.mapThumbnail(m.GetThumbnail())
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (mp *FileMapper) mapOriginal(m *model.S3Object) *Download {
|
|
download := &Download{
|
|
Extension: filepath.Ext(m.Key),
|
|
Size: m.Size,
|
|
}
|
|
if m.Image != nil {
|
|
download.Image = &ImageProps{
|
|
Width: m.Image.Width,
|
|
Height: m.Image.Height,
|
|
}
|
|
}
|
|
return download
|
|
}
|
|
|
|
func (mp *FileMapper) mapPreview(m *model.S3Object) *Download {
|
|
download := &Download{
|
|
Extension: filepath.Ext(m.Key),
|
|
Size: m.Size,
|
|
}
|
|
if m.Image != nil {
|
|
download.Image = &ImageProps{
|
|
Width: m.Image.Width,
|
|
Height: m.Image.Height,
|
|
}
|
|
}
|
|
return download
|
|
}
|
|
|
|
func (mp *FileMapper) mapThumbnail(m *model.Thumbnail) *Thumbnail {
|
|
return &Thumbnail{
|
|
Base64: m.Base64,
|
|
Width: m.Width,
|
|
Height: m.Height,
|
|
}
|
|
}
|
|
|
|
func (mp *FileMapper) mapSnapshots(snapshots []model.Snapshot) []*Snapshot {
|
|
res := make([]*Snapshot, 0)
|
|
for _, s := range snapshots {
|
|
res = append(res, mp.mapSnapshot(s))
|
|
}
|
|
return res
|
|
}
|