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
This commit is contained in:
2026-02-22 10:09:46 +01:00
parent dd5366f931
commit 3145dba255
11 changed files with 66 additions and 51 deletions

View File

@@ -40,8 +40,9 @@ linters-settings:
revive:
rules:
- name: exported
arguments:
- disableStutteringCheck
disabled: true
- name: package-comments
disabled: true
exhaustive:
default-signifies-exhaustive: true

View File

@@ -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"}]`,

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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() {

View File

@@ -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"`
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
}