fix: replace silent json.Encode with error-logging pattern across all handlers

This commit is contained in:
2026-07-11 17:50:07 +01:00
parent 0bef0f7973
commit 5d9fa1178b
24 changed files with 629 additions and 433 deletions
+14 -8
View File
@@ -58,7 +58,9 @@ func GetDefaultHours(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(hours)
if err := json.NewEncoder(w).Encode(hours); err != nil {
log.Printf("Failed to encode JSON response: %v", err)
}
}
func UpdateDefaultHours(w http.ResponseWriter, r *http.Request) {
@@ -71,7 +73,7 @@ func UpdateDefaultHours(w http.ResponseWriter, r *http.Request) {
for _, h := range hours {
if err := validators.Validate.Struct(&h); err != nil {
log.Printf("Failed to process request: %v", err)
http.Error(w, "Invalid request", http.StatusBadRequest)
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
}
@@ -96,10 +98,10 @@ func UpdateDefaultHours(w http.ResponseWriter, r *http.Request) {
return
}
defer func() {
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
weekdays := make([]int, len(hours))
startTimes := make([]string, len(hours))
@@ -294,7 +296,9 @@ func GetWorkingHours(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(results)
if err := json.NewEncoder(w).Encode(results); err != nil {
log.Printf("Failed to encode JSON response: %v", err)
}
}
// isValidTime15Min checks that a time string (HH:MM or HH:MM:SS) has minutes in {00, 15, 30, 45}.
@@ -610,7 +614,9 @@ func GetAvailableHours(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(results)
if err := json.NewEncoder(w).Encode(results); err != nil {
log.Printf("Failed to encode JSON response: %v", err)
}
}
// normalizeTime strips seconds from HH:MM:SS to HH:MM for consistent string
@@ -119,8 +119,9 @@ func ListExceptionalGroups(w http.ResponseWriter, r *http.Request) {
}
}
_ = json.NewEncoder(w).Encode(groups)
if err := json.NewEncoder(w).Encode(groups); err != nil {
log.Printf("Failed to encode JSON response: %v", err)
}
}
// --- Create Group with Hours and Applications (bulk) ---
@@ -187,10 +188,10 @@ func CreateExceptionalGroup(w http.ResponseWriter, r *http.Request) {
return
}
defer func() {
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
// Create group
err = tx.QueryRow(r.Context(), `
@@ -236,9 +237,10 @@ func CreateExceptionalGroup(w http.ResponseWriter, r *http.Request) {
return
}
w.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(w).Encode(g)
if err := json.NewEncoder(w).Encode(g); err != nil {
log.Printf("Failed to encode JSON response: %v", err)
}
}
// --- Delete Group (cascades to hours and applications) ---
@@ -264,10 +266,10 @@ func DeleteExceptionalGroup(w http.ResponseWriter, r *http.Request) {
return
}
defer func() {
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
result, err := tx.Exec(r.Context(), `
DELETE FROM exceptional_working_hours_groups WHERE id=$1
@@ -333,10 +335,10 @@ func UpdateExceptionalApplications(w http.ResponseWriter, r *http.Request) {
return
}
defer func() {
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
// Delete existing applications for this group
_, err = tx.Exec(r.Context(), `
+63 -59
View File
@@ -9,8 +9,8 @@ import (
"net/http"
"time"
"crussell/db"
"crussell/clock"
"crussell/db"
"crussell/internal/validators"
"crussell/mw"
@@ -113,7 +113,9 @@ func ListTimeBlockers(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(blockers)
if err := json.NewEncoder(w).Encode(blockers); err != nil {
log.Printf("Failed to encode JSON response: %v", err)
}
}
// --- Create Time Blocker ---
@@ -153,10 +155,10 @@ func CreateTimeBlocker(w http.ResponseWriter, r *http.Request) {
return
}
defer func() {
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
// Insert the time blocker
var blocker TimeBlocker
@@ -180,7 +182,9 @@ func CreateTimeBlocker(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(w).Encode(blocker)
if err := json.NewEncoder(w).Encode(blocker); err != nil {
log.Printf("Failed to encode JSON response: %v", err)
}
}
// --- Delete Time Blocker ---
@@ -198,10 +202,10 @@ func DeleteTimeBlocker(w http.ResponseWriter, r *http.Request) {
return
}
defer func() {
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(r.Context()); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
result, err := tx.Exec(r.Context(), `
DELETE FROM time_blockers WHERE id = $1
@@ -361,14 +365,14 @@ func CheckTimeBlockerOverlap(ctx context.Context, startTime, endTime time.Time,
}
// CleanupOldReservations deletes expired reservations:
// - Logged-in (RESERVATION:user): older than 1 hour
// - Anonymous (RESERVATION:anon): older than 10 minutes
// - Admin walk-in (RESERVATION:admin:walkin:%): older than 15 minutes
// - Admin call-in (RESERVATION:admin:callin:%): older than 15 minutes
// - Edit request (RESERVATION:edit_request:%): older than 24 hours
// - Payment in-flight (PAYMENT_IN_FLIGHT:%): TTL via duration_minutes column
// (AcquirePaymentLock sets duration_minutes = PaymentLockDuration = 5min and
// start_time = NOW(), so the condition evaluates to "cleanup after 5 minutes".
// - Logged-in (RESERVATION:user): older than 1 hour
// - Anonymous (RESERVATION:anon): older than 10 minutes
// - Admin walk-in (RESERVATION:admin:walkin:%): older than 15 minutes
// - Admin call-in (RESERVATION:admin:callin:%): older than 15 minutes
// - Edit request (RESERVATION:edit_request:%): older than 24 hours
// - Payment in-flight (PAYMENT_IN_FLIGHT:%): TTL via duration_minutes column
// (AcquirePaymentLock sets duration_minutes = PaymentLockDuration = 5min and
// start_time = NOW(), so the condition evaluates to "cleanup after 5 minutes".
func CleanupOldReservations(ctx context.Context) (int, error) {
oneHourAgo := clock.Now().Add(-1 * time.Hour)
tenMinutesAgo := clock.Now().Add(-10 * time.Minute)
@@ -380,10 +384,10 @@ func CleanupOldReservations(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
tag, err := tx.Exec(ctx, `
DELETE FROM time_blockers
@@ -411,10 +415,10 @@ func AnonymizeStaleGuestAccounts(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
var totalRows int
@@ -507,10 +511,10 @@ func CleanupExpiredLoyaltyRedemptions(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
tag, err := tx.Exec(ctx, `
DELETE FROM loyalty_redemptions
@@ -555,10 +559,10 @@ func CleanupExpiredFinancialRecords(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
var totalRows int
@@ -584,7 +588,7 @@ func CleanupExpiredFinancialRecords(ctx context.Context) (int, error) {
LEFT JOIN bookings b ON p.booking_id = b.id
LEFT JOIN users u ON b.user_id = u.id
WHERE p.created_at < NOW() - INTERVAL '7 years'
` + retentionFilter + `
`+retentionFilter+`
GROUP BY DATE_TRUNC('month', p.created_at)::date
ON CONFLICT (month) DO UPDATE SET
total_payments = financial_aggregates.total_payments + EXCLUDED.total_payments,
@@ -617,7 +621,7 @@ func CleanupExpiredFinancialRecords(ctx context.Context) (int, error) {
LEFT JOIN bookings b ON p.booking_id = b.id
LEFT JOIN users u ON b.user_id = u.id
WHERE p.created_at < NOW() - INTERVAL '7 years'
` + retentionFilter + `
`+retentionFilter+`
GROUP BY DATE_TRUNC('month', r.created_at)::date
ON CONFLICT (month) DO UPDATE SET
total_refunds = financial_aggregates.total_refunds + EXCLUDED.total_refunds
@@ -633,7 +637,7 @@ func CleanupExpiredFinancialRecords(ctx context.Context) (int, error) {
LEFT JOIN users u ON b.user_id = u.id
WHERE p.booking_id = b.id
AND p.created_at < NOW() - INTERVAL '7 years'
` + retentionFilter + `
`+retentionFilter+`
`)
if err != nil {
return 0, fmt.Errorf("failed to delete expired payments: %w", err)
@@ -647,7 +651,7 @@ func CleanupExpiredFinancialRecords(ctx context.Context) (int, error) {
LEFT JOIN users u ON b.user_id = u.id
WHERE r.payment_id = p.id
AND p.created_at < NOW() - INTERVAL '7 years'
` + retentionFilter + `
`+retentionFilter+`
`)
if err != nil {
return 0, fmt.Errorf("failed to delete expired refunds: %w", err)
@@ -671,10 +675,10 @@ func CleanupExpiredDeposits(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
// Collect all evicted booking IDs from both updates so we can notify
// and clean up in one pass instead of re-scanning via updated_at = NOW().
@@ -803,10 +807,10 @@ func CleanupExpiredGiftCards(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
rows, err := tx.Query(ctx, `
SELECT id, amount_remaining
@@ -905,10 +909,10 @@ func CleanupIdleAccounts(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
rowsWithBalance, err := tx.Query(ctx, `
SELECT u.id, COALESCE(b.balance, 0) as balance
@@ -1014,10 +1018,10 @@ func CleanupOldIdempotencyKeys(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
var totalRows int
@@ -1069,10 +1073,10 @@ func CleanupOldNameHistory(ctx context.Context) (int, error) {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer func() {
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
if err := tx.Rollback(ctx); err != nil && err.Error() != "tx is closed" {
slog.Error("failed to rollback transaction", "err", err)
}
}()
tag, err := tx.Exec(ctx, `
DELETE FROM name_history