From bbbffce20db6d5a6d2fc566a8aa5371530585aca Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Sat, 11 Jul 2026 18:03:05 +0100 Subject: [PATCH] fix: gocritic linter issues (appendAssign, ifElseChain, regexpMust) --- backend/handlers/bookings/admin_reserve.go | 3 ++- backend/handlers/bookings/manage.go | 3 ++- backend/handlers/portfolio/images.go | 26 +++++++++++----------- backend/internal/jobs/scheduler.go | 7 +++--- backend/mw/ratelimit.go | 9 ++++---- 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/backend/handlers/bookings/admin_reserve.go b/backend/handlers/bookings/admin_reserve.go index b184942..60abfcb 100644 --- a/backend/handlers/bookings/admin_reserve.go +++ b/backend/handlers/bookings/admin_reserve.go @@ -88,7 +88,8 @@ func AdminReserveSlotHandler(w http.ResponseWriter, r *http.Request) { return } var err error - allIDs := append(req.ServiceIDs, req.CustomServiceIDs...) + allIDs := append([]string{}, req.ServiceIDs...) + allIDs = append(allIDs, req.CustomServiceIDs...) svcDuration, err = calculateServiceDurationWithOverrides(r.Context(), allIDs, req.ServiceOverrides) if err != nil { log.Printf("Failed to calculate duration: %v", err) diff --git a/backend/handlers/bookings/manage.go b/backend/handlers/bookings/manage.go index f9a4133..a774b99 100644 --- a/backend/handlers/bookings/manage.go +++ b/backend/handlers/bookings/manage.go @@ -646,7 +646,8 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) { } // Check for overlapping confirmed/in_progress/completed bookings - allIDs := append(req.ServiceIDs, req.CustomServiceIDs...) + allIDs := append([]string{}, req.ServiceIDs...) + allIDs = append(allIDs, req.CustomServiceIDs...) var dur int err := db.Conn.QueryRow(r.Context(), ` SELECT COALESCE(SUM(dur), 0) FROM ( diff --git a/backend/handlers/portfolio/images.go b/backend/handlers/portfolio/images.go index c404794..bbbc3f3 100644 --- a/backend/handlers/portfolio/images.go +++ b/backend/handlers/portfolio/images.go @@ -30,6 +30,9 @@ import ( const MaxInputLength = 256 +var filterRegexp = regexp.MustCompile(`^filter\[(.+)\]$`) +var timestampRegexp = regexp.MustCompile(`^\d{15,20}$`) + func mimeTypeForField(fieldName string) string { switch fieldName { case "file_full_avif", "file_thumb_avif": @@ -173,11 +176,9 @@ func ListImages(w http.ResponseWriter, r *http.Request) { if len(values) == 0 || values[0] == "" { continue } - match, _ := regexp.Compile(`^filter\[(.+)\]$`) - if match != nil { - matches := match.FindStringSubmatch(key) - if len(matches) == 2 { - category := matches[1] + match := filterRegexp.FindStringSubmatch(key) + if len(match) == 2 { + category := match[1] value := values[0] // Validate category exists @@ -193,17 +194,17 @@ func ListImages(w http.ResponseWriter, r *http.Request) { } filterClauses.WriteString(fmt.Sprintf(" AND $%d::text = ANY(tag_names)", len(filterArgs)+1)) - filterArgs = append(filterArgs, category+":"+value) - } + filterArgs = append(filterArgs, category+":"+value) } } - + var query string var args []any const formatCols = `, full_avif_url, full_webp_url, full_jpg_url, full_jxl_url, thumb_avif_url, thumb_webp_url, thumb_jpg_url` - if tagsFilter != "" { + switch { + case tagsFilter != "": tagList := strings.Split(tagsFilter, ",") cleanTags := make([]string, len(tagList)) for i, t := range tagList { @@ -255,7 +256,7 @@ func ListImages(w http.ResponseWriter, r *http.Request) { } queryArgs[len(filterArgs)+len(cleanTags)+len(cursorArgs)] = limit args = queryArgs - } else if tagFilter != "" { + case tagFilter != "": argOffset := len(filterArgs) searchPattern := "%" + tagFilter + "%" searchIdx := argOffset + 1 @@ -289,7 +290,7 @@ func ListImages(w http.ResponseWriter, r *http.Request) { } queryArgs[searchIdx+len(cursorArgs)] = limit args = queryArgs - } else { + default: argOffset := len(filterArgs) query = fmt.Sprintf(` SELECT id, url, thumbnail_url, tag_names, created_at%s, 0 as match_count, 0.0 as relevance @@ -1098,8 +1099,7 @@ func GetImage(w http.ResponseWriter, r *http.Request) { // Lookup by timestamp (nanosecond Unix epoch from URL) // Only allow numeric timestamps to prevent pattern enumeration - timestampMatch, _ := regexp.Compile(`^\d{15,20}$`) - if !timestampMatch.MatchString(imageID) { + if !timestampRegexp.MatchString(imageID) { log.Printf("Invalid image ID format: %s", imageID) http.Error(w, "Image not found", http.StatusNotFound) return diff --git a/backend/internal/jobs/scheduler.go b/backend/internal/jobs/scheduler.go index 18ee99b..a07ae74 100644 --- a/backend/internal/jobs/scheduler.go +++ b/backend/internal/jobs/scheduler.go @@ -189,11 +189,12 @@ func (s *Scheduler) wrapJob(job Job) func() { } n, err := job.Handler(ctx) - if err != nil { + switch { + case err != nil: log.Printf("%s[ERROR]%s %s \"%sJOB %s%s%s\" %sfailed%s: %v in %s", colorRed, colorReset, jidStr, colorBoldMagenta, colorRed, job.Name, colorReset, colorDim, colorReset, err, coloredDuration(time.Since(start))) - } else if n > 0 { + case n > 0: log.Printf("%s[INFO]%s %s \"%sJOB %s%s%s\" %scompleted%s: %d %s in %s", colorGreen, colorReset, jidStr, colorBoldMagenta, colorGreen, job.Name, colorReset, colorDim, colorReset, n, coloredRows(n), coloredDuration(time.Since(start))) - } else { + default: log.Printf("%s[DEBUG]%s %s \"%sJOB %s%s%s\" %scompleted%s: %s in %s", colorCyan, colorReset, jidStr, colorBoldMagenta, colorGreen, job.Name, colorReset, colorDim, colorReset, coloredRows(0), coloredDuration(time.Since(start))) } } diff --git a/backend/mw/ratelimit.go b/backend/mw/ratelimit.go index daa48e8..dd64b53 100644 --- a/backend/mw/ratelimit.go +++ b/backend/mw/ratelimit.go @@ -89,13 +89,14 @@ func (prl *ProgressiveRateLimiter) Check(ip string) (delayMs int) { // Progressive delay based on how far over the sustained limit they are // Rate = requests per minute - if sustainedCount <= 140 { + switch { + case sustainedCount <= 140: return 500 // 500ms - scraping but not too aggressively - } else if sustainedCount <= 200 { + case sustainedCount <= 200: return 2000 // 2s - moderate spam - } else if sustainedCount <= 300 { + case sustainedCount <= 300: return 5000 // 5s - heavy spam - } else { + default: return 10000 // 10s - abuse } }