From 83f82a26299b2de13961aa92b37b2a60cf2b7099 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Thu, 18 Sep 2025 11:20:31 +0700
Subject: [PATCH 01/15] added logger to handlers
---
handlers/handlers.go | 236 +++++++++++++++++++-------------------
handlers/model.go | 6 +-
internal/server/server.go | 3 +-
3 files changed, 124 insertions(+), 121 deletions(-)
diff --git a/handlers/handlers.go b/handlers/handlers.go
index 728dfd5..e337121 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -55,7 +55,7 @@ func (h *Handler) RequestVerificationHandler(w http.ResponseWriter, r *http.Requ
verificationJWT, err := user.VerificationToken(req.Email, req.Username, req.Password)
if err != nil {
- log.Printf("GenerateEmailVerificationToken failed: %v", err)
+ h.Logger.Error("GenerateEmailVerificationToken failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to create token")
return
}
@@ -63,7 +63,7 @@ func (h *Handler) RequestVerificationHandler(w http.ResponseWriter, r *http.Requ
verifyURL := os.Getenv("FRONTEND_URL") + "verify-email?token=" + verificationJWT
go func() {
if err := h.Mailer.SendVerificationEmail(req.Email, verifyURL); err != nil {
- log.Printf("SendVerificationEmail failed: %v", err)
+ h.Logger.Error("SendVerificationEmail failed: %v", err)
}
}()
@@ -84,7 +84,7 @@ func (h *Handler) CheckEmailHandler(w http.ResponseWriter, r *http.Request) {
Email string `json:"email"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- log.Printf("Decoding check-email body failed: %v", err)
+ h.Logger.Error("Decoding check-email body failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
@@ -99,7 +99,7 @@ func (h *Handler) CheckEmailHandler(w http.ResponseWriter, r *http.Request) {
RespondWithJSON(w, http.StatusOK, map[string]bool{"exists": false})
return
}
- log.Printf("CheckEmailHandler internal error: %v", err)
+ h.Logger.Error("CheckEmailHandler internal error: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -118,21 +118,21 @@ func (h *Handler) CreateUsersHandler(w http.ResponseWriter, r *http.Request) {
}
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
userCreated, err := user.CreateUser(h.UserRepo, req.Token)
if err != nil {
- log.Printf("CreateUser error: %v", err)
+ h.Logger.Error("CreateUser error: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
err = h.Mailer.SendWelcome(userCreated.Email)
if err != nil {
- log.Printf("h.Mailer.SendWelcome failed: %v", err)
+ h.Logger.Error("h.Mailer.SendWelcome failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -159,20 +159,20 @@ func (h *Handler) GetUsersHandler(w http.ResponseWriter, r *http.Request) {
userIDParam, err := GetPathID(r, "/api/users/")
if err != nil {
- log.Printf("GetPathID error: %v\n", err)
+ h.Logger.Error("GetPathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "UserID required")
return
}
if userID != userIDParam {
- log.Printf("UserID mismatch: %v vs. %v", userID, userIDParam)
+ h.Logger.Error("UserID mismatch: %v vs. %v", userID, userIDParam)
RespondWithError(w, http.StatusUnauthorized, "Invalid ID")
return
}
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUsers error: %v", err)
+ h.Logger.Error("GetUsers error: %v", err)
return
}
@@ -199,48 +199,48 @@ func (h *Handler) DeleteUserHandler(w http.ResponseWriter, r *http.Request) {
userIDParam, err := GetPathID(r, "/api/users/delete/")
if err != nil {
- log.Printf("GetPathID error: %v\n", err)
+ h.Logger.Error("GetPathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "UserID required")
return
}
if userID != userIDParam {
- log.Printf("UserID mismatch: %v vs. %v", userID, userIDParam)
+ h.Logger.Error("UserID mismatch: %v vs. %v", userID, userIDParam)
RespondWithError(w, http.StatusUnauthorized, "Invalid ID")
return
}
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUser error: %v", err)
+ h.Logger.Error("GetUser error: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to find user")
return
}
err = h.Billing.CancelSubscription(h.UserRepo, userReturned.Email)
if err != nil {
- log.Printf("h.Billing.CancelSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.CancelSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
err = user.MarkUserDeleted(h.UserRepo, userID)
if err != nil {
- log.Printf("DeleteUser failed: %v", err)
+ h.Logger.Error("DeleteUser failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to delete user")
return
}
err = token.DeleteRefreshToken(h.TokenRepo, userID)
if err != nil {
- log.Printf("DeleteRefreshTokensForUser failed: %v", err)
+ h.Logger.Error("DeleteRefreshTokensForUser failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
err = h.Mailer.SendDeletionConfirmation(userReturned.Email)
if err != nil {
- log.Printf("h.Mailer.SendDeletionConfirmation failed: %v", err)
+ h.Logger.Error("h.Mailer.SendDeletionConfirmation failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -257,13 +257,13 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
if params.Email == "" || params.Password == "" {
- log.Printf("Invalid username or password.")
+ h.Logger.Error("Invalid username or password.")
RespondWithError(w, http.StatusBadRequest, "Invalid username or password.")
return
@@ -271,7 +271,7 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
jwToken, username, userID, err := user.LoginUser(h.UserRepo, params.Email, params.Password)
if err != nil {
- log.Printf("LoginUser error: %v", err)
+ h.Logger.Error("LoginUser error: %v", err)
if errors.Is(err, user.ErrAccountDeleted) {
RespondWithError(w, http.StatusUnauthorized, user.ErrAccountDeleted.Error())
return
@@ -282,7 +282,7 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, userID)
if err != nil {
- log.Printf("RefreshToken error: %v", err)
+ h.Logger.Error("RefreshToken error: %v", err)
RespondWithError(w, http.StatusUnauthorized, "")
return
}
@@ -344,14 +344,14 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
client = &http.Client{}
req, err = http.NewRequest("GET", "https://api.github.com/user", nil)
if err != nil {
- log.Printf("http.NewRequest failed: %v", err)
+ h.Logger.Error("http.NewRequest failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
req.Header.Set("Authorization", "Bearer "+tokenResp.AccessToken)
githubResp, err := client.Do(req)
if err != nil {
- log.Printf("GET api.github.com/user failed: %v", err)
+ h.Logger.Error("GET api.github.com/user failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -366,14 +366,14 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
if githubUser.Email == "" {
req, err := http.NewRequest("GET", "https://api.github.com/user/emails", nil)
if err != nil {
- log.Printf("http.NewRequest failed: %v", err)
+ h.Logger.Error("http.NewRequest failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
req.Header.Set("Authorization", "Bearer "+tokenResp.AccessToken)
emailResp, err := client.Do(req)
if err != nil {
- log.Printf("GET api.github.com/user/emails failed: %v", err)
+ h.Logger.Error("GET api.github.com/user/emails failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -395,7 +395,7 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
}
if githubUser.Email == "" {
- log.Printf("GitHub login failed: no verified email found for user %s", githubUser.Login)
+ h.Logger.Error("GitHub login failed: no verified email found for user %s", githubUser.Login)
RespondWithError(w, http.StatusUnauthorized, "We couldn’t retrieve a valid email address from GitHub. Please check your GitHub email settings and try again.")
return
}
@@ -408,13 +408,13 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
jwt, err := token.CreateJWT(strconv.Itoa(user.ID), 0)
if err != nil {
- log.Printf("token.CreateJWT failed: %v", err)
+ h.Logger.Error("token.CreateJWT failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, user.ID)
if err != nil {
- log.Printf("token.CreateRefreshToken failed: %v", err)
+ h.Logger.Error("token.CreateRefreshToken failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -443,48 +443,48 @@ func (h *Handler) RefreshTokensHandler(w http.ResponseWriter, r *http.Request) {
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
if params.UserID == 0 {
- log.Printf("Invalid userID")
+ h.Logger.Error("Invalid userID")
RespondWithError(w, http.StatusBadRequest, "Invalid username or password")
return
}
storedToken, err := token.GetStoredRefreshToken(h.TokenRepo, params.UserID)
if err != nil {
- log.Printf("GetStoredRefreshToken error: %v", err)
+ h.Logger.Error("GetStoredRefreshToken error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid user_id")
return
}
ok := token.VerifyRefreshToken(storedToken, providedToken)
if !ok {
- log.Printf("VerifyRefreshToken error")
+ h.Logger.Error("VerifyRefreshToken error")
RespondWithError(w, http.StatusUnauthorized, "Refresh token is invalid")
return
}
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, params.UserID)
if err != nil {
- log.Printf("CreateRefreshToken error: %v", err)
+ h.Logger.Error("CreateRefreshToken error: %v", err)
RespondWithError(w, http.StatusUnauthorized, "")
return
}
user, err := h.UserRepo.GetUser(params.UserID)
if err != nil || user.AccountStatus == "deleted" {
- log.Printf("Refresh attempt for deleted account ID %d", params.UserID)
+ h.Logger.Error("Refresh attempt for deleted account ID %d", params.UserID)
RespondWithError(w, http.StatusUnauthorized, "Account deactivated")
return
}
jwToken, err := token.CreateJWT(strconv.Itoa(params.UserID), 0)
if err != nil {
- log.Printf("JWT creation failed: %v", err)
+ h.Logger.Error("JWT creation failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "")
return
}
@@ -512,14 +512,14 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUser error: %v", err)
+ h.Logger.Error("GetUser error: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to find user")
return
}
@@ -537,7 +537,7 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
@@ -545,21 +545,21 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
RespondWithError(w, http.StatusPaymentRequired, "You do not have enough credits to start a new interview or your subscription has expired.")
return
}
- log.Printf("Interview failed to start: %v", err)
+ h.Logger.Error("Interview failed to start: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to start interview.")
return
}
conversationID, err := conversation.CreateEmptyConversation(h.ConversationRepo, interviewStarted.Id, interviewStarted.Subtopic)
if err != nil {
- log.Printf("conversation.CreateEmptyConversation failed: %v", err)
+ h.Logger.Error("conversation.CreateEmptyConversation failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
err = interview.LinkConversation(h.InterviewRepo, interviewStarted.Id, conversationID)
if err != nil {
- log.Printf("interview.LinkConversation failed: %v", err)
+ h.Logger.Error("interview.LinkConversation failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -587,19 +587,19 @@ func (h *Handler) GetInterviewHandler(w http.ResponseWriter, r *http.Request) {
interviewID, err := GetPathID(r, "/api/interviews/")
if err != nil {
- log.Printf("GetPathID failed: %v", err)
+ h.Logger.Error("GetPathID failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid interview ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview failed: %v", err)
+ h.Logger.Error("GetInterview failed: %v", err)
RespondWithError(w, http.StatusNotFound, "Interview not found")
return
}
if interviewReturned.UserId != userID {
- log.Printf("User ID mismatch on interview fetch")
+ h.Logger.Error("User ID mismatch on interview fetch")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -639,21 +639,21 @@ func (h *Handler) UpdateInterviewStatusHandler(w http.ResponseWriter, r *http.Re
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
err = ValidateInterviewStatusTransition(interviewReturned.Status, payload.Status)
if err != nil {
- log.Printf("ValidateInterviewStatusTransition failed: %v", err)
+ h.Logger.Error("ValidateInterviewStatusTransition failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid status transition")
return
}
err = h.InterviewRepo.UpdateStatus(interviewID, userID, payload.Status)
if err != nil {
- log.Printf("UpdateInterviewStatus failed: %v", err)
+ h.Logger.Error("UpdateInterviewStatus failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not update status")
return
}
@@ -676,7 +676,7 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
@@ -684,19 +684,19 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
interviewID, err := GetPathID(r, "/api/conversations/create/")
if err != nil {
- log.Printf("PathID error: %v\n", err)
+ h.Logger.Error("PathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- log.Printf("interview.userid != token user_id")
+ h.Logger.Error("interview.userid != token user_id")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -707,7 +707,7 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- log.Printf("conversation.GetConversation failed: %v", err)
+ h.Logger.Error("conversation.GetConversation failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
@@ -725,11 +725,11 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("CreateConversation error: %v", err)
+ h.Logger.Error("CreateConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid interview_id")
return
}
@@ -755,32 +755,32 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
if params.Message == "" {
- log.Printf("messageUserResponse is nil")
+ h.Logger.Error("messageUserResponse is nil")
RespondWithError(w, http.StatusBadRequest, "Missing message")
return
}
interviewID, err := GetPathID(r, "/api/conversations/append/")
if err != nil {
- log.Printf("PathID error: %v\n", err)
+ h.Logger.Error("PathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- log.Printf("interview.userid != token user_id")
+ h.Logger.Error("interview.userid != token user_id")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -791,7 +791,7 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- log.Printf("GetConversation error: %v", err)
+ h.Logger.Error("GetConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -808,11 +808,11 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("AppendConversation error: %v", err)
+ h.Logger.Error("AppendConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -837,26 +837,26 @@ func (h *Handler) GetConversationHandler(w http.ResponseWriter, r *http.Request)
interviewID, err := GetPathID(r, "/api/conversations/")
if err != nil {
- log.Printf("PathID error: %v\n", err)
+ h.Logger.Error("PathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- log.Printf("interview.userid != token user_id")
+ h.Logger.Error("interview.userid != token user_id")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- log.Printf("GetConversation error: %v", err)
+ h.Logger.Error("GetConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -876,14 +876,14 @@ func (h *Handler) RequestResetHandler(w http.ResponseWriter, r *http.Request) {
var params PasswordResetRequest
err := json.NewDecoder(r.Body).Decode(¶ms)
if err != nil {
- log.Printf("Decoding request failed: %v", err)
+ h.Logger.Error("Decoding request failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
resetJWT, err := user.RequestPasswordReset(h.UserRepo, params.Email)
if err != nil {
- log.Printf("Error generating reset token for email %s: %v", params.Email, err)
+ h.Logger.Error("Error generating reset token for email %s: %v", params.Email, err)
w.WriteHeader(http.StatusOK)
return
}
@@ -894,7 +894,7 @@ func (h *Handler) RequestResetHandler(w http.ResponseWriter, r *http.Request) {
go func(email, resetURL string) {
err := h.Mailer.SendPasswordReset(email, resetURL)
if err != nil {
- log.Printf("SendPasswordReset error: %v", err)
+ h.Logger.Error("SendPasswordReset error: %v", err)
return
}
}(params.Email, resetURL)
@@ -911,13 +911,13 @@ func (h *Handler) ResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
var params PasswordResetPayload
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil {
- log.Printf("Decoding payload failed: %v", err)
+ h.Logger.Error("Decoding payload failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
err := user.ResetPassword(h.UserRepo, params.NewPassword, params.Token)
if err != nil {
- log.Printf("ResetPasswordHandler failed: %v", err)
+ h.Logger.Error("ResetPasswordHandler failed: %v", err)
RespondWithError(w, http.StatusUnauthorized, "Invalid or expired token")
return
}
@@ -934,14 +934,14 @@ func (h *Handler) CreateCheckoutSessionHandler(w http.ResponseWriter, r *http.Re
userID, ok := r.Context().Value(middleware.ContextKeyTokenParams).(int)
if !ok {
- log.Printf("r.Context().Value() failed")
+ h.Logger.Error("r.Context().Value() failed")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
var params CheckoutRequest
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil || params.Tier == "" {
- log.Printf("jsonNewDecoder failed: %v", err)
+ h.Logger.Error("jsonNewDecoder failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Missing or invalid tier")
return
}
@@ -967,14 +967,14 @@ func (h *Handler) CreateCheckoutSessionHandler(w http.ResponseWriter, r *http.Re
priceIDInt, err := strconv.Atoi(priceID)
if err != nil {
- log.Printf("strconv.Atoi() failed: %v", err)
+ h.Logger.Error("strconv.Atoi() failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
url, err := h.Billing.RequestCheckoutSession(user.Email, priceIDInt)
if err != nil {
- log.Printf("billing.CreateCheckoutSession failed: %v", err)
+ h.Logger.Error("billing.CreateCheckoutSession failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not start checkout")
return
}
@@ -996,14 +996,14 @@ func (h *Handler) CancelSubscriptionHandler(w http.ResponseWriter, r *http.Reque
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUser failed: %v", err)
+ h.Logger.Error("GetUser failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not retrieve user")
return
}
err = h.Billing.RequestDeleteSubscription(userReturned.SubscriptionID)
if err != nil {
- log.Printf("DeleteSubscription failed: %v", err)
+ h.Logger.Error("DeleteSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not cancel subscription")
return
}
@@ -1025,14 +1025,14 @@ func (h *Handler) ResumeSubscriptionHandler(w http.ResponseWriter, r *http.Reque
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUser failed: %v", err)
+ h.Logger.Error("GetUser failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not retrieve user")
return
}
err = h.Billing.RequestResumeSubscription(userReturned.SubscriptionID)
if err != nil {
- log.Printf("DeleteSubscription failed: %v", err)
+ h.Logger.Error("DeleteSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not cancel subscription")
return
}
@@ -1054,7 +1054,7 @@ func (h *Handler) ChangePlanHandler(w http.ResponseWriter, r *http.Request) {
var params CheckoutRequest
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil || params.Tier == "" {
- log.Printf("jsonNewDecoder failed: %v", err)
+ h.Logger.Error("jsonNewDecoder failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Missing or invalid tier")
return
}
@@ -1078,13 +1078,13 @@ func (h *Handler) ChangePlanHandler(w http.ResponseWriter, r *http.Request) {
priceIDInt, err := strconv.Atoi(priceID)
if err != nil {
- log.Printf("strconv.Atoi() failed: %v", err)
+ h.Logger.Error("strconv.Atoi() failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
if err := h.Billing.RequestUpdateSubscriptionVariant(user.SubscriptionID, priceIDInt); err != nil {
- log.Printf("UpdateLemonSubscriptionVariant failed: %v", err)
+ h.Logger.Error("UpdateLemonSubscriptionVariant failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update subscription")
return
}
@@ -1100,7 +1100,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
body, err := io.ReadAll(r.Body)
if err != nil {
- log.Printf("io.ReadAll failed: %v", err)
+ h.Logger.Error("io.ReadAll failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Bad Request")
return
}
@@ -1108,7 +1108,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
signature := r.Header.Get("X-Signature")
if !h.Billing.VerifyBillingSignature(signature, body, os.Getenv("LEMON_WEBHOOK_SECRET")) {
- log.Printf("Invalid billing event signature")
+ h.Logger.Error("Invalid billing event signature")
RespondWithError(w, http.StatusUnauthorized, "Invalid signature")
return
}
@@ -1116,7 +1116,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
var webhookPayload billing.BillingWebhookPayload
err = json.Unmarshal(body, &webhookPayload)
if err != nil {
- log.Printf("json.Unmarshal failed: %v", err)
+ h.Logger.Error("json.Unmarshal failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid JSON")
return
}
@@ -1124,12 +1124,12 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
webhookID := webhookPayload.Meta.WebhookID
exists, err := h.BillingRepo.HasWebhookBeenProcessed(webhookID)
if err != nil {
- log.Printf("h.BillingRepo.HasWebhookBeenProcessed failed: %v", err)
+ h.Logger.Error("h.BillingRepo.HasWebhookBeenProcessed failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Error checking webhook")
return
}
if exists {
- log.Printf("Webhook %s already processed", webhookID)
+ h.Logger.Error("Webhook %s already processed", webhookID)
w.WriteHeader(http.StatusOK)
return
}
@@ -1140,91 +1140,91 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
eventType := webhookPayload.Meta.EventName
- log.Printf("Received webhook: eventType=%q, webhookID=%s, subscriptionID=%s", eventType, webhookID, subscriptionID)
+ h.Logger.Error("Received webhook: eventType=%q, webhookID=%s, subscriptionID=%s", eventType, webhookID, subscriptionID)
switch eventType {
case "order_created":
var orderAttrs billing.OrderAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &orderAttrs); err != nil {
- log.Printf("Unmarshal order_created failed: %v", err)
+ h.Logger.Error("Unmarshal order_created failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid order_created payload")
return
}
err = h.Billing.ApplyCredits(h.UserRepo, h.BillingRepo, orderAttrs.UserEmail, orderAttrs.FirstOrderItem.VariantID)
if err != nil {
- log.Printf("h.Billing.ApplyCredits failed: %v", err)
+ h.Logger.Error("h.Billing.ApplyCredits failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_created":
var SubCreatedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubCreatedAttrs); err != nil {
- log.Printf("Unmarshal subscription_created failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_created failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_created payload")
return
}
exists, err := h.UserRepo.HasActiveOrCancelledSubscription(SubCreatedAttrs.UserEmail)
if err != nil {
- log.Printf("Subscription duplicate check failed: %v", err)
+ h.Logger.Error("Subscription duplicate check failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Subscription check failed")
return
}
if exists {
- log.Printf("Duplicate subscription attempt blocked for %s", SubCreatedAttrs.UserEmail)
+ h.Logger.Error("Duplicate subscription attempt blocked for %s", SubCreatedAttrs.UserEmail)
return
}
err = h.Billing.CreateSubscription(h.UserRepo, SubCreatedAttrs, subscriptionID)
if err != nil {
- log.Printf("h.Billing.CreateSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.CreateSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_cancelled":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal subscription_cancelled failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_cancelled failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_cancelled payload")
return
}
err = h.Billing.CancelSubscription(h.UserRepo, emailAttribute.UserEmail)
if err != nil {
- log.Printf("h.Billing.CancelSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.CancelSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_resumed":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal subscription_resumed failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_resumed failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_resumed payload")
return
}
err = h.Billing.ResumeSubscription(h.UserRepo, emailAttribute.UserEmail)
if err != nil {
- log.Printf("h.Billing.ResumeSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.ResumeSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_expired":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal subscription_expired failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_expired failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_expired payload")
return
}
err = h.Billing.ExpireSubscription(h.UserRepo, h.BillingRepo, emailAttribute.UserEmail)
if err != nil {
- log.Printf("h.Billing.ExpireSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.ExpireSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_payment_success":
var SubRenewAttrs billing.SubscriptionRenewAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubRenewAttrs); err != nil {
- log.Printf("Unmarshal subscription_payment_success failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_payment_success failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_payment_success payload")
return
}
@@ -1236,74 +1236,74 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
err = h.Billing.RenewSubscription(h.UserRepo, h.BillingRepo, SubRenewAttrs)
if err != nil {
- log.Printf("h.Billing.RenewSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.RenewSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_plan_changed":
var SubChangedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubChangedAttrs); err != nil {
- log.Printf("Unmarshal subscription_plan_changed failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_plan_changed failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_plan_changed payload")
return
}
err = h.Billing.ChangeSubscription(h.UserRepo, h.BillingRepo, SubChangedAttrs)
if err != nil {
- log.Printf("h.Billing.ChangeSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.ChangeSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_updated":
var SubChangedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubChangedAttrs); err != nil {
- log.Printf("Unmarshal subscription_updated failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_updated failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_updated payload")
return
}
err = h.Billing.UpdateSubscription(h.UserRepo, SubChangedAttrs, subscriptionID)
if err != nil {
- log.Printf("h.Billing.UpdateSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.UpdateSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "order_refunded":
var orderAttrs billing.OrderAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &orderAttrs); err != nil {
- log.Printf("Unmarshal order_created failed: %v", err)
+ h.Logger.Error("Unmarshal order_created failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid order_created payload")
return
}
err = h.Billing.DeductCredits(h.UserRepo, h.BillingRepo, orderAttrs)
if err != nil {
- log.Printf("h.Billing.DeductCredits failed: %v", err)
+ h.Logger.Error("h.Billing.DeductCredits failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_payment_failed", "subscription_payment_recovered":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal %s failed: %v", eventType, err)
+ h.Logger.Error("Unmarshal %s failed: %v", eventType, err)
RespondWithError(w, http.StatusBadRequest, "Invalid payment status payload")
return
}
- log.Printf("Payment event: %s for user %s", eventType, emailAttribute.UserEmail)
+ h.Logger.Error("Payment event: %s for user %s", eventType, emailAttribute.UserEmail)
default:
- log.Printf("Unhandled event type: %s", eventType)
+ h.Logger.Error("Unhandled event type: %s", eventType)
RespondWithError(w, http.StatusNotImplemented, "Unhandled event type")
return
}
if err != nil {
- log.Printf("eventType switch func failed: %v", err)
+ h.Logger.Error("eventType switch func failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
err = h.BillingRepo.MarkWebhookProcessed(webhookID, eventType)
if err != nil {
- log.Printf("MarkWebhookProcessed failed: %v", err)
+ h.Logger.Error("MarkWebhookProcessed failed: %v", err)
w.WriteHeader(http.StatusOK)
return
}
@@ -1329,7 +1329,7 @@ func (h *Handler) DashboardHandler(w http.ResponseWriter, r *http.Request) {
RespondWithError(w, http.StatusUnauthorized, "User not found")
return
}
- log.Printf("dashboard.GetDashboardData failed: %v", err)
+ h.Logger.Error("dashboard.GetDashboardData failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not load dashboard")
return
}
@@ -1355,11 +1355,11 @@ func (h *Handler) JDInputHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("chatgpt.ExtractJDInput failed: %v", err)
+ h.Logger.Error("chatgpt.ExtractJDInput failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to process job description")
return
}
@@ -1368,11 +1368,11 @@ func (h *Handler) JDInputHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("chatgpt.ExtractJDSummary failed: %v", err)
+ h.Logger.Error("chatgpt.ExtractJDSummary failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to process job description")
return
diff --git a/handlers/model.go b/handlers/model.go
index f28abac..be887d3 100644
--- a/handlers/model.go
+++ b/handlers/model.go
@@ -2,6 +2,7 @@ package handlers
import (
"database/sql"
+ "log/slog"
"github.com/michaelboegner/interviewer/billing"
"github.com/michaelboegner/interviewer/chatgpt"
@@ -61,6 +62,7 @@ type Handler struct {
Mailer mailer.MailerClient
OpenAI chatgpt.AIClient
DB *sql.DB
+ Logger *slog.Logger
}
func NewHandler(
@@ -72,7 +74,8 @@ func NewHandler(
billing *billing.Billing,
mailer mailer.MailerClient,
openAI chatgpt.AIClient,
- db *sql.DB) *Handler {
+ db *sql.DB,
+ logger *slog.Logger) *Handler {
return &Handler{
InterviewRepo: interviewRepo,
UserRepo: userRepo,
@@ -83,5 +86,6 @@ func NewHandler(
Mailer: mailer,
OpenAI: openAI,
DB: db,
+ Logger: logger,
}
}
diff --git a/internal/server/server.go b/internal/server/server.go
index d3e9ca8..59e033b 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -23,7 +23,6 @@ type Server struct {
}
func NewServer(logger *slog.Logger) (*Server, error) {
- logger.Info("logger is functioning in server")
mux := http.NewServeMux()
db, err := database.StartDB()
@@ -44,7 +43,7 @@ func NewServer(logger *slog.Logger) (*Server, error) {
return nil, err
}
- handler := handlers.NewHandler(interviewRepo, userRepo, tokenRepo, conversationRepo, billingRepo, billing, mailer, openAI, db)
+ handler := handlers.NewHandler(interviewRepo, userRepo, tokenRepo, conversationRepo, billingRepo, billing, mailer, openAI, db, logger)
mux.Handle("/api/users", http.HandlerFunc(handler.CreateUsersHandler))
mux.Handle("/api/auth/login", http.HandlerFunc(handler.LoginHandler))
From bc914426255a854f1abc4ad06bc2468de1e7e2fc Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Thu, 18 Sep 2025 11:52:47 +0700
Subject: [PATCH 02/15] changing log.Printf to slogs
---
handlers/handlers.go | 234 +++++++++++++++++++++----------------------
1 file changed, 117 insertions(+), 117 deletions(-)
diff --git a/handlers/handlers.go b/handlers/handlers.go
index e337121..a07fd49 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -55,7 +55,7 @@ func (h *Handler) RequestVerificationHandler(w http.ResponseWriter, r *http.Requ
verificationJWT, err := user.VerificationToken(req.Email, req.Username, req.Password)
if err != nil {
- h.Logger.Error("GenerateEmailVerificationToken failed: %v", err)
+ h.Logger.Error("GenerateEmailVerificationToken failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to create token")
return
}
@@ -63,7 +63,7 @@ func (h *Handler) RequestVerificationHandler(w http.ResponseWriter, r *http.Requ
verifyURL := os.Getenv("FRONTEND_URL") + "verify-email?token=" + verificationJWT
go func() {
if err := h.Mailer.SendVerificationEmail(req.Email, verifyURL); err != nil {
- h.Logger.Error("SendVerificationEmail failed: %v", err)
+ h.Logger.Error("SendVerificationEmail failed", "error", err)
}
}()
@@ -84,7 +84,7 @@ func (h *Handler) CheckEmailHandler(w http.ResponseWriter, r *http.Request) {
Email string `json:"email"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- h.Logger.Error("Decoding check-email body failed: %v", err)
+ h.Logger.Error("Decoding check-email body failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
@@ -99,7 +99,7 @@ func (h *Handler) CheckEmailHandler(w http.ResponseWriter, r *http.Request) {
RespondWithJSON(w, http.StatusOK, map[string]bool{"exists": false})
return
}
- h.Logger.Error("CheckEmailHandler internal error: %v", err)
+ h.Logger.Error("CheckEmailHandler internal error", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -118,21 +118,21 @@ func (h *Handler) CreateUsersHandler(w http.ResponseWriter, r *http.Request) {
}
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
- h.Logger.Error("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
userCreated, err := user.CreateUser(h.UserRepo, req.Token)
if err != nil {
- h.Logger.Error("CreateUser error: %v", err)
+ h.Logger.Error("CreateUser error", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
err = h.Mailer.SendWelcome(userCreated.Email)
if err != nil {
- h.Logger.Error("h.Mailer.SendWelcome failed: %v", err)
+ h.Logger.Error("h.Mailer.SendWelcome failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -159,20 +159,20 @@ func (h *Handler) GetUsersHandler(w http.ResponseWriter, r *http.Request) {
userIDParam, err := GetPathID(r, "/api/users/")
if err != nil {
- h.Logger.Error("GetPathID error: %v\n", err)
+ h.Logger.Error("GetPathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "UserID required")
return
}
if userID != userIDParam {
- h.Logger.Error("UserID mismatch: %v vs. %v", userID, userIDParam)
+ h.Logger.Error("UserID mismatch", "got", userIDParam, "want", userID)
RespondWithError(w, http.StatusUnauthorized, "Invalid ID")
return
}
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- h.Logger.Error("GetUsers error: %v", err)
+ h.Logger.Error("GetUsers error", "error", err)
return
}
@@ -199,48 +199,48 @@ func (h *Handler) DeleteUserHandler(w http.ResponseWriter, r *http.Request) {
userIDParam, err := GetPathID(r, "/api/users/delete/")
if err != nil {
- h.Logger.Error("GetPathID error: %v\n", err)
+ h.Logger.Error("GetPathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "UserID required")
return
}
if userID != userIDParam {
- h.Logger.Error("UserID mismatch: %v vs. %v", userID, userIDParam)
+ h.Logger.Error("UserID mismatch", "got", userIDParam, "want", userID)
RespondWithError(w, http.StatusUnauthorized, "Invalid ID")
return
}
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- h.Logger.Error("GetUser error: %v", err)
+ h.Logger.Error("GetUser error", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to find user")
return
}
err = h.Billing.CancelSubscription(h.UserRepo, userReturned.Email)
if err != nil {
- h.Logger.Error("h.Billing.CancelSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.CancelSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
err = user.MarkUserDeleted(h.UserRepo, userID)
if err != nil {
- h.Logger.Error("DeleteUser failed: %v", err)
+ h.Logger.Error("DeleteUser failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to delete user")
return
}
err = token.DeleteRefreshToken(h.TokenRepo, userID)
if err != nil {
- h.Logger.Error("DeleteRefreshTokensForUser failed: %v", err)
+ h.Logger.Error("DeleteRefreshTokensForUser failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
err = h.Mailer.SendDeletionConfirmation(userReturned.Email)
if err != nil {
- h.Logger.Error("h.Mailer.SendDeletionConfirmation failed: %v", err)
+ h.Logger.Error("h.Mailer.SendDeletionConfirmation failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -257,7 +257,7 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- h.Logger.Error("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
@@ -271,7 +271,7 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
jwToken, username, userID, err := user.LoginUser(h.UserRepo, params.Email, params.Password)
if err != nil {
- h.Logger.Error("LoginUser error: %v", err)
+ h.Logger.Error("LoginUser error", "error", err)
if errors.Is(err, user.ErrAccountDeleted) {
RespondWithError(w, http.StatusUnauthorized, user.ErrAccountDeleted.Error())
return
@@ -282,7 +282,7 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, userID)
if err != nil {
- h.Logger.Error("RefreshToken error: %v", err)
+ h.Logger.Error("RefreshToken error", "error", err)
RespondWithError(w, http.StatusUnauthorized, "")
return
}
@@ -344,14 +344,14 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
client = &http.Client{}
req, err = http.NewRequest("GET", "https://api.github.com/user", nil)
if err != nil {
- h.Logger.Error("http.NewRequest failed: %v", err)
+ log.Printf("http.NewRequest failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
req.Header.Set("Authorization", "Bearer "+tokenResp.AccessToken)
githubResp, err := client.Do(req)
if err != nil {
- h.Logger.Error("GET api.github.com/user failed: %v", err)
+ log.Printf("GET api.github.com/user failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -366,14 +366,14 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
if githubUser.Email == "" {
req, err := http.NewRequest("GET", "https://api.github.com/user/emails", nil)
if err != nil {
- h.Logger.Error("http.NewRequest failed: %v", err)
+ log.Printf("http.NewRequest failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
req.Header.Set("Authorization", "Bearer "+tokenResp.AccessToken)
emailResp, err := client.Do(req)
if err != nil {
- h.Logger.Error("GET api.github.com/user/emails failed: %v", err)
+ log.Printf("GET api.github.com/user/emails failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -395,7 +395,7 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
}
if githubUser.Email == "" {
- h.Logger.Error("GitHub login failed: no verified email found for user %s", githubUser.Login)
+ log.Printf("GitHub login failed: no verified email found for user %s", githubUser.Login)
RespondWithError(w, http.StatusUnauthorized, "We couldn’t retrieve a valid email address from GitHub. Please check your GitHub email settings and try again.")
return
}
@@ -408,13 +408,13 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
jwt, err := token.CreateJWT(strconv.Itoa(user.ID), 0)
if err != nil {
- h.Logger.Error("token.CreateJWT failed: %v", err)
+ log.Printf("token.CreateJWT failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, user.ID)
if err != nil {
- h.Logger.Error("token.CreateRefreshToken failed: %v", err)
+ log.Printf("token.CreateRefreshToken failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -443,48 +443,48 @@ func (h *Handler) RefreshTokensHandler(w http.ResponseWriter, r *http.Request) {
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- h.Logger.Error("Decoding params failed: %v", err)
+ log.Printf("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
if params.UserID == 0 {
- h.Logger.Error("Invalid userID")
+ log.Printf("Invalid userID")
RespondWithError(w, http.StatusBadRequest, "Invalid username or password")
return
}
storedToken, err := token.GetStoredRefreshToken(h.TokenRepo, params.UserID)
if err != nil {
- h.Logger.Error("GetStoredRefreshToken error: %v", err)
+ log.Printf("GetStoredRefreshToken error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid user_id")
return
}
ok := token.VerifyRefreshToken(storedToken, providedToken)
if !ok {
- h.Logger.Error("VerifyRefreshToken error")
+ log.Printf("VerifyRefreshToken error")
RespondWithError(w, http.StatusUnauthorized, "Refresh token is invalid")
return
}
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, params.UserID)
if err != nil {
- h.Logger.Error("CreateRefreshToken error: %v", err)
+ log.Printf("CreateRefreshToken error: %v", err)
RespondWithError(w, http.StatusUnauthorized, "")
return
}
user, err := h.UserRepo.GetUser(params.UserID)
if err != nil || user.AccountStatus == "deleted" {
- h.Logger.Error("Refresh attempt for deleted account ID %d", params.UserID)
+ log.Printf("Refresh attempt for deleted account ID %d", params.UserID)
RespondWithError(w, http.StatusUnauthorized, "Account deactivated")
return
}
jwToken, err := token.CreateJWT(strconv.Itoa(params.UserID), 0)
if err != nil {
- h.Logger.Error("JWT creation failed: %v", err)
+ log.Printf("JWT creation failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "")
return
}
@@ -512,14 +512,14 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- h.Logger.Error("Decoding params failed: %v", err)
+ log.Printf("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- h.Logger.Error("GetUser error: %v", err)
+ log.Printf("GetUser error: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to find user")
return
}
@@ -537,7 +537,7 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- h.Logger.Error("OpenAI error: %v", openaiErr)
+ log.Printf("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
@@ -545,21 +545,21 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
RespondWithError(w, http.StatusPaymentRequired, "You do not have enough credits to start a new interview or your subscription has expired.")
return
}
- h.Logger.Error("Interview failed to start: %v", err)
+ log.Printf("Interview failed to start: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to start interview.")
return
}
conversationID, err := conversation.CreateEmptyConversation(h.ConversationRepo, interviewStarted.Id, interviewStarted.Subtopic)
if err != nil {
- h.Logger.Error("conversation.CreateEmptyConversation failed: %v", err)
+ log.Printf("conversation.CreateEmptyConversation failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
err = interview.LinkConversation(h.InterviewRepo, interviewStarted.Id, conversationID)
if err != nil {
- h.Logger.Error("interview.LinkConversation failed: %v", err)
+ log.Printf("interview.LinkConversation failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -587,19 +587,19 @@ func (h *Handler) GetInterviewHandler(w http.ResponseWriter, r *http.Request) {
interviewID, err := GetPathID(r, "/api/interviews/")
if err != nil {
- h.Logger.Error("GetPathID failed: %v", err)
+ log.Printf("GetPathID failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid interview ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- h.Logger.Error("GetInterview failed: %v", err)
+ log.Printf("GetInterview failed: %v", err)
RespondWithError(w, http.StatusNotFound, "Interview not found")
return
}
if interviewReturned.UserId != userID {
- h.Logger.Error("User ID mismatch on interview fetch")
+ log.Printf("User ID mismatch on interview fetch")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -639,21 +639,21 @@ func (h *Handler) UpdateInterviewStatusHandler(w http.ResponseWriter, r *http.Re
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- h.Logger.Error("GetInterview error: %v\n", err)
+ log.Printf("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
err = ValidateInterviewStatusTransition(interviewReturned.Status, payload.Status)
if err != nil {
- h.Logger.Error("ValidateInterviewStatusTransition failed: %v", err)
+ log.Printf("ValidateInterviewStatusTransition failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid status transition")
return
}
err = h.InterviewRepo.UpdateStatus(interviewID, userID, payload.Status)
if err != nil {
- h.Logger.Error("UpdateInterviewStatus failed: %v", err)
+ log.Printf("UpdateInterviewStatus failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not update status")
return
}
@@ -676,7 +676,7 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- h.Logger.Error("Decoding params failed: %v", err)
+ log.Printf("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
@@ -684,19 +684,19 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
interviewID, err := GetPathID(r, "/api/conversations/create/")
if err != nil {
- h.Logger.Error("PathID error: %v\n", err)
+ log.Printf("PathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- h.Logger.Error("GetInterview error: %v\n", err)
+ log.Printf("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- h.Logger.Error("interview.userid != token user_id")
+ log.Printf("interview.userid != token user_id")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -707,7 +707,7 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- h.Logger.Error("conversation.GetConversation failed: %v", err)
+ log.Printf("conversation.GetConversation failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
@@ -725,11 +725,11 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- h.Logger.Error("OpenAI error: %v", openaiErr)
+ log.Printf("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- h.Logger.Error("CreateConversation error: %v", err)
+ log.Printf("CreateConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid interview_id")
return
}
@@ -755,32 +755,32 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- h.Logger.Error("Decoding params failed: %v", err)
+ log.Printf("Decoding params failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
if params.Message == "" {
- h.Logger.Error("messageUserResponse is nil")
+ log.Printf("messageUserResponse is nil")
RespondWithError(w, http.StatusBadRequest, "Missing message")
return
}
interviewID, err := GetPathID(r, "/api/conversations/append/")
if err != nil {
- h.Logger.Error("PathID error: %v\n", err)
+ log.Printf("PathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- h.Logger.Error("GetInterview error: %v\n", err)
+ log.Printf("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- h.Logger.Error("interview.userid != token user_id")
+ log.Printf("interview.userid != token user_id")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -791,7 +791,7 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- h.Logger.Error("GetConversation error: %v", err)
+ log.Printf("GetConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -808,11 +808,11 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- h.Logger.Error("OpenAI error: %v", openaiErr)
+ log.Printf("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- h.Logger.Error("AppendConversation error: %v", err)
+ log.Printf("AppendConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -837,26 +837,26 @@ func (h *Handler) GetConversationHandler(w http.ResponseWriter, r *http.Request)
interviewID, err := GetPathID(r, "/api/conversations/")
if err != nil {
- h.Logger.Error("PathID error: %v\n", err)
+ log.Printf("PathID error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- h.Logger.Error("GetInterview error: %v\n", err)
+ log.Printf("GetInterview error: %v\n", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- h.Logger.Error("interview.userid != token user_id")
+ log.Printf("interview.userid != token user_id")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- h.Logger.Error("GetConversation error: %v", err)
+ log.Printf("GetConversation error: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -876,14 +876,14 @@ func (h *Handler) RequestResetHandler(w http.ResponseWriter, r *http.Request) {
var params PasswordResetRequest
err := json.NewDecoder(r.Body).Decode(¶ms)
if err != nil {
- h.Logger.Error("Decoding request failed: %v", err)
+ log.Printf("Decoding request failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
resetJWT, err := user.RequestPasswordReset(h.UserRepo, params.Email)
if err != nil {
- h.Logger.Error("Error generating reset token for email %s: %v", params.Email, err)
+ log.Printf("Error generating reset token for email %s: %v", params.Email, err)
w.WriteHeader(http.StatusOK)
return
}
@@ -894,7 +894,7 @@ func (h *Handler) RequestResetHandler(w http.ResponseWriter, r *http.Request) {
go func(email, resetURL string) {
err := h.Mailer.SendPasswordReset(email, resetURL)
if err != nil {
- h.Logger.Error("SendPasswordReset error: %v", err)
+ log.Printf("SendPasswordReset error: %v", err)
return
}
}(params.Email, resetURL)
@@ -911,13 +911,13 @@ func (h *Handler) ResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
var params PasswordResetPayload
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil {
- h.Logger.Error("Decoding payload failed: %v", err)
+ log.Printf("Decoding payload failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
err := user.ResetPassword(h.UserRepo, params.NewPassword, params.Token)
if err != nil {
- h.Logger.Error("ResetPasswordHandler failed: %v", err)
+ log.Printf("ResetPasswordHandler failed: %v", err)
RespondWithError(w, http.StatusUnauthorized, "Invalid or expired token")
return
}
@@ -934,14 +934,14 @@ func (h *Handler) CreateCheckoutSessionHandler(w http.ResponseWriter, r *http.Re
userID, ok := r.Context().Value(middleware.ContextKeyTokenParams).(int)
if !ok {
- h.Logger.Error("r.Context().Value() failed")
+ log.Printf("r.Context().Value() failed")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
var params CheckoutRequest
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil || params.Tier == "" {
- h.Logger.Error("jsonNewDecoder failed: %v", err)
+ log.Printf("jsonNewDecoder failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Missing or invalid tier")
return
}
@@ -967,14 +967,14 @@ func (h *Handler) CreateCheckoutSessionHandler(w http.ResponseWriter, r *http.Re
priceIDInt, err := strconv.Atoi(priceID)
if err != nil {
- h.Logger.Error("strconv.Atoi() failed: %v", err)
+ log.Printf("strconv.Atoi() failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
url, err := h.Billing.RequestCheckoutSession(user.Email, priceIDInt)
if err != nil {
- h.Logger.Error("billing.CreateCheckoutSession failed: %v", err)
+ log.Printf("billing.CreateCheckoutSession failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not start checkout")
return
}
@@ -996,14 +996,14 @@ func (h *Handler) CancelSubscriptionHandler(w http.ResponseWriter, r *http.Reque
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- h.Logger.Error("GetUser failed: %v", err)
+ log.Printf("GetUser failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not retrieve user")
return
}
err = h.Billing.RequestDeleteSubscription(userReturned.SubscriptionID)
if err != nil {
- h.Logger.Error("DeleteSubscription failed: %v", err)
+ log.Printf("DeleteSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not cancel subscription")
return
}
@@ -1025,14 +1025,14 @@ func (h *Handler) ResumeSubscriptionHandler(w http.ResponseWriter, r *http.Reque
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- h.Logger.Error("GetUser failed: %v", err)
+ log.Printf("GetUser failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not retrieve user")
return
}
err = h.Billing.RequestResumeSubscription(userReturned.SubscriptionID)
if err != nil {
- h.Logger.Error("DeleteSubscription failed: %v", err)
+ log.Printf("DeleteSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not cancel subscription")
return
}
@@ -1054,7 +1054,7 @@ func (h *Handler) ChangePlanHandler(w http.ResponseWriter, r *http.Request) {
var params CheckoutRequest
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil || params.Tier == "" {
- h.Logger.Error("jsonNewDecoder failed: %v", err)
+ log.Printf("jsonNewDecoder failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Missing or invalid tier")
return
}
@@ -1078,13 +1078,13 @@ func (h *Handler) ChangePlanHandler(w http.ResponseWriter, r *http.Request) {
priceIDInt, err := strconv.Atoi(priceID)
if err != nil {
- h.Logger.Error("strconv.Atoi() failed: %v", err)
+ log.Printf("strconv.Atoi() failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
if err := h.Billing.RequestUpdateSubscriptionVariant(user.SubscriptionID, priceIDInt); err != nil {
- h.Logger.Error("UpdateLemonSubscriptionVariant failed: %v", err)
+ log.Printf("UpdateLemonSubscriptionVariant failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update subscription")
return
}
@@ -1100,7 +1100,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
body, err := io.ReadAll(r.Body)
if err != nil {
- h.Logger.Error("io.ReadAll failed: %v", err)
+ log.Printf("io.ReadAll failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Bad Request")
return
}
@@ -1108,7 +1108,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
signature := r.Header.Get("X-Signature")
if !h.Billing.VerifyBillingSignature(signature, body, os.Getenv("LEMON_WEBHOOK_SECRET")) {
- h.Logger.Error("Invalid billing event signature")
+ log.Printf("Invalid billing event signature")
RespondWithError(w, http.StatusUnauthorized, "Invalid signature")
return
}
@@ -1116,7 +1116,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
var webhookPayload billing.BillingWebhookPayload
err = json.Unmarshal(body, &webhookPayload)
if err != nil {
- h.Logger.Error("json.Unmarshal failed: %v", err)
+ log.Printf("json.Unmarshal failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid JSON")
return
}
@@ -1124,12 +1124,12 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
webhookID := webhookPayload.Meta.WebhookID
exists, err := h.BillingRepo.HasWebhookBeenProcessed(webhookID)
if err != nil {
- h.Logger.Error("h.BillingRepo.HasWebhookBeenProcessed failed: %v", err)
+ log.Printf("h.BillingRepo.HasWebhookBeenProcessed failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Error checking webhook")
return
}
if exists {
- h.Logger.Error("Webhook %s already processed", webhookID)
+ log.Printf("Webhook %s already processed", webhookID)
w.WriteHeader(http.StatusOK)
return
}
@@ -1140,91 +1140,91 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
eventType := webhookPayload.Meta.EventName
- h.Logger.Error("Received webhook: eventType=%q, webhookID=%s, subscriptionID=%s", eventType, webhookID, subscriptionID)
+ log.Printf("Received webhook: eventType=%q, webhookID=%s, subscriptionID=%s", eventType, webhookID, subscriptionID)
switch eventType {
case "order_created":
var orderAttrs billing.OrderAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &orderAttrs); err != nil {
- h.Logger.Error("Unmarshal order_created failed: %v", err)
+ log.Printf("Unmarshal order_created failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid order_created payload")
return
}
err = h.Billing.ApplyCredits(h.UserRepo, h.BillingRepo, orderAttrs.UserEmail, orderAttrs.FirstOrderItem.VariantID)
if err != nil {
- h.Logger.Error("h.Billing.ApplyCredits failed: %v", err)
+ log.Printf("h.Billing.ApplyCredits failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_created":
var SubCreatedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubCreatedAttrs); err != nil {
- h.Logger.Error("Unmarshal subscription_created failed: %v", err)
+ log.Printf("Unmarshal subscription_created failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_created payload")
return
}
exists, err := h.UserRepo.HasActiveOrCancelledSubscription(SubCreatedAttrs.UserEmail)
if err != nil {
- h.Logger.Error("Subscription duplicate check failed: %v", err)
+ log.Printf("Subscription duplicate check failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Subscription check failed")
return
}
if exists {
- h.Logger.Error("Duplicate subscription attempt blocked for %s", SubCreatedAttrs.UserEmail)
+ log.Printf("Duplicate subscription attempt blocked for %s", SubCreatedAttrs.UserEmail)
return
}
err = h.Billing.CreateSubscription(h.UserRepo, SubCreatedAttrs, subscriptionID)
if err != nil {
- h.Logger.Error("h.Billing.CreateSubscription failed: %v", err)
+ log.Printf("h.Billing.CreateSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_cancelled":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- h.Logger.Error("Unmarshal subscription_cancelled failed: %v", err)
+ log.Printf("Unmarshal subscription_cancelled failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_cancelled payload")
return
}
err = h.Billing.CancelSubscription(h.UserRepo, emailAttribute.UserEmail)
if err != nil {
- h.Logger.Error("h.Billing.CancelSubscription failed: %v", err)
+ log.Printf("h.Billing.CancelSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_resumed":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- h.Logger.Error("Unmarshal subscription_resumed failed: %v", err)
+ log.Printf("Unmarshal subscription_resumed failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_resumed payload")
return
}
err = h.Billing.ResumeSubscription(h.UserRepo, emailAttribute.UserEmail)
if err != nil {
- h.Logger.Error("h.Billing.ResumeSubscription failed: %v", err)
+ log.Printf("h.Billing.ResumeSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_expired":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- h.Logger.Error("Unmarshal subscription_expired failed: %v", err)
+ log.Printf("Unmarshal subscription_expired failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_expired payload")
return
}
err = h.Billing.ExpireSubscription(h.UserRepo, h.BillingRepo, emailAttribute.UserEmail)
if err != nil {
- h.Logger.Error("h.Billing.ExpireSubscription failed: %v", err)
+ log.Printf("h.Billing.ExpireSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_payment_success":
var SubRenewAttrs billing.SubscriptionRenewAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubRenewAttrs); err != nil {
- h.Logger.Error("Unmarshal subscription_payment_success failed: %v", err)
+ log.Printf("Unmarshal subscription_payment_success failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_payment_success payload")
return
}
@@ -1236,74 +1236,74 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
err = h.Billing.RenewSubscription(h.UserRepo, h.BillingRepo, SubRenewAttrs)
if err != nil {
- h.Logger.Error("h.Billing.RenewSubscription failed: %v", err)
+ log.Printf("h.Billing.RenewSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_plan_changed":
var SubChangedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubChangedAttrs); err != nil {
- h.Logger.Error("Unmarshal subscription_plan_changed failed: %v", err)
+ log.Printf("Unmarshal subscription_plan_changed failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_plan_changed payload")
return
}
err = h.Billing.ChangeSubscription(h.UserRepo, h.BillingRepo, SubChangedAttrs)
if err != nil {
- h.Logger.Error("h.Billing.ChangeSubscription failed: %v", err)
+ log.Printf("h.Billing.ChangeSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_updated":
var SubChangedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubChangedAttrs); err != nil {
- h.Logger.Error("Unmarshal subscription_updated failed: %v", err)
+ log.Printf("Unmarshal subscription_updated failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_updated payload")
return
}
err = h.Billing.UpdateSubscription(h.UserRepo, SubChangedAttrs, subscriptionID)
if err != nil {
- h.Logger.Error("h.Billing.UpdateSubscription failed: %v", err)
+ log.Printf("h.Billing.UpdateSubscription failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "order_refunded":
var orderAttrs billing.OrderAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &orderAttrs); err != nil {
- h.Logger.Error("Unmarshal order_created failed: %v", err)
+ log.Printf("Unmarshal order_created failed: %v", err)
RespondWithError(w, http.StatusBadRequest, "Invalid order_created payload")
return
}
err = h.Billing.DeductCredits(h.UserRepo, h.BillingRepo, orderAttrs)
if err != nil {
- h.Logger.Error("h.Billing.DeductCredits failed: %v", err)
+ log.Printf("h.Billing.DeductCredits failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_payment_failed", "subscription_payment_recovered":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- h.Logger.Error("Unmarshal %s failed: %v", eventType, err)
+ log.Printf("Unmarshal %s failed: %v", eventType, err)
RespondWithError(w, http.StatusBadRequest, "Invalid payment status payload")
return
}
- h.Logger.Error("Payment event: %s for user %s", eventType, emailAttribute.UserEmail)
+ log.Printf("Payment event: %s for user %s", eventType, emailAttribute.UserEmail)
default:
- h.Logger.Error("Unhandled event type: %s", eventType)
+ log.Printf("Unhandled event type: %s", eventType)
RespondWithError(w, http.StatusNotImplemented, "Unhandled event type")
return
}
if err != nil {
- h.Logger.Error("eventType switch func failed: %v", err)
+ log.Printf("eventType switch func failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
err = h.BillingRepo.MarkWebhookProcessed(webhookID, eventType)
if err != nil {
- h.Logger.Error("MarkWebhookProcessed failed: %v", err)
+ log.Printf("MarkWebhookProcessed failed: %v", err)
w.WriteHeader(http.StatusOK)
return
}
@@ -1329,7 +1329,7 @@ func (h *Handler) DashboardHandler(w http.ResponseWriter, r *http.Request) {
RespondWithError(w, http.StatusUnauthorized, "User not found")
return
}
- h.Logger.Error("dashboard.GetDashboardData failed: %v", err)
+ log.Printf("dashboard.GetDashboardData failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Could not load dashboard")
return
}
@@ -1355,11 +1355,11 @@ func (h *Handler) JDInputHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- h.Logger.Error("OpenAI error: %v", openaiErr)
+ log.Printf("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- h.Logger.Error("chatgpt.ExtractJDInput failed: %v", err)
+ log.Printf("chatgpt.ExtractJDInput failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to process job description")
return
}
@@ -1368,11 +1368,11 @@ func (h *Handler) JDInputHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- h.Logger.Error("OpenAI error: %v", openaiErr)
+ log.Printf("OpenAI error: %v", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- h.Logger.Error("chatgpt.ExtractJDSummary failed: %v", err)
+ log.Printf("chatgpt.ExtractJDSummary failed: %v", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to process job description")
return
From 0c7653d50d708ee050ffe4ef3be0158512f33957 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Fri, 19 Sep 2025 12:12:56 +0700
Subject: [PATCH 03/15] changed out log.Printfs for slog Errors and Infos
---
handlers/handlers.go | 204 ++++++++++++++++++++++---------------------
handlers/helpers.go | 2 +-
2 files changed, 105 insertions(+), 101 deletions(-)
diff --git a/handlers/handlers.go b/handlers/handlers.go
index a07fd49..fab1bdf 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -5,7 +5,6 @@ import (
"encoding/json"
"errors"
"io"
- "log"
"net/http"
"net/url"
"os"
@@ -344,14 +343,14 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
client = &http.Client{}
req, err = http.NewRequest("GET", "https://api.github.com/user", nil)
if err != nil {
- log.Printf("http.NewRequest failed: %v", err)
+ h.Logger.Error("http.NewRequest failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
req.Header.Set("Authorization", "Bearer "+tokenResp.AccessToken)
githubResp, err := client.Do(req)
if err != nil {
- log.Printf("GET api.github.com/user failed: %v", err)
+ h.Logger.Error("GET api.github.com/user failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -366,14 +365,14 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
if githubUser.Email == "" {
req, err := http.NewRequest("GET", "https://api.github.com/user/emails", nil)
if err != nil {
- log.Printf("http.NewRequest failed: %v", err)
+ h.Logger.Error("http.NewRequest failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
req.Header.Set("Authorization", "Bearer "+tokenResp.AccessToken)
emailResp, err := client.Do(req)
if err != nil {
- log.Printf("GET api.github.com/user/emails failed: %v", err)
+ h.Logger.Error("GET api.github.com/user/emails failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -395,7 +394,7 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
}
if githubUser.Email == "" {
- log.Printf("GitHub login failed: no verified email found for user %s", githubUser.Login)
+ h.Logger.Error("GitHub login failed: no verified email found for user", "userEmail", githubUser.Login)
RespondWithError(w, http.StatusUnauthorized, "We couldn’t retrieve a valid email address from GitHub. Please check your GitHub email settings and try again.")
return
}
@@ -408,13 +407,13 @@ func (h *Handler) GithubLoginHandler(w http.ResponseWriter, r *http.Request) {
jwt, err := token.CreateJWT(strconv.Itoa(user.ID), 0)
if err != nil {
- log.Printf("token.CreateJWT failed: %v", err)
+ h.Logger.Error("token.CreateJWT failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, user.ID)
if err != nil {
- log.Printf("token.CreateRefreshToken failed: %v", err)
+ h.Logger.Error("token.CreateRefreshToken failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -443,48 +442,53 @@ func (h *Handler) RefreshTokensHandler(w http.ResponseWriter, r *http.Request) {
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
if params.UserID == 0 {
- log.Printf("Invalid userID")
+ h.Logger.Error("Invalid userID")
RespondWithError(w, http.StatusBadRequest, "Invalid username or password")
return
}
storedToken, err := token.GetStoredRefreshToken(h.TokenRepo, params.UserID)
if err != nil {
- log.Printf("GetStoredRefreshToken error: %v", err)
+ h.Logger.Error("GetStoredRefreshToken error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid user_id")
return
}
ok := token.VerifyRefreshToken(storedToken, providedToken)
if !ok {
- log.Printf("VerifyRefreshToken error")
+ h.Logger.Error("VerifyRefreshToken error")
RespondWithError(w, http.StatusUnauthorized, "Refresh token is invalid")
return
}
refreshToken, err := token.CreateRefreshToken(h.TokenRepo, params.UserID)
if err != nil {
- log.Printf("CreateRefreshToken error: %v", err)
+ h.Logger.Error("CreateRefreshToken error", "error", err)
RespondWithError(w, http.StatusUnauthorized, "")
return
}
user, err := h.UserRepo.GetUser(params.UserID)
- if err != nil || user.AccountStatus == "deleted" {
- log.Printf("Refresh attempt for deleted account ID %d", params.UserID)
+ if err != nil {
+ h.Logger.Error("h.UserRepo.GetUser error", "error", err)
+ RespondWithError(w, http.StatusUnauthorized, "Account deactivated")
+ return
+ }
+ if user.AccountStatus == "deleted" {
+ h.Logger.Error("Refresh attempt for deleted account ID", "userID", params.UserID)
RespondWithError(w, http.StatusUnauthorized, "Account deactivated")
return
}
jwToken, err := token.CreateJWT(strconv.Itoa(params.UserID), 0)
if err != nil {
- log.Printf("JWT creation failed: %v", err)
+ h.Logger.Error("JWT creation failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "")
return
}
@@ -512,14 +516,14 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUser error: %v", err)
+ h.Logger.Error("GetUser error", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to find user")
return
}
@@ -537,7 +541,7 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error", "error", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
@@ -545,21 +549,21 @@ func (h *Handler) InterviewsHandler(w http.ResponseWriter, r *http.Request) {
RespondWithError(w, http.StatusPaymentRequired, "You do not have enough credits to start a new interview or your subscription has expired.")
return
}
- log.Printf("Interview failed to start: %v", err)
+ h.Logger.Error("Interview failed to start", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to start interview.")
return
}
conversationID, err := conversation.CreateEmptyConversation(h.ConversationRepo, interviewStarted.Id, interviewStarted.Subtopic)
if err != nil {
- log.Printf("conversation.CreateEmptyConversation failed: %v", err)
+ h.Logger.Error("conversation.CreateEmptyConversation failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
err = interview.LinkConversation(h.InterviewRepo, interviewStarted.Id, conversationID)
if err != nil {
- log.Printf("interview.LinkConversation failed: %v", err)
+ h.Logger.Error("interview.LinkConversation failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
@@ -587,19 +591,19 @@ func (h *Handler) GetInterviewHandler(w http.ResponseWriter, r *http.Request) {
interviewID, err := GetPathID(r, "/api/interviews/")
if err != nil {
- log.Printf("GetPathID failed: %v", err)
+ h.Logger.Error("GetPathID failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid interview ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview failed: %v", err)
+ h.Logger.Error("GetInterview failed", "error", err)
RespondWithError(w, http.StatusNotFound, "Interview not found")
return
}
if interviewReturned.UserId != userID {
- log.Printf("User ID mismatch on interview fetch")
+ h.Logger.Error("User ID mismatch on interview fetch", "got", userID, "want", interviewReturned.UserId)
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -639,21 +643,21 @@ func (h *Handler) UpdateInterviewStatusHandler(w http.ResponseWriter, r *http.Re
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
err = ValidateInterviewStatusTransition(interviewReturned.Status, payload.Status)
if err != nil {
- log.Printf("ValidateInterviewStatusTransition failed: %v", err)
+ h.Logger.Error("ValidateInterviewStatusTransition failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid status transition")
return
}
err = h.InterviewRepo.UpdateStatus(interviewID, userID, payload.Status)
if err != nil {
- log.Printf("UpdateInterviewStatus failed: %v", err)
+ h.Logger.Error("UpdateInterviewStatus failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Could not update status")
return
}
@@ -676,7 +680,7 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
@@ -684,19 +688,19 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
interviewID, err := GetPathID(r, "/api/conversations/create/")
if err != nil {
- log.Printf("PathID error: %v\n", err)
+ h.Logger.Error("PathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- log.Printf("interview.userid != token user_id")
+ h.Logger.Error("userID does not exist", "got", userID, "want", interviewReturned.UserId)
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -707,7 +711,7 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- log.Printf("conversation.GetConversation failed: %v", err)
+ h.Logger.Error("conversation.GetConversation failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
@@ -725,11 +729,11 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error", "error", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("CreateConversation error: %v", err)
+ h.Logger.Error("CreateConversation error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid interview_id")
return
}
@@ -755,32 +759,32 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
params := &middleware.AcceptedVals{}
err := json.NewDecoder(r.Body).Decode(params)
if err != nil {
- log.Printf("Decoding params failed: %v", err)
+ h.Logger.Error("Decoding params failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal Server Error")
return
}
if params.Message == "" {
- log.Printf("messageUserResponse is nil")
+ h.Logger.Error("messageUserResponse is nil")
RespondWithError(w, http.StatusBadRequest, "Missing message")
return
}
interviewID, err := GetPathID(r, "/api/conversations/append/")
if err != nil {
- log.Printf("PathID error: %v\n", err)
+ h.Logger.Error("PathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- log.Printf("interview.userid != token user_id")
+ h.Logger.Error("incorrect userID", "got", userID, "want", interviewReturned.UserId)
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
@@ -791,7 +795,7 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- log.Printf("GetConversation error: %v", err)
+ h.Logger.Error("GetConversation error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -808,11 +812,11 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error", "error", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("AppendConversation error: %v", err)
+ h.Logger.Error("AppendConversation error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -837,26 +841,26 @@ func (h *Handler) GetConversationHandler(w http.ResponseWriter, r *http.Request)
interviewID, err := GetPathID(r, "/api/conversations/")
if err != nil {
- log.Printf("PathID error: %v\n", err)
+ h.Logger.Error("PathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
return
}
interviewReturned, err := interview.GetInterview(h.InterviewRepo, interviewID)
if err != nil {
- log.Printf("GetInterview error: %v\n", err)
+ h.Logger.Error("GetInterview error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID")
return
}
if interviewReturned.UserId != userID {
- log.Printf("interview.userid != token user_id")
+ h.Logger.Error("incorrect userID", "got", userID, "want", interviewReturned.UserId)
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
conversationReturned, err := conversation.GetConversation(h.ConversationRepo, interviewID)
if err != nil {
- log.Printf("GetConversation error: %v", err)
+ h.Logger.Error("GetConversation error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid ID.")
return
}
@@ -876,14 +880,14 @@ func (h *Handler) RequestResetHandler(w http.ResponseWriter, r *http.Request) {
var params PasswordResetRequest
err := json.NewDecoder(r.Body).Decode(¶ms)
if err != nil {
- log.Printf("Decoding request failed: %v", err)
+ h.Logger.Error("Decoding request failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
resetJWT, err := user.RequestPasswordReset(h.UserRepo, params.Email)
if err != nil {
- log.Printf("Error generating reset token for email %s: %v", params.Email, err)
+ h.Logger.Error("Error generating reset token for email", "error", err)
w.WriteHeader(http.StatusOK)
return
}
@@ -894,7 +898,7 @@ func (h *Handler) RequestResetHandler(w http.ResponseWriter, r *http.Request) {
go func(email, resetURL string) {
err := h.Mailer.SendPasswordReset(email, resetURL)
if err != nil {
- log.Printf("SendPasswordReset error: %v", err)
+ h.Logger.Error("SendPasswordReset error", "error", err)
return
}
}(params.Email, resetURL)
@@ -911,13 +915,13 @@ func (h *Handler) ResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
var params PasswordResetPayload
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil {
- log.Printf("Decoding payload failed: %v", err)
+ h.Logger.Error("Decoding payload failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid request body")
return
}
err := user.ResetPassword(h.UserRepo, params.NewPassword, params.Token)
if err != nil {
- log.Printf("ResetPasswordHandler failed: %v", err)
+ h.Logger.Error("ResetPasswordHandler failed", "error", err)
RespondWithError(w, http.StatusUnauthorized, "Invalid or expired token")
return
}
@@ -934,14 +938,14 @@ func (h *Handler) CreateCheckoutSessionHandler(w http.ResponseWriter, r *http.Re
userID, ok := r.Context().Value(middleware.ContextKeyTokenParams).(int)
if !ok {
- log.Printf("r.Context().Value() failed")
+ h.Logger.Error("r.Context().Value() error")
RespondWithError(w, http.StatusUnauthorized, "Unauthorized")
return
}
var params CheckoutRequest
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil || params.Tier == "" {
- log.Printf("jsonNewDecoder failed: %v", err)
+ h.Logger.Error("jsonNewDecoder failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Missing or invalid tier")
return
}
@@ -967,14 +971,14 @@ func (h *Handler) CreateCheckoutSessionHandler(w http.ResponseWriter, r *http.Re
priceIDInt, err := strconv.Atoi(priceID)
if err != nil {
- log.Printf("strconv.Atoi() failed: %v", err)
+ h.Logger.Error("strconv.Atoi() failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
url, err := h.Billing.RequestCheckoutSession(user.Email, priceIDInt)
if err != nil {
- log.Printf("billing.CreateCheckoutSession failed: %v", err)
+ h.Logger.Error("billing.CreateCheckoutSession failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Could not start checkout")
return
}
@@ -996,14 +1000,14 @@ func (h *Handler) CancelSubscriptionHandler(w http.ResponseWriter, r *http.Reque
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUser failed: %v", err)
+ h.Logger.Error("GetUser failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Could not retrieve user")
return
}
err = h.Billing.RequestDeleteSubscription(userReturned.SubscriptionID)
if err != nil {
- log.Printf("DeleteSubscription failed: %v", err)
+ h.Logger.Error("DeleteSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Could not cancel subscription")
return
}
@@ -1025,14 +1029,14 @@ func (h *Handler) ResumeSubscriptionHandler(w http.ResponseWriter, r *http.Reque
userReturned, err := user.GetUser(h.UserRepo, userID)
if err != nil {
- log.Printf("GetUser failed: %v", err)
+ h.Logger.Error("GetUser failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Could not retrieve user")
return
}
err = h.Billing.RequestResumeSubscription(userReturned.SubscriptionID)
if err != nil {
- log.Printf("DeleteSubscription failed: %v", err)
+ h.Logger.Error("DeleteSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Could not cancel subscription")
return
}
@@ -1054,7 +1058,7 @@ func (h *Handler) ChangePlanHandler(w http.ResponseWriter, r *http.Request) {
var params CheckoutRequest
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil || params.Tier == "" {
- log.Printf("jsonNewDecoder failed: %v", err)
+ h.Logger.Error("jsonNewDecoder failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Missing or invalid tier")
return
}
@@ -1078,13 +1082,13 @@ func (h *Handler) ChangePlanHandler(w http.ResponseWriter, r *http.Request) {
priceIDInt, err := strconv.Atoi(priceID)
if err != nil {
- log.Printf("strconv.Atoi() failed: %v", err)
+ h.Logger.Error("strconv.Atoi() failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
if err := h.Billing.RequestUpdateSubscriptionVariant(user.SubscriptionID, priceIDInt); err != nil {
- log.Printf("UpdateLemonSubscriptionVariant failed: %v", err)
+ h.Logger.Error("UpdateLemonSubscriptionVariant failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update subscription")
return
}
@@ -1100,7 +1104,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
body, err := io.ReadAll(r.Body)
if err != nil {
- log.Printf("io.ReadAll failed: %v", err)
+ h.Logger.Error("io.ReadAll failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Bad Request")
return
}
@@ -1108,7 +1112,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
signature := r.Header.Get("X-Signature")
if !h.Billing.VerifyBillingSignature(signature, body, os.Getenv("LEMON_WEBHOOK_SECRET")) {
- log.Printf("Invalid billing event signature")
+ h.Logger.Error("Invalid billing event signature")
RespondWithError(w, http.StatusUnauthorized, "Invalid signature")
return
}
@@ -1116,7 +1120,7 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
var webhookPayload billing.BillingWebhookPayload
err = json.Unmarshal(body, &webhookPayload)
if err != nil {
- log.Printf("json.Unmarshal failed: %v", err)
+ h.Logger.Error("json.Unmarshal failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid JSON")
return
}
@@ -1124,12 +1128,12 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
webhookID := webhookPayload.Meta.WebhookID
exists, err := h.BillingRepo.HasWebhookBeenProcessed(webhookID)
if err != nil {
- log.Printf("h.BillingRepo.HasWebhookBeenProcessed failed: %v", err)
+ h.Logger.Error("h.BillingRepo.HasWebhookBeenProcessed failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Error checking webhook")
return
}
if exists {
- log.Printf("Webhook %s already processed", webhookID)
+ h.Logger.Info("Webhook already processed", "webhookID", webhookID)
w.WriteHeader(http.StatusOK)
return
}
@@ -1140,170 +1144,170 @@ func (h *Handler) BillingWebhookHandler(w http.ResponseWriter, r *http.Request)
eventType := webhookPayload.Meta.EventName
- log.Printf("Received webhook: eventType=%q, webhookID=%s, subscriptionID=%s", eventType, webhookID, subscriptionID)
+ h.Logger.Info("Received webhook", "eventType", eventType, "webhookID", webhookID, "subscriptionID", subscriptionID)
switch eventType {
case "order_created":
var orderAttrs billing.OrderAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &orderAttrs); err != nil {
- log.Printf("Unmarshal order_created failed: %v", err)
+ h.Logger.Error("Unmarshal order_created failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid order_created payload")
return
}
err = h.Billing.ApplyCredits(h.UserRepo, h.BillingRepo, orderAttrs.UserEmail, orderAttrs.FirstOrderItem.VariantID)
if err != nil {
- log.Printf("h.Billing.ApplyCredits failed: %v", err)
+ h.Logger.Error("h.Billing.ApplyCredits failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_created":
var SubCreatedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubCreatedAttrs); err != nil {
- log.Printf("Unmarshal subscription_created failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_created failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_created payload")
return
}
exists, err := h.UserRepo.HasActiveOrCancelledSubscription(SubCreatedAttrs.UserEmail)
if err != nil {
- log.Printf("Subscription duplicate check failed: %v", err)
+ h.Logger.Error("Subscription duplicate check failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Subscription check failed")
return
}
if exists {
- log.Printf("Duplicate subscription attempt blocked for %s", SubCreatedAttrs.UserEmail)
+ h.Logger.Info("Duplicate subscription attempt blocked", "userEmail", SubCreatedAttrs.UserEmail)
return
}
err = h.Billing.CreateSubscription(h.UserRepo, SubCreatedAttrs, subscriptionID)
if err != nil {
- log.Printf("h.Billing.CreateSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.CreateSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_cancelled":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal subscription_cancelled failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_cancelled failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_cancelled payload")
return
}
err = h.Billing.CancelSubscription(h.UserRepo, emailAttribute.UserEmail)
if err != nil {
- log.Printf("h.Billing.CancelSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.CancelSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_resumed":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal subscription_resumed failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_resumed failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_resumed payload")
return
}
err = h.Billing.ResumeSubscription(h.UserRepo, emailAttribute.UserEmail)
if err != nil {
- log.Printf("h.Billing.ResumeSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.ResumeSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_expired":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal subscription_expired failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_expired failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_expired payload")
return
}
err = h.Billing.ExpireSubscription(h.UserRepo, h.BillingRepo, emailAttribute.UserEmail)
if err != nil {
- log.Printf("h.Billing.ExpireSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.ExpireSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_payment_success":
var SubRenewAttrs billing.SubscriptionRenewAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubRenewAttrs); err != nil {
- log.Printf("Unmarshal subscription_payment_success failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_payment_success failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_payment_success payload")
return
}
if SubRenewAttrs.BillingReason == "initial" {
- log.Println("Skipping credits on initial charge (already granted via order_created)")
+ h.Logger.Info("Skipping credits on initial charge (already granted via order_created)")
return
}
err = h.Billing.RenewSubscription(h.UserRepo, h.BillingRepo, SubRenewAttrs)
if err != nil {
- log.Printf("h.Billing.RenewSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.RenewSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_plan_changed":
var SubChangedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubChangedAttrs); err != nil {
- log.Printf("Unmarshal subscription_plan_changed failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_plan_changed failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_plan_changed payload")
return
}
err = h.Billing.ChangeSubscription(h.UserRepo, h.BillingRepo, SubChangedAttrs)
if err != nil {
- log.Printf("h.Billing.ChangeSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.ChangeSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_updated":
var SubChangedAttrs billing.SubscriptionAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &SubChangedAttrs); err != nil {
- log.Printf("Unmarshal subscription_updated failed: %v", err)
+ h.Logger.Error("Unmarshal subscription_updated failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid subscription_updated payload")
return
}
err = h.Billing.UpdateSubscription(h.UserRepo, SubChangedAttrs, subscriptionID)
if err != nil {
- log.Printf("h.Billing.UpdateSubscription failed: %v", err)
+ h.Logger.Error("h.Billing.UpdateSubscription failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "order_refunded":
var orderAttrs billing.OrderAttributes
if err := json.Unmarshal(webhookPayload.Data.Attributes, &orderAttrs); err != nil {
- log.Printf("Unmarshal order_created failed: %v", err)
+ h.Logger.Error("Unmarshal order_created failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid order_created payload")
return
}
err = h.Billing.DeductCredits(h.UserRepo, h.BillingRepo, orderAttrs)
if err != nil {
- log.Printf("h.Billing.DeductCredits failed: %v", err)
+ h.Logger.Error("h.Billing.DeductCredits failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
case "subscription_payment_failed", "subscription_payment_recovered":
if err := json.Unmarshal(webhookPayload.Data.Attributes, &emailAttribute); err != nil {
- log.Printf("Unmarshal %s failed: %v", eventType, err)
+ h.Logger.Error("Unmarshal failed", "eventType", eventType, "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid payment status payload")
return
}
- log.Printf("Payment event: %s for user %s", eventType, emailAttribute.UserEmail)
+ h.Logger.Info("Payment event", "eventType", eventType, "user", emailAttribute.UserEmail)
default:
- log.Printf("Unhandled event type: %s", eventType)
+ h.Logger.Info("Unhandled event type", "eventType", eventType)
RespondWithError(w, http.StatusNotImplemented, "Unhandled event type")
return
}
if err != nil {
- log.Printf("eventType switch func failed: %v", err)
+ h.Logger.Error("eventType switch func failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to update user")
return
}
err = h.BillingRepo.MarkWebhookProcessed(webhookID, eventType)
if err != nil {
- log.Printf("MarkWebhookProcessed failed: %v", err)
+ h.Logger.Error("MarkWebhookProcessed failed", "error", err)
w.WriteHeader(http.StatusOK)
return
}
@@ -1329,7 +1333,7 @@ func (h *Handler) DashboardHandler(w http.ResponseWriter, r *http.Request) {
RespondWithError(w, http.StatusUnauthorized, "User not found")
return
}
- log.Printf("dashboard.GetDashboardData failed: %v", err)
+ h.Logger.Error("dashboard.GetDashboardData failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Could not load dashboard")
return
}
@@ -1355,11 +1359,11 @@ func (h *Handler) JDInputHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error", "error", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("chatgpt.ExtractJDInput failed: %v", err)
+ h.Logger.Error("chatgpt.ExtractJDInput failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to process job description")
return
}
@@ -1368,11 +1372,11 @@ func (h *Handler) JDInputHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
var openaiErr *chatgpt.OpenAIError
if errors.As(err, &openaiErr) {
- log.Printf("OpenAI error: %v", openaiErr)
+ h.Logger.Error("OpenAI error", "error", openaiErr)
RespondWithError(w, openaiErr.StatusCode, openaiErr.Message)
return
}
- log.Printf("chatgpt.ExtractJDSummary failed: %v", err)
+ h.Logger.Error("chatgpt.ExtractJDSummary failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Failed to process job description")
return
diff --git a/handlers/helpers.go b/handlers/helpers.go
index 052145b..9bceae4 100644
--- a/handlers/helpers.go
+++ b/handlers/helpers.go
@@ -37,7 +37,7 @@ func GetPathID(r *http.Request, prefix string) (int, error) {
if path == "" {
log.Printf("getPathID returned empty string")
- err := errors.New("Missing or invalid url param")
+ err := errors.New("missing or invalid url param")
return 0, err
}
From 28a5405d78c8aff2234f49d68f226837ee7bda62 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sat, 20 Sep 2025 12:26:11 +0700
Subject: [PATCH 04/15] passed slog to chatgpt package
---
chatgpt/model.go | 5 ++++-
chatgpt/service.go | 19 +++++++++----------
internal/server/server.go | 2 +-
3 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/chatgpt/model.go b/chatgpt/model.go
index c143ab2..9a1c81b 100644
--- a/chatgpt/model.go
+++ b/chatgpt/model.go
@@ -2,6 +2,7 @@ package chatgpt
import (
"fmt"
+ "log/slog"
"os"
"strings"
)
@@ -24,6 +25,7 @@ type ChatGPTResponse struct {
type OpenAIClient struct {
APIKey string
+ Logger *slog.Logger
}
type OpenAIError struct {
@@ -43,9 +45,10 @@ func (e *OpenAIError) Error() string {
return fmt.Sprintf("OpenAI error %d: %s", e.StatusCode, e.Message)
}
-func NewOpenAI() *OpenAIClient {
+func NewOpenAI(logger *slog.Logger) *OpenAIClient {
return &OpenAIClient{
APIKey: os.Getenv("OPENAI_API_KEY"),
+ Logger: logger,
}
}
diff --git a/chatgpt/service.go b/chatgpt/service.go
index e627646..03c6d19 100644
--- a/chatgpt/service.go
+++ b/chatgpt/service.go
@@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"io"
- "log"
"net/http"
"strings"
)
@@ -33,7 +32,7 @@ func (c *OpenAIClient) GetChatGPTResponse(prompt string) (*ChatGPTResponse, erro
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.openai.com/v1/chat/completions", bytes.NewBuffer(requestBody))
if err != nil {
- log.Printf("NewRequestWithContext failed: %v", err)
+ c.Logger.Error("NewRequestWithContext failed", "error", err)
return nil, err
}
@@ -64,7 +63,7 @@ func (c *OpenAIClient) GetChatGPTResponse(prompt string) (*ChatGPTResponse, erro
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
- log.Printf("Unmarshal result err: %v", err)
+ c.Logger.Error("Unmarshal result err", "error", err)
return nil, err
}
@@ -78,7 +77,7 @@ func (c *OpenAIClient) GetChatGPTResponse(prompt string) (*ChatGPTResponse, erro
var chatGPTResponse ChatGPTResponse
if err := json.Unmarshal([]byte(chatGPTResponseRaw), &chatGPTResponse); err != nil {
- log.Printf("Unmarshal chatGPTResponse err: %v", err)
+ c.Logger.Error("Unmarshal chatGPTResponse err", "error", err)
return nil, err
}
@@ -100,7 +99,7 @@ func (c *OpenAIClient) GetChatGPTResponseConversation(conversationHistory []map[
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.openai.com/v1/chat/completions", bytes.NewBuffer(requestBody))
if err != nil {
- log.Printf("NewRequestWithContext failed: %v", err)
+ c.Logger.Error("NewRequestWithContext failed", "error", err)
return nil, err
}
@@ -131,7 +130,7 @@ func (c *OpenAIClient) GetChatGPTResponseConversation(conversationHistory []map[
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
- log.Printf("Unmarshal result err: %v", err)
+ c.Logger.Error("Unmarshal result err", "error", err)
return nil, err
}
@@ -145,7 +144,7 @@ func (c *OpenAIClient) GetChatGPTResponseConversation(conversationHistory []map[
var chatGPTResponse ChatGPTResponse
if err := json.Unmarshal([]byte(chatGPTResponseRaw), &chatGPTResponse); err != nil {
- log.Printf("Unmarshal chatGPTResponse err: %v", err)
+ c.Logger.Error("Unmarshal chatGPTResponse err", "error", err)
return nil, err
}
@@ -173,7 +172,7 @@ func (c *OpenAIClient) GetChatGPT35Response(prompt string) (*ChatGPTResponse, er
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.openai.com/v1/chat/completions", bytes.NewBuffer(requestBody))
if err != nil {
- log.Printf("NewRequestWithContext failed: %v", err)
+ c.Logger.Error("NewRequestWithContext failed", "error", err)
return nil, err
}
@@ -204,7 +203,7 @@ func (c *OpenAIClient) GetChatGPT35Response(prompt string) (*ChatGPTResponse, er
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
- log.Printf("Unmarshal result err: %v", err)
+ c.Logger.Error("Unmarshal result err", "error", err)
return nil, err
}
@@ -218,7 +217,7 @@ func (c *OpenAIClient) GetChatGPT35Response(prompt string) (*ChatGPTResponse, er
var chatGPTResponse ChatGPTResponse
if err := json.Unmarshal([]byte(chatGPTResponseRaw), &chatGPTResponse); err != nil {
- log.Printf("Unmarshal chatGPTResponse err: %v", err)
+ c.Logger.Error("Unmarshal chatGPTResponse err", "error", err)
return nil, err
}
diff --git a/internal/server/server.go b/internal/server/server.go
index 59e033b..cae0c30 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -35,7 +35,7 @@ func NewServer(logger *slog.Logger) (*Server, error) {
tokenRepo := token.NewRepository(db)
conversationRepo := conversation.NewRepository(db)
billingRepo := billing.NewRepository(db)
- openAI := chatgpt.NewOpenAI()
+ openAI := chatgpt.NewOpenAI(logger)
mailer := mailer.NewMailer()
billing, err := billing.NewBilling()
if err != nil {
From 29f2abb56e9db9474936f3b7ead659e35a00e079 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sat, 20 Sep 2025 12:46:38 +0700
Subject: [PATCH 05/15] added slog to Billing package
---
billing/model.go | 5 ++-
billing/service.go | 71 +++++++++++++++++++--------------------
internal/server/server.go | 4 +--
mailer/model.go | 9 +++--
mailer/service.go | 17 +++++-----
5 files changed, 56 insertions(+), 50 deletions(-)
diff --git a/billing/model.go b/billing/model.go
index c74554c..10f6bf0 100644
--- a/billing/model.go
+++ b/billing/model.go
@@ -3,6 +3,7 @@ package billing
import (
"encoding/json"
"fmt"
+ "log/slog"
"os"
"strconv"
"time"
@@ -13,6 +14,7 @@ type Billing struct {
VariantIDIndividual int
VariantIDPro int
VariantIDPremium int
+ Logger *slog.Logger
}
type CheckoutPayload struct {
Data CheckoutData `json:"data"`
@@ -100,7 +102,7 @@ type BillingRepo interface {
MarkWebhookProcessed(id string, event string) error
}
-func NewBilling() (*Billing, error) {
+func NewBilling(logger *slog.Logger) (*Billing, error) {
individualID, err := strconv.Atoi(os.Getenv("LEMON_VARIANT_ID_INDIVIDUAL"))
if err != nil {
return nil, fmt.Errorf("invalid INDIVIDUAL ID: %w", err)
@@ -118,5 +120,6 @@ func NewBilling() (*Billing, error) {
VariantIDIndividual: individualID,
VariantIDPro: proID,
VariantIDPremium: premiumID,
+ Logger: logger,
}, nil
}
diff --git a/billing/service.go b/billing/service.go
index dcd0607..fb3fd41 100644
--- a/billing/service.go
+++ b/billing/service.go
@@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io"
- "log"
"net/http"
"os"
"strconv"
@@ -45,13 +44,13 @@ func (b *Billing) RequestCheckoutSession(userEmail string, variantID int) (strin
body, err := json.Marshal(payload)
if err != nil {
- log.Printf("json.Marshal failed: %v", err)
+ b.Logger.Error("json.Marshal failed", "error", err)
return "", err
}
req, err := http.NewRequest("POST", "https://api.lemonsqueezy.com/v1/checkouts", bytes.NewBuffer(body))
if err != nil {
- log.Printf("http.NewRequest failed: %v", err)
+ b.Logger.Error("http.NewRequest failed", "error", err)
return "", err
}
@@ -64,20 +63,20 @@ func (b *Billing) RequestCheckoutSession(userEmail string, variantID int) (strin
}
res, err := client.Do(req)
if err != nil {
- log.Printf("client.Do failed: %v", err)
+ b.Logger.Error("client.Do failed", "error", err)
return "", err
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(res.Body)
- log.Printf("LemonSqueezy returned error: %s", string(bodyBytes))
+ b.Logger.Error("LemonSqueezy returned error", "error", string(bodyBytes))
return "", fmt.Errorf("LemonSqueezy API error: %s", res.Status)
}
var result CheckoutResponse
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
- log.Printf("NewDecoder failed: %v", err)
+ b.Logger.Error("NewDecoder failed", "error", err)
return "", err
}
@@ -190,7 +189,7 @@ func (b *Billing) VerifyBillingSignature(signature string, body []byte, secret s
func (b *Billing) ApplyCredits(userRepo user.UserRepo, billingRepo BillingRepo, email string, variantID int) error {
user, err := userRepo.GetUserByEmail(email)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -213,12 +212,12 @@ func (b *Billing) ApplyCredits(userRepo user.UserRepo, billingRepo BillingRepo,
creditType = "subscription"
reason = "Premium subscription monthly credit grant"
default:
- log.Printf("ERROR: unknown variantID: %d", variantID)
+ b.Logger.Error("ERROR: unknown variantID", "error", variantID)
return fmt.Errorf("unknown variant ID: %d", variantID)
}
if err := userRepo.AddCredits(user.ID, credits, creditType); err != nil {
- log.Printf("repo.AddCredits failed: %v", err)
+ b.Logger.Error("repo.AddCredits failed", "error", err)
return err
}
@@ -229,7 +228,7 @@ func (b *Billing) ApplyCredits(userRepo user.UserRepo, billingRepo BillingRepo,
Reason: reason,
}
if err := billingRepo.LogCreditTransaction(tx); err != nil {
- log.Printf("Warning: credit granted but failed to log transaction: %v", err)
+ b.Logger.Error("Warning: credit granted but failed to log transaction", "error", err)
return err
}
@@ -239,7 +238,7 @@ func (b *Billing) ApplyCredits(userRepo user.UserRepo, billingRepo BillingRepo,
func (b *Billing) DeductCredits(userRepo user.UserRepo, billingRepo BillingRepo, orderAttrs OrderAttributes) error {
user, err := userRepo.GetUserByEmail(orderAttrs.UserEmail)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -264,12 +263,12 @@ func (b *Billing) DeductCredits(userRepo user.UserRepo, billingRepo BillingRepo,
creditType = "subscription"
reason = "Premium subscription monthly credit refund"
default:
- log.Printf("ERROR: unknown variantID: %d", variantID)
+ b.Logger.Error("unknown variantID", "variantID", variantID)
return fmt.Errorf("unknown variant ID: %d", variantID)
}
if err := userRepo.AddCredits(user.ID, -credits, creditType); err != nil {
- log.Printf("repo.DeductCredits failed: %v", err)
+ b.Logger.Error("repo.DeductCredits failed", "error", err)
return err
}
@@ -280,7 +279,7 @@ func (b *Billing) DeductCredits(userRepo user.UserRepo, billingRepo BillingRepo,
Reason: reason,
}
if err := billingRepo.LogCreditTransaction(tx); err != nil {
- log.Printf("Warning: refund deduction succeeded but failed to log transaction: %v", err)
+ b.Logger.Warn("Refund deduction succeeded but failed to log transaction", "error", err)
return err
}
@@ -290,7 +289,7 @@ func (b *Billing) DeductCredits(userRepo user.UserRepo, billingRepo BillingRepo,
func (b *Billing) CreateSubscription(userRepo user.UserRepo, subCreatedAttrs SubscriptionAttributes, subscriptionID string) error {
user, err := userRepo.GetUserByEmail(subCreatedAttrs.UserEmail)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -301,7 +300,7 @@ func (b *Billing) CreateSubscription(userRepo user.UserRepo, subCreatedAttrs Sub
case b.VariantIDPremium:
tier = "premium"
default:
- log.Printf("ERROR: unknown variantID: %d", subCreatedAttrs.VariantID)
+ b.Logger.Error("unknown variantID", "variantID", subCreatedAttrs.VariantID)
return fmt.Errorf("unknown variant ID: %d", subCreatedAttrs.VariantID)
}
@@ -314,7 +313,7 @@ func (b *Billing) CreateSubscription(userRepo user.UserRepo, subCreatedAttrs Sub
subCreatedAttrs.EndsAt,
)
if err != nil {
- log.Printf("CreateSubscriptionData failed: %v", err)
+ b.Logger.Error("CreateSubscriptionData failed", "error", err)
return err
}
@@ -324,7 +323,7 @@ func (b *Billing) CreateSubscription(userRepo user.UserRepo, subCreatedAttrs Sub
func (b *Billing) CancelSubscription(userRepo user.UserRepo, email string) error {
user, err := userRepo.GetUserByEmail(email)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -333,7 +332,7 @@ func (b *Billing) CancelSubscription(userRepo user.UserRepo, email string) error
"cancelled",
)
if err != nil {
- log.Printf("CancelSubscriptionData failed: %v", err)
+ b.Logger.Error("CancelSubscriptionData failed", "error", err)
return err
}
@@ -343,7 +342,7 @@ func (b *Billing) CancelSubscription(userRepo user.UserRepo, email string) error
func (b *Billing) ResumeSubscription(userRepo user.UserRepo, email string) error {
user, err := userRepo.GetUserByEmail(email)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -352,7 +351,7 @@ func (b *Billing) ResumeSubscription(userRepo user.UserRepo, email string) error
"active",
)
if err != nil {
- log.Printf("CancelSubscriptionData failed: %v", err)
+ b.Logger.Error("CancelSubscriptionData failed", "error", err)
return err
}
@@ -362,7 +361,7 @@ func (b *Billing) ResumeSubscription(userRepo user.UserRepo, email string) error
func (b *Billing) ExpireSubscription(userRepo user.UserRepo, billingRepo BillingRepo, email string) error {
user, err := userRepo.GetUserByEmail(email)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -371,14 +370,14 @@ func (b *Billing) ExpireSubscription(userRepo user.UserRepo, billingRepo Billing
"expired",
)
if err != nil {
- log.Printf("CancelSubscriptionData failed: %v", err)
+ b.Logger.Error("CancelSubscriptionData failed", "error", err)
return err
}
if user.SubscriptionCredits > 0 {
err = userRepo.AddCredits(user.ID, -user.SubscriptionCredits, "subscription")
if err != nil {
- log.Printf("repo.AddCredits failed: %v", err)
+ b.Logger.Error("repo.AddCredits failed", "error", err)
return err
}
@@ -389,7 +388,7 @@ func (b *Billing) ExpireSubscription(userRepo user.UserRepo, billingRepo Billing
Reason: "Zeroed out credits on subscription expiration",
}
if err := billingRepo.LogCreditTransaction(tx); err != nil {
- log.Printf("Warning: zero-out succeeded but failed to log transaction: %v", err)
+ b.Logger.Warn("Zero-out succeeded but failed to log transaction", "error", err)
}
}
@@ -399,7 +398,7 @@ func (b *Billing) ExpireSubscription(userRepo user.UserRepo, billingRepo Billing
func (b *Billing) RenewSubscription(userRepo user.UserRepo, billingRepo BillingRepo, subRenewAttrs SubscriptionRenewAttributes) error {
user, err := userRepo.GetUserByEmail(subRenewAttrs.UserEmail)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -419,12 +418,12 @@ func (b *Billing) RenewSubscription(userRepo user.UserRepo, billingRepo BillingR
credits = 20
reason = "Premium subscription monthly credit"
default:
- log.Printf("ERROR: unknown user.SubscriptionTier: %s", user.SubscriptionTier)
+ b.Logger.Error("unknown user.SubscriptionTier", "subscriptionTier", user.SubscriptionTier)
return fmt.Errorf("unknown user.SubscriptionTier: %s", user.SubscriptionTier)
}
if err := userRepo.AddCredits(user.ID, credits, "subscription"); err != nil {
- log.Printf("repo.AddCredits failed: %v", err)
+ b.Logger.Error("repo.AddCredits failed", "error", err)
return err
}
@@ -435,7 +434,7 @@ func (b *Billing) RenewSubscription(userRepo user.UserRepo, billingRepo BillingR
Reason: reason,
}
if err := billingRepo.LogCreditTransaction(tx); err != nil {
- log.Printf("Warning: credit granted but failed to log transaction: %v", err)
+ b.Logger.Warn("credit granted but failed to log transaction", "error", err)
return err
}
@@ -445,7 +444,7 @@ func (b *Billing) RenewSubscription(userRepo user.UserRepo, billingRepo BillingR
func (b *Billing) ChangeSubscription(userRepo user.UserRepo, billingRepo BillingRepo, subChangedAttrs SubscriptionAttributes) error {
user, err := userRepo.GetUserByEmail(subChangedAttrs.UserEmail)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -468,12 +467,12 @@ func (b *Billing) ChangeSubscription(userRepo user.UserRepo, billingRepo Billing
credits = 10
reason = "Pro upgraded to Premium subscription monthly credit"
default:
- log.Printf("ERROR: unknown user.SubscriptionTier: %s", user.SubscriptionTier)
+ b.Logger.Error("unknown user.SubscriptionTier", "error", user.SubscriptionTier)
return fmt.Errorf("unknown user.SubscriptionTier: %s", user.SubscriptionTier)
}
if err := userRepo.AddCredits(user.ID, credits, "subscription"); err != nil {
- log.Printf("repo.AddCredits failed: %v", err)
+ b.Logger.Error("repo.AddCredits failed", "error", err)
return err
}
@@ -484,7 +483,7 @@ func (b *Billing) ChangeSubscription(userRepo user.UserRepo, billingRepo Billing
Reason: reason,
}
if err := billingRepo.LogCreditTransaction(tx); err != nil {
- log.Printf("Warning: credit granted but failed to log transaction: %v", err)
+ b.Logger.Warn("credit granted but failed to log transaction", "error", err)
return err
}
@@ -494,7 +493,7 @@ func (b *Billing) ChangeSubscription(userRepo user.UserRepo, billingRepo Billing
func (b *Billing) UpdateSubscription(userRepo user.UserRepo, subUpdatedAttrs SubscriptionAttributes, subscriptionID string) error {
user, err := userRepo.GetUserByEmail(subUpdatedAttrs.UserEmail)
if err != nil {
- log.Printf("repo.GetUserByEmail failed: %v", err)
+ b.Logger.Error("repo.GetUserByEmail failed", "error", err)
return err
}
@@ -505,7 +504,7 @@ func (b *Billing) UpdateSubscription(userRepo user.UserRepo, subUpdatedAttrs Sub
case b.VariantIDPremium:
tier = "premium"
default:
- log.Printf("ERROR: unknown variantID: %d", subUpdatedAttrs.VariantID)
+ b.Logger.Error("unknown variantID", "variantID", subUpdatedAttrs.VariantID)
return fmt.Errorf("unknown variant ID: %d", subUpdatedAttrs.VariantID)
}
@@ -518,7 +517,7 @@ func (b *Billing) UpdateSubscription(userRepo user.UserRepo, subUpdatedAttrs Sub
subUpdatedAttrs.EndsAt,
)
if err != nil {
- log.Printf("UpdateSubscriptionData failed: %v", err)
+ b.Logger.Error("UpdateSubscriptionData failed", "error", err)
return err
}
diff --git a/internal/server/server.go b/internal/server/server.go
index cae0c30..d456027 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -36,8 +36,8 @@ func NewServer(logger *slog.Logger) (*Server, error) {
conversationRepo := conversation.NewRepository(db)
billingRepo := billing.NewRepository(db)
openAI := chatgpt.NewOpenAI(logger)
- mailer := mailer.NewMailer()
- billing, err := billing.NewBilling()
+ mailer := mailer.NewMailer(logger)
+ billing, err := billing.NewBilling(logger)
if err != nil {
log.Printf("billing.NewBilling failed: %v", err)
return nil, err
diff --git a/mailer/model.go b/mailer/model.go
index 0294e70..36170c3 100644
--- a/mailer/model.go
+++ b/mailer/model.go
@@ -1,10 +1,14 @@
package mailer
-import "os"
+import (
+ "log/slog"
+ "os"
+)
type Mailer struct {
APIKey string
BaseURL string
+ Logger *slog.Logger
}
const signature = `
@@ -19,10 +23,11 @@ const signature = `
`
-func NewMailer() *Mailer {
+func NewMailer(logger *slog.Logger) *Mailer {
return &Mailer{
APIKey: os.Getenv("RESEND_API_KEY"),
BaseURL: "https://api.resend.com",
+ Logger: logger,
}
}
diff --git a/mailer/service.go b/mailer/service.go
index 01327bb..70c3b6e 100644
--- a/mailer/service.go
+++ b/mailer/service.go
@@ -4,7 +4,6 @@ import (
"bytes"
"encoding/json"
"fmt"
- "log"
"net/http"
)
@@ -23,13 +22,13 @@ func (m *Mailer) SendPasswordReset(email, resetURL string) error {
body, err := json.Marshal(payload)
if err != nil {
- log.Printf("Marshal failed: %v", err)
+ m.Logger.Error("Marshal failed", "error", err)
return err
}
req, err := http.NewRequest("POST", fmt.Sprintf("%s/emails", m.BaseURL), bytes.NewBuffer(body))
if err != nil {
- log.Printf("Mailer NewRequest failed: %v", err)
+ m.Logger.Error("Mailer NewRequest failed", "error", err)
return err
}
req.Header.Set("Authorization", "Bearer "+m.APIKey)
@@ -37,13 +36,13 @@ func (m *Mailer) SendPasswordReset(email, resetURL string) error {
resp, err := http.DefaultClient.Do(req)
if err != nil {
- log.Printf("Mailer Client Do failed: %v", err)
+ m.Logger.Error("Mailer Client Do failed", "error", err)
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
- return fmt.Errorf("Resend error: %s", resp.Status)
+ return fmt.Errorf("resend error: %s", resp.Status)
}
return nil
@@ -152,13 +151,13 @@ func (m *Mailer) SendDeletionConfirmation(email string) error {
body, err := json.Marshal(payload)
if err != nil {
- log.Printf("Marshal failed: %v", err)
+ m.Logger.Error("Marshal failed", "error", err)
return err
}
req, err := http.NewRequest("POST", fmt.Sprintf("%s/emails", m.BaseURL), bytes.NewBuffer(body))
if err != nil {
- log.Printf("Mailer NewRequest failed: %v", err)
+ m.Logger.Error("Mailer NewRequest failed", "error", err)
return err
}
req.Header.Set("Authorization", "Bearer "+m.APIKey)
@@ -166,13 +165,13 @@ func (m *Mailer) SendDeletionConfirmation(email string) error {
resp, err := http.DefaultClient.Do(req)
if err != nil {
- log.Printf("Mailer Client Do failed: %v", err)
+ m.Logger.Error("Mailer Client Do failed", "error", err)
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
- return fmt.Errorf("Resend error: %s", resp.Status)
+ return fmt.Errorf("resend error: %s", resp.Status)
}
return nil
From a56255a2218500e1f7552335ba72c3e00df6be89 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sun, 21 Sep 2025 14:48:31 +0700
Subject: [PATCH 06/15] added slog to TestMain and InitTestServer
---
billing/repository_mock.go | 2 +-
handlers/handlers_test.go | 33 +++++++++++++++++++++------------
internal/testutil/server.go | 16 ++++++++--------
3 files changed, 30 insertions(+), 21 deletions(-)
diff --git a/billing/repository_mock.go b/billing/repository_mock.go
index 9caba66..9ec7131 100644
--- a/billing/repository_mock.go
+++ b/billing/repository_mock.go
@@ -12,7 +12,7 @@ func NewMockRepo() *MockRepo {
func (m *MockRepo) LogCreditTransaction(tx CreditTransaction) error {
if m.FailLogCreditTransaction {
- return errors.New("Mocked LogCreditTransaction failure")
+ return errors.New("mocked LogCreditTransaction failure")
}
return nil
}
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index 28acbd4..e143cb2 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"log"
+ "log/slog"
"net/http"
"os"
"strings"
@@ -53,33 +54,41 @@ var (
mockAI *mocks.MockOpenAIClient
)
-func TestMain(m *testing.M) {
- log.SetFlags(log.LstdFlags | log.Llongfile)
+var logger *slog.Logger
- log.Println("Loading environment variables...")
- err := godotenv.Load("../.env.test")
- if err != nil {
- log.Fatalf("Error loading .env.test file: %v", err)
+func TestMain(m *testing.M) {
+ handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
+ Level: slog.LevelDebug,
+ })
+ logger = slog.New(handler)
+
+ logger.Info("Loading environment variables...")
+ if err := godotenv.Load("../.env.test"); err != nil {
+ logger.Error("failed to load .env.test", "error", err)
+ os.Exit(1)
}
- log.Println("Initializing test server...")
- Handler, err = testutil.InitTestServer()
+ logger.Info("Initializing test server...")
+ var err error
+ Handler, err = testutil.InitTestServer(logger)
if err != nil {
- log.Fatalf("Test server initialization failed: %v", err)
+ logger.Error("test server initialization failed", "error", err)
+ os.Exit(1)
}
if testutil.TestServerURL == "" {
- log.Fatal("TestMain: TestServerURL is empty! The server did not start properly.")
+ logger.Error("TestServerURL is empty! The server did not start properly")
+ os.Exit(1)
}
- log.Printf("TestMain: Test server started successfully at: %s", testutil.TestServerURL)
+ logger.Info("Test server started", "url", testutil.TestServerURL)
mockAI = Handler.OpenAI.(*mocks.MockOpenAIClient)
conversationBuilder = testutil.NewConversationBuilder()
code := m.Run()
- log.Println("Stopping test server...")
+ logger.Info("Stopping test server...")
testutil.StopTestServer()
os.Exit(code)
diff --git a/internal/testutil/server.go b/internal/testutil/server.go
index 8d547e2..5d29ffa 100644
--- a/internal/testutil/server.go
+++ b/internal/testutil/server.go
@@ -1,7 +1,7 @@
package testutil
import (
- "log"
+ "log/slog"
"net/http"
"net/http/httptest"
@@ -22,15 +22,15 @@ var (
TestServerURL string
)
-func InitTestServer() (*handlers.Handler, error) {
- log.Println("Initializing test database connection...")
+func InitTestServer(logger *slog.Logger) (*handlers.Handler, error) {
+ logger.Info("Initializing test database connection...")
db, err := database.StartDB()
if err != nil {
- log.Fatalf("Failed to connect to test database: %v", err)
+ logger.Error("Failed to connect to test database", "error", err)
}
- log.Println("Database connected successfully.")
+ logger.Info("Database connected successfully.")
interviewRepo := interview.NewRepository(db)
userRepo := user.NewRepository(db)
@@ -41,11 +41,11 @@ func InitTestServer() (*handlers.Handler, error) {
mailer := mocks.NewMockMailer()
billing, err := billing.NewBilling()
if err != nil {
- log.Printf("billing.NewBilling failed: %v", err)
+ logger.Error("billing.NewBilling failed", "error", err)
return nil, err
}
- handler := handlers.NewHandler(interviewRepo, userRepo, tokenRepo, conversationRepo, billingRepo, billing, mailer, openAI, db)
+ handler := handlers.NewHandler(interviewRepo, userRepo, tokenRepo, conversationRepo, billingRepo, billing, mailer, openAI, db, logger)
TestMux = http.NewServeMux()
TestMux.Handle("/api/users", http.HandlerFunc(handler.CreateUsersHandler))
@@ -154,7 +154,7 @@ func InitTestServer() (*handlers.Handler, error) {
),
)
- log.Println("Starting in-memory test server...")
+ logger.Info("Starting in-memory test server...")
TestServer = httptest.NewServer(TestMux)
TestServerURL = TestServer.URL
From dcd4f4fbb3f2bbcb4fd5410714ee70645e951b68 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Mon, 22 Sep 2025 16:35:23 +0700
Subject: [PATCH 07/15] added logger to NewBilling in InitTestServer. Also
added NewMockOpenAIClient() instantiation for consistency.
---
internal/mocks/openai_mock.go | 6 ++++++
internal/testutil/server.go | 4 ++--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/internal/mocks/openai_mock.go b/internal/mocks/openai_mock.go
index e16832f..93bd72f 100644
--- a/internal/mocks/openai_mock.go
+++ b/internal/mocks/openai_mock.go
@@ -77,6 +77,12 @@ type MockOpenAIClient struct {
Scenario string
}
+func NewMockOpenAIClient() *MockOpenAIClient {
+ mockOpenAIClient := &MockOpenAIClient{}
+
+ return mockOpenAIClient
+}
+
func (m *MockOpenAIClient) GetChatGPTResponse(prompt string) (*chatgpt.ChatGPTResponse, error) {
return responseFixtures[ScenarioInterview], nil
}
diff --git a/internal/testutil/server.go b/internal/testutil/server.go
index 5d29ffa..081bf42 100644
--- a/internal/testutil/server.go
+++ b/internal/testutil/server.go
@@ -37,9 +37,9 @@ func InitTestServer(logger *slog.Logger) (*handlers.Handler, error) {
tokenRepo := token.NewRepository(db)
conversationRepo := conversation.NewRepository(db)
billingRepo := billing.NewRepository(db)
- openAI := &mocks.MockOpenAIClient{}
+ openAI := mocks.NewMockOpenAIClient()
mailer := mocks.NewMockMailer()
- billing, err := billing.NewBilling()
+ billing, err := billing.NewBilling(logger)
if err != nil {
logger.Error("billing.NewBilling failed", "error", err)
return nil, err
From cfe844323b30e6cb70814fd9a904ace3b249588f Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Mon, 22 Sep 2025 17:10:53 +0700
Subject: [PATCH 08/15] added slog to tests
---
handlers/handlers_test.go | 55 +++++++++++++++++++++++----------------
1 file changed, 33 insertions(+), 22 deletions(-)
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index e143cb2..ce186ec 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"io"
- "log"
"log/slog"
"net/http"
"os"
@@ -20,7 +19,6 @@ import (
"github.com/michaelboegner/interviewer/internal/mocks"
"github.com/michaelboegner/interviewer/internal/testutil"
"github.com/michaelboegner/interviewer/interview"
- "github.com/michaelboegner/interviewer/middleware"
"github.com/michaelboegner/interviewer/token"
"github.com/michaelboegner/interviewer/user"
)
@@ -35,9 +33,7 @@ type TestCase struct {
reqBody string
headerKey string
headerValue string
- params middleware.AcceptedVals
expectedStatus int
- expectError bool
respBody handlers.ReturnVals
respBodyFunc func() handlers.ReturnVals
Interview *interview.Interview
@@ -63,7 +59,7 @@ func TestMain(m *testing.M) {
logger = slog.New(handler)
logger.Info("Loading environment variables...")
- if err := godotenv.Load("../.env.test"); err != nil {
+ if err := godotenv.Load("../.env.dev"); err != nil {
logger.Error("failed to load .env.test", "error", err)
os.Exit(1)
}
@@ -157,18 +153,20 @@ func Test_RequestVerificationHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- log.Fatalf("TestRequest for interview creation failed: %v", err)
+ logger.Error("TestRequest for interview creation failed", "error", err)
+ t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -236,12 +234,12 @@ func Test_CreateUsersHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
verificationJWT, err := user.VerificationToken(tc.email, tc.username, tc.password)
if err != nil {
- log.Printf("GenerateEmailVerificationToken failed: %v", err)
+ logger.Error("GenerateEmailVerificationToken failed", "error", err)
}
reqBodyUser := strings.NewReader(fmt.Sprintf(`{
"token": "%s"
@@ -250,12 +248,14 @@ func Test_CreateUsersHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, reqBodyUser)
if err != nil {
- log.Fatalf("TestRequest for interview creation failed: %v", err)
+ logger.Error("TestRequest for interview creation failed", "error", err)
+ t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -338,18 +338,20 @@ func Test_GetUsersHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- log.Fatalf("TestRequest for interview creation failed: %v", err)
+ logger.Error("TestRequest for interview creation failed", "error", err)
+ t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -452,18 +454,20 @@ func Test_LoginHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- log.Fatalf("TestRequest for interview creation failed: %v", err)
+ logger.Error("TestRequest for interview creation failed", "error", err)
+ t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -587,18 +591,20 @@ func Test_RefreshTokensHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- log.Fatalf("TestRequest for interview creation failed: %v", err)
+ logger.Error("TestRequest for interview creation failed", "error", err)
+ t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -736,7 +742,7 @@ func Test_InterviewsHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
if tc.setup != nil {
@@ -746,12 +752,14 @@ func Test_InterviewsHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- log.Fatalf("TestRequest for interview creation failed: %v", err)
+ logger.Error("TestRequest for interview creation failed", "error", err)
+ t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -845,7 +853,7 @@ func Test_CreateConversationsHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
if tc.setup != nil {
@@ -855,12 +863,14 @@ func Test_CreateConversationsHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- log.Fatalf("TestRequest for interview creation failed: %v", err)
+ logger.Error("TestRequest for interview creation failed", "error", err)
+ t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -983,7 +993,7 @@ func Test_AppendConversationsHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var buf strings.Builder
- log.SetOutput(&buf)
+ logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
defer showLogsIfFail(t, tc.name, buf)
if tc.name == "AppendConversation_IsFinished" {
@@ -1005,11 +1015,13 @@ func Test_AppendConversationsHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
+ logger.Error("TestRequest failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
if err := json.Unmarshal(resp, respUnmarshalled); err != nil {
+ logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -1042,7 +1054,6 @@ func Test_AppendConversationsHandler_Integration(t *testing.T) {
}
func showLogsIfFail(t *testing.T, name string, buf strings.Builder) {
- log.SetOutput(os.Stderr)
if t.Failed() {
fmt.Printf("---- logs for test: %s ----\n%s\n", name, buf.String())
}
From 61a83ad3ba7ae38730dad7674c67af2f834600af Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Wed, 24 Sep 2025 16:42:08 +0700
Subject: [PATCH 09/15] added slog params to testutil.helpers.go
---
internal/testutil/helpers.go | 40 ++++++++++++++++++------------------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/internal/testutil/helpers.go b/internal/testutil/helpers.go
index 652f5f4..50587d6 100644
--- a/internal/testutil/helpers.go
+++ b/internal/testutil/helpers.go
@@ -5,7 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
- "log"
+ "log/slog"
"net/http"
"os"
"strconv"
@@ -18,7 +18,7 @@ import (
"github.com/michaelboegner/interviewer/user"
)
-func CreateTestUserAndJWT() (string, int) {
+func CreateTestUserAndJWT(logger *slog.Logger) (string, int) {
var (
jwt string
userID int
@@ -30,16 +30,16 @@ func CreateTestUserAndJWT() (string, int) {
verificationJWT, err := user.VerificationToken(email, username, password)
if err != nil {
- log.Printf("GenerateEmailVerificationToken failed: %v", err)
+ logger.Error("GenerateEmailVerificationToken failed", "error", err)
}
reqBodyUser := strings.NewReader(fmt.Sprintf(`{
"token": "%s"
}`, verificationJWT))
- _, err = testRequests("", "", "POST", TestServerURL+"/api/users", reqBodyUser)
+ _, err = testRequests("", "", "POST", TestServerURL+"/api/users", reqBodyUser, logger)
if err != nil {
- log.Printf("CreateTestUserAndJWT user creation failed: %v", err)
+ logger.Error("CreateTestUserAndJWT user creation failed", "error", err)
}
//test jwt retrieve
@@ -50,9 +50,9 @@ func CreateTestUserAndJWT() (string, int) {
}
`)
- resp, err := testRequests("", "", "POST", TestServerURL+"/api/auth/login", reqBodyLogin)
+ resp, err := testRequests("", "", "POST", TestServerURL+"/api/auth/login", reqBodyLogin, logger)
if err != nil {
- log.Printf("CreateTestUserAndJWT JWT creation failed: %v", err)
+ logger.Error("CreateTestUserAndJWT JWT creation failed", "error", err)
}
returnVals := &handlers.ReturnVals{}
@@ -63,18 +63,18 @@ func CreateTestUserAndJWT() (string, int) {
//test userID extract
userID, err = token.ExtractUserIDFromToken(jwt)
if err != nil {
- log.Printf("CreateTestUserandJWT userID extraction failed: %v", err)
+ logger.Error("CreateTestUserandJWT userID extraction failed", "error", err)
}
return jwt, userID
}
-func CreateTestInterview(jwt string) int {
+func CreateTestInterview(jwt string, logger *slog.Logger) int {
reqBodyInterview := strings.NewReader(`{}`)
- resp, err := testRequests("Authorization", "Bearer "+jwt, "POST", TestServerURL+"/api/interviews", reqBodyInterview)
+ resp, err := testRequests("Authorization", "Bearer "+jwt, "POST", TestServerURL+"/api/interviews", reqBodyInterview, logger)
if err != nil {
- log.Printf("CreateTestUserAndJWT JWT creation failed: %v", err)
+ logger.Error("CreateTestUserAndJWT JWT creation failed", "error", err)
return 0
}
@@ -84,16 +84,16 @@ func CreateTestInterview(jwt string) int {
return returnVals.InterviewID
}
-func CreateTestConversation(jwt string, interviewID int) int {
+func CreateTestConversation(jwt string, interviewID int, logger *slog.Logger) int {
reqBodyConversation := strings.NewReader(`{
"conversation_id" : 1,
"message" : "T1Q1A1"
}`)
reqURL := TestServerURL + fmt.Sprintf("/api/conversations/create/%d", interviewID)
- resp, err := testRequests("Authorization", "Bearer "+jwt, "POST", reqURL, reqBodyConversation)
+ resp, err := testRequests("Authorization", "Bearer "+jwt, "POST", reqURL, reqBodyConversation, logger)
if err != nil {
- log.Printf("CreateTestUserAndJWT JWT creation failed: %v", err)
+ logger.Error("CreateTestUserAndJWT JWT creation failed", "error", err)
return 0
}
@@ -103,7 +103,7 @@ func CreateTestConversation(jwt string, interviewID int) int {
return returnVals.Conversation.ID
}
-func CreateTestExpiredJWT(id, expires int) string {
+func CreateTestExpiredJWT(id, expires int, logger *slog.Logger) string {
var token *jwt.Token
jwtSecret := os.Getenv("JWT_SECRET")
key := []byte(jwtSecret)
@@ -124,7 +124,7 @@ func CreateTestExpiredJWT(id, expires int) string {
s, err := token.SignedString(key)
if err != nil {
- log.Printf("SignedString failed: %s", err)
+ logger.Error("SignedString failed", "error", err)
return ""
}
@@ -138,12 +138,12 @@ func TruncateAllTables(db *sql.DB) error {
return err
}
-func testRequests(headerKey, headerValue, method, url string, reqBody *strings.Reader) ([]byte, error) {
+func testRequests(headerKey, headerValue, method, url string, reqBody *strings.Reader, logger *slog.Logger) ([]byte, error) {
client := &http.Client{}
req, err := http.NewRequest(method, url, reqBody)
if err != nil {
- log.Printf("CreateTestUserAndJWT user creation failed: %v", err)
+ logger.Error("CreateTestUserAndJWT user creation failed", "error", err)
return nil, err
}
req.Header.Set("Content-Type", "application/json")
@@ -153,14 +153,14 @@ func testRequests(headerKey, headerValue, method, url string, reqBody *strings.R
resp, err := client.Do(req)
if err != nil {
- log.Printf("Request failed: %v", err)
+ logger.Error("Request failed", "error", err)
return nil, err
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
- log.Printf("Reading response failed: %v", err)
+ logger.Error("Reading response failed", "error", err)
return nil, err
}
From bed920b438f2d25c62f6792d5675d64edb9f48b6 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Fri, 26 Sep 2025 16:24:13 +0700
Subject: [PATCH 10/15] removed unnecessary logger in favor of t.logs for
t.runs
---
handlers/handlers_test.go | 76 ++++++---------------------------------
1 file changed, 11 insertions(+), 65 deletions(-)
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index ce186ec..4515fa6 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -152,21 +152,15 @@ func Test_RequestVerificationHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- logger.Error("TestRequest for interview creation failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -233,13 +227,9 @@ func Test_CreateUsersHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
verificationJWT, err := user.VerificationToken(tc.email, tc.username, tc.password)
if err != nil {
- logger.Error("GenerateEmailVerificationToken failed", "error", err)
+ t.Fatalf("GenerateEmailVerificationToken failed: %v", err)
}
reqBodyUser := strings.NewReader(fmt.Sprintf(`{
"token": "%s"
@@ -248,14 +238,12 @@ func Test_CreateUsersHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, reqBodyUser)
if err != nil {
- logger.Error("TestRequest for interview creation failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -292,7 +280,7 @@ func Test_CreateUsersHandler_Integration(t *testing.T) {
func Test_GetUsersHandler_Integration(t *testing.T) {
cleanDBOrFail(t)
- jwtoken, userID := testutil.CreateTestUserAndJWT()
+ jwtoken, userID := testutil.CreateTestUserAndJWT(logger)
tests := []TestCase{
{
@@ -337,21 +325,15 @@ func Test_GetUsersHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- logger.Error("TestRequest for interview creation failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -388,7 +370,7 @@ func Test_GetUsersHandler_Integration(t *testing.T) {
func Test_LoginHandler_Integration(t *testing.T) {
cleanDBOrFail(t)
- _, _ = testutil.CreateTestUserAndJWT()
+ _, _ = testutil.CreateTestUserAndJWT(logger)
tests := []TestCase{
{
@@ -453,21 +435,15 @@ func Test_LoginHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- logger.Error("TestRequest for interview creation failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -515,7 +491,7 @@ func Test_LoginHandler_Integration(t *testing.T) {
func Test_RefreshTokensHandler_Integration(t *testing.T) {
cleanDBOrFail(t)
- _, userID := testutil.CreateTestUserAndJWT()
+ _, userID := testutil.CreateTestUserAndJWT(logger)
refreshToken, err := token.GetStoredRefreshToken(Handler.TokenRepo, userID)
if err != nil {
t.Fatalf("TC GetStoredRefreshToken failed: %v", err)
@@ -590,21 +566,15 @@ func Test_RefreshTokensHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- logger.Error("TestRequest for interview creation failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -652,8 +622,8 @@ func Test_RefreshTokensHandler_Integration(t *testing.T) {
func Test_InterviewsHandler_Integration(t *testing.T) {
cleanDBOrFail(t)
- jwtoken, userID := testutil.CreateTestUserAndJWT()
- expiredJWT := testutil.CreateTestExpiredJWT(userID, -1)
+ jwtoken, userID := testutil.CreateTestUserAndJWT(logger)
+ expiredJWT := testutil.CreateTestExpiredJWT(userID, -1, logger)
tests := []TestCase{
{
@@ -741,10 +711,6 @@ func Test_InterviewsHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
if tc.setup != nil {
tc.setup()
}
@@ -752,14 +718,12 @@ func Test_InterviewsHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- logger.Error("TestRequest for interview creation failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -796,9 +760,9 @@ func Test_InterviewsHandler_Integration(t *testing.T) {
func Test_CreateConversationsHandler_Integration(t *testing.T) {
cleanDBOrFail(t)
- jwtoken, _ := testutil.CreateTestUserAndJWT()
+ jwtoken, _ := testutil.CreateTestUserAndJWT(logger)
mockAI.Scenario = mocks.ScenarioInterview
- interviewID := testutil.CreateTestInterview(jwtoken)
+ interviewID := testutil.CreateTestInterview(jwtoken, logger)
conversationsURL := testutil.TestServerURL + fmt.Sprintf("/api/conversations/create/%d", interviewID)
tests := []TestCase{
@@ -852,10 +816,6 @@ func Test_CreateConversationsHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
if tc.setup != nil {
tc.setup()
}
@@ -863,14 +823,12 @@ func Test_CreateConversationsHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- logger.Error("TestRequest for interview creation failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
err = json.Unmarshal(resp, respUnmarshalled)
if err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -912,13 +870,13 @@ func Test_CreateConversationsHandler_Integration(t *testing.T) {
func Test_AppendConversationsHandler_Integration(t *testing.T) {
cleanDBOrFail(t)
- jwtoken, _ := testutil.CreateTestUserAndJWT()
+ jwtoken, _ := testutil.CreateTestUserAndJWT(logger)
mockAI.Scenario = mocks.ScenarioInterview
- interviewID := testutil.CreateTestInterview(jwtoken)
+ interviewID := testutil.CreateTestInterview(jwtoken, logger)
mockAI.Scenario = mocks.ScenarioCreated
- conversationID := testutil.CreateTestConversation(jwtoken, interviewID)
+ conversationID := testutil.CreateTestConversation(jwtoken, interviewID, logger)
urlTest := testutil.TestServerURL + fmt.Sprintf("/api/conversations/append/%d", interviewID)
tests := []TestCase{
@@ -992,10 +950,6 @@ func Test_AppendConversationsHandler_Integration(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- var buf strings.Builder
- logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))
- defer showLogsIfFail(t, tc.name, buf)
-
if tc.name == "AppendConversation_IsFinished" {
reqBodyPre := fmt.Sprintf(`{
"conversation_id" : %d,
@@ -1015,13 +969,11 @@ func Test_AppendConversationsHandler_Integration(t *testing.T) {
// Act
resp, respCode, err := testRequests(t, tc.headerKey, tc.headerValue, tc.method, tc.url, strings.NewReader(tc.reqBody))
if err != nil {
- logger.Error("TestRequest failed", "error", err)
t.Fatalf("TestRequest failed: %v", err)
}
respUnmarshalled := &handlers.ReturnVals{}
if err := json.Unmarshal(resp, respUnmarshalled); err != nil {
- logger.Error("json.Unmarshal failed", "error", err)
t.Fatalf("failed to unmarshal response: %v", err)
}
@@ -1053,12 +1005,6 @@ func Test_AppendConversationsHandler_Integration(t *testing.T) {
}
}
-func showLogsIfFail(t *testing.T, name string, buf strings.Builder) {
- if t.Failed() {
- fmt.Printf("---- logs for test: %s ----\n%s\n", name, buf.String())
- }
-}
-
func cleanDBOrFail(t *testing.T) {
if err := testutil.TruncateAllTables(Handler.DB); err != nil {
t.Fatalf("Failed to clean DB: %v", err)
From 574b56bc8d7a97c7a1db7c90d0b496e49fd18036 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sat, 27 Sep 2025 16:04:20 +0700
Subject: [PATCH 11/15] added logger to StartServer() in main
---
cmd/interviewer/main.go | 2 +-
handlers/handlers.go | 2 +-
internal/server/server.go | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/cmd/interviewer/main.go b/cmd/interviewer/main.go
index 2627568..3a26c07 100644
--- a/cmd/interviewer/main.go
+++ b/cmd/interviewer/main.go
@@ -30,5 +30,5 @@ func main() {
}
logger.Info("starting server...")
- srv.StartServer()
+ srv.StartServer(logger)
}
diff --git a/handlers/handlers.go b/handlers/handlers.go
index fab1bdf..5bb5855 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -263,7 +263,7 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
if params.Email == "" || params.Password == "" {
h.Logger.Error("Invalid username or password.")
- RespondWithError(w, http.StatusBadRequest, "Invalid username or password.")
+ RespondWithError(w, http.StatusBadRequest, "Authentication failed.")
return
}
diff --git a/internal/server/server.go b/internal/server/server.go
index d456027..608440a 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -39,7 +39,7 @@ func NewServer(logger *slog.Logger) (*Server, error) {
mailer := mailer.NewMailer(logger)
billing, err := billing.NewBilling(logger)
if err != nil {
- log.Printf("billing.NewBilling failed: %v", err)
+ logger.Error("billing.NewBilling failed", "error", err)
return nil, err
}
@@ -154,7 +154,7 @@ func NewServer(logger *slog.Logger) (*Server, error) {
return &Server{mux: mux}, nil
}
-func (s *Server) StartServer() {
- log.Printf("Serving files from %s on port: %s\n", ".", "8080")
+func (s *Server) StartServer(logger *slog.Logger) {
+ logger.Info("Serving files from %s on port: %s\n", ".", "8080")
log.Fatal(http.ListenAndServe(":8080", middleware.EnableCors(s.mux)))
}
From 71a77ca04db9280fa521ca560d64a8114328fcf5 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sat, 27 Sep 2025 16:34:02 +0700
Subject: [PATCH 12/15] fixed stray logs not yet using slog and clarified some
informational logs
---
billing/service.go | 4 ++--
handlers/handlers.go | 20 ++++++++++----------
handlers/helpers.go | 7 ++++---
3 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/billing/service.go b/billing/service.go
index fb3fd41..8dc7d4f 100644
--- a/billing/service.go
+++ b/billing/service.go
@@ -212,7 +212,7 @@ func (b *Billing) ApplyCredits(userRepo user.UserRepo, billingRepo BillingRepo,
creditType = "subscription"
reason = "Premium subscription monthly credit grant"
default:
- b.Logger.Error("ERROR: unknown variantID", "error", variantID)
+ b.Logger.Error("ERROR: unknown variantID", "variantID", variantID)
return fmt.Errorf("unknown variant ID: %d", variantID)
}
@@ -467,7 +467,7 @@ func (b *Billing) ChangeSubscription(userRepo user.UserRepo, billingRepo Billing
credits = 10
reason = "Pro upgraded to Premium subscription monthly credit"
default:
- b.Logger.Error("unknown user.SubscriptionTier", "error", user.SubscriptionTier)
+ b.Logger.Error("unknown user.SubscriptionTier", "subscriptionTier", user.SubscriptionTier)
return fmt.Errorf("unknown user.SubscriptionTier: %s", user.SubscriptionTier)
}
diff --git a/handlers/handlers.go b/handlers/handlers.go
index ff6b9c0..364b0f5 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -63,7 +63,7 @@ func (h *Handler) RequestVerificationHandler(w http.ResponseWriter, r *http.Requ
go func(email, url string) {
if err := h.Mailer.SendVerificationEmail(email, url); err != nil {
- log.Printf("SendVerificationEmail failed: %v", err)
+ h.Logger.Error("SendVerificationEmail failed", "error", err)
}
}(req.Email, verifyURL)
@@ -132,14 +132,14 @@ func (h *Handler) CreateUsersHandler(w http.ResponseWriter, r *http.Request) {
jwt, err := token.CreateJWT(strconv.Itoa(userCreated.ID), 0)
if err != nil {
- log.Printf("token.CreateJWT failed: %v", err)
+ h.Logger.Error("token.CreateJWT failed", "error", err)
RespondWithError(w, http.StatusInternalServerError, "Internal server error")
return
}
go func(email string) {
if err := h.Mailer.SendWelcome(email); err != nil {
- log.Printf("SendWelcome failed: %v", err)
+ h.Logger.Error("SendWelcome failed", "error", err)
}
}(userCreated.Email)
@@ -164,7 +164,7 @@ func (h *Handler) GetUsersHandler(w http.ResponseWriter, r *http.Request) {
return
}
- userIDParam, err := GetPathID(r, "/api/users/")
+ userIDParam, err := GetPathID(r, "/api/users/", h.Logger)
if err != nil {
h.Logger.Error("GetPathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "UserID required")
@@ -204,7 +204,7 @@ func (h *Handler) DeleteUserHandler(w http.ResponseWriter, r *http.Request) {
return
}
- userIDParam, err := GetPathID(r, "/api/users/delete/")
+ userIDParam, err := GetPathID(r, "/api/users/delete/", h.Logger)
if err != nil {
h.Logger.Error("GetPathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "UserID required")
@@ -597,7 +597,7 @@ func (h *Handler) GetInterviewHandler(w http.ResponseWriter, r *http.Request) {
return
}
- interviewID, err := GetPathID(r, "/api/interviews/")
+ interviewID, err := GetPathID(r, "/api/interviews/", h.Logger)
if err != nil {
h.Logger.Error("GetPathID failed", "error", err)
RespondWithError(w, http.StatusBadRequest, "Invalid interview ID")
@@ -635,7 +635,7 @@ func (h *Handler) UpdateInterviewStatusHandler(w http.ResponseWriter, r *http.Re
return
}
- interviewID, err := GetPathID(r, "/api/interviews/")
+ interviewID, err := GetPathID(r, "/api/interviews/", h.Logger)
if err != nil {
RespondWithError(w, http.StatusBadRequest, "Invalid interview ID")
return
@@ -693,7 +693,7 @@ func (h *Handler) CreateConversationsHandler(w http.ResponseWriter, r *http.Requ
return
}
- interviewID, err := GetPathID(r, "/api/conversations/create/")
+ interviewID, err := GetPathID(r, "/api/conversations/create/", h.Logger)
if err != nil {
h.Logger.Error("PathID error", "error", err)
@@ -778,7 +778,7 @@ func (h *Handler) AppendConversationsHandler(w http.ResponseWriter, r *http.Requ
return
}
- interviewID, err := GetPathID(r, "/api/conversations/append/")
+ interviewID, err := GetPathID(r, "/api/conversations/append/", h.Logger)
if err != nil {
h.Logger.Error("PathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
@@ -847,7 +847,7 @@ func (h *Handler) GetConversationHandler(w http.ResponseWriter, r *http.Request)
return
}
- interviewID, err := GetPathID(r, "/api/conversations/")
+ interviewID, err := GetPathID(r, "/api/conversations/", h.Logger)
if err != nil {
h.Logger.Error("PathID error", "error", err)
RespondWithError(w, http.StatusBadRequest, "Missing ID")
diff --git a/handlers/helpers.go b/handlers/helpers.go
index 9bceae4..6adce44 100644
--- a/handlers/helpers.go
+++ b/handlers/helpers.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
+ "log/slog"
"net/http"
"strconv"
"strings"
@@ -31,19 +32,19 @@ func ValidateInterviewStatusTransition(currentStatus, nextStatus string) error {
return fmt.Errorf("invalid state transition: %s → %s", currentStatus, nextStatus)
}
-func GetPathID(r *http.Request, prefix string) (int, error) {
+func GetPathID(r *http.Request, prefix string, logger *slog.Logger) (int, error) {
path := strings.TrimPrefix(r.URL.Path, prefix)
path = strings.Trim(path, "/")
if path == "" {
- log.Printf("getPathID returned empty string")
+ logger.Error("getPathID returned empty string")
err := errors.New("missing or invalid url param")
return 0, err
}
id, err := strconv.Atoi(path)
if err != nil {
- log.Printf("getPathID failed: %v", err)
+ logger.Error("getPathID failed", "error", err)
return 0, err
}
From 608b112296decbecd785fec86df3e079a088c856 Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sat, 27 Sep 2025 17:04:21 +0700
Subject: [PATCH 13/15] fixed tests
---
billing/service_test.go | 30 +++++++++++++++++++-----------
handlers/handlers_test.go | 8 ++++++++
2 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/billing/service_test.go b/billing/service_test.go
index 5056966..07b13b9 100644
--- a/billing/service_test.go
+++ b/billing/service_test.go
@@ -5,6 +5,8 @@ import (
"crypto/sha256"
"fmt"
"log"
+ "log/slog"
+ "os"
"strings"
"testing"
@@ -12,6 +14,20 @@ import (
"github.com/michaelboegner/interviewer/user"
)
+func NewTestBilling() *billing.Billing {
+ handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
+ Level: slog.LevelDebug,
+ })
+ logger := slog.New(handler)
+
+ return &billing.Billing{
+ VariantIDIndividual: 1,
+ VariantIDPro: 2,
+ VariantIDPremium: 3,
+ Logger: logger,
+ }
+}
+
func TestApplyCredits(t *testing.T) {
tests := []struct {
name string
@@ -84,11 +100,7 @@ func TestApplyCredits(t *testing.T) {
userRepo.FailAddCredits = tc.failCredit
billingRepo.FailLogCreditTransaction = tc.failLog
- b := billing.Billing{
- VariantIDIndividual: 1,
- VariantIDPro: 2,
- VariantIDPremium: 3,
- }
+ b := NewTestBilling()
err := b.ApplyCredits(userRepo, billingRepo, "test@example.com", tc.variantID)
if tc.expectErr && err == nil {
@@ -165,11 +177,7 @@ func TestDeductCredits(t *testing.T) {
userRepo.FailAddCredits = tc.failCredit
billingRepo.FailLogCreditTransaction = tc.failLog
- b := billing.Billing{
- VariantIDIndividual: 1,
- VariantIDPro: 2,
- VariantIDPremium: 3,
- }
+ b := NewTestBilling()
attrs := billing.OrderAttributes{
UserEmail: "test@example.com",
@@ -192,7 +200,7 @@ func TestDeductCredits(t *testing.T) {
}
func TestVerifyBillingSignature(t *testing.T) {
- b := billing.Billing{}
+ b := NewTestBilling()
body := []byte(`{"key":"value"}`)
secret := "testsecret"
mac := hmacSha256(body, secret)
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index 4515fa6..4ce5658 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -255,6 +255,14 @@ func Test_CreateUsersHandler_Integration(t *testing.T) {
expected := tc.respBody
got := *respUnmarshalled
+ // Assert JWT is non-empty
+ if got.JWToken == "" {
+ t.Fatalf("[%s] expected JWT, got empty string", tc.name)
+ }
+
+ // Ignore JWToken when diffing
+ got.JWToken = ""
+
if diff := cmp.Diff(expected, got, cmpopts.EquateApproxTime(time.Second)); diff != "" {
t.Errorf("Mismatch (-expected +got):\n%s", diff)
}
From d85fef4ed13768f7217023fadfd68139583bed5c Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sat, 27 Sep 2025 17:15:49 +0700
Subject: [PATCH 14/15] fixed syntax and inconsistencies
---
billing/service.go | 2 +-
handlers/handlers.go | 4 ++--
internal/server/server.go | 2 +-
token/service.go | 4 ++--
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/billing/service.go b/billing/service.go
index 8dc7d4f..7f32790 100644
--- a/billing/service.go
+++ b/billing/service.go
@@ -71,7 +71,7 @@ func (b *Billing) RequestCheckoutSession(userEmail string, variantID int) (strin
if res.StatusCode < 200 || res.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(res.Body)
b.Logger.Error("LemonSqueezy returned error", "error", string(bodyBytes))
- return "", fmt.Errorf("LemonSqueezy API error: %s", res.Status)
+ return "", fmt.Errorf("lemonSqueezy API error: %s", res.Status)
}
var result CheckoutResponse
diff --git a/handlers/handlers.go b/handlers/handlers.go
index 364b0f5..8c34f36 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -283,7 +283,7 @@ func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
RespondWithError(w, http.StatusUnauthorized, user.ErrAccountDeleted.Error())
return
}
- RespondWithError(w, http.StatusUnauthorized, "Invalid username or password.")
+ RespondWithError(w, http.StatusUnauthorized, "Authentication failed.")
return
}
@@ -457,7 +457,7 @@ func (h *Handler) RefreshTokensHandler(w http.ResponseWriter, r *http.Request) {
if params.UserID == 0 {
h.Logger.Error("Invalid userID")
- RespondWithError(w, http.StatusBadRequest, "Invalid username or password")
+ RespondWithError(w, http.StatusBadRequest, "Authentication failed")
return
}
diff --git a/internal/server/server.go b/internal/server/server.go
index 608440a..ec7f53c 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -155,6 +155,6 @@ func NewServer(logger *slog.Logger) (*Server, error) {
}
func (s *Server) StartServer(logger *slog.Logger) {
- logger.Info("Serving files from %s on port: %s\n", ".", "8080")
+ logger.Info("Serving files", "directory", ".", "port", "8080")
log.Fatal(http.ListenAndServe(":8080", middleware.EnableCors(s.mux)))
}
diff --git a/token/service.go b/token/service.go
index 22e8bae..f1c07f5 100644
--- a/token/service.go
+++ b/token/service.go
@@ -97,7 +97,7 @@ func ExtractUserIDFromToken(tokenString string) (int, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(tokenString *jwt.Token) (interface{}, error) {
if _, ok := tokenString.Method.(*jwt.SigningMethodHMAC); !ok {
- return 0, fmt.Errorf("Unauthorized")
+ return 0, fmt.Errorf("unauthorized")
}
return []byte(jwtSecret), nil
})
@@ -116,5 +116,5 @@ func ExtractUserIDFromToken(tokenString string) (int, error) {
return userID, nil
}
- return 0, fmt.Errorf("Unauthorized")
+ return 0, fmt.Errorf("unauthorized")
}
From a13036c7cd3136cdc0382758c0fed2d2ca2beabe Mon Sep 17 00:00:00 2001
From: MichaelBoegner
Date: Sun, 28 Sep 2025 11:32:57 +0700
Subject: [PATCH 15/15] fixed loading env.test instead of env.dev
---
handlers/handlers_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index 4ce5658..7e1d311 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -59,7 +59,7 @@ func TestMain(m *testing.M) {
logger = slog.New(handler)
logger.Info("Loading environment variables...")
- if err := godotenv.Load("../.env.dev"); err != nil {
+ if err := godotenv.Load("../.env.test"); err != nil {
logger.Error("failed to load .env.test", "error", err)
os.Exit(1)
}