diff --git a/.env b/.env index d45c67a..c4ee18d 100644 --- a/.env +++ b/.env @@ -63,4 +63,7 @@ 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 +DSN=root:Maal12345678@tcp(localhost:3306)/nalu + +LOG_LEVEL=warn +LOG_COLORIZE=true \ No newline at end of file diff --git a/app/cmd/cmds/startServer.go b/app/cmd/cmds/startServer.go index c23d8b2..44c3adf 100644 --- a/app/cmd/cmds/startServer.go +++ b/app/cmd/cmds/startServer.go @@ -4,8 +4,11 @@ import ( "log" "os" + "git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/delivery/web" + "git.ma-al.com/goc_daniel/b2b/app/service/langsService" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "git.ma-al.com/goc_daniel/b2b/app/utils/version" "github.com/spf13/cobra" ) @@ -21,6 +24,8 @@ var ( return } + logger.Init("b2b", nil, config.Get().Log.LogLevel, config.Get().Log.LogColorize) + // Create and setup the server server := web.New() diff --git a/app/config/config.go b/app/config/config.go index 1963d38..16cb64b 100644 --- a/app/config/config.go +++ b/app/config/config.go @@ -28,6 +28,7 @@ type Config struct { Cors CorsConfig MeiliSearch MeiliSearchConfig Storage StorageConfig + Log LogConfig } type I18n struct { @@ -87,6 +88,11 @@ type AppConfig struct { BaseURL string `env:"APP_BASE_URL,http://localhost:5173"` } +type LogConfig struct { + LogLevel string `env:"LOG_LEVEL,warn"` + LogColorize bool `env:"LOG_COLORIZE,true"` +} + type EmailConfig struct { SMTPHost string `env:"EMAIL_SMTP_HOST,localhost"` SMTPPort int `env:"EMAIL_SMTP_PORT,587"` @@ -209,6 +215,11 @@ func load() *Config { if err != nil { slog.Error("not possible to load env variables for storage : ", err.Error(), "") } + + err = loadEnv(&cfg.Log) + if err != nil { + slog.Error("not possible to load env variables for logger : ", err.Error(), "") + } cfg.Storage.RootFolder = ResolveRelativePath(cfg.Storage.RootFolder) return cfg diff --git a/app/delivery/web/api/public/auth.go b/app/delivery/web/api/public/auth.go index 527a587..7492e1d 100644 --- a/app/delivery/web/api/public/auth.go +++ b/app/delivery/web/api/public/auth.go @@ -1,7 +1,6 @@ package public import ( - "log" "strconv" "time" @@ -11,6 +10,7 @@ import ( "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/logger" "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" @@ -178,7 +178,13 @@ func (h *AuthHandler) ForgotPassword(c fiber.Ctx) error { // Request password reset - always return success to prevent email enumeration err := h.authService.RequestPasswordReset(req.Email) if err != nil { - log.Printf("Password reset request error: %v", err) + + logger.Warn("password reset request failed", + "handler", "AuthHandler.ForgotPassword", + + "email", req.Email, + "error", err.Error(), + ) } return c.JSON(fiber.Map{ @@ -307,7 +313,6 @@ func (h *AuthHandler) Register(c fiber.Ctx) error { // Attempt registration err := h.authService.Register(&req) if err != nil { - log.Printf("Register error: %v", err) return c.Status(responseErrors.GetErrorStatus(err)).JSON(fiber.Map{ "error": responseErrors.GetErrorCode(c, err), }) @@ -447,7 +452,6 @@ func (h *AuthHandler) GoogleCallback(c fiber.Ctx) error { response, rawRefreshToken, err := h.authService.HandleGoogleCallback(code) if err != nil { - log.Printf("Google OAuth callback error: %v", err) return c.Status(responseErrors.GetErrorStatus(err)).JSON(fiber.Map{ "error": responseErrors.GetErrorCode(c, err), }) diff --git a/app/delivery/web/api/restricted/addresses.go b/app/delivery/web/api/restricted/addresses.go index f33b7cb..2b00324 100644 --- a/app/delivery/web/api/restricted/addresses.go +++ b/app/delivery/web/api/restricted/addresses.go @@ -6,6 +6,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/service/addressesService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -45,6 +46,13 @@ func (h *AddressesHandler) GetTemplate(c fiber.Ctx) error { template, err := h.addressesService.GetTemplate(uint(country_id)) if err != nil { + + logger.Error("failed to get address template", + "handler", "AddressesHandler.GetTemplate", + + "country_id", country_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -74,6 +82,13 @@ func (h *AddressesHandler) AddNewAddress(c fiber.Ctx) error { err = h.addressesService.AddNewAddress(userID, address_info, uint(country_id)) if err != nil { + + logger.Error("failed to add new address", + "handler", "AddressesHandler.AddNewAddress", + + "user_id", userID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -110,6 +125,14 @@ func (h *AddressesHandler) ModifyAddress(c fiber.Ctx) error { err = h.addressesService.ModifyAddress(userID, uint(address_id), address_info, uint(country_id)) if err != nil { + + logger.Error("failed to modify address", + "handler", "AddressesHandler.ModifyAddress", + + "user_id", userID, + "address_id", address_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -126,6 +149,13 @@ func (h *AddressesHandler) RetrieveAddressesInfo(c fiber.Ctx) error { addresses, err := h.addressesService.RetrieveAddresses(userID) if err != nil { + + logger.Error("failed to retrieve addresses", + "handler", "AddressesHandler.RetrieveAddressesInfo", + + "user_id", userID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -149,6 +179,14 @@ func (h *AddressesHandler) DeleteAddress(c fiber.Ctx) error { err = h.addressesService.DeleteAddress(userID, uint(address_id)) if err != nil { + + logger.Error("failed to delete address", + "handler", "AddressesHandler.DeleteAddress", + + "user_id", userID, + "address_id", address_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/carts.go b/app/delivery/web/api/restricted/carts.go index 6118af2..abd55c0 100644 --- a/app/delivery/web/api/restricted/carts.go +++ b/app/delivery/web/api/restricted/carts.go @@ -6,6 +6,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/service/cartsService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -49,6 +50,13 @@ func (h *CartsHandler) AddNewCart(c fiber.Ctx) error { name := c.Query("name") new_cart, err := h.cartsService.CreateNewCart(userID, name) if err != nil { + + logger.Error("failed to create cart", + "handler", "CartsHandler.AddNewCart", + + "user_id", userID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -72,6 +80,14 @@ func (h *CartsHandler) RemoveCart(c fiber.Ctx) error { err = h.cartsService.RemoveCart(userID, uint(cart_id)) if err != nil { + + logger.Error("failed to remove cart", + "handler", "CartsHandler.RemoveCart", + + "user_id", userID, + "cart_id", cart_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -97,6 +113,14 @@ func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error { err = h.cartsService.UpdateCartName(userID, uint(cart_id), new_name) if err != nil { + + logger.Error("failed to update cart name", + "handler", "CartsHandler.ChangeCartName", + + "user_id", userID, + "cart_id", cart_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -113,6 +137,13 @@ func (h *CartsHandler) RetrieveCartsInfo(c fiber.Ctx) error { carts_info, err := h.cartsService.RetrieveCartsInfo(userID) if err != nil { + + logger.Error("failed to retrieve carts info", + "handler", "CartsHandler.RetrieveCartsInfo", + + "user_id", userID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -136,6 +167,14 @@ func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error { cart, err := h.cartsService.RetrieveCart(userID, uint(cart_id)) if err != nil { + + logger.Error("failed to retrieve cart", + "handler", "CartsHandler.RetrieveCart", + + "user_id", userID, + "cart_id", cart_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -195,6 +234,15 @@ func (h *CartsHandler) AddProduct(c fiber.Ctx) error { err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, amount, set_amount) if err != nil { + + logger.Error("failed to add product to cart", + "handler", "CartsHandler.AddProduct", + + "user_id", userID, + "cart_id", cart_id, + "product_id", product_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -240,6 +288,15 @@ func (h *CartsHandler) RemoveProduct(c fiber.Ctx) error { err = h.cartsService.RemoveProduct(userID, uint(cart_id), uint(product_id), product_attribute_id) if err != nil { + + logger.Error("failed to remove product from cart", + "handler", "CartsHandler.RemoveProduct", + + "user_id", userID, + "cart_id", cart_id, + "product_id", product_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/currency.go b/app/delivery/web/api/restricted/currency.go index d3bda40..10a08d3 100644 --- a/app/delivery/web/api/restricted/currency.go +++ b/app/delivery/web/api/restricted/currency.go @@ -9,6 +9,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/service/currencyService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -46,6 +47,12 @@ func (h *CurrencyHandler) PostCurrencyRate(c fiber.Ctx) error { err := h.CurrencyService.CreateCurrencyRate(¤cyRate) if err != nil { + + logger.Error("failed to create currency rate", + "handler", "CurrencyHandler.PostCurrencyRate", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -63,6 +70,13 @@ func (h *CurrencyHandler) GetCurrencyRate(c fiber.Ctx) error { currency, err := h.CurrencyService.GetCurrency(uint(id)) if err != nil { + + logger.Error("failed to get currency", + "handler", "CurrencyHandler.GetCurrencyRate", + + "currency_id", id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)).JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/customer.go b/app/delivery/web/api/restricted/customer.go index 9d15c11..030c82d 100644 --- a/app/delivery/web/api/restricted/customer.go +++ b/app/delivery/web/api/restricted/customer.go @@ -9,6 +9,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/service/customerService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -64,6 +65,13 @@ func (h *customerHandler) customerData(fc fiber.Ctx) error { customer, err := h.service.GetById(customerId) if err != nil { + + logger.Error("failed to get customer", + "handler", "customerHandler.customerData", + + "customer_id", customerId, + "error", err.Error(), + ) return fc.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) } @@ -88,6 +96,12 @@ func (h *customerHandler) listCustomers(fc fiber.Ctx) error { customer, err := h.service.Find(user.LangID, p, filt, search) if err != nil { + + logger.Error("failed to list customers", + "handler", "customerHandler.listCustomers", + + "error", err.Error(), + ) return fc.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) } @@ -120,6 +134,13 @@ func (h *customerHandler) setCustomerNoVatStatus(fc fiber.Ctx) error { } if err := h.service.SetCustomerNoVatStatus(req.CustomerID, req.IsNoVat); err != nil { + + logger.Error("failed to set customer no vat status", + "handler", "customerHandler.setCustomerNoVatStatus", + + "customer_id", req.CustomerID, + "error", err.Error(), + ) return fc.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) } diff --git a/app/delivery/web/api/restricted/localeSelector.go b/app/delivery/web/api/restricted/localeSelector.go index 8131501..d385643 100644 --- a/app/delivery/web/api/restricted/localeSelector.go +++ b/app/delivery/web/api/restricted/localeSelector.go @@ -3,6 +3,7 @@ package restricted import ( "git.ma-al.com/goc_daniel/b2b/app/service/localeSelectorService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -34,6 +35,12 @@ func LocaleSelectorHandlerRoutes(r fiber.Router) fiber.Router { func (h *LocaleSelectorHandler) GetLanguages(c fiber.Ctx) error { languages, err := h.localeSelectorService.GetLanguages() if err != nil { + + logger.Error("failed to get languages", + "handler", "LocaleSelectorHandler.GetLanguages", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -44,6 +51,12 @@ func (h *LocaleSelectorHandler) GetLanguages(c fiber.Ctx) error { func (h *LocaleSelectorHandler) GetCountries(c fiber.Ctx) error { countries, err := h.localeSelectorService.GetCountriesAndCurrencies() if err != nil { + + logger.Error("failed to get countries", + "handler", "LocaleSelectorHandler.GetCountries", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/menu.go b/app/delivery/web/api/restricted/menu.go index 8114e9c..959d288 100644 --- a/app/delivery/web/api/restricted/menu.go +++ b/app/delivery/web/api/restricted/menu.go @@ -6,6 +6,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/service/menuService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -49,6 +50,12 @@ func (h *MenuHandler) GetCategoryTree(c fiber.Ctx) error { category_tree, err := h.menuService.GetCategoryTree(uint(root_category_id), lang_id) if err != nil { + + logger.Error("failed to get category tree", + "handler", "MenuHandler.GetCategoryTree", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -79,6 +86,12 @@ func (h *MenuHandler) GetBreadcrumb(c fiber.Ctx) error { breadcrumb, err := h.menuService.GetBreadcrumb(uint(root_category_id), uint(category_id), lang_id) if err != nil { + + logger.Error("failed to get breadcrumb", + "handler", "MenuHandler.GetBreadcrumb", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -94,6 +107,12 @@ func (h *MenuHandler) GetTopMenu(c fiber.Ctx) error { } menu, err := h.menuService.GetTopMenu(customer.LangID, customer.RoleID) if err != nil { + + logger.Error("failed to get top menu", + "handler", "MenuHandler.GetTopMenu", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/orders.go b/app/delivery/web/api/restricted/orders.go index 652d9d2..679ec1a 100644 --- a/app/delivery/web/api/restricted/orders.go +++ b/app/delivery/web/api/restricted/orders.go @@ -7,6 +7,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/service/orderService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -53,6 +54,12 @@ func (h *OrdersHandler) ListOrders(c fiber.Ctx) error { list, err := h.ordersService.Find(user, paging, filters) if err != nil { + + logger.Error("failed to list orders", + "handler", "OrdersHandler.ListOrders", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -104,6 +111,13 @@ func (h *OrdersHandler) PlaceNewOrder(c fiber.Ctx) error { err = h.ordersService.PlaceNewOrder(userID, uint(cart_id), name, uint(country_id), address_info) if err != nil { + + logger.Error("failed to place order", + "handler", "OrdersHandler.PlaceNewOrder", + + "user_id", userID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -141,6 +155,13 @@ func (h *OrdersHandler) ChangeOrderAddress(c fiber.Ctx) error { err = h.ordersService.ChangeOrderAddress(user, uint(order_id), uint(country_id), address_info) if err != nil { + + logger.Error("failed to change order address", + "handler", "OrdersHandler.ChangeOrderAddress", + + "order_id", order_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -168,6 +189,13 @@ func (h *OrdersHandler) ChangeOrderStatus(c fiber.Ctx) error { err = h.ordersService.ChangeOrderStatus(user, uint(order_id), status) if err != nil { + + logger.Error("failed to change order status", + "handler", "OrdersHandler.ChangeOrderStatus", + + "order_id", order_id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/product.go b/app/delivery/web/api/restricted/product.go index 9e58550..f6b33ac 100644 --- a/app/delivery/web/api/restricted/product.go +++ b/app/delivery/web/api/restricted/product.go @@ -9,6 +9,7 @@ import ( 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/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -74,6 +75,15 @@ func (h *ProductsHandler) GetProductJson(c fiber.Ctx) error { } productJson, err := h.productService.Get(uint(p_id_product), customer.LangID, customer.ID, uint(b2b_id_country), uint(p_quantity)) if err != nil { + logger.Error("failed to get product", + "handler", "ProductsHandler.GetProductJson", + "product_id", p_id_product, + "lang_id", customer.LangID, + "customer_id", customer.ID, + "b2b_id_country", b2b_id_country, + "quantity", p_quantity, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -96,6 +106,13 @@ func (h *ProductsHandler) ListProducts(c fiber.Ctx) error { list, err := h.productService.Find(customer.LangID, customer.ID, paging, filters, customer, constdata.DEFAULT_PRODUCT_QUANTITY, constdata.SHOP_ID) if err != nil { + + logger.Error("failed to list products", + "handler", "ProductsHandler.ListProducts", + "lang_id", customer.LangID, + "customer_id", customer.ID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -132,6 +149,13 @@ func (h *ProductsHandler) AddToFavorites(c fiber.Ctx) error { err = h.productService.AddToFavorites(userID, uint(productID)) if err != nil { + + logger.Error("failed to add to favorites", + "handler", "ProductsHandler.AddToFavorites", + "user_id", userID, + "product_id", productID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -156,6 +180,12 @@ func (h *ProductsHandler) RemoveFromFavorites(c fiber.Ctx) error { err = h.productService.RemoveFromFavorites(userID, uint(productID)) if err != nil { + logger.Error("failed to remove from favorites", + "handler", "ProductsHandler.RemoveFromFavorites", + "user_id", userID, + "product_id", productID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -180,6 +210,15 @@ func (h *ProductsHandler) ListProductVariants(c fiber.Ctx) error { list, err := h.productService.GetProductAttributes(customer.LangID, uint(productID), constdata.SHOP_ID, customer.ID, customer.CountryID, constdata.DEFAULT_PRODUCT_QUANTITY) if err != nil { + + logger.Error("failed to list product variants", + "handler", "ProductsHandler.ListProductVariants", + "product_id", productID, + "customer_id", customer.ID, + "lang_id", customer.LangID, + "country_id", customer.CountryID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/productTranslation.go b/app/delivery/web/api/restricted/productTranslation.go index eb3fee1..0709e78 100644 --- a/app/delivery/web/api/restricted/productTranslation.go +++ b/app/delivery/web/api/restricted/productTranslation.go @@ -9,6 +9,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/service/productTranslationService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -66,6 +67,13 @@ func (h *ProductTranslationHandler) GetProductDescription(c fiber.Ctx) error { description, err := h.productTranslationService.GetProductDescription(userID, uint(productID), uint(productLangID)) if err != nil { + + logger.Error("failed to get product description", + "handler", "ProductTranslationHandler.GetProductDescription", + + "product_id", productID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -103,6 +111,13 @@ func (h *ProductTranslationHandler) SaveProductDescription(c fiber.Ctx) error { err = h.productTranslationService.SaveProductDescription(userID, uint(productID), uint(productLangID), updates) if err != nil { + + logger.Error("failed to save product description", + "handler", "ProductTranslationHandler.SaveProductDescription", + + "product_id", productID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -147,6 +162,13 @@ func (h *ProductTranslationHandler) TranslateProductDescription(c fiber.Ctx) err description, err := h.productTranslationService.TranslateProductDescription(userID, uint(productID), uint(productFromLangID), uint(productToLangID), aiModel) if err != nil { + + logger.Error("failed to translate product description", + "handler", "ProductTranslationHandler.TranslateProductDescription", + + "product_id", productID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/search.go b/app/delivery/web/api/restricted/search.go index ebb3a76..d7ed61a 100644 --- a/app/delivery/web/api/restricted/search.go +++ b/app/delivery/web/api/restricted/search.go @@ -2,7 +2,6 @@ package restricted import ( "encoding/json" - "fmt" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms" @@ -10,6 +9,7 @@ import ( searchservice "git.ma-al.com/goc_daniel/b2b/app/service/searchService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -47,7 +47,13 @@ func (h *MeiliSearchHandler) CreateIndex(c fiber.Ctx) error { err := h.meiliService.CreateIndex(id_lang) if err != nil { - fmt.Printf("CreateIndex error: %v\n", err) + + logger.Error("failed to create search index", + "handler", "MeiliSearchHandler.CreateIndex", + + "lang_id", id_lang, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -72,6 +78,13 @@ func (h *MeiliSearchHandler) Search(c fiber.Ctx) error { result, err := h.searchService.Search(index, c.Body(), id_lang) if err != nil { + + logger.Error("failed to search", + "handler", "MeiliSearchHandler.Search", + + "index", index, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -80,6 +93,13 @@ func (h *MeiliSearchHandler) Search(c fiber.Ctx) error { if createErr := h.meiliService.CreateIndex(id_lang); createErr == nil { result, err = h.searchService.Search(index, c.Body(), id_lang) if err != nil { + + logger.Error("failed to search after index creation", + "handler", "MeiliSearchHandler.Search", + + "index", index, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -100,6 +120,13 @@ func (h *MeiliSearchHandler) GetSettings(c fiber.Ctx) error { result, err := h.searchService.GetIndexSettings(index) if err != nil { + + logger.Error("failed to get index settings", + "handler", "MeiliSearchHandler.GetSettings", + + "index", index, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/specificPrice.go b/app/delivery/web/api/restricted/specificPrice.go index e007f57..b25ba46 100644 --- a/app/delivery/web/api/restricted/specificPrice.go +++ b/app/delivery/web/api/restricted/specificPrice.go @@ -9,6 +9,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/service/specificPriceService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -51,6 +52,12 @@ func (h *SpecificPriceHandler) Create(c fiber.Ctx) error { result, err := h.SpecificPriceService.Create(c.Context(), &pr) if err != nil { + + logger.Error("failed to create specific price", + "handler", "SpecificPriceHandler.Create", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -74,6 +81,13 @@ func (h *SpecificPriceHandler) Update(c fiber.Ctx) error { result, err := h.SpecificPriceService.Update(c.Context(), id, &pr) if err != nil { + + logger.Error("failed to update specific price", + "handler", "SpecificPriceHandler.Update", + + "specific_price_id", id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -84,6 +98,12 @@ func (h *SpecificPriceHandler) Update(c fiber.Ctx) error { func (h *SpecificPriceHandler) List(c fiber.Ctx) error { result, err := h.SpecificPriceService.List(c.Context()) if err != nil { + + logger.Error("failed to list specific prices", + "handler", "SpecificPriceHandler.List", + + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -101,6 +121,13 @@ func (h *SpecificPriceHandler) GetByID(c fiber.Ctx) error { result, err := h.SpecificPriceService.GetByID(c.Context(), id) if err != nil { + + logger.Error("failed to get specific price", + "handler", "SpecificPriceHandler.GetByID", + + "specific_price_id", id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -118,6 +145,13 @@ func (h *SpecificPriceHandler) Activate(c fiber.Ctx) error { err = h.SpecificPriceService.SetActive(c.Context(), id, true) if err != nil { + + logger.Error("failed to activate specific price", + "handler", "SpecificPriceHandler.Activate", + + "specific_price_id", id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -135,6 +169,13 @@ func (h *SpecificPriceHandler) Deactivate(c fiber.Ctx) error { err = h.SpecificPriceService.SetActive(c.Context(), id, false) if err != nil { + + logger.Error("failed to deactivate specific price", + "handler", "SpecificPriceHandler.Deactivate", + + "specific_price_id", id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -152,6 +193,13 @@ func (h *SpecificPriceHandler) Delete(c fiber.Ctx) error { err = h.SpecificPriceService.Delete(c.Context(), id) if err != nil { + + logger.Error("failed to delete specific price", + "handler", "SpecificPriceHandler.Delete", + + "specific_price_id", id, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/delivery/web/api/restricted/storage.go b/app/delivery/web/api/restricted/storage.go index 1a252b0..29a5454 100644 --- a/app/delivery/web/api/restricted/storage.go +++ b/app/delivery/web/api/restricted/storage.go @@ -9,6 +9,7 @@ import ( "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/localeExtractor" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "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" @@ -52,6 +53,12 @@ func (h *StorageHandler) ListContent(c fiber.Ctx) error { entries_in_list, err := h.storageService.ListContent(abs_path) if err != nil { + + logger.Error("failed to list storage content", + "handler", "StorageHandler.ListContent", + "path", abs_path, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -68,6 +75,12 @@ func (h *StorageHandler) DownloadFile(c fiber.Ctx) error { f, filename, filesize, err := h.storageService.DownloadFilePrep(abs_path) if err != nil { + + logger.Error("failed to prepare file download", + "handler", "StorageHandler.DownloadFile", + "path", abs_path, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } @@ -87,6 +100,12 @@ func (h *StorageHandler) CreateNewWebdavToken(c fiber.Ctx) error { new_token, err := h.storageService.NewWebdavToken(userID) if err != nil { + + logger.Error("failed to create webdav token", + "handler", "StorageHandler.CreateNewWebdavToken", + "user_id", userID, + "error", err.Error(), + ) return c.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) } diff --git a/app/service/authService/auth.go b/app/service/authService/auth.go index af971d5..3433697 100644 --- a/app/service/authService/auth.go +++ b/app/service/authService/auth.go @@ -15,6 +15,7 @@ import ( roleRepo "git.ma-al.com/goc_daniel/b2b/app/repos/rolesRepo" "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/logger" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "github.com/dlclark/regexp2" @@ -68,22 +69,47 @@ func (s *AuthService) Login(req *model.LoginRequest) (*model.AuthResponse, strin // Find user by email if err := s.db.Preload("Role.Permissions").Where("email = ?", req.Email).First(&user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { + logger.Info("login failed - invalid credentials", + "service", "AuthService.Login", + "email", req.Email, + "reason", "user not found", + ) return nil, "", responseErrors.ErrInvalidCredentials } + logger.Error("login failed - database error", + "service", "AuthService.Login", + "email", req.Email, + "error", err.Error(), + ) return nil, "", fmt.Errorf("database error: %w", err) } // Check if user is active if !user.IsActive { + logger.Info("login failed - user inactive", + "service", "AuthService.Login", + "email", req.Email, + "reason", "user account is inactive", + ) return nil, "", responseErrors.ErrUserInactive } // Check if email is verified if !user.EmailVerified { + logger.Info("login failed - email not verified", + "service", "AuthService.Login", + "email", req.Email, + "reason", "email not verified", + ) return nil, "", responseErrors.ErrEmailNotVerified } // Verify password if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil { + logger.Info("login failed - invalid credentials", + "service", "AuthService.Login", + "email", req.Email, + "reason", "wrong password", + ) return nil, "", responseErrors.ErrInvalidCredentials } @@ -94,6 +120,11 @@ func (s *AuthService) Login(req *model.LoginRequest) (*model.AuthResponse, strin if req.LangID != nil { _, err := s.GetLangISOCode(*req.LangID) if err != nil { + logger.Warn("login failed - invalid language ID", + "service", "AuthService.Login", + "email", req.Email, + "reason", "invalid language ID", + ) return nil, "", responseErrors.ErrBadLangID } user.LangID = *req.LangID @@ -105,12 +136,22 @@ func (s *AuthService) Login(req *model.LoginRequest) (*model.AuthResponse, strin // Generate access token (JWT) accessToken, err := s.generateAccessToken(&user) if err != nil { + logger.Error("login failed - token generation error", + "service", "AuthService.Login", + "email", req.Email, + "error", err.Error(), + ) return nil, "", fmt.Errorf("failed to generate access token: %w", err) } // Generate opaque refresh token and store in DB rawRefreshToken, err := s.createRefreshToken(user.ID) if err != nil { + logger.Error("login failed - refresh token creation error", + "service", "AuthService.Login", + "email", req.Email, + "error", err.Error(), + ) return nil, "", fmt.Errorf("failed to create refresh token: %w", err) } @@ -171,6 +212,11 @@ func (s *AuthService) Register(req *model.RegisterRequest) error { } if err := s.db.Create(&user).Error; err != nil { + logger.Error("registration failed - database error", + "service", "AuthService.Register", + "email", req.Email, + "error", err.Error(), + ) return fmt.Errorf("failed to create user: %w", err) } @@ -182,8 +228,11 @@ func (s *AuthService) Register(req *model.RegisterRequest) error { } if err := s.email.SendVerificationEmail(user.Email, user.EmailVerificationToken, baseURL, lang); err != nil { - // Log error but don't fail registration - user can request resend - _ = err + logger.Warn("failed to send verification email", + "service", "AuthService.Register", + "email", req.Email, + "error", err.Error(), + ) } return nil @@ -307,6 +356,10 @@ func (s *AuthService) ResetPassword(token, newPassword string) error { if errors.Is(err, gorm.ErrRecordNotFound) { return responseErrors.ErrInvalidResetToken } + logger.Error("password reset failed - database error", + "service", "AuthService.ResetPassword", + "error", err.Error(), + ) return fmt.Errorf("database error: %w", err) } @@ -333,6 +386,10 @@ func (s *AuthService) ResetPassword(token, newPassword string) error { user.Country = nil if err := s.db.Save(&user).Error; err != nil { + logger.Error("password reset failed - database error", + "service", "AuthService.ResetPassword", + "error", err.Error(), + ) return fmt.Errorf("failed to update password: %w", err) } diff --git a/app/service/authService/google_oauth.go b/app/service/authService/google_oauth.go index 9bcd5ce..c4cde1a 100644 --- a/app/service/authService/google_oauth.go +++ b/app/service/authService/google_oauth.go @@ -8,10 +8,12 @@ import ( "fmt" "io" "net/http" + "strings" "time" "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/utils/logger" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "git.ma-al.com/goc_daniel/b2b/app/view" "golang.org/x/oauth2" @@ -77,6 +79,13 @@ func (s *AuthService) HandleGoogleCallback(code string) (*model.AuthResponse, st // Find or create user user, err := s.findOrCreateGoogleUser(userInfo) if err != nil { + if strings.Contains(err.Error(), "database") { + logger.Error("google oauth callback failed - database error", + "service", "AuthService.HandleGoogleCallback", + "email", userInfo.Email, + "error", err.Error(), + ) + } return nil, "", err } diff --git a/app/service/cartsService/cartsService.go b/app/service/cartsService/cartsService.go index 4d5378a..8889bc9 100644 --- a/app/service/cartsService/cartsService.go +++ b/app/service/cartsService/cartsService.go @@ -4,6 +4,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/repos/cartsRepo" constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" + "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" ) @@ -34,6 +35,15 @@ func (s *CartsService) CreateNewCart(user_id uint, name string) (model.CustomerC // create new cart for customer cart, err = s.repo.CreateNewCart(user_id, name) + if err != nil { + return cart, err + } + + logger.Info("cart created", + "service", "cartsService", + "user_id", user_id, + "cart_id", cart.CartID, + ) return cart, nil } diff --git a/app/service/orderService/orderService.go b/app/service/orderService/orderService.go index a78b17e..a5a90bb 100644 --- a/app/service/orderService/orderService.go +++ b/app/service/orderService/orderService.go @@ -1,7 +1,6 @@ package orderService import ( - "fmt" "strconv" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms" @@ -12,6 +11,7 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/service/addressesService" "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/logger" "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_daniel/b2b/app/utils/responseErrors" @@ -49,9 +49,12 @@ func (s *OrderService) Find(user *model.Customer, p find.Paging, filt *filters.F for i := 0; i < len(list.Items); i++ { address_unparsed, err := s.addressesService.ValidateAddressJson(list.Items[i].AddressString, list.Items[i].CountryID) - // log such errors if err != nil { - fmt.Printf("err: %v\n", err) + logger.Warn("failed to validate address", + "service", "orderService", + "order_id", list.Items[i].OrderID, + "error", err.Error(), + ) } list.Items[i].AddressUnparsed = &address_unparsed @@ -98,16 +101,23 @@ func (s *OrderService) PlaceNewOrder(user_id uint, cart_id uint, name string, co // if no error is returned, remove the cart. This should be smooth err = s.cartsRepo.RemoveCart(user_id, cart_id) if err != nil { - // Log error but don't fail placing order - _ = err + logger.Warn("failed to remove cart after order placement", + "service", "orderService", + "user_id", user_id, + "cart_id", cart_id, + "error", err.Error(), + ) } // send email to admin go func(user_id uint) { err := s.emailService.SendNewOrderPlacedNotification(user_id) if err != nil { - // Log error but don't fail placing order - _ = err + logger.Warn("failed to send new order notification", + "service", "orderService", + "user_id", user_id, + "error", err.Error(), + ) } }(user_id) diff --git a/app/utils/logger/logger.go b/app/utils/logger/logger.go new file mode 100644 index 0000000..4a73d3f --- /dev/null +++ b/app/utils/logger/logger.go @@ -0,0 +1,151 @@ +package logger + +import ( + "context" + "fmt" + "io" + "log/slog" + "os" + "strings" +) + +var L *slog.Logger + +const ( + reset = "\033[0m" + red = "\033[31m" + yellow = "\033[33m" + green = "\033[32m" + blue = "\033[36m" + gray = "\033[90m" +) + +type consoleHandler struct { + w io.Writer + colorize bool +} + +func (h *consoleHandler) Enabled(ctx context.Context, level slog.Level) bool { + return true +} + +func (h *consoleHandler) Handle(ctx context.Context, r slog.Record) error { + level := r.Level.String() + color := reset + + switch r.Level { + case slog.LevelError: + color = red + case slog.LevelWarn: + color = yellow + case slog.LevelInfo: + color = blue + case slog.LevelDebug: + color = gray + } + + var msg string + if h.colorize { + msg = fmt.Sprintf("%s%s%s %s%s%s %s%s%s", + reset, r.Time.Format("15:04:05"), reset, + color, level, reset, + reset, r.Message, reset) + } else { + msg = fmt.Sprintf("%s %s %s", r.Time.Format("15:04:05"), level, r.Message) + } + + var pairs []string + r.Attrs(func(attr slog.Attr) bool { + if h.colorize { + pairs = append(pairs, fmt.Sprintf("%s%s=%s%v%s", green, attr.Key, reset, attr.Value, reset)) + } else { + pairs = append(pairs, fmt.Sprintf("%s=%v", attr.Key, attr.Value)) + } + return true + }) + + if len(pairs) > 0 { + msg += " " + strings.Join(pairs, " ") + } + + fmt.Fprintln(h.w, msg) + return nil +} + +func (h *consoleHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &consoleHandler{w: h.w, colorize: h.colorize} +} + +func (h *consoleHandler) WithGroup(name string) slog.Handler { + return &consoleHandler{w: h.w, colorize: h.colorize} +} + +func Init(service string, output io.Writer, level string, colorize bool) { + if output == nil { + output = os.Stderr + } + + var lvl slog.Level + switch level { + case "debug": + lvl = slog.LevelDebug + case "warn": + lvl = slog.LevelWarn + case "error": + lvl = slog.LevelError + default: + lvl = slog.LevelInfo + } + + if colorize { + L = slog.New(&consoleHandler{w: output, colorize: true}).With("service", service, "level", lvl.String()) + } else { + L = slog.New(slog.NewJSONHandler(output, &slog.HandlerOptions{ + Level: lvl, + AddSource: false, + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.TimeKey && groups == nil { + a.Key = "timestamp" + } + if a.Key == slog.LevelKey { + a.Key = "level" + } + if a.Key == slog.MessageKey { + a.Key = "message" + } + return a + }, + })).With("service", service) + } +} + +func Info(msg string, args ...any) { + if L != nil { + L.Info(msg, args...) + } +} + +func Warn(msg string, args ...any) { + if L != nil { + L.Warn(msg, args...) + } +} + +func Error(msg string, args ...any) { + if L != nil { + L.Error(msg, args...) + } +} + +func Debug(msg string, args ...any) { + if L != nil { + L.Debug(msg, args...) + } +} + +func With(args ...any) *slog.Logger { + if L == nil { + return nil + } + return L.With(args...) +} diff --git a/i18n/migrations/20260302163122_create_tables.sql b/i18n/migrations/20260302163122_create_tables.sql index 50f06c8..f8a1997 100644 --- a/i18n/migrations/20260302163122_create_tables.sql +++ b/i18n/migrations/20260302163122_create_tables.sql @@ -113,7 +113,7 @@ CREATE TABLE IF NOT EXISTS b2b_customers ( created_at DATETIME(6) NULL, updated_at DATETIME(6) NULL, deleted_at DATETIME(6) NULL, - is_no_vat TINYINT(1) NULL DEFAULT 0 + is_no_vat TINYINT(1) NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE UNIQUE INDEX IF NOT EXISTS idx_customers_email