Compare commits
11 Commits
user_telep
...
f6b321b602
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b321b602 | ||
|
|
af91842b14 | ||
| 04e238fd66 | |||
|
|
1bab7f642f | ||
|
|
a988bbbc33 | ||
|
|
395d670298 | ||
|
|
7d4242abb1 | ||
|
|
9c7eb5ee4e | ||
|
|
833f4a5a07 | ||
|
|
b9bc121d43 | ||
|
|
b2acb8c922 |
4
.env
4
.env
@@ -48,6 +48,10 @@ EMAIL_FROM=test@ma-al.com
|
||||
EMAIL_FROM_NAME=Gitea Manager
|
||||
EMAIL_ADMIN=goc_marek@ma-al.pl
|
||||
|
||||
# STORAGE
|
||||
STORAGE_ROOT=./storage
|
||||
|
||||
|
||||
I18N_LANGS=en,pl,cs
|
||||
|
||||
PDF_SERVER_URL=http://localhost:8000
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@ i18n/*.json
|
||||
*_templ.go
|
||||
tmp/main
|
||||
test.go
|
||||
storage/*
|
||||
!storage/.gitkeep
|
||||
@@ -2,8 +2,10 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -24,7 +26,8 @@ type Config struct {
|
||||
GoogleTranslate GoogleTranslateConfig
|
||||
Image ImageConfig
|
||||
Cors CorsConfig
|
||||
MailiSearch MeiliSearchConfig
|
||||
MeiliSearch MeiliSearchConfig
|
||||
Storage StorageConfig
|
||||
}
|
||||
|
||||
type I18n struct {
|
||||
@@ -95,6 +98,10 @@ type EmailConfig struct {
|
||||
Enabled bool `env:"EMAIL_ENABLED,false"`
|
||||
}
|
||||
|
||||
type StorageConfig struct {
|
||||
RootFolder string `env:"STORAGE_ROOT"`
|
||||
}
|
||||
|
||||
type PdfPrinter struct {
|
||||
ServerUrl string `env:"PDF_SERVER_URL,http://localhost:8000"`
|
||||
}
|
||||
@@ -155,7 +162,7 @@ func load() *Config {
|
||||
|
||||
err = loadEnv(&cfg.OAuth.Google)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for outh google : ", err.Error(), "")
|
||||
slog.Error("not possible to load env variables for oauth google : ", err.Error(), "")
|
||||
}
|
||||
|
||||
err = loadEnv(&cfg.App)
|
||||
@@ -170,12 +177,12 @@ func load() *Config {
|
||||
|
||||
err = loadEnv(&cfg.I18n)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for email : ", err.Error(), "")
|
||||
slog.Error("not possible to load env variables for i18n : ", err.Error(), "")
|
||||
}
|
||||
|
||||
err = loadEnv(&cfg.Pdf)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for email : ", err.Error(), "")
|
||||
slog.Error("not possible to load env variables for pdf : ", err.Error(), "")
|
||||
}
|
||||
|
||||
err = loadEnv(&cfg.GoogleTranslate)
|
||||
@@ -185,19 +192,25 @@ func load() *Config {
|
||||
|
||||
err = loadEnv(&cfg.Image)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for google translate : ", err.Error(), "")
|
||||
slog.Error("not possible to load env variables for image : ", err.Error(), "")
|
||||
}
|
||||
|
||||
err = loadEnv(&cfg.Cors)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for google translate : ", err.Error(), "")
|
||||
slog.Error("not possible to load env variables for cors : ", err.Error(), "")
|
||||
}
|
||||
|
||||
err = loadEnv(&cfg.MailiSearch)
|
||||
err = loadEnv(&cfg.MeiliSearch)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for google translate : ", err.Error(), "")
|
||||
slog.Error("not possible to load env variables for meili search : ", err.Error(), "")
|
||||
}
|
||||
|
||||
err = loadEnv(&cfg.Storage)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for storage : ", err.Error(), "")
|
||||
}
|
||||
cfg.Storage.RootFolder = ResolveRelativePath(cfg.Storage.RootFolder)
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -308,6 +321,22 @@ func setValue(field reflect.Value, val string, key string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ResolveRelativePath(relativePath string) string {
|
||||
// get working directory (where program was started)
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// convert to absolute path
|
||||
absPath := relativePath
|
||||
if !filepath.IsAbs(absPath) {
|
||||
absPath = filepath.Join(wd, absPath)
|
||||
}
|
||||
|
||||
return filepath.Clean(absPath)
|
||||
}
|
||||
|
||||
func parseEnvTag(tag string) (key string, def *string) {
|
||||
if tag == "" {
|
||||
return "", nil
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/authService"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
@@ -115,21 +116,14 @@ func AuthMiddleware() fiber.Handler {
|
||||
// RequireAdmin creates admin-only middleware
|
||||
func RequireAdmin() fiber.Handler {
|
||||
return func(c fiber.Ctx) error {
|
||||
user := c.Locals("user")
|
||||
if user == nil {
|
||||
originalUserRole, ok := localeExtractor.GetOriginalUserRole(c)
|
||||
if !ok {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"error": "not authenticated",
|
||||
})
|
||||
}
|
||||
|
||||
userSession, ok := user.(*model.UserSession)
|
||||
if !ok {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "invalid user session",
|
||||
})
|
||||
}
|
||||
|
||||
if userSession.Role != model.RoleAdmin {
|
||||
if originalUserRole != model.RoleAdmin {
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
|
||||
"error": "admin access required",
|
||||
})
|
||||
|
||||
193
app/delivery/web/api/restricted/storage.go
Normal file
193
app/delivery/web/api/restricted/storage.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/storageService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
type StorageHandler struct {
|
||||
storageService *storageService.StorageService
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
func NewStorageHandler() *StorageHandler {
|
||||
return &StorageHandler{
|
||||
storageService: storageService.New(),
|
||||
config: config.Get(),
|
||||
}
|
||||
}
|
||||
|
||||
func StorageHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
handler := NewStorageHandler()
|
||||
|
||||
r.Get("/list-content/*", handler.ListContent)
|
||||
r.Get("/download-file/*", handler.DownloadFile)
|
||||
|
||||
r.Get("/move/*", handler.Move)
|
||||
r.Get("/copy/*", handler.Copy)
|
||||
r.Post("/upload-file/*", handler.UploadFile)
|
||||
r.Get("/create-folder/*", handler.CreateFolder)
|
||||
r.Delete("/delete-file/*", handler.DeleteFile)
|
||||
r.Delete("/delete-folder/*", handler.DeleteFolder)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *StorageHandler) Move(c fiber.Ctx) error {
|
||||
src_abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Params("*"))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
dest_abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("dest_path"))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
err = h.storageService.Move(src_abs_path, dest_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) Copy(c fiber.Ctx) error {
|
||||
src_abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Params("*"))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
dest_abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Query("dest_path"))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
err = h.storageService.Copy(src_abs_path, dest_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)))
|
||||
}
|
||||
|
||||
// accepted path looks like e.g. "/folder1/" or "folder1"
|
||||
func (h *StorageHandler) ListContent(c fiber.Ctx) error {
|
||||
// relative path defaults to root directory
|
||||
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Params("*"))
|
||||
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(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(entries_in_list, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
|
||||
func (h *StorageHandler) DownloadFile(c fiber.Ctx) error {
|
||||
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Params("*"))
|
||||
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(abs_path)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
c.Attachment(filename)
|
||||
c.Set("Content-Length", strconv.FormatInt(filesize, 10))
|
||||
c.Set("Content-Type", "application/octet-stream")
|
||||
return c.SendStream(f, int(filesize))
|
||||
}
|
||||
|
||||
func (h *StorageHandler) UploadFile(c fiber.Ctx) error {
|
||||
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Params("*"))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
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(c, abs_path, f)
|
||||
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) CreateFolder(c fiber.Ctx) error {
|
||||
abs_path, err := h.storageService.AbsPath(h.config.Storage.RootFolder, c.Params("*"))
|
||||
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.Params("*"))
|
||||
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.Params("*"))
|
||||
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)))
|
||||
}
|
||||
|
||||
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
@@ -115,6 +115,10 @@ func (s *Server) Setup() error {
|
||||
carts := s.restricted.Group("/carts")
|
||||
restricted.CartsHandlerRoutes(carts)
|
||||
|
||||
// storage (restricted)
|
||||
storage := s.restricted.Group("/storage")
|
||||
restricted.StorageHandlerRoutes(storage)
|
||||
|
||||
s.api.All("*", func(c fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
})
|
||||
|
||||
6
app/model/entry.go
Normal file
6
app/model/entry.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
type EntryInList struct {
|
||||
Name string
|
||||
IsFolder bool
|
||||
}
|
||||
@@ -32,12 +32,12 @@ func New() UISearchRepo {
|
||||
}
|
||||
|
||||
func (r *SearchRepo) Search(index string, body []byte) (*SearchProxyResponse, error) {
|
||||
url := fmt.Sprintf("%s/indexes/%s/search", r.cfg.MailiSearch.ServerURL, index)
|
||||
url := fmt.Sprintf("%s/indexes/%s/search", r.cfg.MeiliSearch.ServerURL, index)
|
||||
return r.doRequest(http.MethodPost, url, body)
|
||||
}
|
||||
|
||||
func (r *SearchRepo) GetIndexSettings(index string) (*SearchProxyResponse, error) {
|
||||
url := fmt.Sprintf("%s/indexes/%s/settings", r.cfg.MailiSearch.ServerURL, index)
|
||||
url := fmt.Sprintf("%s/indexes/%s/settings", r.cfg.MeiliSearch.ServerURL, index)
|
||||
return r.doRequest(http.MethodGet, url, nil)
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ func (r *SearchRepo) doRequest(method, url string, body []byte) (*SearchProxyRes
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if r.cfg.MailiSearch.ApiKey != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.cfg.MailiSearch.ApiKey))
|
||||
if r.cfg.MeiliSearch.ApiKey != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.cfg.MeiliSearch.ApiKey))
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
100
app/repos/storageRepo/storageRepo.go
Normal file
100
app/repos/storageRepo/storageRepo.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package storageRepo
|
||||
|
||||
import (
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
type UIStorageRepo interface {
|
||||
EntryInfo(abs_path string) (os.FileInfo, error)
|
||||
ListContent(abs_path string) (*[]model.EntryInList, error)
|
||||
Move(src_abs_path string, dest_abs_path string) error
|
||||
Copy(src_abs_path string, dest_abs_path string) error
|
||||
OpenFile(abs_path string) (*os.File, error)
|
||||
UploadFile(c fiber.Ctx, 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{}
|
||||
|
||||
func New() UIStorageRepo {
|
||||
return &StorageRepo{}
|
||||
}
|
||||
|
||||
func (r *StorageRepo) EntryInfo(abs_path string) (os.FileInfo, error) {
|
||||
return os.Stat(abs_path)
|
||||
}
|
||||
|
||||
func (r *StorageRepo) ListContent(abs_path string) (*[]model.EntryInList, error) {
|
||||
entries, err := os.ReadDir(abs_path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var entries_in_list []model.EntryInList
|
||||
|
||||
for _, entry := range entries {
|
||||
var next_entry_in_list model.EntryInList
|
||||
next_entry_in_list.Name = entry.Name()
|
||||
next_entry_in_list.IsFolder = entry.IsDir()
|
||||
|
||||
entries_in_list = append(entries_in_list, next_entry_in_list)
|
||||
}
|
||||
|
||||
return &entries_in_list, nil
|
||||
}
|
||||
|
||||
func (r *StorageRepo) Move(src_abs_path string, dest_abs_path string) error {
|
||||
return os.Rename(src_abs_path, dest_abs_path)
|
||||
}
|
||||
|
||||
func (r *StorageRepo) Copy(src_abs_path string, dest_abs_path string) error {
|
||||
in, err := os.Open(src_abs_path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
info, err := in.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := os.OpenFile(dest_abs_path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if _, err := io.Copy(out, in); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return out.Sync()
|
||||
}
|
||||
|
||||
func (r *StorageRepo) OpenFile(abs_path string) (*os.File, error) {
|
||||
return os.Open(abs_path)
|
||||
}
|
||||
|
||||
func (r *StorageRepo) UploadFile(c fiber.Ctx, abs_path string, f *multipart.FileHeader) error {
|
||||
return c.SaveFile(f, abs_path)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/langsService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/templ/emails"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/view"
|
||||
)
|
||||
@@ -133,6 +134,6 @@ func (s *EmailService) passwordResetEmailTemplate(name, resetURL string, langID
|
||||
// newUserAdminNotificationTemplate returns the HTML template for admin notification
|
||||
func (s *EmailService) newUserAdminNotificationTemplate(userEmail, userName, baseURL string) string {
|
||||
buf := bytes.Buffer{}
|
||||
emails.EmailAdminNotificationWrapper(view.EmailLayout[view.EmailAdminNotificationData]{LangID: 2, Data: view.EmailAdminNotificationData{UserEmail: userEmail, UserName: userName, BaseURL: baseURL}}).Render(context.Background(), &buf)
|
||||
emails.EmailAdminNotificationWrapper(view.EmailLayout[view.EmailAdminNotificationData]{LangID: constdata.ADMIN_NOTIFICATION_LANGUAGE, Data: view.EmailAdminNotificationData{UserEmail: userEmail, UserName: userName, BaseURL: baseURL}}).Render(context.Background(), &buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ type MeiliService struct {
|
||||
func New() *MeiliService {
|
||||
|
||||
client := meilisearch.New(
|
||||
config.Get().MailiSearch.ServerURL,
|
||||
meilisearch.WithAPIKey(config.Get().MailiSearch.ApiKey),
|
||||
config.Get().MeiliSearch.ServerURL,
|
||||
meilisearch.WithAPIKey(config.Get().MeiliSearch.ApiKey),
|
||||
)
|
||||
|
||||
return &MeiliService{
|
||||
|
||||
163
app/service/storageService/storageService.go
Normal file
163
app/service/storageService/storageService.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package storageService
|
||||
|
||||
import (
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/storageRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
type StorageService struct {
|
||||
storageRepo storageRepo.UIStorageRepo
|
||||
}
|
||||
|
||||
func New() *StorageService {
|
||||
return &StorageService{
|
||||
storageRepo: storageRepo.New(),
|
||||
}
|
||||
}
|
||||
|
||||
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(abs_path)
|
||||
return entries_in_list, err
|
||||
}
|
||||
|
||||
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(abs_path)
|
||||
if err != nil {
|
||||
return nil, "", 0, err
|
||||
}
|
||||
|
||||
return f, filepath.Base(abs_path), info.Size(), nil
|
||||
}
|
||||
|
||||
func (s *StorageService) Move(src_abs_path string, dest_abs_path string) error {
|
||||
_, err := s.storageRepo.EntryInfo(src_abs_path)
|
||||
if err != nil {
|
||||
return responseErrors.ErrFileDoesNotExist
|
||||
}
|
||||
|
||||
_, err = s.storageRepo.EntryInfo(dest_abs_path)
|
||||
if err == nil {
|
||||
return responseErrors.ErrNameTaken
|
||||
} else if os.IsNotExist(err) {
|
||||
return s.storageRepo.Move(src_abs_path, dest_abs_path)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
func (s *StorageService) Copy(src_abs_path string, dest_abs_path string) error {
|
||||
_, err := s.storageRepo.EntryInfo(src_abs_path)
|
||||
if err != nil {
|
||||
return responseErrors.ErrFileDoesNotExist
|
||||
}
|
||||
|
||||
_, err = s.storageRepo.EntryInfo(dest_abs_path)
|
||||
if err == nil {
|
||||
return responseErrors.ErrNameTaken
|
||||
} else if os.IsNotExist(err) {
|
||||
return s.storageRepo.Copy(src_abs_path, dest_abs_path)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StorageService) UploadFile(c fiber.Ctx, abs_path string, f *multipart.FileHeader) error {
|
||||
info, err := s.storageRepo.EntryInfo(abs_path)
|
||||
if err != nil || !info.IsDir() {
|
||||
return responseErrors.ErrFolderDoesNotExist
|
||||
}
|
||||
|
||||
name := f.Filename
|
||||
if name == "" || name == "." || name == ".." || filepath.Base(name) != name {
|
||||
return responseErrors.ErrBadAttribute
|
||||
}
|
||||
abs_file_path, err := s.AbsPath(abs_path, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if abs_file_path == abs_path {
|
||||
return responseErrors.ErrBadAttribute
|
||||
}
|
||||
|
||||
info, err = s.storageRepo.EntryInfo(abs_file_path)
|
||||
if err == nil {
|
||||
return responseErrors.ErrNameTaken
|
||||
} else if os.IsNotExist(err) {
|
||||
return s.storageRepo.UploadFile(c, 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) {
|
||||
clean_name := filepath.Clean(relativePath)
|
||||
full_path := filepath.Join(root, clean_name)
|
||||
|
||||
if full_path != root && !strings.HasPrefix(full_path, root+string(os.PathSeparator)) {
|
||||
return "", responseErrors.ErrAccessDenied
|
||||
}
|
||||
|
||||
return full_path, nil
|
||||
}
|
||||
@@ -4,6 +4,7 @@ package constdata
|
||||
const PASSWORD_VALIDATION_REGEX = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$`
|
||||
const SHOP_ID = 1
|
||||
const SHOP_DEFAULT_LANGUAGE = 1
|
||||
const ADMIN_NOTIFICATION_LANGUAGE = 2
|
||||
|
||||
// CATEGORY_TREE_ROOT_ID corresponds to id_category in ps_category which has is_root_category=1
|
||||
const CATEGORY_TREE_ROOT_ID = 2
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
@@ -177,7 +178,7 @@ func (s *TranslationsStore) ReloadTranslations(translations []model.Translation)
|
||||
|
||||
// T_ is meant to be used to translate error messages and other system communicates.
|
||||
func T_[T ~string](c fiber.Ctx, key T, params ...interface{}) string {
|
||||
if langID, ok := c.Locals("langID").(uint); ok {
|
||||
if langID, ok := localeExtractor.GetLangID(c); ok {
|
||||
parts := strings.Split(string(key), ".")
|
||||
|
||||
if len(parts) >= 2 {
|
||||
|
||||
@@ -21,3 +21,11 @@ func GetUserID(c fiber.Ctx) (uint, bool) {
|
||||
}
|
||||
return user_locale.User.ID, true
|
||||
}
|
||||
|
||||
func GetOriginalUserRole(c fiber.Ctx) (model.CustomerRole, bool) {
|
||||
user_locale, ok := c.Locals(constdata.USER_LOCALE).(*model.UserLocale)
|
||||
if !ok || user_locale.OriginalUser == nil {
|
||||
return "", false
|
||||
}
|
||||
return user_locale.OriginalUser.Role, true
|
||||
}
|
||||
|
||||
@@ -59,6 +59,13 @@ var (
|
||||
ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached")
|
||||
ErrUserHasNoSuchCart = errors.New("user does not have cart with given id")
|
||||
ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist")
|
||||
|
||||
// Typed errors for storage
|
||||
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
|
||||
@@ -162,6 +169,17 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
||||
case errors.Is(err, ErrProductOrItsVariationDoesNotExist):
|
||||
return i18n.T_(c, "error.product_or_its_variation_does_not_exist")
|
||||
|
||||
case errors.Is(err, ErrAccessDenied):
|
||||
return i18n.T_(c, "error.access_denied")
|
||||
case errors.Is(err, ErrFolderDoesNotExist):
|
||||
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")
|
||||
}
|
||||
@@ -203,7 +221,12 @@ func GetErrorStatus(err error) int {
|
||||
errors.Is(err, ErrRootNeverReached),
|
||||
errors.Is(err, ErrMaxAmtOfCartsReached),
|
||||
errors.Is(err, ErrUserHasNoSuchCart),
|
||||
errors.Is(err, ErrProductOrItsVariationDoesNotExist):
|
||||
errors.Is(err, ErrProductOrItsVariationDoesNotExist),
|
||||
errors.Is(err, ErrAccessDenied),
|
||||
errors.Is(err, ErrFolderDoesNotExist),
|
||||
errors.Is(err, ErrFileDoesNotExist),
|
||||
errors.Is(err, ErrNameTaken),
|
||||
errors.Is(err, ErrMissingFileFieldDocument):
|
||||
return fiber.StatusBadRequest
|
||||
case errors.Is(err, ErrEmailExists):
|
||||
return fiber.StatusConflict
|
||||
|
||||
28
bruno/b2b-daniel/save-product-description.yml
Normal file
28
bruno/b2b-daniel/save-product-description.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
info:
|
||||
name: save-product-description
|
||||
type: http
|
||||
seq: 19
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: http://localhost:3000/api/v1/restricted/product-translation/save-product-description?productID=1&productLangID=3
|
||||
params:
|
||||
- name: productID
|
||||
value: "1"
|
||||
type: query
|
||||
- name: productLangID
|
||||
value: "3"
|
||||
type: query
|
||||
body:
|
||||
type: json
|
||||
data: |-
|
||||
{
|
||||
"description": "<p>Der Einsatz von Rehabilitationsrollen in verschiedenen Übungen und Behandlungen wirkt sich positiv auf die Reduzierung von Verletzungen und die Genesungschancen aus. Sie werden in der Rehabilitation, bei Korrekturgymnastik sowie in der traditionellen und Sportmassage eingesetzt, da sie ideal zum Anheben und Spreizen von Gliedmaßen geeignet sind. Zudem können sie zur Unterstützung von Knien, Füßen, Armen und Schultern verwendet werden. Auch für Kinder sind Rehabilitationsrollen empfehlenswert; ihre spielerische Anwendung fördert die Entwicklung der Grobmotorik.</p><p> Dank der großen Auswahl an Farben und Größen lässt sich ein Übungsset zusammenstellen, das in jeder Physiotherapiepraxis, jedem Massageraum, jeder Schule oder jedem Kindergarten benötigt wird.</p><p> Die Rehabilitationsrolle ist ein Medizinprodukt, das den grundlegenden Anforderungen an Medizinprodukte und den Bestimmungen des Medizinproduktegesetzes entspricht, im Register für Medizinprodukte des Amtes für die Registrierung von Arzneimitteln, Medizinprodukten und Biozidprodukten eingetragen ist, mit der Konformitätserklärung des Herstellers versehen ist und das CE-Zeichen trägt. </p><p></p><p><img src=\"https://www.naluconcept.com/img/cms/Logotypy/images.jpg\" alt=\"Medizinprodukt\" style=\"margin-left:auto;margin-right:auto;\" width=\"253\" height=\"86\" /></p><h4> <strong>Empfohlene Verwendung:</strong></h4><ul style=\"list-style-type:circle;\"><li> in der Rehabilitation</li><li> während Massagen (traditionell, Sport)</li><li> in der Korrekturgymnastik (insbesondere für Kinder)</li><li> zur Linderung von Verletzungen einzelner Körperteile</li><li> Zur Unterstützung von: Knien, Knöcheln, Kopf des Patienten</li><li> bei Übungen zur Entwicklung der motorischen Fähigkeiten von Kindern</li><li> in Schönheitssalons</li><li> in Kinderspielzimmern</li></ul><p></p><h4> <strong>Materialspezifikationen:</strong></h4><p> <strong>Abdeckung:</strong> PVC-beschichtetes Material, das für medizinische Geräte vorgesehen ist und daher sehr leicht zu reinigen und zu desinfizieren ist:</p><ul style=\"list-style-type:circle;\"><li> Material gemäß REACH-Verordnung, zertifiziert mit dem STANDARD 100 Zertifikat von OEKO-TEX®.</li><li> Enthält keine Phthalate</li><li> feuerfest</li><li> resistent gegenüber physiologischen Flüssigkeiten (Blut, Urin, Schweiß) und Alkohol</li><li> UV-beständig, daher auch für den Einsatz im Freien geeignet.</li><li> kratzfest</li><li> ölbeständig </li></ul><p><img src=\"https://www.naluconcept.com/img/cms/Logotypy/reach.jpg\" alt=\"ERREICHEN\" width=\"115\" height=\"115\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/oeko-tex.jpg\" alt=\"Öko-Tex Standard 100 Zertifikat\" width=\"116\" height=\"114\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/phthalate-free.jpg\" alt=\"Enthält keine Phthalate\" width=\"112\" height=\"111\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/fireresistant.jpg\" alt=\"Feuerfest\" width=\"114\" height=\"113\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/odporny-na-alkohol.jpg\" alt=\"Alkoholbeständig\" width=\"114\" height=\"114\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/odporny-na-uv.jpg\" alt=\"UV-beständig\" width=\"117\" height=\"116\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/outdoor.jpg\" alt=\"Für den Einsatz im Freien konzipiert\" width=\"116\" height=\"116\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/odporny-na-zadrapania.jpg\" alt=\"Kratzfest\" width=\"97\" height=\"96\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/olejoodporny.jpg\" alt=\"Ölbeständig\" width=\"99\" height=\"98\" /></p><p> <strong>Füllung:</strong> mittelharter Polyurethanschaum mit erhöhter Verformungsbeständigkeit:</p><ul style=\"list-style-type:circle;\"><li> besitzt ein Hygienezertifikat, ausgestellt vom Institut für Maritime und Tropenmedizin in Gdynia</li><li> zertifiziert mit dem STANDARD 100 by OEKO-TEX® Zertifikat – Produktklasse I, ausgestellt vom Textilforschungsinstitut in Łódź</li><li> Hergestellt aus hochwertigen Rohstoffen, die die Ozonschicht nicht schädigen. </li></ul><p><img src=\"https://www.naluconcept.com/img/cms/Logotypy/oeko-tex.jpg\" alt=\"Öko-Tex Standard 100 Zertifikat\" width=\"95\" height=\"95\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/Logo_GUMed_kolor-180x180.jpg\" alt=\"Hygienezertifikat\" width=\"94\" height=\"94\" /><img src=\"https://www.naluconcept.com/img/cms/Logotypy/atest_higieniczny_kolor.jpg\" alt=\"Hygienezertifikat\" width=\"79\" height=\"94\" /></p><p></p><p></p>"
|
||||
}
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
28
bruno/b2b-daniel/translate-product-description.yml
Normal file
28
bruno/b2b-daniel/translate-product-description.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
info:
|
||||
name: translate-product-description
|
||||
type: http
|
||||
seq: 21
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/product-translation/translate-product-description?productID=51&productFromLangID=1&productToLangID=3&model=Google
|
||||
params:
|
||||
- name: productID
|
||||
value: "51"
|
||||
type: query
|
||||
- name: productFromLangID
|
||||
value: "1"
|
||||
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
|
||||
7
bruno/b2b_daniel/auth/folder.yml
Normal file
7
bruno/b2b_daniel/auth/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: auth
|
||||
type: folder
|
||||
seq: 1
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: update-choice
|
||||
type: http
|
||||
seq: 3
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: POST
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: add-new-cart
|
||||
type: http
|
||||
seq: 11
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: add-product-to-cart (1)
|
||||
type: http
|
||||
seq: 16
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: add-product-to-cart
|
||||
type: http
|
||||
seq: 15
|
||||
seq: 14
|
||||
|
||||
http:
|
||||
method: GET
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: change-cart-name
|
||||
type: http
|
||||
seq: 12
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
7
bruno/b2b_daniel/carts/folder.yml
Normal file
7
bruno/b2b_daniel/carts/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: carts
|
||||
type: folder
|
||||
seq: 7
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: retrieve-cart
|
||||
type: http
|
||||
seq: 14
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: retrieve-carts-info
|
||||
type: http
|
||||
seq: 13
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
7
bruno/b2b_daniel/langs-and-countries/folder.yml
Normal file
7
bruno/b2b_daniel/langs-and-countries/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: langs-and-countries
|
||||
type: folder
|
||||
seq: 4
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: get_countries
|
||||
type: http
|
||||
seq: 4
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
7
bruno/b2b_daniel/list/folder.yml
Normal file
7
bruno/b2b_daniel/list/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: list
|
||||
type: folder
|
||||
seq: 3
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: list-users
|
||||
type: http
|
||||
seq: 2
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
7
bruno/b2b_daniel/menu/folder.yml
Normal file
7
bruno/b2b_daniel/menu/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: menu
|
||||
type: folder
|
||||
seq: 5
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: get-breadcrumb
|
||||
type: http
|
||||
seq: 18
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: get-category-tree
|
||||
type: http
|
||||
seq: 5
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
7
bruno/b2b_daniel/product-translation/folder.yml
Normal file
7
bruno/b2b_daniel/product-translation/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: product-translation
|
||||
type: folder
|
||||
seq: 2
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: get-product-description
|
||||
type: http
|
||||
seq: 17
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
|
||||
info:
|
||||
name: translate-product-description
|
||||
type: http
|
||||
seq: 24
|
||||
|
||||
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
|
||||
@@ -1,11 +1,11 @@
|
||||
info:
|
||||
name: create-index
|
||||
type: http
|
||||
seq: 7
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/meili-search/create-index
|
||||
url: http://localhost:3000/api/v1/restricted/search/create-index
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
7
bruno/b2b_daniel/search/folder.yml
Normal file
7
bruno/b2b_daniel/search/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: search
|
||||
type: folder
|
||||
seq: 6
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: get-indexes
|
||||
type: http
|
||||
seq: 9
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: remove-index
|
||||
type: http
|
||||
seq: 8
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: DELETE
|
||||
@@ -1,11 +1,11 @@
|
||||
info:
|
||||
name: search
|
||||
type: http
|
||||
seq: 10
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/meili-search/search?query=w&limit=4&id_category=0&price_lower_bound=60.0&price_upper_bound=70.0
|
||||
url: http://localhost:3000/api/v1/restricted/search/search?query=w&limit=4&id_category=0&price_lower_bound=60.0&price_upper_bound=70.0
|
||||
params:
|
||||
- name: query
|
||||
value: w
|
||||
@@ -1,11 +1,11 @@
|
||||
info:
|
||||
name: test
|
||||
type: http
|
||||
seq: 6
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/meili-search/test
|
||||
url: http://localhost:3000/api/v1/restricted/search/test
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
19
bruno/b2b_daniel/storage/copy.yml
Normal file
19
bruno/b2b_daniel/storage/copy.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
info:
|
||||
name: copy
|
||||
type: http
|
||||
seq: 7
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/storage/copy/folder1/test.txt?dest_path=/folder/a.txt
|
||||
params:
|
||||
- name: dest_path
|
||||
value: /folder/a.txt
|
||||
type: query
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
19
bruno/b2b_daniel/storage/create-folder.yml
Normal file
19
bruno/b2b_daniel/storage/create-folder.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
info:
|
||||
name: create-folder
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/storage/create-folder?name=folder
|
||||
params:
|
||||
- name: name
|
||||
value: folder
|
||||
type: query
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
15
bruno/b2b_daniel/storage/delete-file.yml
Normal file
15
bruno/b2b_daniel/storage/delete-file.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
info:
|
||||
name: delete-file
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: DELETE
|
||||
url: http://localhost:3000/api/v1/restricted/storage/delete-file/folder1/TODO.txt
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
15
bruno/b2b_daniel/storage/delete-folder.yml
Normal file
15
bruno/b2b_daniel/storage/delete-folder.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
info:
|
||||
name: delete-folder
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: DELETE
|
||||
url: http://localhost:3000/api/v1/restricted/storage/delete-folder/folder/
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
15
bruno/b2b_daniel/storage/download-file.yml
Normal file
15
bruno/b2b_daniel/storage/download-file.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
info:
|
||||
name: download-file
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/storage/download-file/folder1/test.xlsx
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
7
bruno/b2b_daniel/storage/folder.yml
Normal file
7
bruno/b2b_daniel/storage/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
info:
|
||||
name: storage
|
||||
type: folder
|
||||
seq: 1
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
15
bruno/b2b_daniel/storage/list-content.yml
Normal file
15
bruno/b2b_daniel/storage/list-content.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
info:
|
||||
name: list-content
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/storage/list-content/folder1
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
19
bruno/b2b_daniel/storage/move.yml
Normal file
19
bruno/b2b_daniel/storage/move.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
info:
|
||||
name: move
|
||||
type: http
|
||||
seq: 8
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/storage/move/folder?dest_path=/folder1/test.txt
|
||||
params:
|
||||
- name: dest_path
|
||||
value: /folder1/test.txt
|
||||
type: query
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
22
bruno/b2b_daniel/storage/upload-file.yml
Normal file
22
bruno/b2b_daniel/storage/upload-file.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
info:
|
||||
name: upload-file
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: http://localhost:3000/api/v1/restricted/storage/upload-file/folder1/
|
||||
body:
|
||||
type: multipart-form
|
||||
data:
|
||||
- name: document
|
||||
type: file
|
||||
value:
|
||||
- /home/daniel/TODO.txt
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
0
storage/.gitkeep
Normal file
0
storage/.gitkeep
Normal file
0
storage/folder1/test
Normal file
0
storage/folder1/test
Normal file
1
storage/folder1/test.txt
Normal file
1
storage/folder1/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a test.
|
||||
1
storage/test.txt
Normal file
1
storage/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a test.
|
||||
Reference in New Issue
Block a user