fix: audit nolint:errcheck — fix dangerous suppression patterns
CI / Nginx config check (push) Successful in 14s
CI / Docker compose check (push) Successful in 14s
CI / Env docs check (push) Successful in 15s
CI / Frontend major deps (push) Successful in 25s
CI / Frontend deps check (push) Successful in 26s
CI / Secrets scan (push) Successful in 36s
CI / Go build (push) Successful in 42s
CI / Frontend build (push) Successful in 48s
CI / Knip (push) Successful in 28s
CI / Frontend a11y check (push) Successful in 1m26s
CI / Go vet (prod) (push) Failing after 2m9s
CI / go mod tidy (push) Successful in 59s
CI / Go vet (dev) (push) Failing after 2m10s
CI / Staticcheck (prod) (push) Failing after 2m42s
CI / Staticcheck (dev) (push) Failing after 2m56s
CI / Frontend QC (audit) (push) Successful in 1m7s
CI / golangci-lint (push) Successful in 3m38s
CI / Go vulnerabilities (push) Successful in 1m40s
CI / Frontend QC (typecheck) (push) Successful in 1m45s
CI / Frontend QC (lint) (push) Failing after 1m21s
CI / Svelte strict check (push) Has been skipped
CI / Security scan (prod) (push) Successful in 4m18s
CI / Security scan (dev) (push) Successful in 4m28s
CI / Tests (prod) (push) Has been skipped
CI / Tests (dev) (push) Has been skipped
CI / Race (prod) (push) Has been skipped
CI / Race (dev) (push) Has been skipped
CI / Nginx config check (push) Successful in 14s
CI / Docker compose check (push) Successful in 14s
CI / Env docs check (push) Successful in 15s
CI / Frontend major deps (push) Successful in 25s
CI / Frontend deps check (push) Successful in 26s
CI / Secrets scan (push) Successful in 36s
CI / Go build (push) Successful in 42s
CI / Frontend build (push) Successful in 48s
CI / Knip (push) Successful in 28s
CI / Frontend a11y check (push) Successful in 1m26s
CI / Go vet (prod) (push) Failing after 2m9s
CI / go mod tidy (push) Successful in 59s
CI / Go vet (dev) (push) Failing after 2m10s
CI / Staticcheck (prod) (push) Failing after 2m42s
CI / Staticcheck (dev) (push) Failing after 2m56s
CI / Frontend QC (audit) (push) Successful in 1m7s
CI / golangci-lint (push) Successful in 3m38s
CI / Go vulnerabilities (push) Successful in 1m40s
CI / Frontend QC (typecheck) (push) Successful in 1m45s
CI / Frontend QC (lint) (push) Failing after 1m21s
CI / Svelte strict check (push) Has been skipped
CI / Security scan (prod) (push) Successful in 4m18s
CI / Security scan (dev) (push) Successful in 4m28s
CI / Tests (prod) (push) Has been skipped
CI / Tests (dev) (push) Has been skipped
CI / Race (prod) (push) Has been skipped
CI / Race (dev) (push) Has been skipped
This commit is contained in:
@@ -2698,8 +2698,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Don't award a stamp if this booking already used a loyalty redemption
|
||||
// (take or receive, never both).
|
||||
var loyaltyAppliedOnThisBooking bool
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'loyalty')`, bookingID).Scan(&loyaltyAppliedOnThisBooking)
|
||||
if err := tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'loyalty')`, bookingID).Scan(&loyaltyAppliedOnThisBooking); err != nil {
|
||||
log.Printf("Failed to check loyalty applied on booking %s: %v", bookingID, err)
|
||||
}
|
||||
|
||||
var newStampCount int
|
||||
if bookingTotal > 0 && !loyaltyAppliedOnThisBooking {
|
||||
@@ -2735,8 +2736,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Skip time-based campaign if already applied at payment time
|
||||
var timeBasedApplied bool
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'time_based')`, bookingID).Scan(&timeBasedApplied)
|
||||
if err := tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'time_based')`, bookingID).Scan(&timeBasedApplied); err != nil {
|
||||
log.Printf("Failed to check time-based campaign applied on booking %s: %v", bookingID, err)
|
||||
}
|
||||
if bookingTotal > 0 && !timeBasedApplied {
|
||||
var campaignID string
|
||||
var campaignPercent float64
|
||||
@@ -2778,13 +2780,14 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var milestoneCampaignID string
|
||||
var milestonePercent float64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `
|
||||
if err := tx.QueryRow(r.Context(), `
|
||||
SELECT id, discount_percent FROM discount_campaigns
|
||||
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'per_user_booking_count'
|
||||
AND milestone_value = $1
|
||||
AND NOT EXISTS (SELECT 1 FROM booking_discounts WHERE user_id = $2 AND source_id = discount_campaigns.id)
|
||||
`, userBookingCount, booking.User.ID).Scan(&milestoneCampaignID, &milestonePercent)
|
||||
`, userBookingCount, booking.User.ID).Scan(&milestoneCampaignID, &milestonePercent); err != nil {
|
||||
log.Printf("Failed to query per-user milestone campaign for booking %s: %v", bookingID, err)
|
||||
}
|
||||
|
||||
if milestoneCampaignID != "" {
|
||||
discountAmount := roundTo2(bookingTotal * milestonePercent / 100)
|
||||
@@ -2808,31 +2811,34 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var globalMilestoneApplied bool
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count')`, bookingID).Scan(&globalMilestoneApplied)
|
||||
if err := tx.QueryRow(r.Context(), `SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count')`, bookingID).Scan(&globalMilestoneApplied); err != nil {
|
||||
log.Printf("Failed to check global milestone applied on booking %s: %v", bookingID, err)
|
||||
}
|
||||
if !globalMilestoneApplied {
|
||||
var globalCount int
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount)
|
||||
|
||||
var hasInPersonPayment bool
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `
|
||||
SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND payment_method = 'in_person_card')`, bookingID).Scan(&hasInPersonPayment)
|
||||
var hasInPersonPayment bool
|
||||
if err := tx.QueryRow(r.Context(), `
|
||||
SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND payment_method = 'in_person_card')`, bookingID).Scan(&hasInPersonPayment); err != nil {
|
||||
log.Printf("Failed to check in-person payment on booking %s: %v", bookingID, err)
|
||||
}
|
||||
|
||||
if hasInPersonPayment {
|
||||
var globalCampaignID string
|
||||
var globalPercent float64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `
|
||||
SELECT id, discount_percent FROM discount_campaigns
|
||||
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count'
|
||||
AND milestone_value <= $1
|
||||
AND (max_redemptions IS NULL OR times_redeemed < max_redemptions)
|
||||
ORDER BY milestone_value DESC LIMIT 1
|
||||
`, globalCount).Scan(&globalCampaignID, &globalPercent)
|
||||
if hasInPersonPayment {
|
||||
var globalCampaignID string
|
||||
var globalPercent float64
|
||||
if err := tx.QueryRow(r.Context(), `
|
||||
SELECT id, discount_percent FROM discount_campaigns
|
||||
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count'
|
||||
AND milestone_value <= $1
|
||||
AND (max_redemptions IS NULL OR times_redeemed < max_redemptions)
|
||||
ORDER BY milestone_value DESC LIMIT 1
|
||||
`, globalCount).Scan(&globalCampaignID, &globalPercent); err != nil {
|
||||
log.Printf("Failed to query global milestone campaign for booking %s: %v", bookingID, err)
|
||||
}
|
||||
|
||||
if globalCampaignID != "" {
|
||||
if globalCampaignID != "" {
|
||||
discountAmount := roundTo2(bookingTotal * globalPercent / 100)
|
||||
if _, err := tx.Exec(r.Context(), `
|
||||
INSERT INTO booking_discounts (booking_id, user_id, discount_source, source_id, campaign_type, milestone_type, discount_percent, original_total, discount_amount)
|
||||
|
||||
@@ -283,14 +283,11 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get deposit info
|
||||
var depositRequired bool
|
||||
// Get deposit info — deposit_required already fetched above in the main booking query.
|
||||
var preStartPaid float64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), `SELECT deposit_required FROM bookings WHERE id = $1`, existingID).Scan(&depositRequired)
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure (aggregate with COALESCE)
|
||||
_ = db.Conn.QueryRow(r.Context(), `SELECT COALESCE(SUM(amount), 0) FROM payments WHERE booking_id = $1 AND payment_type IN ('deposit', 'full') AND status = 'completed'`, existingID).Scan(&preStartPaid)
|
||||
populateDepositFields(&existingBooking, depositRequired, preStartPaid)
|
||||
populateDepositFields(&existingBooking, existingBooking.DepositRequired, preStartPaid)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
@@ -1211,13 +1208,19 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Query payment and timing info (used for validation AND auto-approval later)
|
||||
var hasPayments bool
|
||||
hoursUntilCurrent := currentStartTime.Sub(clock.Now()).Hours()
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND status = 'completed')", bookingID).Scan(&hasPayments)
|
||||
if err := db.Conn.QueryRow(r.Context(), "SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND status = 'completed')", bookingID).Scan(&hasPayments); err != nil {
|
||||
log.Printf("Failed to check payments for booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if booking has discounts (affects auto-approval decisions)
|
||||
var hasDiscounts bool
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1)", bookingID).Scan(&hasDiscounts)
|
||||
if err := db.Conn.QueryRow(r.Context(), "SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1)", bookingID).Scan(&hasDiscounts); err != nil {
|
||||
log.Printf("Failed to check discounts for booking %s: %v", bookingID, err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if req.NewStartTime != nil && !req.NewStartTime.Equal(currentStartTime) {
|
||||
if hasPayments && hoursUntilCurrent < 72 {
|
||||
@@ -1447,8 +1450,7 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var durationMinutes int
|
||||
if len(req.NewServices) > 0 {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `
|
||||
if err := tx.QueryRow(r.Context(), `
|
||||
SELECT COALESCE(SUM(dur), 60) FROM (
|
||||
SELECT s.duration_minutes AS dur
|
||||
FROM services s
|
||||
@@ -1458,12 +1460,17 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
FROM custom_services cs
|
||||
WHERE cs.id = ANY($1)
|
||||
) sub
|
||||
`, req.NewServices).Scan(&durationMinutes)
|
||||
`, req.NewServices).Scan(&durationMinutes); err != nil {
|
||||
log.Printf("Failed to calculate duration for edit request %s: %v, using default", bookingID, err)
|
||||
durationMinutes = 60
|
||||
}
|
||||
} else {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `
|
||||
if err := tx.QueryRow(r.Context(), `
|
||||
SELECT total_duration_minutes FROM bookings WHERE id = $1
|
||||
`, bookingID).Scan(&durationMinutes)
|
||||
`, bookingID).Scan(&durationMinutes); err != nil {
|
||||
log.Printf("Failed to get booking duration for edit request %s: %v, using default", bookingID, err)
|
||||
durationMinutes = 60
|
||||
}
|
||||
}
|
||||
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
|
||||
@@ -141,10 +141,11 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
}
|
||||
|
||||
var bookingTotal float64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `
|
||||
if err := db.Conn.QueryRow(ctx, `
|
||||
SELECT total_amount FROM bookings WHERE id = $1
|
||||
`, bookingID).Scan(&bookingTotal)
|
||||
`, bookingID).Scan(&bookingTotal); err != nil {
|
||||
log.Printf("Failed to query booking total for discount preview %s: %v", bookingID, err)
|
||||
}
|
||||
|
||||
if bookingTotal <= 0 {
|
||||
return resp
|
||||
@@ -185,13 +186,14 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri
|
||||
var milestoneCampaignID string
|
||||
var milestonePercent float64
|
||||
var milestoneName string
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(ctx, `
|
||||
if err := db.Conn.QueryRow(ctx, `
|
||||
SELECT id, discount_percent, name FROM discount_campaigns
|
||||
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'per_user_booking_count'
|
||||
AND milestone_value = $1
|
||||
AND NOT EXISTS (SELECT 1 FROM booking_discounts WHERE user_id = $2 AND source_id = discount_campaigns.id)
|
||||
`, userBookingCount, userID).Scan(&milestoneCampaignID, &milestonePercent, &milestoneName)
|
||||
`, userBookingCount, userID).Scan(&milestoneCampaignID, &milestonePercent, &milestoneName); err != nil {
|
||||
log.Printf("Failed to query milestone campaign for discount preview (user %s, count %d): %v", userID, userBookingCount, err)
|
||||
}
|
||||
|
||||
if milestoneCampaignID != "" {
|
||||
var exists int
|
||||
@@ -1021,8 +1023,7 @@ func CreateBookingPayment(w http.ResponseWriter, r *http.Request) {
|
||||
// Promote deposit to confirmed if total paid meets the 20% threshold.
|
||||
// Check is inside the transaction so it sees the just-inserted payments.
|
||||
var depositMet bool
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `
|
||||
if err := tx.QueryRow(r.Context(), `
|
||||
WITH booking_total AS (
|
||||
SELECT total_amount * 100 AS total_cents FROM bookings WHERE id = $1
|
||||
),
|
||||
@@ -1033,7 +1034,9 @@ func CreateBookingPayment(w http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
SELECT pt.paid_cents >= ROUND(bt.total_cents * 0.2)
|
||||
FROM booking_total bt, paid_total pt
|
||||
`, bookingID).Scan(&depositMet)
|
||||
`, bookingID).Scan(&depositMet); err != nil {
|
||||
log.Printf("Failed to check deposit threshold for booking %s: %v", bookingID, err)
|
||||
}
|
||||
|
||||
if depositMet {
|
||||
if _, err := tx.Exec(r.Context(), `
|
||||
@@ -1139,13 +1142,14 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
|
||||
var milestoneCampaignID string
|
||||
var milestonePercent float64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `
|
||||
if err := q.QueryRow(ctx, `
|
||||
SELECT id, discount_percent FROM discount_campaigns
|
||||
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'per_user_booking_count'
|
||||
AND milestone_value = $1
|
||||
AND NOT EXISTS (SELECT 1 FROM booking_discounts WHERE user_id = $2 AND source_id = discount_campaigns.id)
|
||||
`, userBookingCount, userID).Scan(&milestoneCampaignID, &milestonePercent)
|
||||
`, userBookingCount, userID).Scan(&milestoneCampaignID, &milestonePercent); err != nil {
|
||||
log.Printf("Failed to query per-user milestone campaign: %v", err)
|
||||
}
|
||||
|
||||
if milestoneCampaignID != "" {
|
||||
var exists int
|
||||
@@ -1251,15 +1255,16 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI
|
||||
|
||||
var globalCampaignID string
|
||||
var globalPercent float64
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = q.QueryRow(ctx, `
|
||||
if err := q.QueryRow(ctx, `
|
||||
SELECT id, discount_percent FROM discount_campaigns
|
||||
WHERE status = 'active' AND campaign_type = 'milestone' AND milestone_type = 'global_booking_count'
|
||||
AND milestone_value <= $1
|
||||
AND (max_redemptions IS NULL OR times_redeemed < max_redemptions)
|
||||
AND NOT EXISTS (SELECT 1 FROM booking_discounts WHERE source_id = discount_campaigns.id AND booking_id = $2)
|
||||
ORDER BY milestone_value DESC LIMIT 1
|
||||
`, globalCount, bookingID).Scan(&globalCampaignID, &globalPercent)
|
||||
`, globalCount, bookingID).Scan(&globalCampaignID, &globalPercent); err != nil {
|
||||
log.Printf("Failed to query global milestone campaign: %v", err)
|
||||
}
|
||||
|
||||
if globalCampaignID != "" {
|
||||
var exists int
|
||||
|
||||
Reference in New Issue
Block a user