- PostgreSQL metadata overlay layer on top of existing S3 storage - 3 new tables: folders, files, share_links - Folder CRUD: create, get with children, tree, rename, delete (cascade) - File operations: upload to folder, move between folders - Share links: create with optional password/expiry/download limit, public access - S3 compensation on PG write failure - Existing 14 endpoints untouched
107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"rag/file-system/internal/common"
|
|
"rag/file-system/internal/domain/model"
|
|
"rag/file-system/internal/domain/repository"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type CreateFolderCommand struct {
|
|
Name string
|
|
ParentID *string
|
|
OwnerID string
|
|
}
|
|
|
|
type RenameFolderCommand struct {
|
|
FolderID string
|
|
Name string
|
|
OwnerID string
|
|
}
|
|
|
|
type DeleteFolderCommand struct {
|
|
FolderID string
|
|
OwnerID string
|
|
}
|
|
|
|
type CreateFolderHandler struct {
|
|
FolderRepo repository.FolderRepository
|
|
}
|
|
|
|
func NewCreateFolderHandler(folderRepo repository.FolderRepository) *CreateFolderHandler {
|
|
return &CreateFolderHandler{FolderRepo: folderRepo}
|
|
}
|
|
|
|
func (h *CreateFolderHandler) Handle(ctx context.Context, cmd CreateFolderCommand) (*model.Folder, error) {
|
|
now := time.Now()
|
|
folder := &model.Folder{
|
|
ID: uuid.New().String(),
|
|
ParentID: cmd.ParentID,
|
|
Name: cmd.Name,
|
|
OwnerID: cmd.OwnerID,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
if err := h.FolderRepo.Create(ctx, folder); err != nil {
|
|
return nil, fmt.Errorf("failed to create folder: %w", err)
|
|
}
|
|
return folder, nil
|
|
}
|
|
|
|
type RenameFolderHandler struct {
|
|
FolderRepo repository.FolderRepository
|
|
}
|
|
|
|
func NewRenameFolderHandler(folderRepo repository.FolderRepository) *RenameFolderHandler {
|
|
return &RenameFolderHandler{FolderRepo: folderRepo}
|
|
}
|
|
|
|
func (h *RenameFolderHandler) Handle(ctx context.Context, cmd RenameFolderCommand) (*model.Folder, error) {
|
|
folder, err := h.FolderRepo.GetByID(ctx, cmd.FolderID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if folder == nil || folder.OwnerID != cmd.OwnerID {
|
|
return nil, common.NewNotFoundError("目录不存在")
|
|
}
|
|
folder.Name = cmd.Name
|
|
folder.UpdatedAt = time.Now()
|
|
if err := h.FolderRepo.Update(ctx, folder); err != nil {
|
|
return nil, err
|
|
}
|
|
return folder, nil
|
|
}
|
|
|
|
type DeleteFolderHandler struct {
|
|
FolderRepo repository.FolderRepository
|
|
S3Repo repository.FileRepository
|
|
}
|
|
|
|
func NewDeleteFolderHandler(folderRepo repository.FolderRepository, s3Repo repository.FileRepository) *DeleteFolderHandler {
|
|
return &DeleteFolderHandler{FolderRepo: folderRepo, S3Repo: s3Repo}
|
|
}
|
|
|
|
func (h *DeleteFolderHandler) Handle(ctx context.Context, cmd DeleteFolderCommand) (string, error) {
|
|
files, err := h.FolderRepo.GetDescendantFileS3Keys(ctx, cmd.FolderID, cmd.OwnerID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, f := range files {
|
|
if delErr := h.S3Repo.DeleteFile(ctx, f.S3Bucket, f.S3Key); delErr != nil {
|
|
common.Logger.Error("failed to delete S3 object during folder cleanup",
|
|
"s3_key", f.S3Key, "error", delErr)
|
|
}
|
|
}
|
|
|
|
if err := h.FolderRepo.Delete(ctx, cmd.FolderID, cmd.OwnerID); err != nil {
|
|
return "", err
|
|
}
|
|
return "目录删除成功", nil
|
|
}
|