Skip to content

Commit f8adc24

Browse files
committed
Refactor handlers
1 parent d66881e commit f8adc24

9 files changed

Lines changed: 467 additions & 417 deletions

File tree

handlers/File.go

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
package handlers
2+
3+
import (
4+
"io"
5+
"net/http"
6+
"os"
7+
"strconv"
8+
9+
"github.com/DataManager-Go/DataManagerServer/handlers/web"
10+
"github.com/DataManager-Go/DataManagerServer/models"
11+
libdm "github.com/DataManager-Go/libdatamanager"
12+
"github.com/JojiiOfficial/gaw"
13+
"github.com/gorilla/mux"
14+
"github.com/h2non/filetype"
15+
"gorm.io/gorm"
16+
)
17+
18+
// FileHandler handler for updating files
19+
func FileHandler(handlerData web.HandlerData, w http.ResponseWriter, r *http.Request) error {
20+
var request libdm.FileRequest
21+
if !readRequestLimited(w, r, &request, handlerData.Config.Webserver.MaxRequestBodyLength) {
22+
return nil
23+
}
24+
25+
namespace, action, err := validateFileActionRequest(r, w, &handlerData, request)
26+
if err != nil {
27+
return err
28+
}
29+
30+
// Find files
31+
files, err := models.FindFiles(handlerData.Db, handlerData.Config, models.File{
32+
Model: gorm.Model{
33+
ID: request.FileID,
34+
},
35+
Name: request.Name,
36+
Namespace: namespace,
37+
})
38+
if err != nil {
39+
return err
40+
}
41+
42+
// Apply group filter
43+
if len(request.Attributes.Groups) > 0 {
44+
45+
}
46+
47+
// Exit if no file was found
48+
if len(files) == 0 {
49+
return RErrNotFound
50+
}
51+
52+
// Check if files are more than requested
53+
if len(files) > 1 && !request.All {
54+
return NewRequestError("found multiple files with same name", http.StatusConflict)
55+
}
56+
57+
// If namespace was not set, use the namespace of the returned file
58+
if namespace == nil {
59+
namespace = files[0].Namespace
60+
if !handlerData.User.HasAccess(namespace) {
61+
return RErrPermissionDenied.Append("for this namespace")
62+
}
63+
}
64+
65+
// Determine if an update was applied
66+
var didUpdate bool
67+
68+
// Execute action
69+
switch action {
70+
case "delete":
71+
{
72+
ids := make([]uint, len(files))
73+
74+
for i, file := range files {
75+
// Delete each file
76+
err = file.Delete(handlerData.Db, handlerData.Config)
77+
if err != nil {
78+
return err
79+
}
80+
81+
ids[i] = file.ID
82+
}
83+
84+
// Send response
85+
sendResponse(w, libdm.ResponseSuccess, "", libdm.IDsResponse{
86+
IDs: ids,
87+
})
88+
}
89+
case "update":
90+
{
91+
var count uint32
92+
93+
for _, file := range files {
94+
didUpdate, err = updateFile(&file, handlerData, request.Updates)
95+
if err != nil {
96+
return err
97+
}
98+
99+
// Only count if update
100+
// was applied
101+
if didUpdate {
102+
count++
103+
}
104+
}
105+
106+
// Send response
107+
sendResponse(w, libdm.ResponseSuccess, "", libdm.CountResponse{
108+
Count: count,
109+
})
110+
}
111+
// Get file
112+
case "get":
113+
{
114+
// Use first file
115+
err := serveFile(files[0], w, handlerData)
116+
if err != nil {
117+
return err
118+
}
119+
}
120+
// Publish a file
121+
case "publish":
122+
{
123+
resp, err := publishFiles(files, request.PublicName, request.All, handlerData.Db)
124+
if err != nil {
125+
return err
126+
}
127+
128+
sendResponse(w, libdm.ResponseSuccess, "", resp)
129+
}
130+
}
131+
132+
return nil
133+
}
134+
135+
// Validate FileRequest
136+
func validateFileActionRequest(r *http.Request, w http.ResponseWriter, handlerData *web.HandlerData, request libdm.FileRequest) (*models.Namespace, string, error) {
137+
// Validate input
138+
if len(request.Name) == 0 && request.FileID <= 0 {
139+
return nil, "", RErrBadRequest
140+
}
141+
142+
// Get action
143+
vars := mux.Vars(r)
144+
action, has := vars["action"]
145+
if !has {
146+
return nil, "", RErrBadRequest
147+
}
148+
149+
// Getting all files is not allowed
150+
if request.All && action == "get" {
151+
return nil, "", RErrBadRequest
152+
}
153+
154+
var namespace *models.Namespace
155+
156+
// Use given namespace if fileID is not set
157+
if request.FileID == 0 {
158+
// Select namespace
159+
namespace = models.FindNamespace(handlerData.Db, request.Attributes.Namespace, handlerData.User)
160+
161+
// Handle namespace errors (not found || no access)
162+
if !handleNamespaceErorrs(namespace, handlerData.User, w) {
163+
return nil, "", nil
164+
}
165+
}
166+
167+
// Check if action is valid
168+
if !gaw.IsInStringArray(action, []string{"delete", "update", "get", "publish"}) {
169+
return nil, "", RErrInvalid.Append("action")
170+
}
171+
172+
return namespace, action, nil
173+
}
174+
175+
// Serve file contents for client
176+
func serveFile(file models.File, w http.ResponseWriter, handlerData web.HandlerData) error {
177+
// Open local file
178+
f, err := os.Open(handlerData.Config.GetStorageFile(file.LocalName))
179+
if LogError(err) {
180+
if os.IsNotExist(err) {
181+
return RErrNotFound.Prepend("File").Append("on server")
182+
}
183+
184+
return err
185+
}
186+
187+
// Set ContentType header
188+
if len(file.FileType) > 0 && filetype.IsMIMESupported(file.FileType) {
189+
w.Header().Set(libdm.HeaderContentType, file.FileType)
190+
}
191+
192+
// Set filename header
193+
w.Header().Set(libdm.HeaderFileName, file.Name)
194+
195+
// Set checksum header
196+
w.Header().Set(libdm.HeaderChecksum, file.Checksum)
197+
198+
// Set fileID header
199+
w.Header().Set(libdm.HeaderFileID, strconv.FormatUint(uint64(file.ID), 10))
200+
201+
// Set ContentLength header
202+
w.Header().Set(libdm.HeaderContentLength, strconv.FormatInt(file.FileSize, 10))
203+
204+
// Set encryption cipher header
205+
if file.Encryption.Valid {
206+
w.Header().Set(libdm.HeaderEncryption, libdm.ChiperToString(file.Encryption.Int32))
207+
}
208+
209+
// Write contents to responsewriter
210+
buff := make([]byte, 10*1024)
211+
_, err = io.CopyBuffer(w, f, buff)
212+
if err != nil {
213+
return err
214+
}
215+
216+
// Close file
217+
LogError(f.Close())
218+
return nil
219+
}
220+
221+
// Publish multiple files
222+
func publishFiles(files []models.File, publicName string, all bool, db *gorm.DB) (interface{}, error) {
223+
bulkPublishResponse := libdm.BulkPublishResponse{}
224+
225+
for _, file := range files {
226+
// Ignore if already public
227+
if file.IsPublic {
228+
// Send error if publishing only one file
229+
if len(files) == 1 {
230+
return nil, NewRequestError("Already public", http.StatusConflict)
231+
}
232+
233+
continue
234+
}
235+
236+
nameTaken, err := file.Publish(db, publicName)
237+
if err != nil {
238+
return nil, err
239+
}
240+
241+
if nameTaken {
242+
return nil, RErrAlreadyExists.Prepend("Public name")
243+
}
244+
245+
// Use bulk response if requested "all"
246+
if all && len(files) > 1 {
247+
bulkPublishResponse.Files = append(bulkPublishResponse.Files, libdm.UploadResponse{
248+
FileID: file.ID,
249+
Filename: file.Name,
250+
PublicFilename: file.PublicFilename.String,
251+
})
252+
} else {
253+
// Otherwise respond with a single item
254+
return libdm.PublishResponse{
255+
PublicFilename: file.PublicFilename.String,
256+
}, nil
257+
}
258+
}
259+
260+
return bulkPublishResponse, nil
261+
}
262+
263+
// Apply all given updates to a file
264+
func updateFile(file *models.File, handlerData web.HandlerData, update libdm.FileUpdateItem) (didUpdate bool, err error) {
265+
// Update namespace
266+
if len(update.NewNamespace) > 0 {
267+
// Get new namespace
268+
newNamespace := models.FindNamespace(handlerData.Db, update.NewNamespace, handlerData.User)
269+
if newNamespace == nil || file.Namespace.ID == 0 {
270+
err = RErrNotFound.Prepend("New namespace")
271+
return
272+
}
273+
274+
// Check if user can access this new namespace
275+
if !newNamespace.IsOwnedBy(handlerData.User) && !handlerData.User.CanWriteForeignNamespace() {
276+
err = RErrPermissionDenied.Append("for this namespace")
277+
return
278+
}
279+
280+
// Update files namespace
281+
err = file.UpdateNamespace(handlerData.Db, newNamespace, handlerData.User)
282+
if err != nil {
283+
return
284+
}
285+
286+
didUpdate = true
287+
}
288+
289+
// Rename file
290+
if len(update.NewName) > 0 {
291+
if err = file.Rename(handlerData.Db, update.NewName); err != nil {
292+
return
293+
}
294+
295+
didUpdate = true
296+
}
297+
298+
// Set public/private
299+
if len(update.IsPublic) > 0 {
300+
if !file.PublicFilename.Valid {
301+
err = NewRequestError("You need to share this file first", http.StatusBadRequest)
302+
return
303+
}
304+
305+
var newVisibility bool
306+
newVisibility, err = strconv.ParseBool(update.IsPublic)
307+
if err != nil {
308+
err = NewRequestError("isPublic must be a bool", http.StatusUnprocessableEntity)
309+
return
310+
}
311+
312+
if err = file.SetVilibility(handlerData.Db, newVisibility); err != nil {
313+
return
314+
}
315+
316+
didUpdate = true
317+
}
318+
319+
// Add tags
320+
if len(update.AddTags) > 0 {
321+
currLenTags := len(file.Tags)
322+
if err = file.AddTags(handlerData.Db, update.AddTags, handlerData.User); err != nil {
323+
return
324+
}
325+
326+
didUpdate = len(file.Tags) > currLenTags
327+
}
328+
329+
// Remove tags
330+
if len(update.RemoveTags) > 0 {
331+
currLenTags := len(file.Tags)
332+
if err = file.RemoveTags(handlerData.Db, update.RemoveTags); err != nil {
333+
return
334+
}
335+
336+
didUpdate = len(file.Tags) < currLenTags
337+
}
338+
339+
// Add Groups
340+
if len(update.AddGroups) > 0 {
341+
currLenGroups := len(file.Groups)
342+
if err = file.AddGroups(handlerData.Db, update.AddGroups, handlerData.User); err != nil {
343+
return
344+
}
345+
346+
didUpdate = len(file.Groups) > currLenGroups
347+
}
348+
349+
// Remove Groups
350+
if len(update.RemoveGroups) > 0 {
351+
currLenGroups := len(file.Groups)
352+
if err = file.RemoveGroups(handlerData.Db, update.RemoveGroups); err != nil {
353+
return
354+
}
355+
356+
didUpdate = len(file.Groups) < currLenGroups
357+
}
358+
359+
return
360+
}

0 commit comments

Comments
 (0)