getting to upload

This commit is contained in:
Daniel Goc
2026-04-02 10:27:14 +02:00
parent b2acb8c922
commit b9bc121d43
9 changed files with 297 additions and 40 deletions

View File

@@ -29,7 +29,11 @@ func StorageHandlerRoutes(r fiber.Router) fiber.Router {
r.Get("/list-content", handler.ListContent)
r.Get("/download-file", handler.DownloadFile)
r.Post("/upload-file", handler.CreateFolder)
r.Get("/create-folder", handler.CreateFolder)
r.Get("/delete-file", handler.DeleteFile)
r.Get("/delete-folder", handler.DeleteFolder)
return r
}
@@ -37,13 +41,13 @@ func StorageHandlerRoutes(r fiber.Router) fiber.Router {
// accepted path looks like e.g. "/folder1/" or "folder1"
func (h *StorageHandler) ListContent(c fiber.Ctx) error {
// relative path defaults to root directory
absPath, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
entries_in_list, err := h.storageService.ListContent(absPath)
entries_in_list, err := h.storageService.ListContent(abs_path)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
@@ -54,13 +58,13 @@ func (h *StorageHandler) ListContent(c fiber.Ctx) error {
}
func (h *StorageHandler) DownloadFile(c fiber.Ctx) error {
absPath, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
f, filename, filesize, err := h.storageService.DownloadFilePrep(absPath)
f, filename, filesize, err := h.storageService.DownloadFilePrep(abs_path)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
@@ -71,14 +75,69 @@ func (h *StorageHandler) DownloadFile(c fiber.Ctx) error {
return c.SendStream(f, int(filesize))
}
func (h *StorageHandler) CreateFolder(c fiber.Ctx) error {
absPath, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
func (h *StorageHandler) UploadFile(c fiber.Ctx) error {
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
err = h.storageService.CreateFolder(absPath, c.Query("name"))
f, err := c.FormFile("document")
if err != nil {
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrMissingFileFieldDocument)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrMissingFileFieldDocument)))
}
err = h.storageService.UploadFile(abs_path, c.Query("name"), f)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
err = c.SaveFile(f, abs_path)
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
}
func (h *StorageHandler) CreateFolder(c fiber.Ctx) error {
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
err = h.storageService.CreateFolder(abs_path, c.Query("name"))
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
}
func (h *StorageHandler) DeleteFile(c fiber.Ctx) error {
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
err = h.storageService.DeleteFile(abs_path)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
}
func (h *StorageHandler) DeleteFolder(c fiber.Ctx) error {
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("path"))
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
}
err = h.storageService.DeleteFolder(abs_path)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))

View File

