diff --git a/core/cmd/start.go b/core/cmd/start.go index a17da025..d26c0b5c 100644 --- a/core/cmd/start.go +++ b/core/cmd/start.go @@ -22,9 +22,9 @@ import ( "github.com/medama-io/medama/migrations" "github.com/medama-io/medama/services" "github.com/medama-io/medama/util" + "github.com/medama-io/medama/util/logger" "github.com/ogen-go/ogen/middleware" "github.com/rs/cors" - "github.com/rs/zerolog" ) type StartCommand struct { @@ -75,11 +75,10 @@ func (s *StartCommand) ParseFlags(args []string) error { // Run executes the start command. func (s *StartCommand) Run(ctx context.Context) error { - ctx, err := util.SetupLogger(ctx, s.Server.Logger, s.Server.Level) + log, err := logger.Init(s.Server.Logger, s.Server.Level) if err != nil { return errors.Wrap(err, "failed to setup logger") } - log := zerolog.Ctx(ctx) log.Info().Msg(GetVersion()) // Setup database @@ -117,14 +116,13 @@ func (s *StartCommand) Run(ctx context.Context) error { return errors.Wrap(err, "failed to create handlers") } - authMiddleware := middlewares.NewAuthHandler(auth) mw := []middleware.Middleware{ middlewares.RequestLogger(), middlewares.RequestContext(), middlewares.Recovery(), } apiHandler, err := api.NewServer(service, - authMiddleware, + middlewares.NewAuthHandler(auth), api.WithMiddleware(mw...), api.WithErrorHandler(middlewares.ErrorHandler), api.WithNotFound(middlewares.NotFound()), diff --git a/core/db/sqlite/websites.go b/core/db/sqlite/websites.go index faa8c24d..a00a2942 100644 --- a/core/db/sqlite/websites.go +++ b/core/db/sqlite/websites.go @@ -6,8 +6,8 @@ import ( "github.com/go-faster/errors" "github.com/medama-io/medama/model" + "github.com/medama-io/medama/util/logger" "github.com/ncruces/go-sqlite3" - "github.com/rs/zerolog" ) func (c *Client) CreateWebsite(ctx context.Context, website *model.Website) error { @@ -23,8 +23,8 @@ func (c *Client) CreateWebsite(ctx context.Context, website *model.Website) erro return model.ErrUserNotFound } - zerolog.Ctx(ctx). - Error(). + log := logger.Get() + log.Error(). Str("user_id", website.UserID). Str("hostname", website.Hostname). Int64("date_created", website.DateCreated). @@ -39,14 +39,14 @@ func (c *Client) CreateWebsite(ctx context.Context, website *model.Website) erro func (c *Client) ListWebsites(ctx context.Context, userID string) ([]*model.Website, error) { var websites []*model.Website + log := logger.Get() query := `--sql SELECT user_id, hostname, name, date_created, date_updated FROM websites WHERE user_id = ?` err := c.SelectContext(ctx, &websites, query, userID) if err != nil { - zerolog.Ctx(ctx). - Error(). + log.Error(). Str("user_id", userID). Err(err). Msg("failed to list websites") @@ -55,7 +55,7 @@ func (c *Client) ListWebsites(ctx context.Context, userID string) ([]*model.Webs } if len(websites) == 0 { - zerolog.Ctx(ctx).Debug().Str("user_id", userID).Msg("no websites found") + log.Debug().Str("user_id", userID).Msg("no websites found") return nil, model.ErrWebsiteNotFound } @@ -69,7 +69,8 @@ func (c *Client) ListAllHostnames(ctx context.Context) ([]string, error) { var hostnames []string err := c.SelectContext(ctx, &hostnames, query) if err != nil { - zerolog.Ctx(ctx).Error().Err(err).Msg("failed to list all hostnames") + log := logger.Get() + log.Error().Err(err).Msg("failed to list all hostnames") return nil, errors.Wrap(err, "db") } @@ -77,6 +78,7 @@ func (c *Client) ListAllHostnames(ctx context.Context) ([]string, error) { } func (c *Client) UpdateWebsite(ctx context.Context, website *model.Website) error { + log := logger.Get() // Update all values except user_id exec := `--sql UPDATE websites SET hostname = ?, name = ?, date_updated = ? WHERE hostname = ?` @@ -87,8 +89,7 @@ func (c *Client) UpdateWebsite(ctx context.Context, website *model.Website) erro return model.ErrWebsiteExists } - zerolog.Ctx(ctx). - Error(). + log.Error(). Str("hostname", website.Hostname). Str("name", website.Name). Int64("date_updated", website.DateUpdated). @@ -100,8 +101,7 @@ func (c *Client) UpdateWebsite(ctx context.Context, website *model.Website) erro rowsAffected, err := res.RowsAffected() if err != nil { - zerolog.Ctx(ctx). - Error(). + log.Error(). Str("hostname", website.Hostname). Str("name", website.Name). Int64("date_updated", website.DateUpdated). @@ -112,7 +112,7 @@ func (c *Client) UpdateWebsite(ctx context.Context, website *model.Website) erro } if rowsAffected == 0 { - zerolog.Ctx(ctx).Debug().Str("hostname", website.Hostname).Msg("website not found") + log.Debug().Str("hostname", website.Hostname).Msg("website not found") return model.ErrWebsiteNotFound } @@ -121,13 +121,13 @@ func (c *Client) UpdateWebsite(ctx context.Context, website *model.Website) erro func (c *Client) GetWebsite(ctx context.Context, hostname string) (*model.Website, error) { var website model.Website + log := logger.Get() query := `--sql SELECT user_id, hostname, name, date_created, date_updated FROM websites WHERE hostname = ?` err := c.QueryRowxContext(ctx, query, hostname).StructScan(&website) if err != nil { - log := zerolog.Ctx(ctx) if errors.Is(err, sql.ErrNoRows) { log.Debug().Str("hostname", hostname).Msg("website not found") return nil, model.ErrWebsiteNotFound @@ -142,13 +142,13 @@ func (c *Client) GetWebsite(ctx context.Context, hostname string) (*model.Websit } func (c *Client) DeleteWebsite(ctx context.Context, hostname string) error { + log := logger.Get() exec := `--sql DELETE FROM websites WHERE hostname = ?` res, err := c.DB.ExecContext(ctx, exec, hostname) if err != nil { - zerolog.Ctx(ctx). - Error(). + log.Error(). Str("hostname", hostname). Err(err). Msg("failed to delete website") @@ -158,8 +158,7 @@ func (c *Client) DeleteWebsite(ctx context.Context, hostname string) error { rowsAffected, err := res.RowsAffected() if err != nil { - zerolog.Ctx(ctx). - Error(). + log.Error(). Str("hostname", hostname). Err(err). Msg("failed to get rows affected") @@ -168,7 +167,7 @@ func (c *Client) DeleteWebsite(ctx context.Context, hostname string) error { } if rowsAffected == 0 { - zerolog.Ctx(ctx).Debug().Str("hostname", hostname).Msg("website not found") + log.Debug().Str("hostname", hostname).Msg("website not found") return model.ErrWebsiteNotFound } diff --git a/core/middlewares/404.go b/core/middlewares/404.go index 0da699f2..6708438d 100644 --- a/core/middlewares/404.go +++ b/core/middlewares/404.go @@ -4,7 +4,7 @@ import ( "net/http" "github.com/go-faster/jx" - "github.com/rs/zerolog" + "github.com/medama-io/medama/util/logger" ) const errMessage = "api path not found" @@ -27,8 +27,9 @@ func NotFound() func(w http.ResponseWriter, req *http.Request) { _, _ = w.Write(e.Bytes()) - zerolog.Ctx(req.Context()). - Info(). + log := logger.Get() + + log.Info(). Str("path", req.URL.Path). Str("method", req.Method). Int("status_code", http.StatusNotFound). diff --git a/core/middlewares/errors.go b/core/middlewares/errors.go index 78b077a8..943e291a 100644 --- a/core/middlewares/errors.go +++ b/core/middlewares/errors.go @@ -7,8 +7,9 @@ import ( "strings" "github.com/go-faster/jx" + "github.com/medama-io/medama/model" + "github.com/medama-io/medama/util/logger" "github.com/ogen-go/ogen/ogenerrors" - "github.com/rs/zerolog" ) // ErrorHandler is a middleware that handles any unhandled errors by ogen. @@ -16,8 +17,7 @@ func ErrorHandler(ctx context.Context, w http.ResponseWriter, req *http.Request, code := ogenerrors.ErrorCode(err) errMessage := strings.ReplaceAll(err.Error(), "\"", "'") - zerolog.Ctx(ctx). - Error(). + log := logger.Get().With(). Str("path", req.URL.Path). Str("method", req.Method). Int("status_code", code). @@ -25,8 +25,13 @@ func ErrorHandler(ctx context.Context, w http.ResponseWriter, req *http.Request, Str("Connection", req.Header.Get("Connection")). Str("Content-Type", req.Header.Get("Content-Type")). Str("Content-Length", req.Header.Get("Content-Length")). - Str("User-Agent", req.Header.Get("User-Agent")). - Msg("Error 500") + Str("User-Agent", req.Header.Get("User-Agent")).Logger() + + if errors.Is(err, model.ErrUnauthorised) || code == http.StatusUnauthorized { + log.Warn().Msg("unauthorised") + } else { + log.Error().Msg(err.Error()) + } if errors.Is(err, ogenerrors.ErrSecurityRequirementIsNotSatisfied) { errMessage = "missing security token or cookie" diff --git a/core/middlewares/logger.go b/core/middlewares/logger.go index 4f190727..69a08cb7 100644 --- a/core/middlewares/logger.go +++ b/core/middlewares/logger.go @@ -5,8 +5,8 @@ import ( "time" "github.com/medama-io/medama/api" + "github.com/medama-io/medama/util/logger" "github.com/ogen-go/ogen/middleware" - "github.com/rs/zerolog" ) // getCode returns the http status code from the error type. @@ -40,12 +40,15 @@ func RequestLogger() middleware.Middleware { req middleware.Request, next func(req middleware.Request) (middleware.Response, error), ) (middleware.Response, error) { + // Add the logger to request context + log := logger.Get() + startTime := time.Now() resp, err := next(req) duration := time.Since(startTime) if err == nil { - log := zerolog.Ctx(req.Context).With(). + log = log.With(). Str("operation", req.OperationName). Str("operationId", req.OperationID). Str("method", req.Raw.Method). diff --git a/core/middlewares/recover.go b/core/middlewares/recover.go index c2e63765..9faec4be 100644 --- a/core/middlewares/recover.go +++ b/core/middlewares/recover.go @@ -4,9 +4,8 @@ import ( "errors" "net/http" + "github.com/medama-io/medama/util/logger" "github.com/ogen-go/ogen/middleware" - "github.com/rs/zerolog" - "github.com/rs/zerolog/pkgerrors" ) // Recovery is a middleware that recovers from panics. @@ -23,10 +22,8 @@ func Recovery() middleware.Middleware { panic(rvr) } - //nolint: reassign // We need to reassign the stack marshaler. - zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack - zerolog.Ctx(req.Context). - Error(). + log := logger.Get() + log.Error(). Str("path", req.Raw.URL.Path). Str("method", req.Raw.Method). Str("User-Agent", req.Raw.Header.Get("User-Agent")). diff --git a/core/migrations/migrations.go b/core/migrations/migrations.go index 30fa1d23..b3d1546e 100644 --- a/core/migrations/migrations.go +++ b/core/migrations/migrations.go @@ -7,7 +7,7 @@ import ( "github.com/go-faster/errors" "github.com/medama-io/medama/db/duckdb" "github.com/medama-io/medama/db/sqlite" - "github.com/rs/zerolog" + "github.com/medama-io/medama/util/logger" ) type MigrationType string @@ -55,7 +55,7 @@ func NewMigrationsService(ctx context.Context, sqliteC *sqlite.Client, duckdbC * {ID: 2, Name: "0002_duckdb_schema.go", Type: DuckDB, Up: Up0002, Down: Down0002}, } - log := zerolog.Ctx(ctx) + log := logger.Get() err := CreateMigrationsTable(sqliteC) if err != nil { log.Error().Err(err).Msg("failed to create migrations table") @@ -78,7 +78,7 @@ func runMigrator[Client sqlite.Client | duckdb.Client](ctx context.Context, sqli var id int err := sqlite.GetContext(ctx, &id, "SELECT id FROM migrations WHERE id = ?", migration.ID) - log := zerolog.Ctx(ctx).With(). + log := logger.Get().With(). Int("id", migration.ID). Str("name", migration.Name). Str("type", string(migration.Type)). diff --git a/core/services/event.go b/core/services/event.go index a3b7e62a..cdc737a1 100644 --- a/core/services/event.go +++ b/core/services/event.go @@ -9,7 +9,7 @@ import ( "github.com/medama-io/medama/api" "github.com/medama-io/medama/model" - "github.com/rs/zerolog" + "github.com/medama-io/medama/util/logger" "golang.org/x/text/language" "golang.org/x/text/language/display" ) @@ -45,7 +45,8 @@ func (h *Handler) GetEventPing(ctx context.Context, params api.GetEventPingParam // Parse the if-modified-since header and check if it is older than a day. lastModifiedTime, err := time.Parse(http.TimeFormat, ifModified) if err != nil { - zerolog.Ctx(ctx).Error().Err(err).Msg("failed to parse if-modified-since header") + log := logger.Get() + log.Error().Err(err).Msg("failed to parse if-modified-since header") return ErrBadRequest(err), nil } @@ -80,11 +81,12 @@ func (h *Handler) GetEventPing(ctx context.Context, params api.GetEventPingParam } func (h *Handler) PostEventHit(ctx context.Context, req api.EventHit, params api.PostEventHitParams) (api.PostEventHitRes, error) { + log := logger.Get() switch req.Type { case api.EventLoadEventHit: hostname := req.EventLoad.U.Hostname() pathname := req.EventLoad.U.Path - log := zerolog.Ctx(ctx).With().Str("hostname", hostname).Logger() + log = log.With().Str("hostname", hostname).Logger() // Verify hostname exists exists := h.hostnames.Has(hostname) @@ -206,7 +208,7 @@ func (h *Handler) PostEventHit(ctx context.Context, req api.EventHit, params api DurationMs: req.EventUnload.M, } - log := zerolog.Ctx(ctx).With(). + log = log.With(). Str("bid", event.BID). Str("event_type", string(req.Type)). Int("duration_ms", event.DurationMs). @@ -222,7 +224,7 @@ func (h *Handler) PostEventHit(ctx context.Context, req api.EventHit, params api log.Debug().Msg("hit: updated page view") default: - zerolog.Ctx(ctx).Error().Str("type", string(req.Type)).Msg("hit: invalid event hit type") + log.Error().Str("type", string(req.Type)).Msg("hit: invalid event hit type") return ErrBadRequest(model.ErrInvalidTrackerEvent), nil } diff --git a/core/services/stats.go b/core/services/stats.go index 8681bc38..1325f1d5 100644 --- a/core/services/stats.go +++ b/core/services/stats.go @@ -6,11 +6,11 @@ import ( "github.com/medama-io/medama/api" "github.com/medama-io/medama/db" "github.com/medama-io/medama/model" - "github.com/rs/zerolog" + "github.com/medama-io/medama/util/logger" ) func (h *Handler) GetWebsiteIDSummary(ctx context.Context, params api.GetWebsiteIDSummaryParams) (api.GetWebsiteIDSummaryRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -104,7 +104,7 @@ func (h *Handler) GetWebsiteIDSummary(ctx context.Context, params api.GetWebsite } func (h *Handler) GetWebsiteIDPages(ctx context.Context, params api.GetWebsiteIDPagesParams) (api.GetWebsiteIDPagesRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists. exists := h.hostnames.Has(params.Hostname) @@ -193,7 +193,7 @@ func (h *Handler) GetWebsiteIDPages(ctx context.Context, params api.GetWebsiteID } func (h *Handler) GetWebsiteIDTime(ctx context.Context, params api.GetWebsiteIDTimeParams) (api.GetWebsiteIDTimeRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -282,7 +282,7 @@ func (h *Handler) GetWebsiteIDTime(ctx context.Context, params api.GetWebsiteIDT } func (h *Handler) GetWebsiteIDReferrers(ctx context.Context, params api.GetWebsiteIDReferrersParams) (api.GetWebsiteIDReferrersRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -369,7 +369,7 @@ func (h *Handler) GetWebsiteIDReferrers(ctx context.Context, params api.GetWebsi } func (h *Handler) GetWebsiteIDSources(ctx context.Context, params api.GetWebsiteIDSourcesParams) (api.GetWebsiteIDSourcesRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -452,7 +452,7 @@ func (h *Handler) GetWebsiteIDSources(ctx context.Context, params api.GetWebsite } func (h *Handler) GetWebsiteIDMediums(ctx context.Context, params api.GetWebsiteIDMediumsParams) (api.GetWebsiteIDMediumsRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -538,7 +538,7 @@ func (h *Handler) GetWebsiteIDMediums(ctx context.Context, params api.GetWebsite } func (h *Handler) GetWebsiteIDCampaigns(ctx context.Context, params api.GetWebsiteIDCampaignsParams) (api.GetWebsiteIDCampaignsRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -624,7 +624,7 @@ func (h *Handler) GetWebsiteIDCampaigns(ctx context.Context, params api.GetWebsi } func (h *Handler) GetWebsiteIDBrowsers(ctx context.Context, params api.GetWebsiteIDBrowsersParams) (api.GetWebsiteIDBrowsersRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -710,7 +710,7 @@ func (h *Handler) GetWebsiteIDBrowsers(ctx context.Context, params api.GetWebsit } func (h *Handler) GetWebsiteIDOs(ctx context.Context, params api.GetWebsiteIDOsParams) (api.GetWebsiteIDOsRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -796,7 +796,7 @@ func (h *Handler) GetWebsiteIDOs(ctx context.Context, params api.GetWebsiteIDOsP } func (h *Handler) GetWebsiteIDDevice(ctx context.Context, params api.GetWebsiteIDDeviceParams) (api.GetWebsiteIDDeviceRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -882,7 +882,7 @@ func (h *Handler) GetWebsiteIDDevice(ctx context.Context, params api.GetWebsiteI } func (h *Handler) GetWebsiteIDLanguage(ctx context.Context, params api.GetWebsiteIDLanguageParams) (api.GetWebsiteIDLanguageRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) @@ -968,7 +968,7 @@ func (h *Handler) GetWebsiteIDLanguage(ctx context.Context, params api.GetWebsit } func (h *Handler) GetWebsiteIDCountry(ctx context.Context, params api.GetWebsiteIDCountryParams) (api.GetWebsiteIDCountryRes, error) { - log := zerolog.Ctx(ctx).With().Str("hostname", params.Hostname).Logger() + log := logger.Get().With().Str("hostname", params.Hostname).Logger() // Check if website exists exists := h.hostnames.Has(params.Hostname) diff --git a/core/services/users.go b/core/services/users.go index 8a6f7fbc..97037afb 100644 --- a/core/services/users.go +++ b/core/services/users.go @@ -7,7 +7,7 @@ import ( "github.com/go-faster/errors" "github.com/medama-io/medama/api" "github.com/medama-io/medama/model" - "github.com/rs/zerolog" + "github.com/medama-io/medama/util/logger" ) func (h *Handler) GetUser(ctx context.Context, params api.GetUserParams) (api.GetUserRes, error) { @@ -19,7 +19,7 @@ func (h *Handler) GetUser(ctx context.Context, params api.GetUserParams) (api.Ge user, err := h.db.GetUser(ctx, userId) if err != nil { - log := zerolog.Ctx(ctx).With().Err(err).Logger() + log := logger.Get().With().Err(err).Logger() if errors.Is(err, model.ErrUserNotFound) { log.Debug().Msg("user not found") @@ -39,6 +39,7 @@ func (h *Handler) GetUser(ctx context.Context, params api.GetUserParams) (api.Ge } func (h *Handler) PatchUser(ctx context.Context, req *api.UserPatch, params api.PatchUserParams) (api.PatchUserRes, error) { + log := logger.Get() // Get user id from request context and check if user exists userId, ok := ctx.Value(model.ContextKeyUserID).(string) if !ok { @@ -47,7 +48,7 @@ func (h *Handler) PatchUser(ctx context.Context, req *api.UserPatch, params api. user, err := h.db.GetUser(ctx, userId) if err != nil { - log := zerolog.Ctx(ctx).With().Err(err).Logger() + log := log.With().Err(err).Logger() if errors.Is(err, model.ErrUserNotFound) { log.Debug().Msg("user not found") @@ -65,7 +66,7 @@ func (h *Handler) PatchUser(ctx context.Context, req *api.UserPatch, params api. user.Username = username err = h.db.UpdateUserUsername(ctx, user.ID, username, dateUpdated) if err != nil { - log := zerolog.Ctx(ctx).With().Str("username", username).Err(err).Logger() + log := log.With().Str("username", username).Err(err).Logger() if errors.Is(err, model.ErrUserExists) { log.Debug().Msg("username already exists") @@ -86,13 +87,13 @@ func (h *Handler) PatchUser(ctx context.Context, req *api.UserPatch, params api. if password != "" { pwdHash, err := h.auth.HashPassword(password) if err != nil { - zerolog.Ctx(ctx).Error().Err(err).Msg("failed to hash password") + log.Error().Err(err).Msg("failed to hash password") return nil, errors.Wrap(err, "services") } err = h.db.UpdateUserPassword(ctx, user.ID, pwdHash, dateUpdated) if err != nil { - zerolog.Ctx(ctx).Error().Err(err).Msg("failed to update user password") + log.Error().Err(err).Msg("failed to update user password") return nil, errors.Wrap(err, "services") } } @@ -106,6 +107,7 @@ func (h *Handler) PatchUser(ctx context.Context, req *api.UserPatch, params api. } func (h *Handler) DeleteUser(ctx context.Context, params api.DeleteUserParams) (api.DeleteUserRes, error) { + log := logger.Get() // Get user id from request context and check if user exists userId, ok := ctx.Value(model.ContextKeyUserID).(string) if !ok { @@ -114,7 +116,7 @@ func (h *Handler) DeleteUser(ctx context.Context, params api.DeleteUserParams) ( user, err := h.db.GetUser(ctx, userId) if err != nil { - log := zerolog.Ctx(ctx).With().Err(err).Logger() + log = log.With().Err(err).Logger() if errors.Is(err, model.ErrUserNotFound) { log.Debug().Msg("user not found") @@ -127,7 +129,7 @@ func (h *Handler) DeleteUser(ctx context.Context, params api.DeleteUserParams) ( err = h.db.DeleteUser(ctx, user.ID) if err != nil { - zerolog.Ctx(ctx).Error(). + log.Error(). Str("username", user.Username). Str("language", user.Language). Int64("date_created", user.DateCreated). diff --git a/core/tests/e2e/__init__.py b/core/tests/e2e/__init__.py deleted file mode 100644 index 2a71bf30..00000000 --- a/core/tests/e2e/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import requests -import schemathesis -from schemathesis import DataGenerationMethod - -schema = schemathesis.from_uri( - "http://api:8080/openapi.yaml", - data_generation_methods=[ - DataGenerationMethod.positive, - DataGenerationMethod.negative, - ], - sanitize_output=False, -) -EXAMPLE_COUNT = os.getenv("SCHEMA_EXAMPLE_COUNT", default=100) - -# Authentication -AUTH_ENDPOINT = "http://api:8080/auth/login" -CREATE_ENDPOINT = "http://api:8080/user" -TEST_EMAIL = "test@e2e.com" -TEST_PASSWORD = "test1234" - - -class UserAuth: - def get(self, case, context): - response = requests.post( - CREATE_ENDPOINT, - json={"email": TEST_EMAIL, "password": TEST_PASSWORD}, - ) - - # User may already exist - if response.status_code == 409: - response = requests.post( - AUTH_ENDPOINT, - json={"email": TEST_EMAIL, "password": TEST_PASSWORD}, - ) - - token = response.headers.get("Set-Cookie") - return token - - if response.status_code != 201: - raise Exception("Failed to create user" + response.text) - - token = response.headers.get("Set-Cookie") - return token - - def set(self, case, data, context): - case.headers = case.headers or {} - case.headers["Cookie"] = data diff --git a/core/tests/e2e/test_auth.py b/core/tests/e2e/test_auth.py deleted file mode 100644 index f95d516b..00000000 --- a/core/tests/e2e/test_auth.py +++ /dev/null @@ -1,9 +0,0 @@ -from hypothesis import settings -from . import schema, EXAMPLE_COUNT, UserAuth - - -@schema.auth(UserAuth) -@schema.parametrize(operation_id="auth-login") -@settings(max_examples=EXAMPLE_COUNT) -def test_auth_login(case): - case.call_and_validate() diff --git a/core/tests/e2e/test_user.py b/core/tests/e2e/test_user.py deleted file mode 100644 index 50b999bf..00000000 --- a/core/tests/e2e/test_user.py +++ /dev/null @@ -1,34 +0,0 @@ -from hypothesis import settings -from schemathesis import DataGenerationMethod -from . import schema, EXAMPLE_COUNT, UserAuth - - -@schema.parametrize(operation_id="post-user") -@settings(max_examples=EXAMPLE_COUNT) -def test_post_user(case): - case.call_and_validate() - - -@schema.auth(UserAuth) -@schema.parametrize( - operation_id="get-user", data_generation_methods=[DataGenerationMethod.positive] -) # Not possible to do negative tests on this endpoint -@settings(max_examples=EXAMPLE_COUNT) -def test_get_user(case): - case.call_and_validate() - - -@schema.auth(UserAuth) -@schema.parametrize(operation_id="patch-user") -@settings(max_examples=EXAMPLE_COUNT) -def test_patch_user(case): - case.call_and_validate() - - -@schema.auth(UserAuth) -@schema.parametrize( - operation_id="delete-user", data_generation_methods=[DataGenerationMethod.positive] -) -@settings(max_examples=EXAMPLE_COUNT) -def test_delete_user(case): - case.call_and_validate() diff --git a/core/tests/e2e/test_websites.py b/core/tests/e2e/test_websites.py deleted file mode 100644 index 5db06878..00000000 --- a/core/tests/e2e/test_websites.py +++ /dev/null @@ -1,45 +0,0 @@ -from hypothesis import settings -from schemathesis import DataGenerationMethod -from . import schema, EXAMPLE_COUNT, UserAuth - - -## POST /websites -@schema.parametrize(operation_id="post-websites") -@settings(max_examples=EXAMPLE_COUNT) -def test_post_websites(case): - case.call_and_validate() - - -## GET /websites -@schema.auth(UserAuth) -@schema.parametrize( - operation_id="^get-websites$", - data_generation_methods=[DataGenerationMethod.positive], -) -@settings(max_examples=EXAMPLE_COUNT) -def test_get_websites(case): - case.call_and_validate() - - -## GET /websites/{hostname} -@schema.auth(UserAuth) -@schema.parametrize(operation_id="^get-websites-id$") -@settings(max_examples=EXAMPLE_COUNT) -def test_get_websites_id(case): - case.call_and_validate() - - -## PATCH /websites/{hostname} -@schema.auth(UserAuth) -@schema.parametrize(operation_id="patch-websites-id") -@settings(max_examples=EXAMPLE_COUNT) -def test_patch_websites_id(case): - case.call_and_validate() - - -# DELETE /websites/{hostname} -@schema.auth(UserAuth) -@schema.parametrize(operation_id="delete-websites-id") -@settings(max_examples=EXAMPLE_COUNT) -def test_delete_websites_id(case): - case.call_and_validate() diff --git a/core/util/logger.go b/core/util/logger.go deleted file mode 100644 index 6916e7a5..00000000 --- a/core/util/logger.go +++ /dev/null @@ -1,41 +0,0 @@ -package util - -import ( - "context" - "fmt" - "os" - - "github.com/rs/zerolog" -) - -// SetupLogger sets the default logger. -func SetupLogger(ctx context.Context, logger string, level string) (context.Context, error) { - log := zerolog.New(os.Stderr).With().Timestamp().Logger() - - switch logger { - case "json": - // Do nothing - case "pretty": - log = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) - default: - return nil, fmt.Errorf("invalid logger type \"%s\"", logger) - } - - switch level { - case "debug": - log = log.Level(zerolog.DebugLevel) - log.Debug().Msg("Logging level set to debug") - case "info": - log = log.Level(zerolog.InfoLevel) - case "warn": - log = log.Level(zerolog.WarnLevel) - log.Info().Msg("Logging level set to warn") - case "error": - log = log.Level(zerolog.ErrorLevel) - log.Info().Msg("Logging level set to error") - default: - return nil, fmt.Errorf("invalid log level \"%s\"", level) - } - - return log.WithContext(ctx), nil -} diff --git a/core/util/logger/logger.go b/core/util/logger/logger.go new file mode 100644 index 00000000..8fc737fe --- /dev/null +++ b/core/util/logger/logger.go @@ -0,0 +1,66 @@ +package logger + +import ( + "fmt" + "os" + "sync" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/pkgerrors" +) + +var ( + //nolint:gochecknoglobals // Global logger is required for the singleton pattern. + log zerolog.Logger + //nolint:gochecknoglobals // Global once is required for the singleton pattern. + once sync.Once + err error +) + +func Init(logger string, level string) (zerolog.Logger, error) { + once.Do(func() { + // Set the stack marshalling and time format for zerolog. + //nolint:reassign // Reassigning is the recommended way to set stack marshalling. + zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack + zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + log = zerolog.New(os.Stderr).With().Timestamp().Logger() + + // Configure the logger format. + switch logger { + case "json": + // Default JSON format, do nothing + case "pretty": + log = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + default: + err = fmt.Errorf("invalid logger type \"%s\"", logger) + return + } + + // Configure the log level. + switch level { + case "debug": + log = log.Level(zerolog.DebugLevel) + log.Debug().Msg("Logging level set to debug") + case "info": + log = log.Level(zerolog.InfoLevel) + case "warn": + log = log.Level(zerolog.WarnLevel) + log.Warn().Msg("Logging level set to warn") + case "error": + log = log.Level(zerolog.ErrorLevel) + log.Error().Msg("Logging level set to error") + default: + err = fmt.Errorf("invalid log level \"%s\"", level) + return + } + }) + + return log, err +} + +// Get initialises and returns a singleton zerolog.Logger instance. +// logger: The logger format, either "json" or "pretty". +// level: The log level, can be "debug", "info", "warn", or "error". +func Get() zerolog.Logger { + return log +} diff --git a/dashboard/.dockerignore b/dashboard/.dockerignore deleted file mode 100644 index 085ea174..00000000 --- a/dashboard/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -./cache/ -build/ -node_modules/ diff --git a/dashboard/Dockerfile b/dashboard/Dockerfile deleted file mode 100644 index b6eae504..00000000 --- a/dashboard/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -# syntax = docker/dockerfile:1 - -# Adjust NODE_VERSION as desired -FROM node:21-slim - -LABEL fly_launch_runtime="Remix" - -# Install pnpm -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable - -COPY . /dashboard -WORKDIR /dashboard - -# Install packages needed to build node modules -RUN pnpm install - -# Build dashboard -ENV NODE_ENV="production" -ENV API_URL="https://medama-core.fly.dev" -RUN pnpm run build - -# Start the server by default, this can be overwritten at runtime -EXPOSE 3000 -CMD [ "pnpm", "run", "start" ] diff --git a/dashboard/fly.toml b/dashboard/fly.toml deleted file mode 100644 index 5a7f75ee..00000000 --- a/dashboard/fly.toml +++ /dev/null @@ -1,23 +0,0 @@ -# fly.toml app configuration file generated for medama-core on 2024-01-24T16:58:35Z -# -# See https://fly.io/docs/reference/configuration/ for information about how to use this file. -# - -app = "medama-dashboard" -primary_region = "lhr" - -[env] -PORT = "3000" - -[http_service] -internal_port = 3000 -force_https = true -auto_stop_machines = true -auto_start_machines = true -min_machines_running = 0 -processes = ["app"] - -[[vm]] -cpu_kind = "shared" -cpus = 1 -memory_mb = 256