diff --git a/backend/app/api/app.go b/backend/app/api/app.go index 8d087e4..fe2b694 100644 --- a/backend/app/api/app.go +++ b/backend/app/api/app.go @@ -7,14 +7,12 @@ import ( "github.com/hay-kot/content/backend/internal/config" "github.com/hay-kot/content/backend/internal/repo" "github.com/hay-kot/content/backend/internal/services" - "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/mailer" "github.com/hay-kot/content/backend/pkgs/server" ) type app struct { conf *config.Config - logger *logger.Logger mailer mailer.Mailer db *ent.Client server *server.Server diff --git a/backend/app/api/base/base_ctrl.go b/backend/app/api/base/base_ctrl.go index 119967e..7f62541 100644 --- a/backend/app/api/base/base_ctrl.go +++ b/backend/app/api/base/base_ctrl.go @@ -4,20 +4,17 @@ import ( "net/http" "github.com/hay-kot/content/backend/internal/types" - "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/server" ) type ReadyFunc func() bool type BaseController struct { - log *logger.Logger svr *server.Server } -func NewBaseController(log *logger.Logger, svr *server.Server) *BaseController { +func NewBaseController(svr *server.Server) *BaseController { h := &BaseController{ - log: log, svr: svr, } return h diff --git a/backend/app/api/base/base_ctrl_test.go b/backend/app/api/base/base_ctrl_test.go index 71dca76..c4b987b 100644 --- a/backend/app/api/base/base_ctrl_test.go +++ b/backend/app/api/base/base_ctrl_test.go @@ -4,12 +4,10 @@ import ( "net/http" "net/http/httptest" "testing" - - "github.com/hay-kot/content/backend/internal/mocks" ) func GetTestHandler(t *testing.T) *BaseController { - return NewBaseController(mocks.GetStructLogger(), nil) + return NewBaseController(nil) } func TestHandlersv1_HandleBase(t *testing.T) { diff --git a/backend/app/api/docs/docs.go b/backend/app/api/docs/docs.go index 4c3fe07..3037137 100644 --- a/backend/app/api/docs/docs.go +++ b/backend/app/api/docs/docs.go @@ -52,215 +52,6 @@ const docTemplate = `{ } } }, - "/v1/admin/users": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Gets all users from the database", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "type": "array", - "items": { - "$ref": "#/definitions/ent.User" - } - } - } - } - ] - } - } - } - }, - "post": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Create a new user", - "parameters": [ - { - "description": "User Data", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/types.UserCreate" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } - } - } - } - }, - "/v1/admin/users/{id}": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Get a user from the database", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } - } - } - }, - "put": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Update a User", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "User Data", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/types.UserUpdate" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } - } - } - }, - "delete": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Delete a User", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "" - } - } - } - }, "/v1/items": { "get": { "security": [ @@ -1643,9 +1434,6 @@ const docTemplate = `{ "description": { "type": "string" }, - "groupId": { - "type": "string" - }, "id": { "type": "string" }, @@ -1680,9 +1468,6 @@ const docTemplate = `{ "description": { "type": "string" }, - "groupId": { - "type": "string" - }, "id": { "type": "string" }, @@ -1709,9 +1494,6 @@ const docTemplate = `{ "description": { "type": "string" }, - "groupId": { - "type": "string" - }, "id": { "type": "string" }, @@ -1734,26 +1516,6 @@ const docTemplate = `{ } } }, - "types.UserCreate": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "groupID": { - "type": "string" - }, - "isSuperuser": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, "types.UserIn": { "type": "object", "properties": { diff --git a/backend/app/api/docs/swagger.json b/backend/app/api/docs/swagger.json index 260bbeb..3a8d485 100644 --- a/backend/app/api/docs/swagger.json +++ b/backend/app/api/docs/swagger.json @@ -44,215 +44,6 @@ } } }, - "/v1/admin/users": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Gets all users from the database", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "type": "array", - "items": { - "$ref": "#/definitions/ent.User" - } - } - } - } - ] - } - } - } - }, - "post": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Create a new user", - "parameters": [ - { - "description": "User Data", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/types.UserCreate" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } - } - } - } - }, - "/v1/admin/users/{id}": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Get a user from the database", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } - } - } - }, - "put": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Update a User", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "User Data", - "name": "payload", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/types.UserUpdate" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } - } - } - }, - "delete": { - "security": [ - { - "Bearer": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Admin: Users" - ], - "summary": "Delete a User", - "parameters": [ - { - "type": "string", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "" - } - } - } - }, "/v1/items": { "get": { "security": [ @@ -1635,9 +1426,6 @@ "description": { "type": "string" }, - "groupId": { - "type": "string" - }, "id": { "type": "string" }, @@ -1672,9 +1460,6 @@ "description": { "type": "string" }, - "groupId": { - "type": "string" - }, "id": { "type": "string" }, @@ -1701,9 +1486,6 @@ "description": { "type": "string" }, - "groupId": { - "type": "string" - }, "id": { "type": "string" }, @@ -1726,26 +1508,6 @@ } } }, - "types.UserCreate": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "groupID": { - "type": "string" - }, - "isSuperuser": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, "types.UserIn": { "type": "object", "properties": { diff --git a/backend/app/api/docs/swagger.yaml b/backend/app/api/docs/swagger.yaml index e26ca1e..c5ee5af 100644 --- a/backend/app/api/docs/swagger.yaml +++ b/backend/app/api/docs/swagger.yaml @@ -493,8 +493,6 @@ definitions: type: string description: type: string - groupId: - type: string id: type: string itemCount: @@ -517,8 +515,6 @@ definitions: type: string description: type: string - groupId: - type: string id: type: string items: @@ -536,8 +532,6 @@ definitions: type: string description: type: string - groupId: - type: string id: type: string name: @@ -552,19 +546,6 @@ definitions: token: type: string type: object - types.UserCreate: - properties: - email: - type: string - groupID: - type: string - isSuperuser: - type: boolean - name: - type: string - password: - type: string - type: object types.UserIn: properties: email: @@ -616,124 +597,6 @@ paths: summary: Retrieves the basic information about the API tags: - Base - /v1/admin/users: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/server.Result' - - properties: - item: - items: - $ref: '#/definitions/ent.User' - type: array - type: object - security: - - Bearer: [] - summary: Gets all users from the database - tags: - - 'Admin: Users' - post: - parameters: - - description: User Data - in: body - name: payload - required: true - schema: - $ref: '#/definitions/types.UserCreate' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/server.Result' - - properties: - item: - $ref: '#/definitions/ent.User' - type: object - security: - - Bearer: [] - summary: Create a new user - tags: - - 'Admin: Users' - /v1/admin/users/{id}: - delete: - parameters: - - description: User ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "204": - description: "" - security: - - Bearer: [] - summary: Delete a User - tags: - - 'Admin: Users' - get: - parameters: - - description: User ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/server.Result' - - properties: - item: - $ref: '#/definitions/ent.User' - type: object - security: - - Bearer: [] - summary: Get a user from the database - tags: - - 'Admin: Users' - put: - parameters: - - description: User ID - in: path - name: id - required: true - type: string - - description: User Data - in: body - name: payload - required: true - schema: - $ref: '#/definitions/types.UserUpdate' - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/server.Result' - - properties: - item: - $ref: '#/definitions/ent.User' - type: object - security: - - Bearer: [] - summary: Update a User - tags: - - 'Admin: Users' /v1/items: get: produces: diff --git a/backend/app/api/main.go b/backend/app/api/main.go index 838b648..a3a5137 100644 --- a/backend/app/api/main.go +++ b/backend/app/api/main.go @@ -2,8 +2,6 @@ package main import ( "context" - "io" - "log" "os" "time" @@ -12,9 +10,10 @@ import ( "github.com/hay-kot/content/backend/internal/config" "github.com/hay-kot/content/backend/internal/repo" "github.com/hay-kot/content/backend/internal/services" - "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/server" _ "github.com/mattn/go-sqlite3" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" ) // @title Go API Templates @@ -28,6 +27,10 @@ import ( // @name Authorization // @description "Type 'Bearer TOKEN' to correctly set the API Key" func main() { + // Logger Init + // zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + cfgFile := "config.yml" cfg, err := config.NewConfig(cfgFile) @@ -45,42 +48,24 @@ func main() { func run(cfg *config.Config) error { app := NewApp(cfg) - // ========================================================================= - // Setup Logger - - var wrt io.Writer - wrt = os.Stdout - if app.conf.Log.File != "" { - f, err := os.OpenFile(app.conf.Log.File, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - log.Fatalf("error opening file: %v", err) - } - defer func(f *os.File) { - _ = f.Close() - }(f) - wrt = io.MultiWriter(wrt, f) - } - - app.logger = logger.New(wrt, logger.LevelDebug) - // ========================================================================= // Initialize Database & Repos c, err := ent.Open(cfg.Database.GetDriver(), cfg.Database.GetUrl()) if err != nil { - app.logger.Fatal(err, logger.Props{ - "details": "failed to connect to database", - "database": cfg.Database.GetDriver(), - "url": cfg.Database.GetUrl(), - }) + log.Fatal(). + Err(err). + Str("driver", cfg.Database.GetDriver()). + Str("url", cfg.Database.GetUrl()). + Msg("failed opening connection to sqlite") } defer func(c *ent.Client) { _ = c.Close() }(c) if err := c.Schema.Create(context.Background()); err != nil { - app.logger.Fatal(err, logger.Props{ - "details": "failed to create schema", - }) + log.Fatal(). + Err(err). + Msg("failed creating schema resources") } app.db = c @@ -99,10 +84,7 @@ func run(cfg *config.Config) error { app.SeedDatabase(app.repos) - app.logger.Info("Starting HTTP Server", logger.Props{ - "host": app.server.Host, - "port": app.server.Port, - }) + log.Info().Msgf("Starting HTTP Server on %s:%s", app.server.Host, app.server.Port) // ========================================================================= // Start Reoccurring Tasks @@ -110,9 +92,9 @@ func run(cfg *config.Config) error { go app.StartReoccurringTasks(time.Duration(24)*time.Hour, func() { _, err := app.repos.AuthTokens.PurgeExpiredTokens(context.Background()) if err != nil { - app.logger.Error(err, logger.Props{ - "details": "failed to purge expired tokens", - }) + log.Error(). + Err(err). + Msg("failed to purge expired tokens") } }) diff --git a/backend/app/api/middleware.go b/backend/app/api/middleware.go index f2121d4..bb8f898 100644 --- a/backend/app/api/middleware.go +++ b/backend/app/api/middleware.go @@ -10,8 +10,8 @@ import ( "github.com/go-chi/chi/v5/middleware" "github.com/hay-kot/content/backend/internal/config" "github.com/hay-kot/content/backend/internal/services" - "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/server" + "github.com/rs/zerolog/log" ) func (a *app) setGlobalMiddleware(r *chi.Mux) { @@ -24,7 +24,7 @@ func (a *app) setGlobalMiddleware(r *chi.Mux) { // Use struct logger in production for requests, but use // pretty console logger in development. if a.conf.Mode == config.ModeDevelopment { - r.Use(middleware.Logger) + r.Use(a.mwSummaryLogger) } else { r.Use(a.mwStructLogger) } @@ -98,13 +98,38 @@ func (a *app) mwStructLogger(next http.Handler) http.Handler { url := fmt.Sprintf("%s://%s%s %s", scheme, r.Host, r.RequestURI, r.Proto) - a.logger.Info(fmt.Sprintf("[%s] %s", r.Method, url), logger.Props{ - "id": middleware.GetReqID(r.Context()), - "method": r.Method, - "url": url, - "remote": r.RemoteAddr, - }) - + log.Info(). + Str("id", middleware.GetReqID(r.Context())). + Str("url", url). + Str("method", r.Method). + Str("remote_addr", r.RemoteAddr). + Msgf("[%s] %s", r.Method, url) + next.ServeHTTP(w, r) + }) +} + +func (a *app) mwSummaryLogger(next http.Handler) http.Handler { + bold := func(s string) string { + return "\033[1m" + s + "\033[0m" + } + + pink := func(s string) string { + return "\033[35m" + s + "\033[0m" + } + + aqua := func(s string) string { + return "\033[36m" + s + "\033[0m" + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + scheme := "http" + if r.TLS != nil { + scheme = "https" + } + + url := fmt.Sprintf("%s://%s%s %s", scheme, r.Host, r.RequestURI, r.Proto) + + log.Info().Msgf("%s %s", bold(pink("["+r.Method+"]")), aqua(url)) next.ServeHTTP(w, r) }) } diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index 41e1344..9b5d877 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -31,7 +31,7 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux { http.ServeFile(w, r, "static/favicon.ico") }) - baseHandler := base.NewBaseController(a.logger, a.server) + baseHandler := base.NewBaseController(a.server) r.Get(prefix+"/status", baseHandler.HandleBase(func() bool { return true }, "v1")) // ========================================================================= @@ -39,7 +39,7 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux { v1Base := v1.BaseUrlFunc(prefix) { - v1Handlers := v1.NewControllerV1(a.logger, a.services) + v1Handlers := v1.NewControllerV1(a.services) r.Post(v1Base("/users/register"), v1Handlers.HandleUserRegistration()) r.Post(v1Base("/users/login"), v1Handlers.HandleAuthLogin()) r.Group(func(r chi.Router) { @@ -68,15 +68,6 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux { r.Put(v1Base("/items/{id}"), v1Handlers.HandleItemUpdate()) r.Delete(v1Base("/items/{id}"), v1Handlers.HandleItemDelete()) }) - - r.Group(func(r chi.Router) { - r.Use(a.mwAdminOnly) - r.Get(v1Base("/admin/users"), v1Handlers.HandleAdminUserGetAll()) - r.Post(v1Base("/admin/users"), v1Handlers.HandleAdminUserCreate()) - r.Get(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserGet()) - r.Put(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserUpdate()) - r.Delete(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserDelete()) - }) } return r diff --git a/backend/app/api/seed.go b/backend/app/api/seed.go index 42c3600..94382dd 100644 --- a/backend/app/api/seed.go +++ b/backend/app/api/seed.go @@ -7,7 +7,7 @@ import ( "github.com/hay-kot/content/backend/internal/repo" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/hasher" - "github.com/hay-kot/content/backend/pkgs/logger" + "github.com/rs/zerolog/log" ) const ( @@ -23,7 +23,7 @@ func (a *app) EnsureAdministrator() { superusers, err := a.repos.Users.GetSuperusers(context.Background()) if err != nil { - a.logger.Fatal(err, nil) + log.Fatal().Err(err).Msg("failed to get superusers") } if len(superusers) > 0 { return @@ -37,15 +37,15 @@ func (a *app) EnsureAdministrator() { Password: pw, } - a.logger.Info("creating default superuser", logger.Props{ - "name": newSuperUser.Name, - "email": newSuperUser.Email, - }) + log.Info(). + Str("name", newSuperUser.Name). + Str("email", newSuperUser.Email). + Msg("no superusers found, creating default superuser") _, err = a.repos.Users.Create(context.Background(), newSuperUser) if err != nil { - a.logger.Fatal(err, nil) + log.Fatal().Err(err).Msg("failed to create default superuser") } } @@ -57,7 +57,7 @@ func (a *app) SeedDatabase(repos *repo.AllRepos) { group, err := repos.Groups.Create(context.Background(), DefaultGroup) if err != nil { - a.logger.Fatal(err, nil) + log.Fatal().Err(err).Msg("failed to create default group") } for _, user := range a.conf.Seed.Users { @@ -66,19 +66,13 @@ func (a *app) SeedDatabase(repos *repo.AllRepos) { usr, _ := repos.Users.GetOneEmail(context.Background(), user.Email) if usr.ID != uuid.Nil { - a.logger.Info("seed user already exists", logger.Props{ - "user": user.Name, - }) + log.Info().Str("email", user.Email).Msg("user already exists, skipping") continue } hashedPw, err := hasher.HashPassword(user.Password) - if err != nil { - a.logger.Error(err, logger.Props{ - "details": "failed to hash password", - "user": user.Name, - }) + log.Fatal().Err(err).Msg("failed to hash password") } _, err = repos.Users.Create(context.Background(), types.UserCreate{ @@ -90,14 +84,9 @@ func (a *app) SeedDatabase(repos *repo.AllRepos) { }) if err != nil { - a.logger.Error(err, logger.Props{ - "details": "failed to create seed user", - "name": user.Name, - }) + log.Fatal().Err(err).Msg("failed to create user") } - a.logger.Info("creating seed user", logger.Props{ - "name": user.Name, - }) + log.Info().Str("email", user.Email).Msg("created user") } } diff --git a/backend/app/api/v1/controller.go b/backend/app/api/v1/controller.go index b72cde7..72be1b1 100644 --- a/backend/app/api/v1/controller.go +++ b/backend/app/api/v1/controller.go @@ -2,11 +2,9 @@ package v1 import ( "github.com/hay-kot/content/backend/internal/services" - "github.com/hay-kot/content/backend/pkgs/logger" ) type V1Controller struct { - log *logger.Logger svc *services.AllServices } @@ -19,9 +17,8 @@ func BaseUrlFunc(prefix string) func(s string) string { return prefixFunc } -func NewControllerV1(log *logger.Logger, svc *services.AllServices) *V1Controller { +func NewControllerV1(svc *services.AllServices) *V1Controller { ctrl := &V1Controller{ - log: log, svc: svc, } diff --git a/backend/app/api/v1/controller_test.go b/backend/app/api/v1/controller_test.go index 685d8c6..21a33cd 100644 --- a/backend/app/api/v1/controller_test.go +++ b/backend/app/api/v1/controller_test.go @@ -9,12 +9,10 @@ import ( func Test_NewHandlerV1(t *testing.T) { v1Base := BaseUrlFunc("/testing/v1") - ctrl := NewControllerV1(mockHandler.log, mockHandler.svc) + ctrl := NewControllerV1(mockHandler.svc) assert.NotNil(t, ctrl) - assert.Equal(t, ctrl.log, mockHandler.log) - assert.Equal(t, "/testing/v1/v1/abc123", v1Base("/abc123")) assert.Equal(t, "/testing/v1/v1/abc123", v1Base("/abc123")) } diff --git a/backend/app/api/v1/main_test.go b/backend/app/api/v1/main_test.go index dfaeaf7..408a641 100644 --- a/backend/app/api/v1/main_test.go +++ b/backend/app/api/v1/main_test.go @@ -39,7 +39,6 @@ func userPool() func() { func TestMain(m *testing.M) { // Set Handler Vars - mockHandler.log = mocks.GetStructLogger() repos, closeDb := mocks.GetEntRepos() mockHandler.svc = mocks.GetMockServices(repos) diff --git a/backend/app/api/v1/partials.go b/backend/app/api/v1/partials.go index 018c043..7fe0cbf 100644 --- a/backend/app/api/v1/partials.go +++ b/backend/app/api/v1/partials.go @@ -7,8 +7,8 @@ import ( "github.com/google/uuid" "github.com/hay-kot/content/backend/internal/services" "github.com/hay-kot/content/backend/internal/types" - "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/server" + "github.com/rs/zerolog/log" ) /* @@ -24,9 +24,7 @@ and makes it a little more consistent when error handling and logging. func (ctrl *V1Controller) partialParseIdAndUser(w http.ResponseWriter, r *http.Request) (uuid.UUID, *types.UserOut, error) { uid, err := uuid.Parse(chi.URLParam(r, "id")) if err != nil { - ctrl.log.Debug(err.Error(), logger.Props{ - "details": "failed to convert id to valid UUID", - }) + log.Err(err).Msg("failed to parse id") server.RespondError(w, http.StatusBadRequest, err) return uuid.Nil, nil, err } diff --git a/backend/app/api/v1/v1_ctrl_admin.go b/backend/app/api/v1/v1_ctrl_admin.go deleted file mode 100644 index a67a6f2..0000000 --- a/backend/app/api/v1/v1_ctrl_admin.go +++ /dev/null @@ -1,207 +0,0 @@ -package v1 - -import ( - "errors" - "net/http" - - "github.com/go-chi/chi/v5" - "github.com/google/uuid" - "github.com/hay-kot/content/backend/internal/services" - "github.com/hay-kot/content/backend/internal/types" - "github.com/hay-kot/content/backend/pkgs/hasher" - "github.com/hay-kot/content/backend/pkgs/logger" - "github.com/hay-kot/content/backend/pkgs/server" -) - -// HandleAdminUserGetAll godoc -// @Summary Gets all users from the database -// @Tags Admin: Users -// @Produce json -// @Success 200 {object} server.Result{item=[]ent.User} -// @Router /v1/admin/users [get] -// @Security Bearer -func (ctrl *V1Controller) HandleAdminUserGetAll() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - users, err := ctrl.svc.Admin.GetAll(r.Context()) - - if err != nil { - server.RespondError(w, http.StatusInternalServerError, err) - return - } - - server.Respond(w, http.StatusOK, server.Wrap(users)) - } -} - -// HandleAdminUserGet godoc -// @Summary Get a user from the database -// @Tags Admin: Users -// @Produce json -// @Param id path string true "User ID" -// @Success 200 {object} server.Result{item=ent.User} -// @Router /v1/admin/users/{id} [get] -// @Security Bearer -func (ctrl *V1Controller) HandleAdminUserGet() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - uid, err := uuid.Parse(chi.URLParam(r, "id")) - - if err != nil { - ctrl.log.Debug(err.Error(), logger.Props{ - "scope": "admin", - "details": "failed to convert id to valid UUID", - }) - server.RespondError(w, http.StatusBadRequest, err) - return - } - - user, err := ctrl.svc.Admin.GetByID(r.Context(), uid) - - if err != nil { - ctrl.log.Error(err, nil) - server.RespondError(w, http.StatusInternalServerError, err) - return - } - server.Respond(w, http.StatusOK, server.Wrap(user)) - - } -} - -// HandleAdminUserCreate godoc -// @Summary Create a new user -// @Tags Admin: Users -// @Produce json -// @Param payload body types.UserCreate true "User Data" -// @Success 200 {object} server.Result{item=ent.User} -// @Router /v1/admin/users [POST] -// @Security Bearer -func (ctrl *V1Controller) HandleAdminUserCreate() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - createData := types.UserCreate{} - - if err := server.Decode(r, &createData); err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "admin", - "details": "failed to decode user create data", - }) - server.RespondError(w, http.StatusBadRequest, err) - return - } - - err := createData.Validate() - - if err != nil { - server.RespondError(w, http.StatusUnprocessableEntity, err) - return - } - - hashedPw, err := hasher.HashPassword(createData.Password) - - if err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "admin", - "details": "failed to hash password", - }) - - server.RespondError(w, http.StatusInternalServerError, err) - return - } - - createData.Password = hashedPw - userOut, err := ctrl.svc.Admin.Create(r.Context(), createData) - - if err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "admin", - "details": "failed to create user", - }) - - server.RespondError(w, http.StatusInternalServerError, err) - return - } - - server.Respond(w, http.StatusCreated, server.Wrap(userOut)) - } -} - -// HandleAdminUserUpdate godoc -// @Summary Update a User -// @Tags Admin: Users -// @Param id path string true "User ID" -// @Param payload body types.UserUpdate true "User Data" -// @Produce json -// @Success 200 {object} server.Result{item=ent.User} -// @Router /v1/admin/users/{id} [PUT] -// @Security Bearer -func (ctrl *V1Controller) HandleAdminUserUpdate() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - uid, err := uuid.Parse(chi.URLParam(r, "id")) - if err != nil { - ctrl.log.Debug(err.Error(), logger.Props{ - "scope": "admin", - "details": "failed to convert id to valid UUID", - }) - } - - updateData := types.UserUpdate{} - - if err := server.Decode(r, &updateData); err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "admin", - "details": "failed to decode user update data", - }) - server.RespondError(w, http.StatusBadRequest, err) - return - } - - newData, err := ctrl.svc.Admin.UpdateProperties(r.Context(), uid, updateData) - - if err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "admin", - "details": "failed to update user", - }) - server.RespondError(w, http.StatusInternalServerError, err) - return - } - - server.Respond(w, http.StatusOK, server.Wrap(newData)) - } -} - -// HandleAdminUserDelete godoc -// @Summary Delete a User -// @Tags Admin: Users -// @Param id path string true "User ID" -// @Produce json -// @Success 204 -// @Router /v1/admin/users/{id} [DELETE] -// @Security Bearer -func (ctrl *V1Controller) HandleAdminUserDelete() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - uid, err := uuid.Parse(chi.URLParam(r, "id")) - if err != nil { - ctrl.log.Debug(err.Error(), logger.Props{ - "scope": "admin", - "details": "failed to convert id to valid UUID", - }) - } - - actor := services.UseUserCtx(r.Context()) - - if actor.ID == uid { - server.RespondError(w, http.StatusBadRequest, errors.New("cannot delete yourself")) - return - } - - err = ctrl.svc.Admin.Delete(r.Context(), uid) - - if err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "admin", - "details": "failed to delete user", - }) - server.RespondError(w, http.StatusInternalServerError, err) - return - } - } -} diff --git a/backend/app/api/v1/v1_ctrl_admin_test.go b/backend/app/api/v1/v1_ctrl_admin_test.go deleted file mode 100644 index b85cfec..0000000 --- a/backend/app/api/v1/v1_ctrl_admin_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package v1 - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/hay-kot/content/backend/ent" - "github.com/hay-kot/content/backend/internal/mocks/chimocker" - "github.com/hay-kot/content/backend/internal/mocks/factories" - "github.com/hay-kot/content/backend/pkgs/server" - "github.com/stretchr/testify/assert" -) - -const ( - UrlUser = "/api/v1/admin/users" - UrlUserId = "/api/v1/admin/users/%v" - UrlUserIdChi = "/api/v1/admin/users/{id}" -) - -type usersResponse struct { - Users []*ent.User `json:"item"` -} - -type userResponse struct { - User *ent.User `json:"item"` -} - -func Test_HandleAdminUserGetAll_Success(t *testing.T) { - r := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, UrlUser, nil) - - mockHandler.HandleAdminUserGetAll()(r, req) - - response := usersResponse{ - Users: []*ent.User{}, - } - - _ = json.Unmarshal(r.Body.Bytes(), &response) - assert.Equal(t, http.StatusOK, r.Code) - assert.Equal(t, len(users), len(response.Users)) - - knowEmail := []string{ - users[0].Email, - users[1].Email, - users[2].Email, - users[3].Email, - } - - for _, user := range users { - assert.Contains(t, knowEmail, user.Email) - } - -} - -func Test_HandleAdminUserGet_Success(t *testing.T) { - targetUser := users[2] - res := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, fmt.Sprintf(UrlUserId, targetUser.ID), nil) - - req = chimocker.WithUrlParam(req, "id", fmt.Sprintf("%v", targetUser.ID)) - - mockHandler.HandleAdminUserGet()(res, req) - assert.Equal(t, http.StatusOK, res.Code) - - response := userResponse{ - User: &ent.User{}, - } - - _ = json.Unmarshal(res.Body.Bytes(), &response) - assert.Equal(t, targetUser.ID, response.User.ID) -} - -func Test_HandleAdminUserCreate_Success(t *testing.T) { - payload := factories.UserFactory() - - r := httptest.NewRecorder() - - body, err := json.Marshal(payload) - assert.NoError(t, err) - - req := httptest.NewRequest(http.MethodGet, UrlUser, bytes.NewBuffer(body)) - req.Header.Set(server.ContentType, server.ContentJSON) - - mockHandler.HandleAdminUserCreate()(r, req) - - assert.Equal(t, http.StatusCreated, r.Code) - - usr, err := mockHandler.svc.Admin.GetByEmail(context.Background(), payload.Email) - - assert.NoError(t, err) - assert.Equal(t, payload.Email, usr.Email) - assert.Equal(t, payload.Name, usr.Name) - assert.NotEqual(t, payload.Password, usr.Password) // smoke test - check password is hashed - - _ = mockHandler.svc.Admin.Delete(context.Background(), usr.ID) -} - -func Test_HandleAdminUserUpdate_Success(t *testing.T) { - t.Skip() -} - -func Test_HandleAdminUserUpdate_Delete(t *testing.T) { - t.Skip() -} diff --git a/backend/app/api/v1/v1_ctrl_auth.go b/backend/app/api/v1/v1_ctrl_auth.go index 4d265ea..851a638 100644 --- a/backend/app/api/v1/v1_ctrl_auth.go +++ b/backend/app/api/v1/v1_ctrl_auth.go @@ -6,8 +6,8 @@ import ( "github.com/hay-kot/content/backend/internal/services" "github.com/hay-kot/content/backend/internal/types" - "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/server" + "github.com/rs/zerolog/log" ) // HandleAuthLogin godoc @@ -28,7 +28,7 @@ func (ctrl *V1Controller) HandleAuthLogin() http.HandlerFunc { err := r.ParseForm() if err != nil { server.Respond(w, http.StatusBadRequest, server.Wrap(err)) - ctrl.log.Error(errors.New("failed to decode login form (FORM)"), logger.Props{"error": err.Error()}) + log.Error().Err(err).Msg("failed to parse form") return } @@ -38,9 +38,7 @@ func (ctrl *V1Controller) HandleAuthLogin() http.HandlerFunc { err := server.Decode(r, loginForm) if err != nil { - ctrl.log.Error(errors.New("failed to decode login form (JSON)"), logger.Props{ - "error": err.Error(), - }) + log.Err(err).Msg("failed to decode login form") server.Respond(w, http.StatusBadRequest, server.Wrap(err)) return } diff --git a/backend/app/api/v1/v1_ctrl_items.go b/backend/app/api/v1/v1_ctrl_items.go index f8d237b..c2ba923 100644 --- a/backend/app/api/v1/v1_ctrl_items.go +++ b/backend/app/api/v1/v1_ctrl_items.go @@ -6,6 +6,7 @@ import ( "github.com/hay-kot/content/backend/internal/services" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/server" + "github.com/rs/zerolog/log" ) // HandleItemsGetAll godoc @@ -20,7 +21,7 @@ func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc { user := services.UseUserCtx(r.Context()) items, err := ctrl.svc.Items.GetAll(r.Context(), user.GroupID) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to get items") server.RespondServerError(w) return } @@ -40,7 +41,7 @@ func (ctrl *V1Controller) HandleItemsCreate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { createData := types.ItemCreate{} if err := server.Decode(r, &createData); err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to decode request body") server.RespondError(w, http.StatusInternalServerError, err) return } @@ -48,7 +49,7 @@ func (ctrl *V1Controller) HandleItemsCreate() http.HandlerFunc { user := services.UseUserCtx(r.Context()) item, err := ctrl.svc.Items.Create(r.Context(), user.GroupID, createData) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to create item") server.RespondServerError(w) return } @@ -75,7 +76,7 @@ func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc { err = ctrl.svc.Items.Delete(r.Context(), user.GroupID, uid) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to delete item") server.RespondServerError(w) return } @@ -100,7 +101,7 @@ func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc { items, err := ctrl.svc.Items.GetOne(r.Context(), user.GroupID, uid) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to get item") server.RespondServerError(w) return } @@ -120,7 +121,7 @@ func (ctrl *V1Controller) HandleItemUpdate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { body := types.ItemUpdate{} if err := server.Decode(r, &body); err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to decode request body") server.RespondError(w, http.StatusInternalServerError, err) return } @@ -132,7 +133,7 @@ func (ctrl *V1Controller) HandleItemUpdate() http.HandlerFunc { body.ID = uid result, err := ctrl.svc.Items.Update(r.Context(), user.GroupID, body) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to update item") server.RespondServerError(w) return } diff --git a/backend/app/api/v1/v1_ctrl_labels.go b/backend/app/api/v1/v1_ctrl_labels.go index 19dfebc..7cac7c5 100644 --- a/backend/app/api/v1/v1_ctrl_labels.go +++ b/backend/app/api/v1/v1_ctrl_labels.go @@ -6,6 +6,7 @@ import ( "github.com/hay-kot/content/backend/internal/services" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/server" + "github.com/rs/zerolog/log" ) // HandleLabelsGetAll godoc @@ -20,7 +21,7 @@ func (ctrl *V1Controller) HandleLabelsGetAll() http.HandlerFunc { user := services.UseUserCtx(r.Context()) labels, err := ctrl.svc.Labels.GetAll(r.Context(), user.GroupID) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("error getting labels") server.RespondServerError(w) return } @@ -40,7 +41,7 @@ func (ctrl *V1Controller) HandleLabelsCreate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { createData := types.LabelCreate{} if err := server.Decode(r, &createData); err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("error decoding label create data") server.RespondError(w, http.StatusInternalServerError, err) return } @@ -48,7 +49,7 @@ func (ctrl *V1Controller) HandleLabelsCreate() http.HandlerFunc { user := services.UseUserCtx(r.Context()) label, err := ctrl.svc.Labels.Create(r.Context(), user.GroupID, createData) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("error creating label") server.RespondServerError(w) return } @@ -75,7 +76,7 @@ func (ctrl *V1Controller) HandleLabelDelete() http.HandlerFunc { err = ctrl.svc.Labels.Delete(r.Context(), user.GroupID, uid) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("error deleting label") server.RespondServerError(w) return } @@ -100,7 +101,7 @@ func (ctrl *V1Controller) HandleLabelGet() http.HandlerFunc { labels, err := ctrl.svc.Labels.Get(r.Context(), user.GroupID, uid) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("error getting label") server.RespondServerError(w) return } @@ -120,7 +121,7 @@ func (ctrl *V1Controller) HandleLabelUpdate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { body := types.LabelUpdate{} if err := server.Decode(r, &body); err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("error decoding label update data") server.RespondError(w, http.StatusInternalServerError, err) return } @@ -132,7 +133,7 @@ func (ctrl *V1Controller) HandleLabelUpdate() http.HandlerFunc { body.ID = uid result, err := ctrl.svc.Labels.Update(r.Context(), user.GroupID, body) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("error updating label") server.RespondServerError(w) return } diff --git a/backend/app/api/v1/v1_ctrl_locations.go b/backend/app/api/v1/v1_ctrl_locations.go index f6d4df7..275679b 100644 --- a/backend/app/api/v1/v1_ctrl_locations.go +++ b/backend/app/api/v1/v1_ctrl_locations.go @@ -6,6 +6,7 @@ import ( "github.com/hay-kot/content/backend/internal/services" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/server" + "github.com/rs/zerolog/log" ) // HandleLocationGetAll godoc @@ -20,7 +21,7 @@ func (ctrl *V1Controller) HandleLocationGetAll() http.HandlerFunc { user := services.UseUserCtx(r.Context()) locations, err := ctrl.svc.Location.GetAll(r.Context(), user.GroupID) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to get locations") server.RespondServerError(w) return } @@ -41,7 +42,7 @@ func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { createData := types.LocationCreate{} if err := server.Decode(r, &createData); err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to decode location create data") server.RespondError(w, http.StatusInternalServerError, err) return } @@ -49,7 +50,7 @@ func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc { user := services.UseUserCtx(r.Context()) location, err := ctrl.svc.Location.Create(r.Context(), user.GroupID, createData) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to create location") server.RespondServerError(w) return } @@ -75,7 +76,7 @@ func (ctrl *V1Controller) HandleLocationDelete() http.HandlerFunc { err = ctrl.svc.Location.Delete(r.Context(), user.GroupID, uid) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to delete location") server.RespondServerError(w) return } @@ -100,7 +101,7 @@ func (ctrl *V1Controller) HandleLocationGet() http.HandlerFunc { location, err := ctrl.svc.Location.GetOne(r.Context(), user.GroupID, uid) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to get location") server.RespondServerError(w) return } @@ -120,7 +121,7 @@ func (ctrl *V1Controller) HandleLocationUpdate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { body := types.LocationUpdate{} if err := server.Decode(r, &body); err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to decode location update data") server.RespondError(w, http.StatusInternalServerError, err) return } @@ -134,7 +135,7 @@ func (ctrl *V1Controller) HandleLocationUpdate() http.HandlerFunc { result, err := ctrl.svc.Location.Update(r.Context(), user.GroupID, body) if err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to update location") server.RespondServerError(w) return } diff --git a/backend/app/api/v1/v1_ctrl_user.go b/backend/app/api/v1/v1_ctrl_user.go index 9abf0ee..5eede9c 100644 --- a/backend/app/api/v1/v1_ctrl_user.go +++ b/backend/app/api/v1/v1_ctrl_user.go @@ -1,14 +1,13 @@ package v1 import ( - "errors" "net/http" "github.com/google/uuid" "github.com/hay-kot/content/backend/internal/services" "github.com/hay-kot/content/backend/internal/types" - "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/server" + "github.com/rs/zerolog/log" ) // HandleUserSelf godoc @@ -23,14 +22,13 @@ func (ctrl *V1Controller) HandleUserRegistration() http.HandlerFunc { regData := types.UserRegistration{} if err := server.Decode(r, ®Data); err != nil { - ctrl.log.Error(err, nil) + log.Err(err).Msg("failed to decode user registration data") server.RespondError(w, http.StatusInternalServerError, err) return } _, err := ctrl.svc.User.RegisterUser(r.Context(), regData) if err != nil { - ctrl.log.Error(err, nil) server.RespondError(w, http.StatusInternalServerError, err) return } @@ -51,7 +49,7 @@ func (ctrl *V1Controller) HandleUserSelf() http.HandlerFunc { token := services.UseTokenCtx(r.Context()) usr, err := ctrl.svc.User.GetSelf(r.Context(), token) if usr.ID == uuid.Nil || err != nil { - ctrl.log.Error(errors.New("no user within request context"), nil) + log.Err(err).Msg("failed to get user") server.RespondServerError(w) return } @@ -72,10 +70,7 @@ func (ctrl *V1Controller) HandleUserUpdate() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { updateData := types.UserUpdate{} if err := server.Decode(r, &updateData); err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "user", - "details": "failed to decode user update data", - }) + log.Err(err).Msg("failed to decode user update data") server.RespondError(w, http.StatusBadRequest, err) return } @@ -84,10 +79,7 @@ func (ctrl *V1Controller) HandleUserUpdate() http.HandlerFunc { newData, err := ctrl.svc.User.UpdateSelf(r.Context(), actor.ID, updateData) if err != nil { - ctrl.log.Error(err, logger.Props{ - "scope": "user", - "details": "failed to update user", - }) + server.RespondError(w, http.StatusInternalServerError, err) return } diff --git a/backend/go.mod b/backend/go.mod index 7549d09..b8100cd 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -31,8 +31,11 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/zerolog v1.28.0 // indirect github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a // indirect github.com/tkrajina/go-reflector v0.5.6 // indirect github.com/zclconf/go-cty v1.11.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index 2ad1220..0084468 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -11,6 +11,7 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/ardanlabs/conf/v2 v2.2.0 h1:ar1+TYIYAh2Tdeg2DQroh7ruR56/vJR8BDfzDIrXgtk= github.com/ardanlabs/conf/v2 v2.2.0/go.mod h1:m37ZKdW9jwMUEhGX36jRNt8VzSQ/HVmSziLZH2p33nY= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -31,6 +32,7 @@ github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/ github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -53,6 +55,12 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= @@ -60,8 +68,12 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= +github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -95,6 +107,9 @@ golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWI golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/backend/internal/mocks/mock_logger.go b/backend/internal/mocks/mock_logger.go deleted file mode 100644 index c3f0a1f..0000000 --- a/backend/internal/mocks/mock_logger.go +++ /dev/null @@ -1,11 +0,0 @@ -package mocks - -import ( - "os" - - "github.com/hay-kot/content/backend/pkgs/logger" -) - -func GetStructLogger() *logger.Logger { - return logger.New(os.Stdout, logger.LevelDebug) -} diff --git a/backend/pkgs/logger/struct_logger.go b/backend/pkgs/logger/struct_logger.go deleted file mode 100644 index a593076..0000000 --- a/backend/pkgs/logger/struct_logger.go +++ /dev/null @@ -1,132 +0,0 @@ -package logger - -import ( - "encoding/json" - "io" - "os" - "runtime/debug" - "sync" - "time" -) - -type Level int8 - -var ( - IncludeTrace = false -) - -const ( - LevelDebug Level = iota - LevelInfo - LevelError - LevelFatal - LevelOff -) - -func (l Level) String() string { - switch l { - case LevelDebug: - return "DEBUG" - case LevelInfo: - return "INFO" - case LevelError: - return "ERROR" - case LevelFatal: - return "FATAL" - default: - return "" - } -} - -type Props map[string]string - -type Logger struct { - out io.Writer - minLevel Level - mu sync.Mutex -} - -func New(out io.Writer, minLevel Level) *Logger { - return &Logger{ - out: out, - minLevel: minLevel, - } -} - -func (l *Logger) Debug(message string, properties map[string]string) { - l.print(LevelDebug, message, properties) -} - -func (l *Logger) Info(message string, properties map[string]string) { - l.print(LevelInfo, message, properties) -} - -func (l *Logger) Error(err error, properties map[string]string) { - l.print(LevelError, err.Error(), properties) -} - -func (l *Logger) Fatal(err error, properties map[string]string) { - l.print(LevelFatal, err.Error(), properties) - os.Exit(1) // For entries at the FATAL level, we also terminate the application. -} - -func (l *Logger) print(level Level, message string, properties map[string]string) (int, error) { - // If the severity level of the log entry is below the minimum severity for the - // logger, then return with no further action. - if level < l.minLevel { - return 0, nil - } - - // Declare an anonymous struct holding the data for the log entry. - aux := struct { - Level string `json:"level"` - Time string `json:"time"` - Message string `json:"message"` - Properties map[string]string `json:"properties,omitempty"` - Trace string `json:"trace,omitempty"` - }{ - Level: level.String(), - Time: time.Now().UTC().Format(time.RFC3339), - Message: message, - Properties: properties, - } - - // Include a stack trace for entries at the ERROR and FATAL levels. - dumpTrace := false - if level >= LevelError { - dumpTrace = true - if IncludeTrace { - aux.Trace = string(debug.Stack()) - } - } - - // Declare a line variable for holding the actual log entry text. - var line []byte - - // Marshal the anonymous struct to JSON and store it in the line variable. If there - // was a problem creating the JSON, set the contents of the log entry to be that - // plain-text error message instead.” - line, err := json.Marshal(aux) - if err != nil { - line = []byte(LevelError.String() + ": unable to marshal log message:" + err.Error()) - } - - // Lock the mutex so that no two writes to the output destination cannot happen - // concurrently. If we don't do this, it's possible that the text for two or more - // log entries will be intermingled in the output. - l.mu.Lock() - defer l.mu.Unlock() - - n, err := l.out.Write(line) - if dumpTrace { - n, err = l.out.Write(debug.Stack()) - } - return n, err -} - -// We also implement a Write() method on our Logger type so that it satisfies the -// io.Writer interface. This writes a log entry at the ERROR level with no additional -// properties. -func (l *Logger) Write(message []byte) (n int, err error) { - return l.print(LevelError, string(message), nil) -} diff --git a/backend/pkgs/logger/struct_logger_test.go b/backend/pkgs/logger/struct_logger_test.go deleted file mode 100644 index 0ab629d..0000000 --- a/backend/pkgs/logger/struct_logger_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package logger - -import ( - "encoding/json" - "errors" - "testing" - - "github.com/stretchr/testify/assert" -) - -func init() { - IncludeTrace = true -} - -var lastWrite = []byte{} - -type testLogRecorder struct { - t *testing.T -} - -func (tlr testLogRecorder) Write(p []byte) (n int, err error) { - lastWrite = p - return len(p), nil -} - -type logEntry struct { - Level string `json:"level"` - Message string `json:"message"` - Props *Props `json:"properties"` -} - -func (lr *logEntry) Unmarshal(t *testing.T, jbytes []byte) { - err := json.Unmarshal(jbytes, lr) - if err != nil { - t.Error(err) - } -} - -func Test_LevelString(t *testing.T) { - assert.Equal(t, "DEBUG", LevelDebug.String()) - assert.Equal(t, "INFO", LevelInfo.String()) - assert.Equal(t, "ERROR", LevelError.String()) - assert.Equal(t, "FATAL", LevelFatal.String()) - assert.Equal(t, "", LevelOff.String()) -} - -func Test_NewLogger(t *testing.T) { - logRecorder := testLogRecorder{t: t} - - logger := New(logRecorder, LevelInfo) - assert.NotNil(t, logger) -} - -func getTestLogger(t *testing.T, level Level) *Logger { - logRecorder := testLogRecorder{t: t} - - logger := New(logRecorder, level) - assert.NotNil(t, logger) - - return logger -} - -func checkLastEntry(t *testing.T, level Level, message string, props *Props) { - t.Helper() - entry := &logEntry{} - entry.Unmarshal(t, lastWrite) - - assert.Equal(t, level.String(), entry.Level) - assert.Equal(t, message, entry.Message) - assert.Equal(t, props, entry.Props) - -} - -func Test_LoggerDebug(t *testing.T) { - lgr := getTestLogger(t, LevelDebug) - - lgr.Debug("Test Debug", Props{"Hello": "World"}) - checkLastEntry(t, LevelDebug, "Test Debug", &Props{"Hello": "World"}) - - lastWrite = []byte{} -} - -func Test_LoggerInfo(t *testing.T) { - lgr := getTestLogger(t, LevelInfo) - - lgr.Info("Test Info", Props{"Hello": "World"}) - checkLastEntry(t, LevelInfo, "Test Info", &Props{"Hello": "World"}) - lastWrite = []byte{} - -} - -func Test_LoggerError(t *testing.T) { - lgr := getTestLogger(t, LevelError) - - myerror := errors.New("Test Error") - - lgr.Error(myerror, Props{"Hello": "World"}) - checkLastEntry(t, LevelError, "Test Error", &Props{"Hello": "World"}) - lastWrite = []byte{} - -} - -func Test_LoggerLevelScale(t *testing.T) { - lgr := getTestLogger(t, LevelInfo) - lastWrite = []byte{} - lgr.Debug("Test Debug", Props{"Hello": "World"}) - - assert.Equal(t, []byte{}, lastWrite) - - lgr = getTestLogger(t, LevelError) - lastWrite = []byte{} - lgr.Info("Test Debug", Props{"Hello": "World"}) - lgr.Debug("Test Debug", Props{"Hello": "World"}) - - assert.Equal(t, []byte{}, lastWrite) - - lgr = getTestLogger(t, LevelFatal) - - lgr.Info("Test Debug", Props{"Hello": "World"}) - lgr.Debug("Test Debug", Props{"Hello": "World"}) - lgr.Error(errors.New("Test Error"), Props{"Hello": "World"}) - - assert.Equal(t, []byte{}, lastWrite) -}