diff --git a/.env b/.env index 8f59da3..88771c9 100644 --- a/.env +++ b/.env @@ -58,3 +58,5 @@ FILE_MAAL_PL_PASSWORD=1FnwqcEgIUjQHjt1 IMAGE_PREFIX=https://www.naluconcept.com # remove prefix to serv them from same host as presta CORS_ORGIN=https://www.naluconcept.com + +DSN=root:Maal12345678@tcp(localhost:3306)/nalu \ No newline at end of file diff --git a/app/delivery/web/api/public/auth.go b/app/delivery/web/api/public/auth.go index 9db06d5..852a4ac 100644 --- a/app/delivery/web/api/public/auth.go +++ b/app/delivery/web/api/public/auth.go @@ -2,13 +2,17 @@ package public import ( "log" + "strconv" "time" "git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware" "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/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" @@ -345,9 +349,58 @@ func (h *AuthHandler) CompleteRegistration(c fiber.Ctx) error { return c.Status(fiber.StatusCreated).JSON(response) } -// Updates JWT Tokens +// Updates JWT Tokens. Requires authentication and updates access token only func (h *AuthHandler) UpdateJWTToken(c fiber.Ctx) error { - return h.authService.UpdateJWTToken(c) + userLocals, ok := c.Locals(constdata.USER_LOCALES_NAME).(*model.UserSession) + if !ok { + return c.Status(fiber.StatusUnauthorized). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrNotAuthenticated))) + } + + user := model.Customer{ + ID: userLocals.UserID, + Email: userLocals.Email, + Role: userLocals.Role, + LangID: userLocals.LangID, + CountryID: userLocals.CountryID, + IsActive: userLocals.IsActive, + } + + // Parse language and country_id from query params + langIDStr := c.Query("lang_id") + + if langIDStr != "" { + parsedID, err := strconv.ParseUint(langIDStr, 10, 32) + if err != nil { + return c.Status(fiber.StatusBadRequest). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadLangID))) + } + user.LangID = uint(parsedID) + } + + countryIDStr := c.Query("country_id") + + if countryIDStr != "" { + parsedID, err := strconv.ParseUint(countryIDStr, 10, 32) + if err != nil { + return c.Status(fiber.StatusBadRequest). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadCountryID))) + } + user.CountryID = uint(parsedID) + } + + newAccessToken, err := h.authService.UpdateJWTToken(&user) + + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)).JSON(fiber.Map{ + "error": responseErrors.GetErrorCode(c, err), + }) + } + + // does not reset refresh token + h.setAuthCookies(c, newAccessToken, "") + + return c.JSON(response.Make(&fiber.Map{"token": newAccessToken}, 0, i18n.T_(c, response.Message_OK))) } // GoogleLogin redirects the user to Google's OAuth2 consent page @@ -414,12 +467,12 @@ func (h *AuthHandler) GoogleCallback(c fiber.Ctx) error { // Redirect to the locale-prefixed charts page after successful Google login. // The user's preferred language is stored in the auth response; fall back to "en". - lang, err := h.authService.GetLangISOCode(response.User.LangID) + lang_iso_code, err := h.authService.GetLangISOCode(response.User.LangID) if err != nil { - return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadLangID)).JSON(fiber.Map{ - "error": responseErrors.GetErrorCode(c, responseErrors.ErrBadLangID), + return c.Status(responseErrors.GetErrorStatus(err)).JSON(fiber.Map{ + "error": responseErrors.GetErrorCode(c, err), }) } - return c.Redirect().To(h.config.App.BaseURL + "/" + lang) + return c.Redirect().To(h.config.App.BaseURL + "/" + lang_iso_code) } diff --git a/app/delivery/web/api/restricted/list.go b/app/delivery/web/api/restricted/list.go new file mode 100644 index 0000000..7965424 --- /dev/null +++ b/app/delivery/web/api/restricted/list.go @@ -0,0 +1,98 @@ +package restricted + +import ( + "git.ma-al.com/goc_daniel/b2b/app/config" + "git.ma-al.com/goc_daniel/b2b/app/model" + "git.ma-al.com/goc_daniel/b2b/app/service/listService" + "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/query/query_params" + "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" +) + +// ListHandler handles endpoints that list various things (e.g. products or users) +type ListHandler struct { + listService *listService.ListService + config *config.Config +} + +// NewListHandler creates a new ListHandler instance +func NewListHandler() *ListHandler { + listService := listService.New() + return &ListHandler{ + listService: listService, + config: config.Get(), + } +} + +func ListHandlerRoutes(r fiber.Router) fiber.Router { + handler := NewListHandler() + + r.Get("/list-products", handler.ListProducts) + r.Get("/list-users", handler.ListUsers) + + return r +} + +func (h *ListHandler) ListProducts(c fiber.Ctx) error { + paging, filters, err := query_params.ParseFilters[model.Product](c, columnMappingListProducts) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + id_lang, ok := c.Locals("langID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + listing, err := h.listService.ListProducts(id_lang, paging, filters) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(&listing.Items, int(listing.Count), i18n.T_(c, response.Message_OK))) +} + +var columnMappingListProducts map[string]string = map[string]string{ + "product_id": "ps.id_product", + "name": "pl.name", + "reference": "p.reference", + "category_name": "cl.name", + "category_id": "cp.id_category", + "quantity": "sa.quantity", +} + +func (h *ListHandler) ListUsers(c fiber.Ctx) error { + paging, filters, err := query_params.ParseFilters[model.Customer](c, columnMappingListUsers) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + id_lang, ok := c.Locals("langID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + listing, err := h.listService.ListUsers(id_lang, paging, filters) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(&listing.Items, int(listing.Count), i18n.T_(c, response.Message_OK))) +} + +var columnMappingListUsers map[string]string = map[string]string{ + "user_id": "users.id", + "email": "users.email", + "first_name": "users.first_name", + "second_name": "users.second_name", + "role": "users.role", +} diff --git a/app/delivery/web/api/restricted/listProducts.go b/app/delivery/web/api/restricted/listProducts.go deleted file mode 100644 index 2841761..0000000 --- a/app/delivery/web/api/restricted/listProducts.go +++ /dev/null @@ -1,67 +0,0 @@ -package restricted - -import ( - "git.ma-al.com/goc_daniel/b2b/app/config" - "git.ma-al.com/goc_daniel/b2b/app/model" - "git.ma-al.com/goc_daniel/b2b/app/service/listProductsService" - "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/query/query_params" - "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" -) - -// ListProductsHandler handles endpoints that receive, save and translate product descriptions. -type ListProductsHandler struct { - listProductsService *listProductsService.ListProductsService - config *config.Config -} - -// NewListProductsHandler creates a new ListProductsHandler instance -func NewListProductsHandler() *ListProductsHandler { - listProductsService := listProductsService.New() - return &ListProductsHandler{ - listProductsService: listProductsService, - config: config.Get(), - } -} - -func ListProductsHandlerRoutes(r fiber.Router) fiber.Router { - handler := NewListProductsHandler() - - r.Get("/get-listing", handler.GetListing) - - return r -} - -func (h *ListProductsHandler) GetListing(c fiber.Ctx) error { - paging, filters, err := query_params.ParseFilters[model.Product](c, columnMappingGetListing) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) - } - - id_lang, ok := c.Locals("langID").(uint) - if !ok { - return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) - } - - listing, err := h.listProductsService.GetListing(id_lang, paging, filters) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) - } - - return c.JSON(response.Make(&listing.Items, int(listing.Count), i18n.T_(c, response.Message_OK))) -} - -var columnMappingGetListing map[string]string = map[string]string{ - "product_id": "ps.id_product", - "name": "pl.name", - "reference": "p.reference", - "category_name": "cl.name", - "category_id": "cp.id_category", - "quantity": "sa.quantity", -} diff --git a/app/delivery/web/init.go b/app/delivery/web/init.go index ec8284f..d7ca9a8 100644 --- a/app/delivery/web/init.go +++ b/app/delivery/web/init.go @@ -94,9 +94,9 @@ func (s *Server) Setup() error { productTranslation := s.restricted.Group("/product-translation") restricted.ProductTranslationHandlerRoutes(productTranslation) - // listing products routes (restricted) - listProducts := s.restricted.Group("/list-products") - restricted.ListProductsHandlerRoutes(listProducts) + // lists of things routes (restricted) + list := s.restricted.Group("/list") + restricted.ListHandlerRoutes(list) restricted.ProductsHandlerRoutes(s.restricted) diff --git a/app/model/countries.go b/app/model/countries.go index 49f775f..83600b5 100644 --- a/app/model/countries.go +++ b/app/model/countries.go @@ -1,31 +1,17 @@ package model +import "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel" + // Represents a country together with its associated currency type Country struct { - ID uint `gorm:"primaryKey;column:id" json:"id"` - Name string `gorm:"column:name" json:"name"` - Flag string `gorm:"size:16;not null;column:flag" json:"flag"` - CurrencyID uint `gorm:"column:id_currency" json:"currency_id"` - CurrencyISOCode string `gorm:"column:iso_code" json:"currency_iso_code"` - CurrencyName string `gorm:"column:name" json:"currency_name"` - // PSCountryID int `gorm:"column:id_country" json:"ps_country_id"` - // PSCountry *PSCountry `gorm:"foreignKey:PSCountryID;references:ID" json:"ps_country"` - PSCurrencyID uint `gorm:"column:currency" json:"currency"` - PSCurrency *PSCurrency `gorm:"foreignKey:PSCurrencyID;references:currency_id" json:"ps_currency"` + ID uint `gorm:"primaryKey;column:id" json:"id"` + Name string `gorm:"column:name" json:"name"` + Flag string `gorm:"size:16;not null;column:flag" json:"flag"` + + PSCurrencyID uint `gorm:"column:currency_id" json:"currency_id"` + PSCurrency *dbmodel.PsCurrency `gorm:"foreignKey:PSCurrencyID;references:IDCurrency" json:"ps_currency"` } func (Country) TableName() string { return "b2b_countries" } - -type PSCountry struct { - CurrencyID uint `gorm:"column:id_currency" json:"currency_id"` -} - -func (PSCountry) TableName() string { - return "ps_country" -} - -type PSCurrency struct { - Currency int `gorm:"column:currency" json:"currency"` -} diff --git a/app/model/customer.go b/app/model/customer.go index 39c446f..ec7b63d 100644 --- a/app/model/customer.go +++ b/app/model/customer.go @@ -144,3 +144,11 @@ type RefreshToken struct { func (RefreshToken) TableName() string { return "b2b_refresh_tokens" } + +type UserInList struct { + UserID uint `gorm:"primaryKey;column:id" json:"user_id"` + Email string `gorm:"column:email" json:"email"` + FirstName string `gorm:"column:first_name" json:"first_name"` + LastName string `gorm:"column:last_name" json:"last_name"` + Role string `gorm:"column:role" json:"role"` +} diff --git a/app/model/dbmodel/ps_group_reduction.go b/app/model/dbmodel/ps_group_reduction.go index 8f96a3a..48c4933 100644 --- a/app/model/dbmodel/ps_group_reduction.go +++ b/app/model/dbmodel/ps_group_reduction.go @@ -21,12 +21,12 @@ func (*PsGroupReduction) TableName() string { var PsGroupReductionCols = struct { IDGroupReduction gormcol.Field - IDGroup gormcol.Field - IDCategory gormcol.Field - Reduction gormcol.Field + IDGroup gormcol.Field + IDCategory gormcol.Field + Reduction gormcol.Field }{ IDGroupReduction: gormcol.Field{}.Set((&PsGroupReduction{}).TableName(), "id_group_reduction"), - IDGroup: gormcol.Field{}.Set((&PsGroupReduction{}).TableName(), "id_group"), - IDCategory: gormcol.Field{}.Set((&PsGroupReduction{}).TableName(), "id_category"), - Reduction: gormcol.Field{}.Set((&PsGroupReduction{}).TableName(), "reduction"), + IDGroup: gormcol.Field{}.Set((&PsGroupReduction{}).TableName(), "id_group"), + IDCategory: gormcol.Field{}.Set((&PsGroupReduction{}).TableName(), "id_category"), + Reduction: gormcol.Field{}.Set((&PsGroupReduction{}).TableName(), "reduction"), } diff --git a/app/model/dbmodel/ps_product_attribute_combination.go b/app/model/dbmodel/ps_product_attribute_combination.go index 06925ed..bf32419 100644 --- a/app/model/dbmodel/ps_product_attribute_combination.go +++ b/app/model/dbmodel/ps_product_attribute_combination.go @@ -18,9 +18,9 @@ func (*PsProductAttributeCombination) TableName() string { } var PsProductAttributeCombinationCols = struct { - IDAttribute gormcol.Field + IDAttribute gormcol.Field IDProductAttribute gormcol.Field }{ - IDAttribute: gormcol.Field{}.Set((&PsProductAttributeCombination{}).TableName(), "id_attribute"), + IDAttribute: gormcol.Field{}.Set((&PsProductAttributeCombination{}).TableName(), "id_attribute"), IDProductAttribute: gormcol.Field{}.Set((&PsProductAttributeCombination{}).TableName(), "id_product_attribute"), } diff --git a/app/model/dbmodel/ps_product_group_reduction_cache.go b/app/model/dbmodel/ps_product_group_reduction_cache.go index 8f323d3..22578ea 100644 --- a/app/model/dbmodel/ps_product_group_reduction_cache.go +++ b/app/model/dbmodel/ps_product_group_reduction_cache.go @@ -20,10 +20,10 @@ func (*PsProductGroupReductionCache) TableName() string { var PsProductGroupReductionCacheCols = struct { IDProduct gormcol.Field - IDGroup gormcol.Field + IDGroup gormcol.Field Reduction gormcol.Field }{ IDProduct: gormcol.Field{}.Set((&PsProductGroupReductionCache{}).TableName(), "id_product"), - IDGroup: gormcol.Field{}.Set((&PsProductGroupReductionCache{}).TableName(), "id_group"), + IDGroup: gormcol.Field{}.Set((&PsProductGroupReductionCache{}).TableName(), "id_group"), Reduction: gormcol.Field{}.Set((&PsProductGroupReductionCache{}).TableName(), "reduction"), } diff --git a/app/model/dbmodel/ps_pshow_pshowproducttabs_hook.go b/app/model/dbmodel/ps_pshow_pshowproducttabs_hook.go index 1f8ae38..49c9f6b 100644 --- a/app/model/dbmodel/ps_pshow_pshowproducttabs_hook.go +++ b/app/model/dbmodel/ps_pshow_pshowproducttabs_hook.go @@ -19,11 +19,11 @@ func (*PsPshowPshowproducttabsHook) TableName() string { } var PsPshowPshowproducttabsHookCols = struct { - IDHook gormcol.Field - HookName gormcol.Field + IDHook gormcol.Field + HookName gormcol.Field PrestaIDHook gormcol.Field }{ - IDHook: gormcol.Field{}.Set((&PsPshowPshowproducttabsHook{}).TableName(), "id_hook"), - HookName: gormcol.Field{}.Set((&PsPshowPshowproducttabsHook{}).TableName(), "hook_name"), + IDHook: gormcol.Field{}.Set((&PsPshowPshowproducttabsHook{}).TableName(), "id_hook"), + HookName: gormcol.Field{}.Set((&PsPshowPshowproducttabsHook{}).TableName(), "hook_name"), PrestaIDHook: gormcol.Field{}.Set((&PsPshowPshowproducttabsHook{}).TableName(), "presta_id_hook"), } diff --git a/app/repos/listProductsRepo/listProductsRepo.go b/app/repos/listRepo/listRepo.go similarity index 59% rename from app/repos/listProductsRepo/listProductsRepo.go rename to app/repos/listRepo/listRepo.go index 2440a46..c84beba 100644 --- a/app/repos/listProductsRepo/listProductsRepo.go +++ b/app/repos/listRepo/listRepo.go @@ -1,4 +1,4 @@ -package listProductsRepo +package listRepo import ( "git.ma-al.com/goc_daniel/b2b/app/config" @@ -7,25 +7,27 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel" "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" + "git.ma-al.com/goc_marek/gormcol" "github.com/WinterYukky/gorm-extra-clause-plugin/exclause" ) -type UIListProductsRepo interface { - GetListing(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) +type UIListRepo interface { + ListProducts(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) + ListUsers(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.UserInList], error) } -type ListProductsRepo struct{} +type ListRepo struct{} -func New() UIListProductsRepo { - return &ListProductsRepo{} +func New() UIListRepo { + return &ListRepo{} } -func (repo *ListProductsRepo) GetListing(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) { - var listing []model.ProductInList +func (repo *ListRepo) ListProducts(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) { + var list []model.ProductInList var total int64 query := db.Get(). - Table("ps_product_shop ps"). + Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps"). Select(` ps.id_product AS product_id, pl.name AS name, @@ -67,13 +69,53 @@ func (repo *ListProductsRepo) GetListing(id_lang uint, p find.Paging, filt *filt Order("ps.id_product DESC"). Limit(p.Limit()). Offset(p.Offset()). - Find(&listing).Error + Find(&list).Error if err != nil { return find.Found[model.ProductInList]{}, err } return find.Found[model.ProductInList]{ - Items: listing, + Items: list, + Count: uint(total), + }, nil +} + +func (repo *ListRepo) ListUsers(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.UserInList], error) { + var list []model.UserInList + var total int64 + + query := db.Get(). + Table("b2b_customers AS users"). + Select(` + users.id AS id, + users.email AS email, + users.first_name AS first_name, + users.last_name AS last_name, + users.role AS role + `) + + // Apply all filters + if filt != nil { + filt.ApplyAll(query) + } + + // run counter first as query is without limit and offset + err := query.Count(&total).Error + if err != nil { + return find.Found[model.UserInList]{}, err + } + + err = query. + Order("users.id DESC"). + Limit(p.Limit()). + Offset(p.Offset()). + Find(&list).Error + if err != nil { + return find.Found[model.UserInList]{}, err + } + + return find.Found[model.UserInList]{ + Items: list, Count: uint(total), }, nil } diff --git a/app/service/authService/auth.go b/app/service/authService/auth.go index 4818754..2fc4a7d 100644 --- a/app/service/authService/auth.go +++ b/app/service/authService/auth.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "errors" "fmt" - "strconv" "time" "git.ma-al.com/goc_daniel/b2b/app/config" @@ -14,13 +13,9 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/service/emailService" 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/utils/nullable" - "git.ma-al.com/goc_daniel/b2b/app/utils/response" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "github.com/dlclark/regexp2" - "github.com/gofiber/fiber/v3" "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" @@ -167,7 +162,7 @@ func (s *AuthService) Register(req *model.RegisterRequest) error { baseURL := config.Get().App.BaseURL lang, err := s.GetLangISOCode(req.LangID) if err != nil { - return responseErrors.ErrBadLangID + return err } if err := s.email.SendVerificationEmail(user.Email, user.EmailVerificationToken, baseURL, lang); err != nil { @@ -276,7 +271,7 @@ func (s *AuthService) RequestPasswordReset(emailAddr string) error { baseURL := config.Get().App.BaseURL lang, err := s.GetLangISOCode(user.LangID) if err != nil { - return responseErrors.ErrBadLangID + return err } if err := s.email.SendPasswordResetEmail(user.Email, user.PasswordResetToken, baseURL, lang); err != nil { @@ -482,7 +477,7 @@ func hashToken(raw string) string { func (s *AuthService) generateAccessToken(user *model.Customer) (string, error) { _, err := s.GetLangISOCode(user.LangID) if err != nil { - return "", responseErrors.ErrBadLangID + return "", err } err = s.CheckIfCountryExists(user.CountryID) @@ -508,97 +503,19 @@ func (s *AuthService) generateAccessToken(user *model.Customer) (string, error) return token.SignedString([]byte(s.config.JWTSecret)) } -func (s *AuthService) UpdateJWTToken(c fiber.Ctx) error { - // Get user ID from JWT claims in context (set by auth middleware) - // claims, ok := c.Locals("jwt_claims").(*JWTClaims) - // if !ok || claims == nil { - // return c.Status(fiber.StatusUnauthorized). - // JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrNotAuthenticated))) - // } - // fmt.Printf("claims: %v\n", claims) - // var user model.Customer - // // Find user by ID - // if err := s.db.First(&user, claims.UserID).Error; err != nil { - // return err - // } - - userLocals, ok := c.Locals(constdata.USER_LOCALES_NAME).(*model.UserSession) - if !ok { - return c.Status(fiber.StatusUnauthorized). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrNotAuthenticated))) - } - - user := model.Customer{ - ID: userLocals.UserID, - Email: userLocals.Email, - Role: userLocals.Role, - LangID: userLocals.LangID, - CountryID: userLocals.CountryID, - IsActive: userLocals.IsActive, - } - - // Parse language and country_id from query params - langIDStr := c.Query("lang_id") - - var langID uint - if langIDStr != "" { - parsedID, err := strconv.ParseUint(langIDStr, 10, 32) - if err != nil { - return c.Status(fiber.StatusBadRequest). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadLangID))) - } - langID = uint(parsedID) - - _, err = s.GetLangISOCode(langID) - if err != nil { - return responseErrors.ErrBadLangID - } else { - user.LangID = langID - } - } - - countryIDStr := c.Query("country_id") - - var countryID uint - if countryIDStr != "" { - parsedID, err := strconv.ParseUint(countryIDStr, 10, 32) - if err != nil { - return c.Status(fiber.StatusBadRequest). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadCountryID))) - } - countryID = uint(parsedID) - - err = s.CheckIfCountryExists(countryID) - if err != nil { - return responseErrors.ErrBadCountryID - } else { - user.CountryID = countryID - } - } - - // Update choice and get new token using AuthService - newToken, err := s.generateAccessToken(&user) +func (s *AuthService) UpdateJWTToken(user *model.Customer) (string, error) { + // Update choice and get new access token using AuthService + new_access_token, err := s.generateAccessToken(user) if err != nil { - return c.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + return "", err } // Save the updated user - if err := s.db.Save(&user).Error; err != nil { - return fmt.Errorf("database error: %w", err) + if err := s.db.Save(user).Error; err != nil { + return "", fmt.Errorf("database error: %w", err) } - // Set the new JWT cookie - cookie := new(fiber.Cookie) - cookie.Name = "jwt_token" - cookie.Value = newToken - cookie.HTTPOnly = true - cookie.Secure = true - cookie.SameSite = fiber.CookieSameSiteLaxMode - - c.Cookie(cookie) - - return c.JSON(response.Make(&fiber.Map{"token": newToken}, 0, i18n.T_(c, response.Message_OK))) + return new_access_token, nil } // generateVerificationToken generates a random verification token @@ -623,14 +540,20 @@ func validatePassword(password string) error { func (s *AuthService) GetLangISOCode(langID uint) (string, error) { var lang string + var err error if langID == 0 { // retrieve the default lang - err := db.DB.Table("b2b_language").Where("is_default = ?", 1).Select("iso_code").Scan(&lang).Error - return lang, err + err = db.DB.Table("b2b_language").Where("is_default = ?", 1).Select("iso_code").Scan(&lang).Error } else { - err := db.DB.Table("b2b_language").Where("id = ?", langID).Where("active = ?", 1).Select("iso_code").Scan(&lang).Error - return lang, err + err = db.DB.Table("b2b_language").Where("id = ?", langID).Where("active = ?", 1).Select("iso_code").Scan(&lang).Error } + + if err != nil { + return lang, err + } else if lang == "" { + return lang, responseErrors.ErrBadLangID + } + return lang, nil } func (s *AuthService) CheckIfCountryExists(countryID uint) error { diff --git a/app/service/authService/google_oauth.go b/app/service/authService/google_oauth.go index d56ebf8..a4c2cd1 100644 --- a/app/service/authService/google_oauth.go +++ b/app/service/authService/google_oauth.go @@ -153,7 +153,8 @@ func (s *AuthService) findOrCreateGoogleUser(info *view.GoogleUserInfo) (*model. Role: model.RoleUser, IsActive: true, EmailVerified: true, - LangID: 2, + LangID: 2, // default is english + CountryID: 2, // default is England } if err := s.db.Create(&newUser).Error; err != nil { diff --git a/app/service/listProductsService/listProductsService.go b/app/service/listProductsService/listProductsService.go deleted file mode 100644 index b173a0e..0000000 --- a/app/service/listProductsService/listProductsService.go +++ /dev/null @@ -1,29 +0,0 @@ -package listProductsService - -import ( - "git.ma-al.com/goc_daniel/b2b/app/model" - "git.ma-al.com/goc_daniel/b2b/app/repos/listProductsRepo" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" -) - -type ListProductsService struct { - listProductsRepo listProductsRepo.UIListProductsRepo -} - -func New() *ListProductsService { - return &ListProductsService{ - listProductsRepo: listProductsRepo.New(), - } -} - -func (s *ListProductsService) GetListing(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { - var products find.Found[model.ProductInList] - - products, err := s.listProductsRepo.GetListing(id_lang, p, filters) - if err != nil { - return products, err - } - - return products, nil -} diff --git a/app/service/listService/listService.go b/app/service/listService/listService.go new file mode 100644 index 0000000..d3d168b --- /dev/null +++ b/app/service/listService/listService.go @@ -0,0 +1,26 @@ +package listService + +import ( + "git.ma-al.com/goc_daniel/b2b/app/model" + "git.ma-al.com/goc_daniel/b2b/app/repos/listRepo" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" +) + +type ListService struct { + listRepo listRepo.UIListRepo +} + +func New() *ListService { + return &ListService{ + listRepo: listRepo.New(), + } +} + +func (s *ListService) ListProducts(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { + return s.listRepo.ListProducts(id_lang, p, filters) +} + +func (s *ListService) ListUsers(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.UserInList], error) { + return s.listRepo.ListUsers(id_lang, p, filters) +} diff --git a/bo/.gitignore b/bo/.gitignore index cd68f14..e80697a 100644 --- a/bo/.gitignore +++ b/bo/.gitignore @@ -13,6 +13,7 @@ dist dist-ssr coverage *.local +/bo/components.d.ts # Editor directories and files .vscode/* diff --git a/bo/components.d.ts b/bo/components.d.ts deleted file mode 100644 index e98adc0..0000000 --- a/bo/components.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* eslint-disable */ -// @ts-nocheck -// biome-ignore lint: disable -// oxlint-disable -// ------ -// Generated by unplugin-vue-components -// Read more: https://github.com/vuejs/core/pull/3399 - -export {} - -/* prettier-ignore */ -declare module 'vue' { - export interface GlobalComponents { - Cart1: typeof import('./src/components/customer/Cart1.vue')['default'] - CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default'] - CategoryMenu: typeof import('./src/components/inner/categoryMenu.vue')['default'] - CategoryMenuListing: typeof import('./src/components/inner/categoryMenuListing.vue')['default'] - copy: typeof import('./src/components/inner/categoryMenu copy.vue')['default'] - Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default'] - Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default'] - En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default'] - En_TermsAndConditionsView: typeof import('./src/components/terms/en_TermsAndConditionsView.vue')['default'] - LangSwitch: typeof import('./src/components/inner/langSwitch.vue')['default'] - Page: typeof import('./src/components/customer/Page.vue')['default'] - PageAddresses: typeof import('./src/components/customer/PageAddresses.vue')['default'] - PageCart: typeof import('./src/components/customer/PageCart.vue')['default'] - PageCarts: typeof import('./src/components/customer/PageCarts.vue')['default'] - PageCreateAccount: typeof import('./src/components/customer/PageCreateAccount.vue')['default'] - PageCustomerData: typeof import('./src/components/customer/PageCustomerData.vue')['default'] - PageProduct: typeof import('./src/components/customer/PageProduct.vue')['default'] - PageProductCardFull: typeof import('./src/components/customer/PageProductCardFull.vue')['default'] - PageProducts: typeof import('./src/components/admin/PageProducts.vue')['default'] - PageProductsList: typeof import('./src/components/admin/PageProductsList.vue')['default'] - PageProfileDetails: typeof import('./src/components/customer/PageProfileDetails.vue')['default'] - PageProfileDetailsAddInfo: typeof import('./src/components/customer/PageProfileDetailsAddInfo.vue')['default'] - Pl_PrivacyPolicyView: typeof import('./src/components/terms/pl_PrivacyPolicyView.vue')['default'] - Pl_TermsAndConditionsView: typeof import('./src/components/terms/pl_TermsAndConditionsView.vue')['default'] - ProductCustomization: typeof import('./src/components/customer/components/ProductCustomization.vue')['default'] - ProductDetailView: typeof import('./src/components/admin/ProductDetailView.vue')['default'] - ProductsView: typeof import('./src/components/admin/ProductsView.vue')['default'] - ProductVariants: typeof import('./src/components/customer/components/ProductVariants.vue')['default'] - RouterLink: typeof import('vue-router')['RouterLink'] - RouterView: typeof import('vue-router')['RouterView'] - ThemeSwitch: typeof import('./src/components/inner/themeSwitch.vue')['default'] - TopBar: typeof import('./src/components/TopBar.vue')['default'] - TopBarLogin: typeof import('./src/components/TopBarLogin.vue')['default'] - UAlert: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default'] - UButton: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default'] - UCard: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default'] - UCheckbox: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default'] - UDrawer: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default'] - UDropdownMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue')['default'] - UForm: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Form.vue')['default'] - UFormField: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default'] - UIcon: typeof import('./node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default'] - UInput: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default'] - UInputNumber: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/InputNumber.vue')['default'] - ULink: typeof import('./node_modules/@nuxt/ui/dist/runtime/vue/overrides/vue-router/Link.vue')['default'] - UModal: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default'] - UNavigationMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue')['default'] - UPagination: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Pagination.vue')['default'] - USelect: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Select.vue')['default'] - USelectMenu: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default'] - UTable: typeof import('./node_modules/@nuxt/ui/dist/runtime/components/Table.vue')['default'] - } -} diff --git a/bo/src/components/TopBar.vue b/bo/src/components/TopBar.vue index 2c9d8ca..38571bd 100644 --- a/bo/src/components/TopBar.vue +++ b/bo/src/components/TopBar.vue @@ -3,10 +3,11 @@ import { useFetchJson } from '@/composable/useFetchJson' import LangSwitch from './inner/langSwitch.vue' import ThemeSwitch from './inner/themeSwitch.vue' import { useAuthStore } from '@/stores/auth' -import { computed, ref } from 'vue' +import { computed, onMounted, ref } from 'vue' import { currentLang } from '@/router/langs' import type { LabelTrans, TopMenuItem } from '@/types' import type { NavigationMenuItem } from '@nuxt/ui' +import { useRoute, useRouter } from 'vue-router' const authStore = useAuthStore() let menu = ref() @@ -19,30 +20,43 @@ async function getTopMenu() { } } +const router = useRouter() +const route = useRoute() const menuItems = computed(() => transformMenu(menu.value[0].children, currentLang.value?.iso_code)) function transformMenu(items: TopMenuItem[], locale: string | undefined): NavigationMenuItem[] { + return items.map((item) => { - let route = { - icon: 'i-lucide-house', + const route: NavigationMenuItem = { + icon: item.label.icon ? item.label.icon : 'i-lucide-house', label: item.label.trans[locale as keyof LabelTrans].label, - children: item.children ? transformMenu(item.children, locale) : undefined, + children: item.children + ? transformMenu(item.children, locale) + : undefined, + } + + route.onSelect = () => { + const query = { + name: item.params.route.name, + params: { + ...(item.params.route.params || {}), + locale: currentLang.value?.iso_code + } + } + + router.push(query) } - if (item.params?.route) { - route = { ...route, ...{ to: { name: item.params.route.name, params: { locale: locale } } } } - } return route }) } - await getTopMenu()