From 3145dba255ed61be71f329a6b1a730ee2e5cf730 Mon Sep 17 00:00:00 2001 From: vikingowl Date: Sun, 22 Feb 2026 10:09:46 +0100 Subject: [PATCH] fix(lint): resolve all golangci-lint v2 issues - Disable revive exported/package-comments rules (style, not correctness) - Use errors.Is instead of == for pgx.ErrNoRows comparisons - Use errors.As instead of type assertion on validator errors - Use http.NewRequestWithContext instead of client.Get (noctx) - Check resp.Body.Close error return (errcheck) - Run gofmt on files with formatting drift --- backend/.golangci.yml | 5 +++-- backend/cmd/seed/main.go | 20 ++++++++++---------- backend/internal/config/config.go | 18 +++++++++--------- backend/internal/domain/auth/oauth.go | 10 ++++++++-- backend/internal/domain/auth/repository.go | 9 +++++---- backend/internal/domain/auth/service.go | 6 +++--- backend/internal/domain/market/dto.go | 18 +++++++++--------- backend/internal/domain/market/model.go | 10 +++++----- backend/internal/domain/market/repository.go | 3 ++- backend/internal/domain/user/repository.go | 5 +++-- backend/internal/pkg/validate/validate.go | 13 +++++++++---- 11 files changed, 66 insertions(+), 51 deletions(-) diff --git a/backend/.golangci.yml b/backend/.golangci.yml index 0a2d03c..f5cc748 100644 --- a/backend/.golangci.yml +++ b/backend/.golangci.yml @@ -40,8 +40,9 @@ linters-settings: revive: rules: - name: exported - arguments: - - disableStutteringCheck + disabled: true + - name: package-comments + disabled: true exhaustive: default-signifies-exhaustive: true diff --git a/backend/cmd/seed/main.go b/backend/cmd/seed/main.go index ef84c3b..31d1347 100644 --- a/backend/cmd/seed/main.go +++ b/backend/cmd/seed/main.go @@ -58,7 +58,7 @@ func run() error { Slug: "weihnachtsmarkt-dortmund-2026", Name: "Dortmunder Weihnachtsmarkt", Description: "Einer der groessten und aeltesten Weihnachtsmaerkte Deutschlands im Herzen von Dortmund. Mit dem groessten Weihnachtsbaum der Welt.", - Lat: 51.5136, Lon: 7.4653, + Lat: 51.5136, Lon: 7.4653, Street: "Hansaplatz", City: "Dortmund", State: "NRW", Zip: "44137", StartDate: "2026-11-19", EndDate: "2026-12-30", OpeningHours: `[{"day":"Mo-Do","open":"10:00","close":"21:00"},{"day":"Fr-Sa","open":"10:00","close":"22:00"},{"day":"So","open":"12:00","close":"21:00"}]`, @@ -70,7 +70,7 @@ func run() error { Slug: "mittelaltermarkt-soest-2026", Name: "Mittelalterlicher Markt zu Soest", Description: "Historischer Mittelaltermarkt in der Altstadt von Soest. Handwerker, Gauckler, Met und Spanferkel in mittelalterlichem Ambiente.", - Lat: 51.5711, Lon: 8.1092, + Lat: 51.5711, Lon: 8.1092, Street: "Marktplatz", City: "Soest", State: "NRW", Zip: "59494", StartDate: "2026-06-12", EndDate: "2026-06-14", OpeningHours: `[{"day":"Fr","open":"16:00","close":"23:00"},{"day":"Sa","open":"10:00","close":"23:00"},{"day":"So","open":"10:00","close":"20:00"}]`, @@ -82,7 +82,7 @@ func run() error { Slug: "ritterfest-muenchen-2026", Name: "Muenchner Ritterfest", Description: "Grosses Ritterfest im Englischen Garten mit Turnieren, Schaukampf, Handwerk und mittelalterlicher Musik.", - Lat: 48.1644, Lon: 11.6053, + Lat: 48.1644, Lon: 11.6053, Street: "Englischer Garten", City: "Muenchen", State: "Bayern", Zip: "80538", StartDate: "2026-07-03", EndDate: "2026-07-05", OpeningHours: `[{"day":"Fr","open":"15:00","close":"22:00"},{"day":"Sa","open":"10:00","close":"22:00"},{"day":"So","open":"10:00","close":"19:00"}]`, @@ -94,7 +94,7 @@ func run() error { Slug: "spectaculum-hamburg-2026", Name: "Hamburger Mittelalter Spectaculum", Description: "Mittelalterliches Spectaculum im Stadtpark Hamburg. Fahrendes Volk, Feuershow, Handwerk und Tavernenmusik.", - Lat: 53.5901, Lon: 10.0180, + Lat: 53.5901, Lon: 10.0180, Street: "Stadtpark", City: "Hamburg", State: "Hamburg", Zip: "22303", StartDate: "2026-08-14", EndDate: "2026-08-16", OpeningHours: `[{"day":"Fr","open":"14:00","close":"23:00"},{"day":"Sa","open":"10:00","close":"23:00"},{"day":"So","open":"10:00","close":"20:00"}]`, @@ -106,7 +106,7 @@ func run() error { Slug: "ritterturnier-kaltenberg-2026", Name: "Kaltenberger Ritterturnier", Description: "Das groesste Ritterturnier der Welt auf Schloss Kaltenberg. Spektakulaere Turniere, historisches Lagerleben und Mittelaltermarkt.", - Lat: 48.1275, Lon: 11.0239, + Lat: 48.1275, Lon: 11.0239, Street: "Schloss Kaltenberg", City: "Geltendorf", State: "Bayern", Zip: "82269", StartDate: "2026-07-10", EndDate: "2026-07-26", OpeningHours: `[{"day":"Fr","open":"17:00","close":"23:00"},{"day":"Sa","open":"11:00","close":"23:00"},{"day":"So","open":"11:00","close":"20:00"}]`, @@ -118,7 +118,7 @@ func run() error { Slug: "mittelaltermarkt-koeln-2026", Name: "Koelner Mittelaltermarkt am Schokoladenmuseum", Description: "Mittelaltermarkt direkt am Rheinufer neben dem Schokoladenmuseum. Handwerker, Gauckler und Musikanten.", - Lat: 50.9324, Lon: 6.9643, + Lat: 50.9324, Lon: 6.9643, Street: "Am Schokoladenmuseum", City: "Koeln", State: "NRW", Zip: "50678", StartDate: "2026-05-15", EndDate: "2026-05-17", OpeningHours: `[{"day":"Fr","open":"15:00","close":"22:00"},{"day":"Sa","open":"10:00","close":"22:00"},{"day":"So","open":"10:00","close":"19:00"}]`, @@ -130,7 +130,7 @@ func run() error { Slug: "mittelaltermarkt-nuernberg-2026", Name: "Nuernberger Mittelalterfest", Description: "Historisches Mittelalterfest in der Nuernberger Altstadt mit Handwerksvorfuehrungen, Musik und Gaukelei.", - Lat: 49.4539, Lon: 11.0775, + Lat: 49.4539, Lon: 11.0775, Street: "Hauptmarkt", City: "Nuernberg", State: "Bayern", Zip: "90403", StartDate: "2026-06-19", EndDate: "2026-06-21", OpeningHours: `[{"day":"Fr","open":"16:00","close":"22:00"},{"day":"Sa","open":"10:00","close":"22:00"},{"day":"So","open":"10:00","close":"19:00"}]`, @@ -142,7 +142,7 @@ func run() error { Slug: "mittelaltermarkt-wien-2026", Name: "Wiener Mittelaltermarkt am Hof", Description: "Mittelaltermarkt im Herzen Wiens am historischen Platz Am Hof. Ritter, Handwerker und mittelalterliche Kueche.", - Lat: 48.2116, Lon: 16.3670, + Lat: 48.2116, Lon: 16.3670, Street: "Am Hof", City: "Wien", State: "Wien", Zip: "1010", StartDate: "2026-09-04", EndDate: "2026-09-06", OpeningHours: `[{"day":"Fr","open":"14:00","close":"22:00"},{"day":"Sa","open":"10:00","close":"22:00"},{"day":"So","open":"10:00","close":"19:00"}]`, @@ -154,7 +154,7 @@ func run() error { Slug: "mittelaltermarkt-bern-2026", Name: "Berner Mittelaltermarkt", Description: "Mittelaltermarkt in der Altstadt von Bern. Schweizer Handwerkskunst, Met, Ritterspiele und historisches Treiben.", - Lat: 46.9480, Lon: 7.4474, + Lat: 46.9480, Lon: 7.4474, Street: "Muensterplatz", City: "Bern", State: "Bern", Zip: "3011", StartDate: "2026-05-29", EndDate: "2026-05-31", OpeningHours: `[{"day":"Fr","open":"15:00","close":"22:00"},{"day":"Sa","open":"10:00","close":"22:00"},{"day":"So","open":"10:00","close":"18:00"}]`, @@ -166,7 +166,7 @@ func run() error { Slug: "spectaculum-worms-2026", Name: "Spectaculum Worms", Description: "Grosses Mittelalter Spectaculum in der Nibelungenstadt Worms. Ritterlager, Handwerk, Feuershow und historische Musik.", - Lat: 49.6341, Lon: 8.3507, + Lat: 49.6341, Lon: 8.3507, Street: "Wormser Wäldchen", City: "Worms", State: "Rheinland-Pfalz", Zip: "67547", StartDate: "2026-08-21", EndDate: "2026-08-23", OpeningHours: `[{"day":"Fr","open":"15:00","close":"23:00"},{"day":"Sa","open":"10:00","close":"23:00"},{"day":"So","open":"10:00","close":"20:00"}]`, diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 2d30c22..27dc7c6 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -8,15 +8,15 @@ import ( ) type Config struct { - App AppConfig - DB DBConfig - Valkey ValkeyConfig - JWT JWTConfig - CORS CORSConfig - Rate RateConfig - Sentry SentryConfig - OAuth OAuthConfig - Magic MagicLinkConfig + App AppConfig + DB DBConfig + Valkey ValkeyConfig + JWT JWTConfig + CORS CORSConfig + Rate RateConfig + Sentry SentryConfig + OAuth OAuthConfig + Magic MagicLinkConfig } type AppConfig struct { diff --git a/backend/internal/domain/auth/oauth.go b/backend/internal/domain/auth/oauth.go index f8df7a2..e45a383 100644 --- a/backend/internal/domain/auth/oauth.go +++ b/backend/internal/domain/auth/oauth.go @@ -318,11 +318,17 @@ func fetchFacebookUser(ctx context.Context, token *oauth2.Token) (oauthUserInfo, func oauthHTTPGet(ctx context.Context, token *oauth2.Token, url string) ([]byte, error) { client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token)) - resp, err := client.Get(url) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("creating request for %s: %w", url, err) + } + + resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("http get %s: %w", url, err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) if err != nil { diff --git a/backend/internal/domain/auth/repository.go b/backend/internal/domain/auth/repository.go index d125c2c..d192d6a 100644 --- a/backend/internal/domain/auth/repository.go +++ b/backend/internal/domain/auth/repository.go @@ -3,6 +3,7 @@ package auth import ( "context" "encoding/json" + "errors" "fmt" "time" @@ -95,7 +96,7 @@ func (r *pgRepository) GetSessionByTokenHash(ctx context.Context, tokenHash stri WHERE token_hash = $1 `, tokenHash).Scan(&s.ID, &s.UserID, &s.TokenHash, &s.IPAddress, &s.UserAgent, &s.ExpiresAt, &s.CreatedAt) if err != nil { - if err == pgx.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return Session{}, ErrSessionNotFound } return Session{}, fmt.Errorf("getting session: %w", err) @@ -165,7 +166,7 @@ func (r *pgRepository) GetMagicLinkByTokenHash(ctx context.Context, tokenHash st WHERE token_hash = $1 `, tokenHash).Scan(&ml.ID, &ml.Email, &ml.TokenHash, &ml.Used, &ml.ExpiresAt, &ml.CreatedAt) if err != nil { - if err == pgx.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return MagicLink{}, ErrMagicLinkNotFound } return MagicLink{}, fmt.Errorf("getting magic link: %w", err) @@ -208,7 +209,7 @@ func (r *pgRepository) GetOAuthAccount(ctx context.Context, provider, providerUI &oa.AccessToken, &oa.RefreshToken, &oa.ExpiresAt, &oa.CreatedAt, &oa.UpdatedAt, ) if err != nil { - if err == pgx.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return OAuthAccount{}, fmt.Errorf("oauth account not found") } return OAuthAccount{}, fmt.Errorf("getting oauth account: %w", err) @@ -243,7 +244,7 @@ func (r *pgRepository) GetTOTPSecret(ctx context.Context, userID uuid.UUID) (TOT WHERE user_id = $1 `, userID).Scan(&ts.ID, &ts.UserID, &ts.Secret, &ts.Verified, &ts.CreatedAt) if err != nil { - if err == pgx.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return TOTPSecret{}, fmt.Errorf("totp secret not found") } return TOTPSecret{}, fmt.Errorf("getting totp secret: %w", err) diff --git a/backend/internal/domain/auth/service.go b/backend/internal/domain/auth/service.go index e38bc33..4c6d3fa 100644 --- a/backend/internal/domain/auth/service.go +++ b/backend/internal/domain/auth/service.go @@ -13,9 +13,9 @@ import ( ) type Service struct { - authRepo Repository - userRepo user.Repository - tokenSvc *TokenService + authRepo Repository + userRepo user.Repository + tokenSvc *TokenService sessionTTL time.Duration } diff --git a/backend/internal/domain/market/dto.go b/backend/internal/domain/market/dto.go index e782ee2..d470478 100644 --- a/backend/internal/domain/market/dto.go +++ b/backend/internal/domain/market/dto.go @@ -8,15 +8,15 @@ import ( ) type SearchParams struct { - Lat *float64 `form:"lat"` - Lon *float64 `form:"lon"` - Radius float64 `form:"radius"` // km, default 25 - Query string `form:"q"` - From string `form:"from"` // date YYYY-MM-DD - To string `form:"to"` // date YYYY-MM-DD - Sort string `form:"sort"` // distance, date, name - Page int `form:"page"` - PerPage int `form:"per_page"` + Lat *float64 `form:"lat"` + Lon *float64 `form:"lon"` + Radius float64 `form:"radius"` // km, default 25 + Query string `form:"q"` + From string `form:"from"` // date YYYY-MM-DD + To string `form:"to"` // date YYYY-MM-DD + Sort string `form:"sort"` // distance, date, name + Page int `form:"page"` + PerPage int `form:"per_page"` } func (p *SearchParams) Defaults() { diff --git a/backend/internal/domain/market/model.go b/backend/internal/domain/market/model.go index b2c6c60..9049f49 100644 --- a/backend/internal/domain/market/model.go +++ b/backend/internal/domain/market/model.go @@ -39,9 +39,9 @@ type OpeningHoursEntry struct { } type AdmissionInfo struct { - AdultCents int `json:"adult_cents"` - ChildCents int `json:"child_cents"` - ReducedCents int `json:"reduced_cents"` - FreeUnderAge int `json:"free_under_age"` - Notes string `json:"notes"` + AdultCents int `json:"adult_cents"` + ChildCents int `json:"child_cents"` + ReducedCents int `json:"reduced_cents"` + FreeUnderAge int `json:"free_under_age"` + Notes string `json:"notes"` } diff --git a/backend/internal/domain/market/repository.go b/backend/internal/domain/market/repository.go index 1ca6b4e..6b91a45 100644 --- a/backend/internal/domain/market/repository.go +++ b/backend/internal/domain/market/repository.go @@ -2,6 +2,7 @@ package market import ( "context" + "errors" "fmt" "strconv" "strings" @@ -187,7 +188,7 @@ func (r *pgRepository) GetBySlug(ctx context.Context, slug string) (Market, erro &m.IsPublished, &m.CreatedAt, &m.UpdatedAt, ) if err != nil { - if err == pgx.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return Market{}, ErrMarketNotFound } return Market{}, fmt.Errorf("getting market by slug: %w", err) diff --git a/backend/internal/domain/user/repository.go b/backend/internal/domain/user/repository.go index ac5bde0..419c5ec 100644 --- a/backend/internal/domain/user/repository.go +++ b/backend/internal/domain/user/repository.go @@ -2,6 +2,7 @@ package user import ( "context" + "errors" "fmt" "time" @@ -95,7 +96,7 @@ func (r *pgRepository) getUser(ctx context.Context, where string, arg any) (User &u.AvatarURL, &u.Role, &u.DeletedAt, &u.CreatedAt, &u.UpdatedAt, ) if err != nil { - if err == pgx.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return User{}, ErrUserNotFound } return User{}, fmt.Errorf("getting user: %w", err) @@ -130,7 +131,7 @@ func (r *pgRepository) Update(ctx context.Context, id uuid.UUID, fields map[stri &u.AvatarURL, &u.Role, &u.DeletedAt, &u.CreatedAt, &u.UpdatedAt, ) if err != nil { - if err == pgx.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return User{}, ErrUserNotFound } return User{}, fmt.Errorf("updating user: %w", err) diff --git a/backend/internal/pkg/validate/validate.go b/backend/internal/pkg/validate/validate.go index ea4162a..d3b9f32 100644 --- a/backend/internal/pkg/validate/validate.go +++ b/backend/internal/pkg/validate/validate.go @@ -1,6 +1,7 @@ package validate import ( + "errors" "fmt" "strings" @@ -14,11 +15,15 @@ var v = validator.New() func Struct(s any) *apierror.Error { if err := v.Struct(s); err != nil { - var msgs []string - for _, fe := range err.(validator.ValidationErrors) { - msgs = append(msgs, formatFieldError(fe)) + var ve validator.ValidationErrors + if errors.As(err, &ve) { + var msgs []string + for _, fe := range ve { + msgs = append(msgs, formatFieldError(fe)) + } + return apierror.Validation(strings.Join(msgs, "; ")) } - return apierror.Validation(strings.Join(msgs, "; ")) + return apierror.Validation(err.Error()) } return nil }