diff --git a/backend/auth/jwt.go b/backend/auth/jwt.go index 79572b4..b3e1f2c 100644 --- a/backend/auth/jwt.go +++ b/backend/auth/jwt.go @@ -3,12 +3,12 @@ package auth import ( "context" "crypto/rand" - "database/sql" "errors" "fmt" "time" "crussell/db" + "github.com/jackc/pgx/v5" "github.com/go-chi/jwtauth/v5" ) @@ -196,7 +196,7 @@ func VerifyRefreshToken(ctx context.Context, tokenString string) (userID string, err = db.DB.QueryRow(ctx, query, tokenString).Scan(&userID, &role) if err != nil { - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, pgx.ErrNoRows) { return "", "", fmt.Errorf("invalid or expired refresh token") } return "", "", fmt.Errorf("failed to verify refresh token: %w", err) diff --git a/backend/handlers/admin/custom_services.go b/backend/handlers/admin/custom_services.go index d9670ca..6f7f5a7 100644 --- a/backend/handlers/admin/custom_services.go +++ b/backend/handlers/admin/custom_services.go @@ -6,11 +6,13 @@ import ( "crussell/mw" "database/sql" "encoding/json" + "errors" "net/http" "strconv" "time" "github.com/go-chi/chi/v5" + "github.com/jackc/pgx/v5" ) type CustomService struct { @@ -268,7 +270,7 @@ func GetCustomService(w http.ResponseWriter, r *http.Request) { SELECT id, name, description, price, duration_minutes, minimum_age_required, notes, created_at, created_by, usage_count, last_used_at FROM custom_services WHERE id = $1 `, id).Scan(&cs.ID, &cs.Name, &desc, &cs.Price, &cs.DurationMinutes, &cs.MinimumAgeRequired, ¬es, &cs.CreatedAt, &createdBy, &cs.UsageCount, &lastUsedAt) - if err == sql.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "Custom service not found", http.StatusNotFound) return } @@ -386,7 +388,7 @@ func PromoteCustomService(w http.ResponseWriter, r *http.Request) { SELECT name, description, price, duration_minutes, minimum_age_required, notes, created_by FROM custom_services WHERE id = $1 `, id).Scan(&name, &desc, &price, &durationMinutes, &minimumAgeRequired, ¬es, &createdBy) - if err == sql.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "Custom service not found", http.StatusNotFound) return } @@ -454,7 +456,7 @@ func DeleteCustomService(w http.ResponseWriter, r *http.Request) { var usageCount int err := db.DB.QueryRow(r.Context(), `SELECT usage_count FROM custom_services WHERE id = $1`, id).Scan(&usageCount) - if err == sql.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "Custom service not found", http.StatusNotFound) return } diff --git a/backend/handlers/admin/discount_campaigns.go b/backend/handlers/admin/discount_campaigns.go index 4207c73..4212e6e 100644 --- a/backend/handlers/admin/discount_campaigns.go +++ b/backend/handlers/admin/discount_campaigns.go @@ -6,6 +6,7 @@ import ( "crussell/mw" "database/sql" "encoding/json" + "errors" "log" "net/http" "strconv" @@ -666,7 +667,7 @@ func GetCampaignStats(w http.ResponseWriter, r *http.Request) { &createdBy, ) - if err == sql.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "Campaign not found", http.StatusNotFound) return } diff --git a/backend/handlers/admin/test_helpers.go b/backend/handlers/admin/test_helpers.go index db519ea..85948d1 100644 --- a/backend/handlers/admin/test_helpers.go +++ b/backend/handlers/admin/test_helpers.go @@ -10,21 +10,12 @@ import ( "net/http" "net/http/httptest" "strings" - "testing" - "crussell/db" "crussell/mw" - "crussell/testutils/testdb" "github.com/go-chi/chi/v5" ) -// resetTestData truncates tables to clean up data between tests -func resetTestData(t *testing.T) { - t.Helper() - testdb.TruncateTables(t, db.DB) -} - // makeAdminRequest creates a request with admin context // Note: Using 12-char IDs to match CHAR(12) columns in schema (e.g., created_by) func makeAdminRequest(handler http.Handler, method, path string, body interface{}) *httptest.ResponseRecorder { diff --git a/backend/handlers/auth/local.go b/backend/handlers/auth/local.go index 03acae2..8836ec3 100644 --- a/backend/handlers/auth/local.go +++ b/backend/handlers/auth/local.go @@ -4,12 +4,12 @@ import ( "context" "crussell/auth" "crussell/db" + "github.com/jackc/pgx/v5" "crussell/internal/dav" "crussell/internal/validators" "crussell/internal/zxcvbnjs" "crussell/mw" "crypto/rand" - "database/sql" "encoding/json" "errors" "fmt" @@ -520,7 +520,7 @@ func GenerateVerificationCodeHandler(w http.ResponseWriter, r *http.Request) { "SELECT id FROM users WHERE LOWER(email) = $1", email, ).Scan(&userID) if err != nil { - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, pgx.ErrNoRows) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(VerificationResponse{Success: true, Message: "If the email exists, a verification code will be sent"}) return @@ -576,7 +576,7 @@ func VerifyCodeHandler(w http.ResponseWriter, r *http.Request) { code, ).Scan(&userID, &purpose, &expiresAt) if err != nil { - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, pgx.ErrNoRows) { // Check if code exists but was already used or expired var checkUsedAt *time.Time checkErr := db.DB.QueryRow(r.Context(), diff --git a/backend/handlers/bookings/admin_reserve.go b/backend/handlers/bookings/admin_reserve.go index ff2c82c..1507f26 100644 --- a/backend/handlers/bookings/admin_reserve.go +++ b/backend/handlers/bookings/admin_reserve.go @@ -3,9 +3,9 @@ package bookings import ( "context" "crussell/db" + "github.com/jackc/pgx/v5" "crussell/handlers/scheduling" "crussell/mw" - "database/sql" "encoding/json" "errors" "fmt" @@ -111,7 +111,7 @@ func AdminReserveSlotHandler(w http.ResponseWriter, r *http.Request) { weekday := int((localStart.Weekday() + 6) % 7) var closeStr string if err := db.DB.QueryRow(r.Context(), `SELECT end_time::text FROM working_hours WHERE weekday = $1`, weekday).Scan(&closeStr); err != nil { - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "Not open on this day", http.StatusBadRequest) return } diff --git a/backend/handlers/services/services.go b/backend/handlers/services/services.go index 9a66e11..8cdf44c 100644 --- a/backend/handlers/services/services.go +++ b/backend/handlers/services/services.go @@ -4,6 +4,7 @@ import ( "context" "crussell/auth" "crussell/db" + "github.com/jackc/pgx/v5" "crussell/internal/validators" "crussell/mw" "database/sql" @@ -407,7 +408,7 @@ func ServicesEligibleForUserHandler(w http.ResponseWriter, r *http.Request) { // Get user's date of birth var dob time.Time err := db.DB.QueryRow(r.Context(), `SELECT date_of_birth FROM users WHERE id = $1`, userID).Scan(&dob) - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "user not found", http.StatusNotFound) return } diff --git a/backend/handlers/user/account.go b/backend/handlers/user/account.go index c1b1764..b1fb577 100644 --- a/backend/handlers/user/account.go +++ b/backend/handlers/user/account.go @@ -3,6 +3,7 @@ package user import ( "context" "database/sql" + "errors" "fmt" "log" "net/http" @@ -13,6 +14,7 @@ import ( "crussell/internal/dav" "crussell/internal/s3" "crussell/mw" + "github.com/jackc/pgx/v5" ) // DELETE /api/user/account @@ -28,7 +30,7 @@ func DeleteAccountHandler(w http.ResponseWriter, r *http.Request) { err := db.DB.QueryRow(r.Context(), `SELECT account_role, profile_pic_url FROM users WHERE id = $1`, userID). Scan(&accountRole, &profilePicURL) if err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "user not found", http.StatusNotFound) return } diff --git a/backend/handlers/user/customer_relationship.go b/backend/handlers/user/customer_relationship.go index a16599c..f6e9628 100644 --- a/backend/handlers/user/customer_relationship.go +++ b/backend/handlers/user/customer_relationship.go @@ -13,6 +13,7 @@ import ( "github.com/go-chi/chi/v5" "crussell/db" + "github.com/jackc/pgx/v5" "crussell/internal/validators" ) @@ -68,7 +69,7 @@ func GetCustomerRelationshipHandler(w http.ResponseWriter, r *http.Request) { LEFT JOIN payments p ON p.booking_id = b.id WHERE b.user_id = $1 `, userID).Scan(&result.TotalSpend, &result.TotalSaved, &result.TotalTips, &result.TotalVisits, &firstVisit, &lastVisit) - if err != nil && !errors.Is(err, sql.ErrNoRows) { + if err != nil && !errors.Is(err, pgx.ErrNoRows) { log.Printf("Failed to get customer relationship data for user %s: %v", userID, err) } @@ -113,7 +114,7 @@ func GetCustomerRelationshipHandler(w http.ResponseWriter, r *http.Request) { ORDER BY count DESC LIMIT 5 `, userID) - if err != nil && !errors.Is(err, sql.ErrNoRows) { + if err != nil && !errors.Is(err, pgx.ErrNoRows) { log.Printf("Failed to get top services for user %s: %v", userID, err) } else { defer rows.Close() diff --git a/backend/handlers/user/guest.go b/backend/handlers/user/guest.go index 4cf8140..31f4c15 100644 --- a/backend/handlers/user/guest.go +++ b/backend/handlers/user/guest.go @@ -1,7 +1,6 @@ package user import ( - "database/sql" "encoding/json" "errors" "log" @@ -12,6 +11,7 @@ import ( "crussell/db" "crussell/handlers/auth" "crussell/internal/validators" + "github.com/jackc/pgx/v5" ) type CreateGuestUserRequest struct { @@ -172,7 +172,7 @@ func CheckEmailHandler(w http.ResponseWriter, r *http.Request) { s := "check" suggestion = &s } - } else if !errors.Is(err, sql.ErrNoRows) { + } else if !errors.Is(err, pgx.ErrNoRows) { log.Printf("Failed to check email: %v", err) http.Error(w, "database error", http.StatusInternalServerError) return