diff --git a/cmd/root.go b/cmd/root.go index c46db8c..97f507a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,6 +5,7 @@ package cmd import ( "log" + "log/slog" "os" "github.com/hazemKrimi/crimson-vault/internal/api" @@ -35,7 +36,8 @@ to quickly create a Cobra application.`, log.Fatal(err) } - server := api.API{ConfigDirectory: dir} + logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + server := api.API{ConfigDirectory: dir, Logger: logger} server.Initialize() }, } diff --git a/internal/api/api.go b/internal/api/api.go index 2ae4ea7..bd09d40 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -2,6 +2,7 @@ package api import ( "fmt" + "log/slog" "github.com/go-playground/validator/v10" "github.com/gorilla/sessions" @@ -11,12 +12,14 @@ import ( "github.com/hazemKrimi/crimson-vault/internal/lib" "github.com/hazemKrimi/crimson-vault/internal/models" + "github.com/hazemKrimi/crimson-vault/internal/types" ) type API struct { ConfigDirectory string instance *echo.Echo db *models.DB + Logger *slog.Logger } func (api *API) Initialize() { @@ -26,6 +29,19 @@ func (api *API) Initialize() { db := &models.DB{} ech := echo.New() ech.Validator = &CustomValidator{validator: validator} + ech.HTTPErrorHandler = func(err error, context echo.Context) { + if context.Response().Committed { + return + } + + custom, ok := err.(types.Error) + + if ok { + context.JSONPretty(custom.Code, map[string][]string{"errors": custom.Messages}, " ") + } + + ech.DefaultHTTPErrorHandler(err, context) + } db.Connect(api.ConfigDirectory) db.MigrateClients() @@ -34,6 +50,7 @@ func (api *API) Initialize() { api.instance = ech api.db = db + api.instance.Use(api.LoggerMiddleware()) // TODO: Update with appropriate origins when finishing v1 api.instance.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: []string{"*"}, diff --git a/internal/api/middleware.go b/internal/api/middleware.go index 6097a11..5304833 100644 --- a/internal/api/middleware.go +++ b/internal/api/middleware.go @@ -1,11 +1,14 @@ package api import ( + "context" + "log/slog" "net/http" "github.com/google/uuid" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" "github.com/hazemKrimi/crimson-vault/internal/types" ) @@ -43,3 +46,28 @@ func (api *API) AuthSessionMiddleware(next echo.HandlerFunc) echo.HandlerFunc { return next(context) } } + +func (api *API) LoggerMiddleware() echo.MiddlewareFunc { + return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ + LogStatus: true, + LogURI: true, + LogError: true, + HandleError: true, + LogMethod: true, + LogValuesFunc: func(logContext echo.Context, values middleware.RequestLoggerValues) error { + if values.Error == nil { + api.Logger.LogAttrs(context.Background(), slog.LevelInfo, "REQUEST", + slog.String("uri", values.URI), + slog.Int("status", values.Status), + ) + } else { + api.Logger.LogAttrs(context.Background(), slog.LevelError, "REQUEST_ERROR", + slog.String("uri", values.URI), + slog.Int("status", values.Status), + slog.String("err", values.Error.Error()), + ) + } + return nil + }, + }) +} diff --git a/internal/api/routes.go b/internal/api/routes.go index e4cc797..f1cabb3 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -15,13 +15,14 @@ func (api *API) ClientRoutes() { func (api *API) UserRoutes() { users := api.instance.Group("/api/users") + users.GET("/", api.GetAllUsersHandler) users.POST("/", api.CreateUserHandler) - users.GET("/", api.GetUserHandler, api.AuthSessionMiddleware) - users.PUT("/", api.UpdateUserHandler, api.AuthSessionMiddleware) - users.PUT("/security/", api.UpdateUserSecurityCredentialsHandler, api.AuthSessionMiddleware) - users.PUT("/logo/", api.UpdateUserLogoHandler, middleware.BodyLimit("2M"), api.AuthSessionMiddleware) - users.DELETE("/", api.DeleteUserHandler, api.AuthSessionMiddleware) - users.DELETE("/logo/", api.DeleteUserLogoHandler, api.AuthSessionMiddleware) + users.GET("/me/", api.GetUserHandler, api.AuthSessionMiddleware) + users.PUT("/me/", api.UpdateUserHandler, api.AuthSessionMiddleware) + users.PUT("/me/security/", api.UpdateUserSecurityCredentialsHandler, api.AuthSessionMiddleware) + users.PUT("/me/logo/", api.UpdateUserLogoHandler, middleware.BodyLimit("2M"), api.AuthSessionMiddleware) + users.DELETE("/me/", api.DeleteUserHandler, api.AuthSessionMiddleware) + users.DELETE("/me/logo/", api.DeleteUserLogoHandler, api.AuthSessionMiddleware) } func (api *API) AuthRoutes() { diff --git a/internal/api/user.go b/internal/api/user.go index 532fa8e..5b0abc3 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -180,8 +180,7 @@ func (api *API) UpdateUserLogoHandler(context echo.Context) error { file, err := context.FormFile("logo") if err != nil { - log.Println(fmt.Sprintf("Error updating logo for User: %v.", err)) - return context.String(http.StatusBadRequest, "No image has been uploaded!") + return types.Error{Code: http.StatusBadRequest, Messages: []string{"No image has been uploaded!"}} } ext := strings.ToLower(filepath.Ext(file.Filename)) diff --git a/internal/api/validator.go b/internal/api/validator.go index e3be9a2..468d742 100644 --- a/internal/api/validator.go +++ b/internal/api/validator.go @@ -6,7 +6,8 @@ import ( "regexp" "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" + + "github.com/hazemKrimi/crimson-vault/internal/types" ) type CustomValidator struct { @@ -16,7 +17,7 @@ type CustomValidator struct { func (v *CustomValidator) Validate(i any) error { if err := v.validator.Struct(i); err != nil { if validationErrors, ok := err.(validator.ValidationErrors); ok { - errors := make(map[string]string) + errors := make([]string, 0, 10) for _, ve := range validationErrors { field := ve.Field() @@ -41,9 +42,9 @@ func (v *CustomValidator) Validate(i any) error { msg = fmt.Sprintf("%s is not valid!", field) } - errors[field] = msg + errors = append(errors, msg) } - return echo.NewHTTPError(http.StatusBadRequest, errors) + return types.Error{Code: http.StatusBadRequest, Messages: errors} } } diff --git a/internal/types/error.go b/internal/types/error.go new file mode 100644 index 0000000..ab02a70 --- /dev/null +++ b/internal/types/error.go @@ -0,0 +1,13 @@ +package types + +import "fmt" + +type Error struct { + Messages []string + Code int +} + +func (err Error) Error() string { + return fmt.Sprintf("%v", + err.Messages) +}