@@ -1,16 +1,20 @@
package storageRepo
import (
"mime/multipart"
"os"
"git.ma-al.com/goc_daniel/b2b/app/model"
)
type UIStorageRepo interface {
EntryInfo(absPath string) (os.FileInfo, error)
ListContent(absPath string) (*[]model.EntryInList, error)
OpenFile(absPath string) (*os.File, error)
CreateFolder(absPath string, name string) error
EntryInfo(abs_path string) (os.FileInfo, error)
ListContent(abs_path string) (*[]model.EntryInList, error)
OpenFile(abs_path string) (*os.File, error)
UploadFile(abs_path string, f *multipart.FileHeader) error
CreateFolder(abs_path string) error
DeleteFile(abs_path string) error
DeleteFolder(abs_path string) error
}
type StorageRepo struct{}
@@ -19,12 +23,12 @@ func New() UIStorageRepo {
return &StorageRepo{}
}
func (r *StorageRepo) EntryInfo(absPath string) (os.FileInfo, error) {
return os.Stat(absPath)
func (r *StorageRepo) EntryInfo(abs_path string) (os.FileInfo, error) {
return os.Stat(abs_path)
}
func (r *StorageRepo) ListContent(absPath string) (*[]model.EntryInList, error) {
entries, err := os.ReadDir(absPath)
func (r *StorageRepo) ListContent(abs_path string) (*[]model.EntryInList, error) {
entries, err := os.ReadDir(abs_path)
if err != nil {
return nil, err
}
@@ -42,10 +46,22 @@ func (r *StorageRepo) ListContent(absPath string) (*[]model.EntryInList, error)
return &entries_in_list, nil
}
func (r *StorageRepo) OpenFile(absPath string) (*os.File, error) {
return os.Open(absPath)
func (r *StorageRepo) OpenFile(abs_path string) (*os.File, error) {
return os.Open(abs_path)
}
func (r *StorageRepo) CreateFolder(absPath string, name string) error {
os.(absPath)
func (r *StorageRepo) UploadFile(abs_path string, f *multipart.FileHeader) error {
return nil
}
func (r *StorageRepo) CreateFolder(abs_path string) error {
return os.Mkdir(abs_path, 0755)
}
func (r *StorageRepo) DeleteFile(abs_path string) error {
return os.Remove(abs_path)
}
func (r *StorageRepo) DeleteFolder(abs_path string) error {
return os.RemoveAll(abs_path)
}

View File

@@ -1,6 +1,7 @@
package storageService
import (
"mime/multipart"
"os"
"path/filepath"
"strings"
@@ -20,56 +21,110 @@ func New() *StorageService {
}
}
func (s *StorageService) ListContent(absPath string) (*[]model.EntryInList, error) {
info, err := s.storageRepo.EntryInfo(absPath)
func (s *StorageService) ListContent(abs_path string) (*[]model.EntryInList, error) {
info, err := s.storageRepo.EntryInfo(abs_path)
if err != nil || !info.IsDir() {
return nil, responseErrors.ErrFolderDoesNotExist
}
entries_in_list, err := s.storageRepo.ListContent(absPath)
entries_in_list, err := s.storageRepo.ListContent(abs_path)
return entries_in_list, err
}
func (s *StorageService) DownloadFilePrep(absPath string) (*os.File, string, int64, error) {
info, err := s.storageRepo.EntryInfo(absPath)
func (s *StorageService) DownloadFilePrep(abs_path string) (*os.File, string, int64, error) {
info, err := s.storageRepo.EntryInfo(abs_path)
if err != nil || info.IsDir() {
return nil, "", 0, responseErrors.ErrFileDoesNotExist
}
f, err := s.storageRepo.OpenFile(absPath)
f, err := s.storageRepo.OpenFile(abs_path)
if err != nil {
return nil, "", 0, err
}
return f, filepath.Base(absPath), info.Size(), nil
return f, filepath.Base(abs_path), info.Size(), nil
}
func (s *StorageService) CreateFolder(absPath string, name string) error {
info, err := s.storageRepo.EntryInfo(absPath)
func (s *StorageService) UploadFile(abs_path string, name string, f *multipart.FileHeader) error {
info, err := s.storageRepo.EntryInfo(abs_path)
if err != nil || !info.IsDir() {
return responseErrors.ErrFolderDoesNotExist
}
if name == "" || name == "." filepath.Base(name) != name {
if name == "" || name == "." || name == ".." || filepath.Base(name) != name {
return responseErrors.ErrBadAttribute
}
absPath2, err := s.AbsPath(absPath, name)
abs_file_path, err := s.AbsPath(abs_path, name)
if err != nil {
return err
}
if abs_file_path == abs_path {
return responseErrors.ErrBadAttribute
}
return s.storageRepo.CreateFolder(absPath, name)
info, err = s.storageRepo.EntryInfo(abs_file_path)
if err == nil {
return responseErrors.ErrNameTaken
} else if os.IsNotExist(err) {
return s.storageRepo.UploadFile(abs_file_path, f)
} else {
return err
}
}
func (s *StorageService) CreateFolder(abs_path string, name string) error {
info, err := s.storageRepo.EntryInfo(abs_path)
if err != nil || !info.IsDir() {
return responseErrors.ErrFolderDoesNotExist
}
if name == "" || name == "." || name == ".." || filepath.Base(name) != name {
return responseErrors.ErrBadAttribute
}
abs_folder_path, err := s.AbsPath(abs_path, name)
if err != nil {
return err
}
if abs_folder_path == abs_path {
return responseErrors.ErrBadAttribute
}
info, err = s.storageRepo.EntryInfo(abs_folder_path)
if err == nil {
return responseErrors.ErrNameTaken
} else if os.IsNotExist(err) {
return s.storageRepo.CreateFolder(abs_folder_path)
} else {
return err
}
}
func (s *StorageService) DeleteFile(abs_path string) error {
info, err := s.storageRepo.EntryInfo(abs_path)
if err != nil || info.IsDir() {
return responseErrors.ErrFileDoesNotExist
}
return s.storageRepo.DeleteFile(abs_path)
}
func (s *StorageService) DeleteFolder(abs_path string) error {
info, err := s.storageRepo.EntryInfo(abs_path)
if err != nil || !info.IsDir() {
return responseErrors.ErrFolderDoesNotExist
}
return s.storageRepo.DeleteFolder(abs_path)
}
// AbsPath extracts an absolute path and validates it
func (s *StorageService) AbsPath(root string, relativePath string) (string, error) {
cleanName := filepath.Clean(relativePath)
fullPath := filepath.Join(root, cleanName)
clean_name := filepath.Clean(relativePath)
full_path := filepath.Join(root, clean_name)
if fullPath != root && !strings.HasPrefix(fullPath, root+string(os.PathSeparator)) {
if full_path != root && !strings.HasPrefix(full_path, root+string(os.PathSeparator)) {
return "", responseErrors.ErrAccessDenied
}
return fullPath, nil
return full_path, nil
}

View File

@@ -64,6 +64,8 @@ var (
ErrAccessDenied = errors.New("access denied!")
ErrFolderDoesNotExist = errors.New("folder does not exist")
ErrFileDoesNotExist = errors.New("file does not exist")
ErrNameTaken = errors.New("name taken")
ErrMissingFileFieldDocument = errors.New("missing file field 'document'")
)
// Error represents an error with HTTP status code
@@ -173,6 +175,10 @@ func GetErrorCode(c fiber.Ctx, err error) string {
return i18n.T_(c, "error.folder_does_not_exist")
case errors.Is(err, ErrFileDoesNotExist):
return i18n.T_(c, "error.file_does_not_exist")
case errors.Is(err, ErrNameTaken):
return i18n.T_(c, "error.name_taken")
case errors.Is(err, ErrMissingFileFieldDocument):
return i18n.T_(c, "error.missing_file_field_document")
default:
return i18n.T_(c, "error.err_internal_server_error")
@@ -218,7 +224,9 @@ func GetErrorStatus(err error) int {
errors.Is(err, ErrProductOrItsVariationDoesNotExist),
errors.Is(err, ErrAccessDenied),
errors.Is(err, ErrFolderDoesNotExist),
errors.Is(err, ErrFileDoesNotExist):
errors.Is(err, ErrFileDoesNotExist),
errors.Is(err, ErrNameTaken),
errors.Is(err, ErrMissingFileFieldDocument):
return fiber.StatusBadRequest
case errors.Is(err, ErrEmailExists):
return fiber.StatusConflict

View File

@@ -0,0 +1,22 @@
info:
name: create-folder
type: http
seq: 22
http:
method: GET
url: http://localhost:3000/api/v1/restricted/storage/create-folder?path&name=../k
params:
- name: path
value: ""
type: query
- name: name
value: ../k
type: query
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,19 @@
info:
name: delete-entry
type: http
seq: 23
http:
method: GET
url: http://localhost:3000/api/v1/restricted/storage/delete-entry?path=folder2
params:
- name: path
value: folder2
type: query
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,22 @@
info:
name: get-description
type: http
seq: 24
http:
method: GET
url: http://localhost:3000/api/v1/restricted/product-translation/get-product-description?productID=51&productLangID=2
params:
- name: productID
value: "51"
type: query
- name: productLangID
value: "2"
type: query
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,28 @@
info:
name: translate-product-description
type: http
seq: 19
http:
method: GET
url: http://localhost:3000/api/v1/restricted/product-translation/translate-product-description?productID=51&productFromLangID=2&productToLangID=3&model=Google
params:
- name: productID
value: "51"
type: query
- name: productFromLangID
value: "2"
type: query
- name: productToLangID
value: "3"
type: query
- name: model
value: Google
type: query
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5