Skip to content

Commit f88c297

Browse files
committed
feat: implement bulk move api with loop validation
1 parent f112e5f commit f88c297

File tree

4 files changed

+115
-0
lines changed

4 files changed

+115
-0
lines changed

controllers/MoveItemsController.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package controllers
2+
3+
import (
4+
"ch/kirari04/videocms/helpers"
5+
"ch/kirari04/videocms/logic"
6+
"ch/kirari04/videocms/models"
7+
8+
"github.com/labstack/echo/v4"
9+
)
10+
11+
func MoveItems(c echo.Context) error {
12+
// parse & validate request
13+
var moveValidation models.MoveItemsValidation
14+
if status, err := helpers.Validate(c, &moveValidation); err != nil {
15+
return c.String(status, err.Error())
16+
}
17+
18+
status, err := logic.MoveItems(
19+
c.Get("UserID").(uint),
20+
moveValidation.ParentFolderID,
21+
moveValidation.FolderIDs,
22+
moveValidation.LinkIDs,
23+
)
24+
if err != nil {
25+
return c.String(status, err.Error())
26+
}
27+
28+
return c.NoContent(status)
29+
}

logic/MoveItems.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package logic
2+
3+
import (
4+
"ch/kirari04/videocms/helpers"
5+
"ch/kirari04/videocms/inits"
6+
"ch/kirari04/videocms/models"
7+
"errors"
8+
"net/http"
9+
)
10+
11+
func MoveItems(userId uint, targetFolderId uint, folderIds []uint, linkIds []uint) (int, error) {
12+
// check if at least one item is being moved
13+
if len(folderIds) == 0 && len(linkIds) == 0 {
14+
return http.StatusBadRequest, errors.New("no items selected to move")
15+
}
16+
17+
// 1. Validate Target Folder
18+
if targetFolderId > 0 {
19+
var targetFolder models.Folder
20+
if res := inits.DB.First(&targetFolder, targetFolderId); res.Error != nil {
21+
return http.StatusBadRequest, errors.New("target folder doesn't exist")
22+
}
23+
if targetFolder.UserID != userId {
24+
return http.StatusForbidden, errors.New("unauthorized access to target folder")
25+
}
26+
}
27+
28+
// 2. Move Folders
29+
for _, folderId := range folderIds {
30+
// Cannot move a folder into itself
31+
if folderId == targetFolderId {
32+
return http.StatusBadRequest, errors.New("cannot move folder into itself")
33+
}
34+
35+
var folder models.Folder
36+
if res := inits.DB.First(&folder, folderId); res.Error != nil {
37+
return http.StatusBadRequest, errors.New("folder to move not found")
38+
}
39+
40+
if folder.UserID != userId {
41+
return http.StatusForbidden, errors.New("unauthorized access to folder")
42+
}
43+
44+
// Loop detection: Check if the folder we are moving contains the target folder
45+
if targetFolderId > 0 {
46+
contains, err := helpers.FolderContainsFolder(folderId, targetFolderId)
47+
if err != nil {
48+
return http.StatusInternalServerError, err
49+
}
50+
if contains {
51+
return http.StatusBadRequest, errors.New("cannot move parent folder into its child")
52+
}
53+
}
54+
55+
folder.ParentFolderID = targetFolderId
56+
if res := inits.DB.Save(&folder); res.Error != nil {
57+
return http.StatusInternalServerError, res.Error
58+
}
59+
}
60+
61+
// 3. Move Links (Files)
62+
for _, linkId := range linkIds {
63+
var link models.Link
64+
if res := inits.DB.First(&link, linkId); res.Error != nil {
65+
return http.StatusBadRequest, errors.New("file to move not found")
66+
}
67+
68+
if link.UserID != userId {
69+
return http.StatusForbidden, errors.New("unauthorized access to file")
70+
}
71+
72+
link.ParentFolderID = targetFolderId
73+
if res := inits.DB.Save(&link); res.Error != nil {
74+
return http.StatusInternalServerError, res.Error
75+
}
76+
}
77+
78+
return http.StatusOK, nil
79+
}

models/Folder.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@ type FolderUpdateValidation struct {
3030
FolderID uint `validate:"required,number" json:"FolderID" form:"FolderID"`
3131
ParentFolderID uint `validate:"number" json:"ParentFolderID" form:"ParentFolderID"`
3232
}
33+
34+
type MoveItemsValidation struct {
35+
ParentFolderID uint `validate:"number" json:"ParentFolderID" form:"ParentFolderID"`
36+
FolderIDs []uint `json:"FolderIDs" form:"FolderIDs"`
37+
LinkIDs []uint `json:"LinkIDs" form:"LinkIDs"`
38+
}

routes/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func Api() {
4242
protectedApi.POST("/folder", controllers.CreateFolder)
4343
protectedApi.PUT("/folder", controllers.UpdateFolder)
4444
protectedApi.DELETE("/folder", controllers.DeleteFolder)
45+
protectedApi.PUT("/move", controllers.MoveItems)
4546
protectedApi.GET("/folders", controllers.ListFolders)
4647
protectedApi.DELETE("/folders", controllers.DeleteFolders)
4748

0 commit comments

Comments
 (0)