fix: add golangci config, restore CI timeouts, svelte-check warnings, RespondError helper
CI / Env docs check (push) Successful in 13s
CI / Nginx config check (push) Successful in 36s
CI / Frontend major deps (push) Successful in 37s
CI / Docker compose check (push) Successful in 38s
CI / Frontend deps check (push) Successful in 41s
CI / Secrets scan (push) Successful in 42s
CI / Go build (push) Successful in 42s
CI / Frontend build (push) Successful in 49s
CI / go mod tidy (push) Successful in 26s
CI / Go vet (dev) (push) Has been cancelled
CI / Go vet (prod) (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
CI / Staticcheck (dev) (push) Has been cancelled
CI / Staticcheck (prod) (push) Has been cancelled
CI / Security scan (dev) (push) Has been cancelled
CI / Security scan (prod) (push) Has been cancelled
CI / Tests (prod) (push) Has been cancelled
CI / Tests (dev) (push) Has been cancelled
CI / Race (prod) (push) Has been cancelled
CI / Race (dev) (push) Has been cancelled
CI / Go vulnerabilities (push) Has been cancelled
CI / Knip (push) Has been cancelled
CI / Frontend a11y check (push) Has been cancelled
CI / Svelte strict check (push) Has been cancelled
CI / Frontend QC (audit) (push) Has been cancelled
CI / Frontend QC (typecheck) (push) Has been cancelled
CI / Frontend QC (lint) (push) Has been cancelled

This commit is contained in:
2026-07-11 17:38:50 +01:00
parent b864873310
commit 8ae592d0c5
5 changed files with 120 additions and 52 deletions
+38
View File
@@ -0,0 +1,38 @@
linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gosec
- gocritic
disable:
- exhaustruct
- nlreturn
- wsl
- wrapcheck
linters-settings:
errcheck:
exclude-functions:
- encoding/json.Encoder.Encode
- io.WriteString
- (io.Closer).Close
issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck
- gosec
- path: handlers/testutils/
linters:
- gosec
- path: internal/square/square_dev.go
linters:
- errcheck
run:
timeout: 5m
+8 -8
View File
@@ -86,7 +86,7 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
// Must accept terms
if !req.AgreedToPolicy {
http.Error(w, "must agree to terms", http.StatusBadRequest)
mw.RespondError(w, http.StatusBadRequest, "must agree to terms")
return
}
@@ -105,11 +105,11 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
// Password must not exceed bcrypt's 72-byte limit
if len(req.Password) > 72 {
http.Error(w, "password must be 72 characters or less", http.StatusBadRequest)
mw.RespondError(w, http.StatusBadRequest, "password must be 72 characters or less")
return
}
if len(req.Password) < 6 {
http.Error(w, "password must be at least 6 characters", http.StatusBadRequest)
mw.RespondError(w, http.StatusBadRequest, "password must be at least 6 characters")
return
}
// Server-side password strength check using the same @zxcvbn-ts/core as the frontend
@@ -353,13 +353,13 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
loginStateMu.Lock()
if t, ok := loginInProgress[userID]; ok && time.Since(t) < 30*time.Second {
loginStateMu.Unlock()
http.Error(w, "login already in progress", http.StatusConflict) // 409
mw.RespondError(w, http.StatusConflict, "login already in progress")
return
}
// Cap the map size - drop new request if at capacity
if len(loginInProgress) >= maxLoginInProgress {
loginStateMu.Unlock()
http.Error(w, "server busy, try again later", http.StatusTooManyRequests)
mw.RespondError(w, http.StatusTooManyRequests, "server busy, try again later")
return
}
loginInProgress[userID] = clock.Now()
@@ -429,7 +429,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
tx, err := db.Conn.Begin(r.Context())
if err != nil {
log.Printf("Failed to begin transaction: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
mw.RespondError(w, http.StatusInternalServerError, "internal server error")
return
}
defer func() {
@@ -497,7 +497,7 @@ func RefreshTokenHandler(w http.ResponseWriter, r *http.Request) {
// Generate new token
newToken, jti, err := auth.GenerateToken(userID, currentRole)
if err != nil {
http.Error(w, "could not generate token", http.StatusInternalServerError)
mw.RespondError(w, http.StatusInternalServerError, "could not generate token")
return
}
@@ -518,7 +518,7 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) {
// Revoke the JTI — match the access token lifetime (1 hour)
if err := auth.RevokeJTI(r.Context(), jti, clock.Now().Add(1*time.Hour)); err != nil {
slog.Error("logout: failed to revoke JTI", "err", err)
http.Error(w, "Failed to revoke token. Please try again.", http.StatusInternalServerError)
mw.RespondError(w, http.StatusInternalServerError, "failed to revoke token. please try again.")
return
}
+8
View File
@@ -14,3 +14,11 @@ func RespondJSON(w http.ResponseWriter, status int, data any) {
_ = json.NewEncoder(w).Encode(data)
}
// RespondError sends a JSON error response with the given status code and message.
// It preserves the application/json Content-Type set by middleware.
func RespondError(w http.ResponseWriter, status int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(map[string]string{"error": msg})
}