fix: resolve golangci-lint violations (errcheck, unused, gosimple, ineffassign)
errcheck: add proper error handling with slog.Error for tx.Rollback, key generation, and s3/dav operations. Add nolint comments for intentionally discarded DB scan errors and HTTP write errors. unused: remove dead code (svcRow type, processImage, nonDepositPaymentType, generateSecureCode, colorBold, nGreen, nRed) gosimple S1021: merge var declaration with assignment in manage.go ineffassign: remove dead assignments in settings.go, till.go, images.go Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -43,7 +44,11 @@ func UserCancelBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
defer func() {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
slog.Error("failed to rollback transaction", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Get current status before updating
|
||||
var originalStatus string
|
||||
@@ -166,7 +171,11 @@ func AdminCancelBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
defer func() {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
slog.Error("failed to rollback transaction", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Get current status and user ID — use FOR UPDATE to lock the row so
|
||||
// the refund and status change are atomic.
|
||||
@@ -289,7 +298,7 @@ func AdminCancelBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
resp["refund_failed"] = true
|
||||
resp["warning"] = "Booking was cancelled but refund processing failed — please process refund manually or retry"
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -445,13 +454,15 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Get deposit info
|
||||
var depositRequired bool
|
||||
var preStartPaid float64
|
||||
db.Conn.QueryRow(r.Context(), `SELECT deposit_required FROM bookings WHERE id = $1`, existingID).Scan(&depositRequired)
|
||||
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)
|
||||
//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
|
||||
_ = 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)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(existingBooking)
|
||||
_ = json.NewEncoder(w).Encode(existingBooking)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -644,8 +655,7 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !req.OutOfHours {
|
||||
// Check if there's an exceptional hours entry that makes this time unavailable
|
||||
var isClosed bool
|
||||
var checkErr error
|
||||
checkErr = db.Conn.QueryRow(r.Context(), `
|
||||
checkErr := db.Conn.QueryRow(r.Context(), `
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM exceptional_working_hours ewh
|
||||
JOIN exceptional_group_applications ega ON ewh.group_id = ega.group_id
|
||||
@@ -698,7 +708,11 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
defer func() {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
slog.Error("failed to rollback transaction", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Evict any pending_release bookings that overlap this slot.
|
||||
if _, evictErr := EvictPendingReleaseOverlapping(r.Context(), tx, req.StartTime, newEnd); evictErr != nil {
|
||||
@@ -1240,7 +1254,11 @@ func DeleteEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
defer func() {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
slog.Error("failed to rollback transaction", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Delete the edit request for this booking
|
||||
res, err := tx.Exec(r.Context(), `
|
||||
@@ -1361,11 +1379,13 @@ 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()
|
||||
db.Conn.QueryRow(r.Context(), "SELECT EXISTS(SELECT 1 FROM payments WHERE booking_id = $1 AND status = 'completed')", bookingID).Scan(&hasPayments)
|
||||
//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)
|
||||
|
||||
// Check if booking has discounts (affects auto-approval decisions)
|
||||
var hasDiscounts bool
|
||||
db.Conn.QueryRow(r.Context(), "SELECT EXISTS(SELECT 1 FROM booking_discounts WHERE booking_id = $1)", bookingID).Scan(&hasDiscounts)
|
||||
//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 req.NewStartTime != nil && !req.NewStartTime.Equal(currentStartTime) {
|
||||
if hasPayments && hoursUntilCurrent < 72 {
|
||||
@@ -1412,7 +1432,11 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
defer func() {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
slog.Error("failed to rollback transaction", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Delete any existing edit request for this booking (upsert behavior)
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
@@ -1574,7 +1598,7 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"auto_approved": true,
|
||||
"edit_request": editReq,
|
||||
})
|
||||
@@ -1591,6 +1615,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(), `
|
||||
SELECT COALESCE(SUM(dur), 60) FROM (
|
||||
SELECT s.duration_minutes AS dur
|
||||
@@ -1603,6 +1628,7 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
) sub
|
||||
`, req.NewServices).Scan(&durationMinutes)
|
||||
} else {
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = tx.QueryRow(r.Context(), `
|
||||
SELECT total_duration_minutes FROM bookings WHERE id = $1
|
||||
`, bookingID).Scan(&durationMinutes)
|
||||
@@ -1668,7 +1694,7 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(editReq)
|
||||
_ = json.NewEncoder(w).Encode(editReq)
|
||||
}
|
||||
|
||||
// AdminListEditRequestsHandler returns all edit requests
|
||||
@@ -1691,7 +1717,8 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var total int
|
||||
|
||||
// Count query (no ORDER BY needed).
|
||||
db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM booking_edit_requests").Scan(&total)
|
||||
//nolint:errcheck // zero value is acceptable fallback on scan failure
|
||||
_ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM booking_edit_requests").Scan(&total)
|
||||
|
||||
rows, err := db.Conn.Query(r.Context(), baseQuery, args...)
|
||||
if err != nil {
|
||||
@@ -1747,7 +1774,7 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"requests": requests,
|
||||
"total": total,
|
||||
})
|
||||
@@ -1773,7 +1800,11 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
defer func() {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
slog.Error("failed to rollback transaction", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Get the edit request
|
||||
var bookingID string
|
||||
@@ -2058,7 +2089,11 @@ func AdminRejectEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(r.Context())
|
||||
defer func() {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
slog.Error("failed to rollback transaction", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Delete the edit request
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
@@ -2153,7 +2188,7 @@ func GetMyEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"edit_request": nil,
|
||||
})
|
||||
return
|
||||
@@ -2172,7 +2207,7 @@ func GetMyEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"edit_request": enriched,
|
||||
})
|
||||
}
|
||||
@@ -2236,7 +2271,7 @@ func GetMyEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"edit_requests": enrichedRequests,
|
||||
})
|
||||
}
|
||||
@@ -2293,7 +2328,7 @@ func AdminListAllEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"edit_requests": enrichedRequests,
|
||||
})
|
||||
}
|
||||
@@ -2335,16 +2370,17 @@ func AdminGetBookingEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
editReq.NewServices = newServices
|
||||
|
||||
enriched, err := buildEnrichedEditRequest(r.Context(), &editReq)
|
||||
if err != nil {
|
||||
log.Printf("Failed to build enriched edit request: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Failed to build enriched edit request: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"edit_request": enriched,
|
||||
})
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"edit_request": enriched,
|
||||
})
|
||||
}
|
||||
|
||||
// ========================================
|
||||
|
||||
Reference in New Issue
Block a user