mirror of
https://github.com/marcusDenslow/terminalShop.git
synced 2026-10-25 10:02:21 +01:00
fix: defer component logger creation past package init
Package-level slog.With() vars are evaluated at init time, before observability.InitLogger() runs in main. They captured the original default slog handler and bypassed the countingHandler that increments log_messages_total — so warn/error calls from these packages weren't being counted. Convert to functions so each call resolves slog.Default() at use time and inherits the active counting handler. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ import (
|
||||
"terminalShop/pkg/utils"
|
||||
)
|
||||
|
||||
var addressLog = slog.With("component", "address")
|
||||
func addressLog() *slog.Logger { return slog.With("component", "address") }
|
||||
|
||||
type AddressHandler struct {
|
||||
shippoKey string
|
||||
@@ -93,7 +93,7 @@ func (h *AddressHandler) CreateAddress(w http.ResponseWriter, r *http.Request) {
|
||||
Phone: req.Phone,
|
||||
})
|
||||
if err != nil {
|
||||
addressLog.Warn("shippo address validation failed", "error", err, "country", "US")
|
||||
addressLog().Warn("shippo address validation failed", "error", err, "country", "US")
|
||||
utils.RespondError(w, http.StatusBadRequest, "INVALID_ADDRESS", "address is invalid", nil)
|
||||
return
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func (h *AddressHandler) CreateAddress(w http.ResponseWriter, r *http.Request) {
|
||||
Phone: req.Phone,
|
||||
})
|
||||
if err != nil {
|
||||
addressLog.Warn("bring address validation failed", "error", err, "country", "NO")
|
||||
addressLog().Warn("bring address validation failed", "error", err, "country", "NO")
|
||||
utils.RespondError(w, http.StatusBadRequest, "INVALID_ADDRESS", "address is invalid", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var authLog = slog.With("component", "auth")
|
||||
func authLog() *slog.Logger { return slog.With("component", "auth") }
|
||||
|
||||
// AuthHandler handles authentication-related requests
|
||||
type AuthHandler struct {
|
||||
@@ -58,7 +58,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
// validate shared secret
|
||||
if req.ClientSecret != h.authFingerprintKey {
|
||||
middleware.RecordAuthAttempt("invalid_secret")
|
||||
authLog.Warn("invalid client secret", "remote", r.RemoteAddr)
|
||||
authLog().Warn("invalid client secret", "remote", r.RemoteAddr)
|
||||
utils.RespondError(w, http.StatusUnauthorized, "INVALID_SECRET", "invalid client secret", nil)
|
||||
return
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
if req.SSHPublicKey == "" {
|
||||
middleware.RecordAuthAttempt("user_not_found")
|
||||
authLog.Warn("user not found and no public key for registration", "fingerprint", req.Fingerprint)
|
||||
authLog().Warn("user not found and no public key for registration", "fingerprint", req.Fingerprint)
|
||||
utils.RespondError(w, http.StatusUnauthorized, "USER_NOT_FOUND", "no user found for this fingerprint", nil)
|
||||
return
|
||||
}
|
||||
@@ -86,15 +86,15 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
middleware.RecordAuthAttempt("error")
|
||||
authLog.Error("failed to create user", "error", err)
|
||||
authLog().Error("failed to create user", "error", err)
|
||||
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to create user", nil)
|
||||
return
|
||||
}
|
||||
middleware.RecordAuthAttempt("registered")
|
||||
authLog.Info("user registered", "user_id", user.ID)
|
||||
authLog().Info("user registered", "user_id", user.ID)
|
||||
} else if err != nil {
|
||||
middleware.RecordAuthAttempt("error")
|
||||
authLog.Error("failed to look up user", "error", err)
|
||||
authLog().Error("failed to look up user", "error", err)
|
||||
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to look up user", nil)
|
||||
return
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
token, err := h.jwtManager.GenerateToken(user.ID, user.Email, user.Name)
|
||||
if err != nil {
|
||||
middleware.RecordAuthAttempt("error")
|
||||
authLog.Error("failed to generate token", "user_id", user.ID, "error", err)
|
||||
authLog().Error("failed to generate token", "user_id", user.ID, "error", err)
|
||||
utils.RespondError(w, http.StatusInternalServerError, "TOKEN_ERROR", "failed to generate token", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var reconcileLog = slog.With("component", "reconcile")
|
||||
func reconcileLog() *slog.Logger { return slog.With("component", "reconcile") }
|
||||
|
||||
// ReconcileOrders finds pending orders with no Stripe ID older than 10 minutes
|
||||
// and checks whether Stripe has a succeeded PaymentIntent for them. This fixes
|
||||
@@ -32,7 +32,7 @@ func ReconcileOrders(stripeKey string) {
|
||||
return
|
||||
}
|
||||
|
||||
reconcileLog.Info("checking pending orders", "count", len(orders))
|
||||
reconcileLog().Info("checking pending orders", "count", len(orders))
|
||||
|
||||
for _, order := range orders {
|
||||
params := &stripe.PaymentIntentSearchParams{
|
||||
@@ -44,7 +44,7 @@ func ReconcileOrders(stripeKey string) {
|
||||
iter := paymentintent.Search(params)
|
||||
for iter.Next() {
|
||||
pi := iter.PaymentIntent()
|
||||
reconcileLog.Warn("found succeeded payment intent for unrecorded order — patching",
|
||||
reconcileLog().Warn("found succeeded payment intent for unrecorded order — patching",
|
||||
"order_id", order.ID, "pi", pi.ID)
|
||||
|
||||
txErr := db.Transaction(func(tx *gorm.DB) error {
|
||||
@@ -67,11 +67,11 @@ func ReconcileOrders(stripeKey string) {
|
||||
}).Error
|
||||
})
|
||||
if txErr != nil {
|
||||
reconcileLog.Error("failed to patch order", "order_id", order.ID, "error", txErr)
|
||||
reconcileLog().Error("failed to patch order", "order_id", order.ID, "error", txErr)
|
||||
}
|
||||
}
|
||||
if err := iter.Err(); err != nil {
|
||||
reconcileLog.Error("stripe search error", "order_id", order.ID, "error", err)
|
||||
reconcileLog().Error("stripe search error", "order_id", order.ID, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"terminalShop/pkg/utils"
|
||||
)
|
||||
|
||||
var webhookLog = slog.With("component", "webhook")
|
||||
func webhookLog() *slog.Logger { return slog.With("component", "webhook") }
|
||||
|
||||
// WebhookHandler handles inbound Stripe webhook events.
|
||||
type WebhookHandler struct {
|
||||
@@ -48,13 +48,13 @@ func (h *WebhookHandler) HandleStripe(w http.ResponseWriter, r *http.Request) {
|
||||
// Verify the Stripe signature. Reject anything that doesn't match.
|
||||
if h.webhookSecret == "" {
|
||||
// No secret configured — log a loud warning but still process in dev.
|
||||
webhookLog.Warn("stripe webhook secret not set; skipping signature verification")
|
||||
webhookLog().Warn("stripe webhook secret not set; skipping signature verification")
|
||||
} else {
|
||||
sig := r.Header.Get("Stripe-Signature")
|
||||
if _, err := webhook.ConstructEventWithOptions(body, sig, h.webhookSecret, webhook.ConstructEventOptions{
|
||||
IgnoreAPIVersionMismatch: true,
|
||||
}); err != nil {
|
||||
webhookLog.Warn("signature verification failed", "error", err)
|
||||
webhookLog().Warn("signature verification failed", "error", err)
|
||||
utils.RespondError(w, http.StatusUnauthorized, "INVALID_SIGNATURE", "webhook signature verification failed", nil)
|
||||
return
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func (h *WebhookHandler) HandleStripe(w http.ResponseWriter, r *http.Request) {
|
||||
"payment_method.detached",
|
||||
"payment_method.updated":
|
||||
// Handled by the reconcile job and direct API calls; log only.
|
||||
webhookLog.Info("payment method event received", "event_type", string(event.Type))
|
||||
webhookLog().Info("payment method event received", "event_type", string(event.Type))
|
||||
}
|
||||
|
||||
// Always return 200 so Stripe doesn't retry.
|
||||
@@ -97,7 +97,7 @@ func (h *WebhookHandler) HandleStripe(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *WebhookHandler) handlePaymentIntentSucceeded(event stripe.Event) {
|
||||
var pi stripe.PaymentIntent
|
||||
if err := json.Unmarshal(event.Data.Raw, &pi); err != nil {
|
||||
webhookLog.Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.succeeded")
|
||||
webhookLog().Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.succeeded")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func (h *WebhookHandler) handlePaymentIntentSucceeded(event stripe.Event) {
|
||||
db := database.GetDB()
|
||||
var order models.Order
|
||||
if err := db.Where("id = ?", orderIDStr).First(&order).Error; err != nil {
|
||||
webhookLog.Warn("order not found for payment intent", "order_id", orderIDStr, "pi", pi.ID)
|
||||
webhookLog().Warn("order not found for payment intent", "order_id", orderIDStr, "pi", pi.ID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -140,19 +140,19 @@ func (h *WebhookHandler) handlePaymentIntentSucceeded(event stripe.Event) {
|
||||
}).Error
|
||||
})
|
||||
if txErr != nil {
|
||||
webhookLog.Error("failed to record paid status",
|
||||
webhookLog().Error("failed to record paid status",
|
||||
"order_id", orderIDStr, "pi", pi.ID, "error", txErr)
|
||||
return
|
||||
}
|
||||
|
||||
audit.OrderPaid(order.UserID, order.ID, int(pi.Amount), pi.ID)
|
||||
webhookLog.Info("order marked paid", "order_id", orderIDStr, "pi", pi.ID)
|
||||
webhookLog().Info("order marked paid", "order_id", orderIDStr, "pi", pi.ID)
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) handlePaymentIntentFailed(event stripe.Event) {
|
||||
var pi stripe.PaymentIntent
|
||||
if err := json.Unmarshal(event.Data.Raw, &pi); err != nil {
|
||||
webhookLog.Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.payment_failed")
|
||||
webhookLog().Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.payment_failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -164,20 +164,20 @@ func (h *WebhookHandler) handlePaymentIntentFailed(event stripe.Event) {
|
||||
db := database.GetDB()
|
||||
if err := db.Model(&models.Order{}).Where("id = ? AND status = ?", orderIDStr, models.OrderStatusPending).
|
||||
Update("status", models.OrderStatusFailed).Error; err != nil {
|
||||
webhookLog.Error("failed to mark order failed", "order_id", orderIDStr, "error", err)
|
||||
webhookLog().Error("failed to mark order failed", "order_id", orderIDStr, "error", err)
|
||||
}
|
||||
|
||||
errMsg := "unknown"
|
||||
if pi.LastPaymentError != nil {
|
||||
errMsg = pi.LastPaymentError.Msg
|
||||
}
|
||||
webhookLog.Warn("payment failed", "order_id", orderIDStr, "pi", pi.ID, "reason", errMsg)
|
||||
webhookLog().Warn("payment failed", "order_id", orderIDStr, "pi", pi.ID, "reason", errMsg)
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) {
|
||||
var sess stripe.CheckoutSession
|
||||
if err := json.Unmarshal(event.Data.Raw, &sess); err != nil {
|
||||
webhookLog.Error("failed to unmarshal checkout session", "error", err)
|
||||
webhookLog().Error("failed to unmarshal checkout session", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) {
|
||||
}
|
||||
|
||||
if sess.SetupIntent == nil || sess.Customer == nil {
|
||||
webhookLog.Warn("checkout session missing setup_intent or customer")
|
||||
webhookLog().Warn("checkout session missing setup_intent or customer")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -194,25 +194,25 @@ func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) {
|
||||
|
||||
si, err := setupintent.Get(sess.SetupIntent.ID, nil)
|
||||
if err != nil {
|
||||
webhookLog.Error("failed to get setup intent", "setup_intent", sess.SetupIntent.ID, "error", err)
|
||||
webhookLog().Error("failed to get setup intent", "setup_intent", sess.SetupIntent.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if si.PaymentMethod == nil {
|
||||
webhookLog.Warn("setup intent has no payment method", "setup_intent", si.ID)
|
||||
webhookLog().Warn("setup intent has no payment method", "setup_intent", si.ID)
|
||||
return
|
||||
}
|
||||
|
||||
pm, err := paymentmethod.Get(si.PaymentMethod.ID, nil)
|
||||
if err != nil {
|
||||
webhookLog.Error("failed to get payment method", "payment_method", si.PaymentMethod.ID, "error", err)
|
||||
webhookLog().Error("failed to get payment method", "payment_method", si.PaymentMethod.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
db := database.GetDB()
|
||||
var user models.User
|
||||
if err := db.Where("stripe_customer_id = ?", sess.Customer.ID).First(&user).Error; err != nil {
|
||||
webhookLog.Warn("user not found for customer", "customer", sess.Customer.ID, "error", err)
|
||||
webhookLog().Warn("user not found for customer", "customer", sess.Customer.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -232,10 +232,10 @@ func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) {
|
||||
}
|
||||
|
||||
if err := db.Create(&card).Error; err != nil {
|
||||
webhookLog.Error("failed to save card", "user_id", user.ID, "error", err)
|
||||
webhookLog().Error("failed to save card", "user_id", user.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
audit.CardAdded(user.ID, card.ID, card.Brand, card.Last4)
|
||||
webhookLog.Info("card saved via checkout.session.completed", "user_id", user.ID)
|
||||
webhookLog().Info("card saved via checkout.session.completed", "user_id", user.ID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user