Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
|
|
@ -0,0 +1,50 @@
|
|||
// monorepo/cloud/backend/internal/usecase/filemetadata/anonymize_old_ips.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
)
|
||||
|
||||
type AnonymizeOldIPsUseCase interface {
|
||||
Execute(ctx context.Context, cutoffDate time.Time) (int, error)
|
||||
}
|
||||
|
||||
type anonymizeOldIPsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewAnonymizeOldIPsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) AnonymizeOldIPsUseCase {
|
||||
logger = logger.Named("FileMetadataAnonymizeOldIPsUseCase")
|
||||
return &anonymizeOldIPsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *anonymizeOldIPsUseCaseImpl) Execute(ctx context.Context, cutoffDate time.Time) (int, error) {
|
||||
uc.logger.Debug("Anonymizing old IPs in file metadata tables",
|
||||
zap.Time("cutoff_date", cutoffDate))
|
||||
|
||||
count, err := uc.repo.AnonymizeOldIPs(ctx, cutoffDate)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to anonymize old IPs in file metadata tables",
|
||||
zap.Error(err),
|
||||
zap.Time("cutoff_date", cutoffDate))
|
||||
return 0, err
|
||||
}
|
||||
|
||||
uc.logger.Info("Successfully anonymized old IPs in file metadata tables",
|
||||
zap.Int("count", count),
|
||||
zap.Time("cutoff_date", cutoffDate))
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/filemetadata/anonymize_user_references.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
)
|
||||
|
||||
// AnonymizeUserReferencesUseCase handles anonymizing CreatedByUserID and ModifiedByUserID
|
||||
// references when a user is deleted, replacing them with a special "deleted user" UUID.
|
||||
type AnonymizeUserReferencesUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) (int, error)
|
||||
}
|
||||
|
||||
type anonymizeUserReferencesUseCaseImpl struct {
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
// NewAnonymizeUserReferencesUseCase creates a new use case for anonymizing user references in files
|
||||
func NewAnonymizeUserReferencesUseCase(
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) AnonymizeUserReferencesUseCase {
|
||||
return &anonymizeUserReferencesUseCaseImpl{
|
||||
logger: logger,
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
// DeletedUserUUID is a well-known UUID representing a deleted user
|
||||
// UUID: 00000000-0000-0000-0000-000000000001 (DELETED_USER)
|
||||
var DeletedUserUUID = gocql.UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||
|
||||
func (uc *anonymizeUserReferencesUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) (int, error) {
|
||||
uc.logger.Info("Anonymizing user references in file metadata",
|
||||
zap.String("user_id", userID.String()))
|
||||
|
||||
// Get all files created or modified by this user
|
||||
files, err := uc.repo.GetByCreatedByUserID(userID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get files by created_by_user_id",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return 0, fmt.Errorf("failed to get files by creator: %w", err)
|
||||
}
|
||||
|
||||
updatedCount := 0
|
||||
|
||||
// Update each file to replace user references with deleted user UUID
|
||||
for _, file := range files {
|
||||
needsUpdate := false
|
||||
|
||||
// Check if this file has references to the deleted user
|
||||
if file.CreatedByUserID == userID {
|
||||
file.CreatedByUserID = DeletedUserUUID
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
if file.ModifiedByUserID == userID {
|
||||
file.ModifiedByUserID = DeletedUserUUID
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
// Update the file with anonymized references
|
||||
if err := uc.repo.Update(file); err != nil {
|
||||
uc.logger.Error("Failed to anonymize user references in file",
|
||||
zap.String("file_id", file.ID.String()),
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
// Continue with other files even if one fails
|
||||
continue
|
||||
}
|
||||
updatedCount++
|
||||
}
|
||||
}
|
||||
|
||||
uc.logger.Info("✅ Anonymized user references in file metadata",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("files_updated", updatedCount))
|
||||
|
||||
return updatedCount, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/check_access.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type CheckFileAccessUseCase interface {
|
||||
Execute(fileID, userID gocql.UUID) (bool, error)
|
||||
}
|
||||
|
||||
type checkFileAccessUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewCheckFileAccessUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CheckFileAccessUseCase {
|
||||
logger = logger.Named("CheckFileAccessUseCase")
|
||||
return &checkFileAccessUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *checkFileAccessUseCaseImpl) Execute(fileID, userID gocql.UUID) (bool, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if fileID.String() == "" {
|
||||
e["file_id"] = "File ID is required"
|
||||
}
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file access check",
|
||||
zap.Any("error", e))
|
||||
return false, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Check access in database.
|
||||
//
|
||||
|
||||
return uc.repo.CheckIfUserHasAccess(fileID, userID)
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/check_exists.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type CheckFileExistsUseCase interface {
|
||||
Execute(id gocql.UUID) (bool, error)
|
||||
}
|
||||
|
||||
type checkFileExistsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewCheckFileExistsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CheckFileExistsUseCase {
|
||||
logger = logger.Named("CheckFileExistsUseCase")
|
||||
return &checkFileExistsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *checkFileExistsUseCaseImpl) Execute(id gocql.UUID) (bool, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "File ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file existence check",
|
||||
zap.Any("error", e))
|
||||
return false, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Check existence in database.
|
||||
//
|
||||
|
||||
return uc.repo.CheckIfExistsByID(id)
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/filemetadata/count_files.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// CountFilesResponse contains the file count for a user
|
||||
type CountFilesResponse struct {
|
||||
TotalFiles int `json:"total_files"`
|
||||
}
|
||||
|
||||
type CountUserFilesUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) (*CountFilesResponse, error)
|
||||
}
|
||||
|
||||
type countUserFilesUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
fileRepo dom_file.FileMetadataRepository
|
||||
collectionRepo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewCountUserFilesUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
collectionRepo dom_collection.CollectionRepository,
|
||||
) CountUserFilesUseCase {
|
||||
logger = logger.Named("CountUserFilesUseCase")
|
||||
return &countUserFilesUseCaseImpl{config, logger, fileRepo, collectionRepo}
|
||||
}
|
||||
|
||||
func (uc *countUserFilesUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) (*CountFilesResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating count user files",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get accessible collections for the user.
|
||||
//
|
||||
|
||||
// Get collections using the efficient filtered query
|
||||
filterOptions := dom_collection.CollectionFilterOptions{
|
||||
UserID: userID,
|
||||
IncludeOwned: true,
|
||||
IncludeShared: true,
|
||||
}
|
||||
|
||||
collectionResult, err := uc.collectionRepo.GetCollectionsWithFilter(ctx, filterOptions)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get accessible collections for file count",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract collection IDs
|
||||
allCollections := collectionResult.GetAllCollections()
|
||||
accessibleCollectionIDs := make([]gocql.UUID, 0, len(allCollections))
|
||||
for _, collection := range allCollections {
|
||||
accessibleCollectionIDs = append(accessibleCollectionIDs, collection.ID)
|
||||
}
|
||||
|
||||
uc.logger.Debug("Found accessible collections for file counting",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("owned_collections", len(collectionResult.OwnedCollections)),
|
||||
zap.Int("shared_collections", len(collectionResult.SharedCollections)),
|
||||
zap.Int("total_accessible", len(accessibleCollectionIDs)))
|
||||
|
||||
//
|
||||
// STEP 3: Count files in accessible collections.
|
||||
//
|
||||
|
||||
fileCount, err := uc.fileRepo.CountFilesByUser(ctx, userID, accessibleCollectionIDs)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to count files for user",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("accessible_collections", len(accessibleCollectionIDs)),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &CountFilesResponse{
|
||||
TotalFiles: fileCount,
|
||||
}
|
||||
|
||||
uc.logger.Debug("Successfully counted user files",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("accessible_collections", len(accessibleCollectionIDs)),
|
||||
zap.Int("total_files", fileCount))
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/create.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type CreateFileMetadataUseCase interface {
|
||||
Execute(file *dom_file.File) error
|
||||
}
|
||||
|
||||
type createFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewCreateFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CreateFileMetadataUseCase {
|
||||
logger = logger.Named("CreateFileMetadataUseCase")
|
||||
return &createFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *createFileMetadataUseCaseImpl) Execute(file *dom_file.File) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if file == nil {
|
||||
e["file"] = "File is required"
|
||||
} else {
|
||||
if file.CollectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if file.OwnerID.String() == "" {
|
||||
e["owner_id"] = "Owner ID is required"
|
||||
}
|
||||
if file.EncryptedMetadata == "" {
|
||||
e["encrypted_metadata"] = "Encrypted metadata is required"
|
||||
}
|
||||
if file.EncryptedFileKey.Ciphertext == nil || len(file.EncryptedFileKey.Ciphertext) == 0 {
|
||||
e["encrypted_file_key"] = "Encrypted file key is required"
|
||||
}
|
||||
if file.EncryptionVersion == "" {
|
||||
e["encryption_version"] = "Encryption version is required"
|
||||
}
|
||||
if file.EncryptedHash == "" {
|
||||
e["encrypted_hash"] = "Encrypted hash is required"
|
||||
}
|
||||
if file.EncryptedFileObjectKey == "" {
|
||||
e["encrypted_file_object_key"] = "Encrypted file object key is required"
|
||||
}
|
||||
if file.EncryptedFileSizeInBytes <= 0 {
|
||||
e["encrypted_file_size_in_bytes"] = "Encrypted file size must be greater than 0"
|
||||
}
|
||||
if file.State == "" {
|
||||
e["state"] = "File state is required"
|
||||
} else if file.State != dom_file.FileStatePending &&
|
||||
file.State != dom_file.FileStateActive &&
|
||||
file.State != dom_file.FileStateDeleted &&
|
||||
file.State != dom_file.FileStateArchived {
|
||||
e["state"] = "Invalid file state"
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata creation",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Insert into database.
|
||||
//
|
||||
|
||||
return uc.repo.Create(file)
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/create_many.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type CreateManyFileMetadataUseCase interface {
|
||||
Execute(files []*dom_file.File) error
|
||||
}
|
||||
|
||||
type createManyFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewCreateManyFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CreateManyFileMetadataUseCase {
|
||||
logger = logger.Named("CreateManyFileMetadataUseCase")
|
||||
return &createManyFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *createManyFileMetadataUseCaseImpl) Execute(files []*dom_file.File) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if files == nil || len(files) == 0 {
|
||||
e["files"] = "Files are required"
|
||||
} else {
|
||||
for i, file := range files {
|
||||
if file == nil {
|
||||
e[fmt.Sprintf("files[%d]", i)] = "File is required"
|
||||
continue
|
||||
}
|
||||
if file.CollectionID.String() == "" {
|
||||
e[fmt.Sprintf("files[%d].collection_id", i)] = "Collection ID is required"
|
||||
}
|
||||
if file.OwnerID.String() == "" {
|
||||
e[fmt.Sprintf("files[%d].owner_id", i)] = "Owner ID is required"
|
||||
}
|
||||
if file.EncryptedMetadata == "" {
|
||||
e[fmt.Sprintf("files[%d].encrypted_metadata", i)] = "Encrypted metadata is required"
|
||||
}
|
||||
if file.EncryptedFileKey.Ciphertext == nil || len(file.EncryptedFileKey.Ciphertext) == 0 {
|
||||
e[fmt.Sprintf("files[%d].encrypted_file_key", i)] = "Encrypted file key is required"
|
||||
}
|
||||
if file.EncryptionVersion == "" {
|
||||
e[fmt.Sprintf("files[%d].encryption_version", i)] = "Encryption version is required"
|
||||
}
|
||||
if file.EncryptedHash == "" {
|
||||
e[fmt.Sprintf("files[%d].encrypted_hash", i)] = "Encrypted hash is required"
|
||||
}
|
||||
if file.EncryptedFileObjectKey == "" {
|
||||
e[fmt.Sprintf("files[%d].encrypted_file_object_key", i)] = "Encrypted file object key is required"
|
||||
}
|
||||
if file.EncryptedFileSizeInBytes <= 0 {
|
||||
e[fmt.Sprintf("files[%d].encrypted_file_size_in_bytes", i)] = "Encrypted file size must be greater than 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata batch creation",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Insert into database.
|
||||
//
|
||||
|
||||
return uc.repo.CreateMany(files)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/delete_many.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type DeleteManyFileMetadataUseCase interface {
|
||||
Execute(ids []gocql.UUID) error
|
||||
}
|
||||
|
||||
type deleteManyFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewDeleteManyFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) DeleteManyFileMetadataUseCase {
|
||||
logger = logger.Named("DeleteManyFileMetadataUseCase")
|
||||
return &deleteManyFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *deleteManyFileMetadataUseCaseImpl) Execute(ids []gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if ids == nil || len(ids) == 0 {
|
||||
e["ids"] = "File IDs are required"
|
||||
} else {
|
||||
for i, id := range ids {
|
||||
if id.String() == "" {
|
||||
e[fmt.Sprintf("ids[%d]", i)] = "File ID is required"
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata batch deletion",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Delete from database.
|
||||
//
|
||||
|
||||
return uc.repo.SoftDeleteMany(ids)
|
||||
}
|
||||
63
cloud/maplefile-backend/internal/usecase/filemetadata/get.go
Normal file
63
cloud/maplefile-backend/internal/usecase/filemetadata/get.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/get.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type GetFileMetadataUseCase interface {
|
||||
Execute(id gocql.UUID) (*dom_file.File, error)
|
||||
}
|
||||
|
||||
type getFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewGetFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataUseCase {
|
||||
logger = logger.Named("GetFileMetadataUseCase")
|
||||
return &getFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getFileMetadataUseCaseImpl) Execute(id gocql.UUID) (*dom_file.File, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "File ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata retrieval",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
file, err := uc.repo.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if file == nil {
|
||||
uc.logger.Debug("File metadata not found",
|
||||
zap.Any("id", id))
|
||||
return nil, httperror.NewForNotFoundWithSingleField("message", "File not found")
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/get_by_collection.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type GetFileMetadataByCollectionUseCase interface {
|
||||
Execute(collectionID gocql.UUID) ([]*dom_file.File, error)
|
||||
}
|
||||
|
||||
type getFileMetadataByCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewGetFileMetadataByCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByCollectionUseCase {
|
||||
logger = logger.Named("GetFileMetadataByCollectionUseCase")
|
||||
return &getFileMetadataByCollectionUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getFileMetadataByCollectionUseCaseImpl) Execute(collectionID gocql.UUID) ([]*dom_file.File, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata retrieval by collection",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
return uc.repo.GetByCollection(collectionID)
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/get_by_created_by_user_id.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type GetFileMetadataByCreatedByUserIDUseCase interface {
|
||||
Execute(createdByUserID gocql.UUID) ([]*dom_file.File, error)
|
||||
}
|
||||
|
||||
type getFileMetadataByCreatedByUserIDUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewGetFileMetadataByCreatedByUserIDUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByCreatedByUserIDUseCase {
|
||||
logger = logger.Named("GetFileMetadataByCreatedByUserIDUseCase")
|
||||
return &getFileMetadataByCreatedByUserIDUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getFileMetadataByCreatedByUserIDUseCaseImpl) Execute(createdByUserID gocql.UUID) ([]*dom_file.File, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if createdByUserID.String() == "" {
|
||||
e["created_by_user_id"] = "Created by user ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata retrieval by created_by_user_id",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
return uc.repo.GetByCreatedByUserID(createdByUserID)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/get_by_ids.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type GetFileMetadataByIDsUseCase interface {
|
||||
Execute(ids []gocql.UUID) ([]*dom_file.File, error)
|
||||
}
|
||||
|
||||
type getFileMetadataByIDsUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewGetFileMetadataByIDsUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByIDsUseCase {
|
||||
logger = logger.Named("GetFileMetadataByIDsUseCase")
|
||||
return &getFileMetadataByIDsUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getFileMetadataByIDsUseCaseImpl) Execute(ids []gocql.UUID) ([]*dom_file.File, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if ids == nil || len(ids) == 0 {
|
||||
e["ids"] = "File IDs are required"
|
||||
} else {
|
||||
for i, id := range ids {
|
||||
if id.String() == "" {
|
||||
e[fmt.Sprintf("ids[%d]", i)] = "File ID is required"
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata retrieval by IDs",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
return uc.repo.GetByIDs(ids)
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/get_by_owner_id.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type GetFileMetadataByOwnerIDUseCase interface {
|
||||
Execute(ownerID gocql.UUID) ([]*dom_file.File, error)
|
||||
}
|
||||
|
||||
type getFileMetadataByOwnerIDUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewGetFileMetadataByOwnerIDUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByOwnerIDUseCase {
|
||||
logger = logger.Named("GetFileMetadataByOwnerIDUseCase")
|
||||
return &getFileMetadataByOwnerIDUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *getFileMetadataByOwnerIDUseCaseImpl) Execute(ownerID gocql.UUID) ([]*dom_file.File, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if ownerID.String() == "" {
|
||||
e["owner_id"] = "Created by user ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata retrieval by owner_id",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
return uc.repo.GetByOwnerID(ownerID)
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/harddelete.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// HardDeleteFileMetadataUseCase permanently deletes file metadata
|
||||
// Used for GDPR right-to-be-forgotten implementation
|
||||
type HardDeleteFileMetadataUseCase interface {
|
||||
Execute(id gocql.UUID) error
|
||||
}
|
||||
|
||||
type hardDeleteFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewHardDeleteFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) HardDeleteFileMetadataUseCase {
|
||||
logger = logger.Named("HardDeleteFileMetadataUseCase")
|
||||
return &hardDeleteFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *hardDeleteFileMetadataUseCaseImpl) Execute(id gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "File ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata hard deletion",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Hard delete from database (no tombstone).
|
||||
//
|
||||
|
||||
uc.logger.Info("Hard deleting file metadata (GDPR mode)",
|
||||
zap.String("file_id", id.String()))
|
||||
|
||||
err := uc.repo.HardDelete(id)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to hard delete file metadata",
|
||||
zap.String("file_id", id.String()),
|
||||
zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
uc.logger.Info("✅ File metadata hard deleted successfully",
|
||||
zap.String("file_id", id.String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package filemetadata
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
)
|
||||
|
||||
// NOTE: Unit tests for HardDeleteFileMetadataUseCase would require mocks.
|
||||
// For now, this use case will be tested via integration tests.
|
||||
// See Task 1.10 in RIGHT_TO_BE_FORGOTTEN_IMPLEMENTATION.md
|
||||
|
||||
func TestHardDeleteFileMetadataUseCase_Constructor(t *testing.T) {
|
||||
// Test that constructor creates use case successfully
|
||||
cfg := &config.Configuration{}
|
||||
logger := zap.NewNop()
|
||||
|
||||
useCase := NewHardDeleteFileMetadataUseCase(cfg, logger, nil)
|
||||
|
||||
if useCase == nil {
|
||||
t.Error("Expected use case to be created, got nil")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/filemetadata/list_by_owner.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type ListFilesByOwnerIDUseCase interface {
|
||||
Execute(ctx context.Context, ownerID gocql.UUID) ([]*dom_file.File, error)
|
||||
}
|
||||
|
||||
type listFilesByOwnerIDUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewListFilesByOwnerIDUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) ListFilesByOwnerIDUseCase {
|
||||
logger = logger.Named("ListFilesByOwnerIDUseCase")
|
||||
return &listFilesByOwnerIDUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *listFilesByOwnerIDUseCaseImpl) Execute(ctx context.Context, ownerID gocql.UUID) ([]*dom_file.File, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if ownerID.String() == "" {
|
||||
e["owner_id"] = "Owner ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating list files by owner",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get from database.
|
||||
//
|
||||
|
||||
files, err := uc.repo.GetByOwnerID(ownerID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get files by owner ID",
|
||||
zap.String("owner_id", ownerID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uc.logger.Debug("Files successfully retrieved by owner ID",
|
||||
zap.String("owner_id", ownerID.String()),
|
||||
zap.Int("count", len(files)))
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
// cloud/maplefile-backend/internal/maplefile/usecase/filemetadata/list_recent_files.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type ListRecentFilesUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID, cursor *dom_file.RecentFilesCursor, limit int64) (*dom_file.RecentFilesResponse, error)
|
||||
}
|
||||
|
||||
type listRecentFilesUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
fileRepo dom_file.FileMetadataRepository
|
||||
collectionRepo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
func NewListRecentFilesUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
collectionRepo dom_collection.CollectionRepository,
|
||||
) ListRecentFilesUseCase {
|
||||
logger = logger.Named("ListRecentFilesUseCase")
|
||||
return &listRecentFilesUseCaseImpl{config, logger, fileRepo, collectionRepo}
|
||||
}
|
||||
|
||||
func (uc *listRecentFilesUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID, cursor *dom_file.RecentFilesCursor, limit int64) (*dom_file.RecentFilesResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if limit <= 0 {
|
||||
e["limit"] = "Limit must be greater than 0"
|
||||
}
|
||||
if limit > 100 {
|
||||
e["limit"] = "Limit cannot exceed 100"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating list recent files",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get accessible collections for the user.
|
||||
//
|
||||
|
||||
uc.logger.Debug("Getting accessible collections for recent files",
|
||||
zap.String("user_id", userID.String()))
|
||||
|
||||
// Get collections using the efficient filtered query
|
||||
filterOptions := dom_collection.CollectionFilterOptions{
|
||||
UserID: userID,
|
||||
IncludeOwned: true,
|
||||
IncludeShared: true,
|
||||
}
|
||||
|
||||
collectionResult, err := uc.collectionRepo.GetCollectionsWithFilter(ctx, filterOptions)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get accessible collections for recent files",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract collection IDs
|
||||
allCollections := collectionResult.GetAllCollections()
|
||||
accessibleCollectionIDs := make([]gocql.UUID, 0, len(allCollections))
|
||||
for _, collection := range allCollections {
|
||||
// Only include active collections
|
||||
if collection.State == "active" {
|
||||
accessibleCollectionIDs = append(accessibleCollectionIDs, collection.ID)
|
||||
}
|
||||
}
|
||||
|
||||
uc.logger.Debug("Found accessible collections for recent files",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("owned_collections", len(collectionResult.OwnedCollections)),
|
||||
zap.Int("shared_collections", len(collectionResult.SharedCollections)),
|
||||
zap.Int("total_accessible", len(accessibleCollectionIDs)))
|
||||
|
||||
// If no accessible collections, return empty response
|
||||
if len(accessibleCollectionIDs) == 0 {
|
||||
uc.logger.Info("User has no accessible collections for recent files",
|
||||
zap.String("user_id", userID.String()))
|
||||
return &dom_file.RecentFilesResponse{
|
||||
Files: []dom_file.RecentFilesItem{},
|
||||
NextCursor: nil,
|
||||
HasMore: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 3: List recent files for accessible collections.
|
||||
//
|
||||
|
||||
recentFiles, err := uc.fileRepo.ListRecentFiles(ctx, userID, cursor, limit, accessibleCollectionIDs)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to list recent files",
|
||||
zap.Any("error", err),
|
||||
zap.String("user_id", userID.String()))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if recentFiles == nil {
|
||||
uc.logger.Debug("Recent files not found",
|
||||
zap.String("user_id", userID.String()))
|
||||
return nil, httperror.NewForNotFoundWithSingleField("message", "Recent files not found")
|
||||
}
|
||||
|
||||
uc.logger.Debug("Recent files successfully retrieved",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Any("next_cursor", recentFiles.NextCursor),
|
||||
zap.Int("files_count", len(recentFiles.Files)))
|
||||
|
||||
return recentFiles, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/filemetadata/list_sync_data.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type ListFileMetadataSyncDataUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID, cursor *dom_file.FileSyncCursor, limit int64, accessibleCollectionIDs []gocql.UUID) (*dom_file.FileSyncResponse, error)
|
||||
}
|
||||
|
||||
type listFileMetadataSyncDataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewListFileMetadataSyncDataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) ListFileMetadataSyncDataUseCase {
|
||||
logger = logger.Named("ListFileMetadataSyncDataUseCase")
|
||||
return &listFileMetadataSyncDataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *listFileMetadataSyncDataUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID, cursor *dom_file.FileSyncCursor, limit int64, accessibleCollectionIDs []gocql.UUID) (*dom_file.FileSyncResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(accessibleCollectionIDs) == 0 {
|
||||
e["accessible_collections"] = "At least one accessible collection is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating list file sync data",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
uc.logger.Debug("Listing file sync data",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("accessible_collections_count", len(accessibleCollectionIDs)),
|
||||
zap.Any("cursor", cursor),
|
||||
zap.Int64("limit", limit))
|
||||
|
||||
//
|
||||
// STEP 2: List file sync data from repository for accessible collections.
|
||||
//
|
||||
|
||||
result, err := uc.repo.ListSyncData(ctx, userID, cursor, limit, accessibleCollectionIDs)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to list file sync data from repository",
|
||||
zap.Any("error", err),
|
||||
zap.String("user_id", userID.String()))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Log the sync items for debugging
|
||||
uc.logger.Debug("File sync data retrieved from repository",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("files_count", len(result.Files)),
|
||||
zap.Bool("has_more", result.HasMore))
|
||||
|
||||
// Log each sync item to verify all fields are populated
|
||||
for i, item := range result.Files {
|
||||
uc.logger.Debug("File sync item",
|
||||
zap.Int("index", i),
|
||||
zap.String("file_id", item.ID.String()),
|
||||
zap.String("collection_id", item.CollectionID.String()),
|
||||
zap.Uint64("version", item.Version),
|
||||
zap.Time("modified_at", item.ModifiedAt),
|
||||
zap.String("state", item.State),
|
||||
zap.Uint64("tombstone_version", item.TombstoneVersion),
|
||||
zap.Time("tombstone_expiry", item.TombstoneExpiry),
|
||||
zap.Int64("encrypted_file_size_in_bytes", item.EncryptedFileSizeInBytes))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
)
|
||||
|
||||
// Wire providers for file metadata use cases
|
||||
|
||||
func ProvideCreateFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CreateFileMetadataUseCase {
|
||||
return NewCreateFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideUpdateFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) UpdateFileMetadataUseCase {
|
||||
return NewUpdateFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataUseCase {
|
||||
return NewGetFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideSoftDeleteFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) SoftDeleteFileMetadataUseCase {
|
||||
return NewSoftDeleteFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetFileMetadataByCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByCollectionUseCase {
|
||||
return NewGetFileMetadataByCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetFileMetadataByCreatedByUserIDUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByCreatedByUserIDUseCase {
|
||||
return NewGetFileMetadataByCreatedByUserIDUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideDeleteManyFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) DeleteManyFileMetadataUseCase {
|
||||
return NewDeleteManyFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideCountUserFilesUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
collectionRepo dom_collection.CollectionRepository,
|
||||
) CountUserFilesUseCase {
|
||||
return NewCountUserFilesUseCase(cfg, logger, fileRepo, collectionRepo)
|
||||
}
|
||||
|
||||
func ProvideCheckFileAccessUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CheckFileAccessUseCase {
|
||||
return NewCheckFileAccessUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetStorageSizeByCollectionUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetStorageSizeByCollectionUseCase {
|
||||
return NewGetStorageSizeByCollectionUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetFileMetadataByOwnerIDUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByOwnerIDUseCase {
|
||||
return NewGetFileMetadataByOwnerIDUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideCheckFileExistsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CheckFileExistsUseCase {
|
||||
return NewCheckFileExistsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetStorageSizeByUserUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
collectionRepo dom_collection.CollectionRepository,
|
||||
) GetStorageSizeByUserUseCase {
|
||||
return NewGetStorageSizeByUserUseCase(cfg, logger, fileRepo, collectionRepo)
|
||||
}
|
||||
|
||||
func ProvideListRecentFilesUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
collectionRepo dom_collection.CollectionRepository,
|
||||
) ListRecentFilesUseCase {
|
||||
return NewListRecentFilesUseCase(cfg, logger, fileRepo, collectionRepo)
|
||||
}
|
||||
|
||||
func ProvideGetFileMetadataByIDsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetFileMetadataByIDsUseCase {
|
||||
return NewGetFileMetadataByIDsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideCreateManyFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) CreateManyFileMetadataUseCase {
|
||||
return NewCreateManyFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideListFileMetadataSyncDataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) ListFileMetadataSyncDataUseCase {
|
||||
return NewListFileMetadataSyncDataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideGetStorageSizeByOwnerUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) GetStorageSizeByOwnerUseCase {
|
||||
return NewGetStorageSizeByOwnerUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideAnonymizeOldIPsUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) AnonymizeOldIPsUseCase {
|
||||
return NewAnonymizeOldIPsUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideListFilesByOwnerIDUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) ListFilesByOwnerIDUseCase {
|
||||
return NewListFilesByOwnerIDUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideRestoreFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) RestoreFileMetadataUseCase {
|
||||
return NewRestoreFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideHardDeleteFileMetadataUseCase(
|
||||
cfg *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) HardDeleteFileMetadataUseCase {
|
||||
return NewHardDeleteFileMetadataUseCase(cfg, logger, repo)
|
||||
}
|
||||
|
||||
func ProvideAnonymizeUserReferencesUseCase(
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) AnonymizeUserReferencesUseCase {
|
||||
return NewAnonymizeUserReferencesUseCase(logger, repo)
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/filemetadata/restore.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type RestoreFileMetadataUseCase interface {
|
||||
Execute(ctx context.Context, id gocql.UUID) error
|
||||
}
|
||||
|
||||
type restoreFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewRestoreFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) RestoreFileMetadataUseCase {
|
||||
logger = logger.Named("RestoreFileMetadataUseCase")
|
||||
return &restoreFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *restoreFileMetadataUseCaseImpl) Execute(ctx context.Context, id gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "File ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata restoration",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Restore file metadata using repository method.
|
||||
//
|
||||
|
||||
err := uc.repo.Restore(id)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to restore file metadata",
|
||||
zap.String("file_id", id.String()),
|
||||
zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
uc.logger.Info("File metadata successfully restored",
|
||||
zap.String("file_id", id.String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/delete.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type SoftDeleteFileMetadataUseCase interface {
|
||||
Execute(id gocql.UUID) error
|
||||
}
|
||||
|
||||
type softDeleteFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewSoftDeleteFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) SoftDeleteFileMetadataUseCase {
|
||||
logger = logger.Named("SoftDeleteFileMetadataUseCase")
|
||||
return &softDeleteFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *softDeleteFileMetadataUseCaseImpl) Execute(id gocql.UUID) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if id.String() == "" {
|
||||
e["id"] = "File ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata deletion",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Soft-delete from database.
|
||||
//
|
||||
|
||||
return uc.repo.SoftDelete(id)
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/filemetadata/storage_size.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// StorageSizeBreakdownResponse contains detailed storage breakdown
|
||||
type StorageSizeBreakdownResponse struct {
|
||||
OwnedSizeBytes int64 `json:"owned_size_bytes"`
|
||||
SharedSizeBytes int64 `json:"shared_size_bytes"`
|
||||
TotalSizeBytes int64 `json:"total_size_bytes"`
|
||||
CollectionBreakdownBytes map[string]int64 `json:"collection_breakdown_bytes"`
|
||||
OwnedCollectionsCount int `json:"owned_collections_count"`
|
||||
SharedCollectionsCount int `json:"shared_collections_count"`
|
||||
}
|
||||
|
||||
// Use case interfaces
|
||||
|
||||
type GetStorageSizeByCollectionUseCase interface {
|
||||
Execute(ctx context.Context, collectionID gocql.UUID) (*StorageSizeResponse, error)
|
||||
}
|
||||
|
||||
// Use case implementations
|
||||
|
||||
type getStorageSizeByCollectionUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
fileRepo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
// Constructors
|
||||
|
||||
func NewGetStorageSizeByCollectionUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
) GetStorageSizeByCollectionUseCase {
|
||||
logger = logger.Named("GetStorageSizeByCollectionUseCase")
|
||||
return &getStorageSizeByCollectionUseCaseImpl{config, logger, fileRepo}
|
||||
}
|
||||
|
||||
// Use case implementations
|
||||
|
||||
func (uc *getStorageSizeByCollectionUseCaseImpl) Execute(ctx context.Context, collectionID gocql.UUID) (*StorageSizeResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if collectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating get storage size by collection",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Calculate storage size.
|
||||
//
|
||||
|
||||
totalSize, err := uc.fileRepo.GetTotalStorageSizeByCollection(ctx, collectionID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get storage size by collection",
|
||||
zap.String("collection_id", collectionID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &StorageSizeResponse{
|
||||
TotalSizeBytes: totalSize,
|
||||
}
|
||||
|
||||
uc.logger.Debug("Successfully calculated storage size by collection",
|
||||
zap.String("collection_id", collectionID.String()),
|
||||
zap.Int64("total_size_bytes", totalSize))
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/filemetadata/storage_size.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// StorageSizeResponse contains storage size information
|
||||
type StorageSizeResponse struct {
|
||||
TotalSizeBytes int64 `json:"total_size_bytes"`
|
||||
}
|
||||
|
||||
// Use case interfaces
|
||||
type GetStorageSizeByOwnerUseCase interface {
|
||||
Execute(ctx context.Context, ownerID gocql.UUID) (*StorageSizeResponse, error)
|
||||
}
|
||||
|
||||
// Use case implementations
|
||||
type getStorageSizeByOwnerUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
fileRepo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
// Constructors
|
||||
func NewGetStorageSizeByOwnerUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
) GetStorageSizeByOwnerUseCase {
|
||||
logger = logger.Named("GetStorageSizeByOwnerUseCase")
|
||||
return &getStorageSizeByOwnerUseCaseImpl{config, logger, fileRepo}
|
||||
}
|
||||
|
||||
// Use case implementations
|
||||
|
||||
func (uc *getStorageSizeByOwnerUseCaseImpl) Execute(ctx context.Context, ownerID gocql.UUID) (*StorageSizeResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if ownerID.String() == "" {
|
||||
e["owner_id"] = "Owner ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating get storage size by owner",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Calculate storage size.
|
||||
//
|
||||
|
||||
totalSize, err := uc.fileRepo.GetTotalStorageSizeByOwner(ctx, ownerID)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get storage size by owner",
|
||||
zap.String("owner_id", ownerID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &StorageSizeResponse{
|
||||
TotalSizeBytes: totalSize,
|
||||
}
|
||||
|
||||
uc.logger.Debug("Successfully calculated storage size by owner",
|
||||
zap.String("owner_id", ownerID.String()),
|
||||
zap.Int64("total_size_bytes", totalSize))
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/usecase/filemetadata/storage_size.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
// Use case interfaces
|
||||
|
||||
type GetStorageSizeByUserUseCase interface {
|
||||
Execute(ctx context.Context, userID gocql.UUID) (*StorageSizeResponse, error)
|
||||
}
|
||||
|
||||
// Use case implementations
|
||||
|
||||
type getStorageSizeByUserUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
fileRepo dom_file.FileMetadataRepository
|
||||
collectionRepo dom_collection.CollectionRepository
|
||||
}
|
||||
|
||||
// Constructors
|
||||
|
||||
func NewGetStorageSizeByUserUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
fileRepo dom_file.FileMetadataRepository,
|
||||
collectionRepo dom_collection.CollectionRepository,
|
||||
) GetStorageSizeByUserUseCase {
|
||||
logger = logger.Named("GetStorageSizeByUserUseCase")
|
||||
return &getStorageSizeByUserUseCaseImpl{config, logger, fileRepo, collectionRepo}
|
||||
}
|
||||
|
||||
// Use case implementations
|
||||
|
||||
func (uc *getStorageSizeByUserUseCaseImpl) Execute(ctx context.Context, userID gocql.UUID) (*StorageSizeResponse, error) {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if userID.String() == "" {
|
||||
e["user_id"] = "User ID is required"
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating get storage size by user",
|
||||
zap.Any("error", e))
|
||||
return nil, httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Get accessible collections for the user.
|
||||
//
|
||||
|
||||
filterOptions := dom_collection.CollectionFilterOptions{
|
||||
UserID: userID,
|
||||
IncludeOwned: true,
|
||||
IncludeShared: true,
|
||||
}
|
||||
|
||||
collectionResult, err := uc.collectionRepo.GetCollectionsWithFilter(ctx, filterOptions)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get accessible collections for storage size calculation",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract collection IDs
|
||||
allCollections := collectionResult.GetAllCollections()
|
||||
accessibleCollectionIDs := make([]gocql.UUID, 0, len(allCollections))
|
||||
for _, collection := range allCollections {
|
||||
accessibleCollectionIDs = append(accessibleCollectionIDs, collection.ID)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 3: Calculate storage size.
|
||||
//
|
||||
|
||||
totalSize, err := uc.fileRepo.GetTotalStorageSizeByUser(ctx, userID, accessibleCollectionIDs)
|
||||
if err != nil {
|
||||
uc.logger.Error("Failed to get storage size by user",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("accessible_collections", len(accessibleCollectionIDs)),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &StorageSizeResponse{
|
||||
TotalSizeBytes: totalSize,
|
||||
}
|
||||
|
||||
uc.logger.Debug("Successfully calculated storage size by user",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Int("accessible_collections", len(accessibleCollectionIDs)),
|
||||
zap.Int64("total_size_bytes", totalSize))
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// monorepo/cloud/backend/internal/maplefile/usecase/filemetadata/update.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
|
||||
)
|
||||
|
||||
type UpdateFileMetadataUseCase interface {
|
||||
Execute(ctx context.Context, file *dom_file.File) error
|
||||
}
|
||||
|
||||
type updateFileMetadataUseCaseImpl struct {
|
||||
config *config.Configuration
|
||||
logger *zap.Logger
|
||||
repo dom_file.FileMetadataRepository
|
||||
}
|
||||
|
||||
func NewUpdateFileMetadataUseCase(
|
||||
config *config.Configuration,
|
||||
logger *zap.Logger,
|
||||
repo dom_file.FileMetadataRepository,
|
||||
) UpdateFileMetadataUseCase {
|
||||
logger = logger.Named("UpdateFileMetadataUseCase")
|
||||
return &updateFileMetadataUseCaseImpl{config, logger, repo}
|
||||
}
|
||||
|
||||
func (uc *updateFileMetadataUseCaseImpl) Execute(ctx context.Context, file *dom_file.File) error {
|
||||
//
|
||||
// STEP 1: Validation.
|
||||
//
|
||||
|
||||
e := make(map[string]string)
|
||||
if file == nil {
|
||||
e["file"] = "File is required"
|
||||
} else {
|
||||
if file.ID.String() == "" {
|
||||
e["id"] = "File ID is required"
|
||||
}
|
||||
if file.CollectionID.String() == "" {
|
||||
e["collection_id"] = "Collection ID is required"
|
||||
}
|
||||
if file.OwnerID.String() == "" {
|
||||
e["owner_id"] = "Owner ID is required"
|
||||
}
|
||||
if file.EncryptedMetadata == "" {
|
||||
e["encrypted_metadata"] = "Encrypted metadata is required"
|
||||
}
|
||||
if file.EncryptedFileKey.Ciphertext == nil || len(file.EncryptedFileKey.Ciphertext) == 0 {
|
||||
e["encrypted_file_key"] = "Encrypted file key is required"
|
||||
}
|
||||
if file.EncryptionVersion == "" {
|
||||
e["encryption_version"] = "Encryption version is required"
|
||||
}
|
||||
if file.EncryptedHash == "" {
|
||||
e["encrypted_hash"] = "Encrypted hash is required"
|
||||
}
|
||||
if file.EncryptedFileObjectKey == "" {
|
||||
e["encrypted_file_object_key"] = "Encrypted file object key is required"
|
||||
}
|
||||
if file.EncryptedFileSizeInBytes <= 0 {
|
||||
e["encrypted_file_size_in_bytes"] = "Encrypted file size must be greater than 0"
|
||||
}
|
||||
}
|
||||
if len(e) != 0 {
|
||||
uc.logger.Warn("Failed validating file metadata update",
|
||||
zap.Any("error", e))
|
||||
return httperror.NewForBadRequest(&e)
|
||||
}
|
||||
|
||||
//
|
||||
// STEP 2: Update in database.
|
||||
//
|
||||
|
||||
return uc.repo.Update(file)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue