various error checks and formatting

This commit is contained in:
2026-04-14 15:26:36 +02:00
parent 8157789e66
commit b92132474b
16 changed files with 242 additions and 94 deletions

View File

@@ -76,7 +76,7 @@ func (h *AddressHandler) CreateAddress(w http.ResponseWriter, r *http.Request) {
switch req.Country {
case "US":
if h.shippoKey == "" {
utils.RespondError(w, http.StatusBadRequest, "CONFIG_ERROR", "shippo not configured for US address validation", nil)
utils.RespondError(w, http.StatusInternalServerError, "CONFIG_ERROR", "shippo not configured for US address validation", nil)
return
}
client := shippo.NewClient(h.shippoKey)
@@ -166,12 +166,14 @@ func (h *AddressHandler) DeleteAddress(w http.ResponseWriter, r *http.Request) {
var address models.Address
if err := db.Where("id = ? AND user_id = ?", id, userID).First(&address).Error; err != nil {
utils.RespondError(w, http.StatusNotFound, "ADDRESS NOT FOUND", "address not found", nil)
utils.RespondError(w, http.StatusNotFound, "ADDRESS_NOT_FOUND", "address not found", nil)
return
}
// ownership is guaranteed by the WHERE clause above
db.Delete(&address)
if err := db.Delete(&address).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to delete address", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"message": "address successfully deleted",
@@ -197,10 +199,16 @@ func (h *AddressHandler) SetDefaultAddress(w http.ResponseWriter, r *http.Reques
return
}
db.Model(&models.Address{}).Where("user_id = ?", userID).Update("is_default", false)
if err := db.Model(&models.Address{}).Where("user_id = ?", userID).Update("is_default", false).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to update address", nil)
return
}
address.IsDefault = true
db.Save(&address)
if err := db.Save(&address).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to save address", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"address": address,

View File

@@ -158,4 +158,3 @@ func TestGetAddressesWithData(t *testing.T) {
t.Errorf("expected 2 addresses, got: %d", len(resp.Data.Addresses))
}
}

View File

@@ -79,7 +79,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
return
}
} else if err != nil {
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "database error", nil)
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to look up user", nil)
return
}
@@ -89,7 +89,13 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
Fingerprint: req.Fingerprint,
PublicKey: req.SSHPublicKey,
}
db.Where(models.SSHKey{Fingerprint: req.Fingerprint}).FirstOrCreate(&sshKey)
if err := db.Where(models.SSHKey{Fingerprint: req.Fingerprint}).FirstOrCreate(&sshKey).Error; err != nil {
// we specifically want to block users whos ssh key failed to save. this is to prevent downstream errors which i dont want to deal with now
// !TODO add a way to just log the errror and app continue without trying to save carts, cards and addresses. this way the users wouldnt
// just be kicked, they would shop normally but on their next login they would need to add all their data again.
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to save ssh key", nil)
return
}
}
token, err := h.jwtManager.GenerateToken(user.ID, user.Email, user.Name)

View File

@@ -119,10 +119,16 @@ func (h *CardHandler) SetDefaultCard(w http.ResponseWriter, r *http.Request) {
return
}
db.Model(&models.Card{}).Where("user_id = ?", userID).Update("is_default", false)
if err := db.Model(&models.Card{}).Where("user_id = ?", userID).Update("is_default", false).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to update card", nil)
return
}
card.IsDefault = true
db.Save(&card)
if err := db.Save(&card).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to save card", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"card": card,
@@ -285,7 +291,10 @@ func (h *CardHandler) DeleteCard(w http.ResponseWriter, r *http.Request) {
// Clear the card from any cart that references it.
cardID := uint(id)
db.Model(&models.Cart{}).Where("card_id = ? AND user_id = ?", cardID, userID).Update("card_id", nil)
if err := db.Model(&models.Cart{}).Where("card_id = ? AND user_id = ?", cardID, userID).Update("card_id", nil).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to clear card from cart", nil)
return
}
// Detach from Stripe (best-effort; don't block deletion on Stripe errors).
if len(card.StripePaymentID) >= 3 && card.StripePaymentID[:3] == "pm_" {
@@ -294,7 +303,10 @@ func (h *CardHandler) DeleteCard(w http.ResponseWriter, r *http.Request) {
}
// Soft-delete the local record.
db.Delete(&card)
if err := db.Delete(&card).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to delete card", nil)
return
}
audit.CardDeleted(userID, card.ID)
@@ -325,6 +337,8 @@ func ensureStripeCustomer(gdb *gorm.DB, user *models.User) error {
return err
}
user.StripeCustomerID = cust.ID
gdb.Save(user)
if err := gdb.Save(user).Error; err != nil {
return fmt.Errorf("Failed to save stripe customer: %w", err)
}
return nil
}

View File

@@ -5,10 +5,10 @@ import (
"fmt"
"net/http"
"gorm.io/gorm"
"github.com/stripe/stripe-go/v78"
"github.com/stripe/stripe-go/v78/paymentintent"
"github.com/stripe/stripe-go/v78/paymentmethod"
"gorm.io/gorm"
"terminalShop/api/middleware"
"terminalShop/pkg/audit"
@@ -117,7 +117,10 @@ func (h *CartHandler) SetItem(w http.ResponseWriter, r *http.Request) {
}
if req.Quantity <= 0 {
db.Where("cart_id = ? AND coffee_id = ?", cart.ID, req.CoffeeID).Delete(&models.CartItem{})
if err := db.Where("cart_id = ? AND coffee_id = ?", cart.ID, req.CoffeeID).Delete(&models.CartItem{}).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to delete cart item", nil)
return
}
} else {
var item models.CartItem
result := db.Where("cart_id = ? AND coffee_id = ?", cart.ID, req.CoffeeID).First(&item)
@@ -127,9 +130,15 @@ func (h *CartHandler) SetItem(w http.ResponseWriter, r *http.Request) {
CoffeeID: req.CoffeeID,
Quantity: req.Quantity,
}
db.Create(&item)
if err := db.Create(&item).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to create item", nil)
return
}
} else {
db.Model(&item).Update("quantity", req.Quantity)
if err := db.Model(&item).Update("quantity", req.Quantity).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to update quantity", nil)
return
}
}
}
@@ -164,7 +173,10 @@ func (h *CartHandler) SetAddress(w http.ResponseWriter, r *http.Request) {
return
}
db.Model(cart).Update("address_id", req.AddressID)
if err := db.Model(cart).Update("address_id", req.AddressID).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to set address", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"message": "address set",
@@ -197,7 +209,10 @@ func (h *CartHandler) SetCard(w http.ResponseWriter, r *http.Request) {
return
}
db.Model(cart).Update("card_id", req.CardID)
if err := db.Model(cart).Update("card_id", req.CardID).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to set card", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"message": "card set",
@@ -214,7 +229,10 @@ func (h *CartHandler) ClearCart(w http.ResponseWriter, r *http.Request) {
return
}
db.Where("cart_id = ?", cart.ID).Delete(&models.CartItem{})
if err := db.Where("cart_id = ?", cart.ID).Delete(&models.CartItem{}).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to delete cart item", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"message": "cart cleared",
@@ -297,7 +315,10 @@ func (h *CartHandler) ConvertCart(w http.ResponseWriter, r *http.Request) {
}
paymentMethodID = pm.ID
card.StripePaymentID = pm.ID
db.Save(&card)
if err := db.Save(&card).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to save card", nil)
return
}
}
var subtotal int
@@ -427,10 +448,12 @@ func (h *CartHandler) ConvertCart(w http.ResponseWriter, r *http.Request) {
audit.OrderPaid(userID, order.ID, total, pi.ID)
db.Preload("Items").First(&order, order.ID)
if err := db.Preload("Items").First(&order, order.ID).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to preload items", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"order": order,
})
}

View File

@@ -57,4 +57,3 @@ func (h *ProductHandler) GetProduct(w http.ResponseWriter, r *http.Request) {
"product": product,
})
}

View File

@@ -23,10 +23,7 @@ func (h *SSHKeyHandler) GetSSHKeys(w http.ResponseWriter, r *http.Request) {
userID := middleware.UserIDFromContext(r.Context())
var keys []models.SSHKey
if err := db.Where("user_id = ?", userID).Order("created_at ASC").Find(&keys).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to fetch ssh keys", nil)
return
}
db.Where("user_id = ?", userID).Order("created_at ASC").Find(&keys)
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"ssh_keys": keys,
@@ -36,7 +33,7 @@ func (h *SSHKeyHandler) GetSSHKeys(w http.ResponseWriter, r *http.Request) {
// DeleteSSHKeys removes one of the users ssh keys by ID
func (h *SSHKeyHandler) DeleteSSHKeys(w http.ResponseWriter, r *http.Request) {
db := database.GetDB()
userId := middleware.UserIDFromContext(r.Context())
userID := middleware.UserIDFromContext(r.Context())
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
@@ -46,11 +43,14 @@ func (h *SSHKeyHandler) DeleteSSHKeys(w http.ResponseWriter, r *http.Request) {
}
var key models.SSHKey
if err := db.Where("id = ? AND user_id = ?", id, userId).First(&key).Error; err != nil {
if err := db.Where("id = ? AND user_id = ?", id, userID).First(&key).Error; err != nil {
utils.RespondError(w, http.StatusNotFound, "SSH_KEY_NOT_FOUND", "ssh key not found", nil)
return
}
db.Delete(&key)
if err := db.Delete(&key).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to delete ssh key", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"message": "ssh key deleted",

View File

@@ -2,6 +2,7 @@ package handlers
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
@@ -128,7 +129,9 @@ func (h *WebhookHandler) handlePaymentIntentSucceeded(event stripe.Event) {
if err := tx.Where("user_id = ?", order.UserID).First(&cart).Error; err != nil {
return nil // Cart already gone.
}
tx.Where("cart_id = ?", cart.ID).Delete(&models.CartItem{})
if err := tx.Where("cart_id = ?", cart.ID).Delete(&models.CartItem{}).Error; err != nil {
return fmt.Errorf("failed to delete cart item: %w", err)
}
return tx.Model(&cart).Updates(map[string]interface{}{
"address_id": nil,
"card_id": nil,
@@ -156,8 +159,10 @@ func (h *WebhookHandler) handlePaymentIntentFailed(event stripe.Event) {
}
db := database.GetDB()
db.Model(&models.Order{}).Where("id = ? AND status = ?", orderIDStr, models.OrderStatusPending).
Update("status", models.OrderStatusFailed)
if err := db.Model(&models.Order{}).Where("id = ? AND status = ?", orderIDStr, models.OrderStatusPending).
Update("status", models.OrderStatusFailed).Error; err != nil {
log.Printf("[webhook] failed to mark order %s as failed: %v", orderIDStr, err)
}
errMsg := "unknown"
if pi.LastPaymentError != nil {

View File

@@ -117,7 +117,7 @@ func SetupRoutes(
})
// SSH Keys
r.Route("/ssh-keys", func(r chi.Router){
r.Route("/ssh-keys", func(r chi.Router) {
// GET doesnt need id because it gets every ssh key
r.Get("/", sshKeyHandler.GetSSHKeys)
// Delete needs an id to delete only one key