Compare commits
4 Commits
fix/sca-al
...
add-ci
| Author | SHA1 | Date | |
|---|---|---|---|
| d68379ff68 | |||
| 23b6835a2a | |||
| d33d6bd354 | |||
| 73697d4037 |
38
.github/workflows/ci.yml
vendored
Normal file
38
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build
|
||||
run: go build ./...
|
||||
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
|
||||
- name: Format check
|
||||
run: |
|
||||
unformatted=$(gofmt -l $(go list -f '{{.Dir}}' ./...))
|
||||
if [ -n "$unformatted" ]; then
|
||||
echo "These files need gofmt:"
|
||||
echo "$unformatted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v7
|
||||
with:
|
||||
version: v2.4.0
|
||||
|
||||
- name: Test (race detector)
|
||||
run: go test -race ./...
|
||||
16
.golangci.yml
Normal file
16
.golangci.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
version: "2"
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- errcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
exclusions:
|
||||
paths:
|
||||
- terminal-shop-source
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func setupAddressTestDB(t *testing.T) (string, models.User) {
|
||||
t.Helper()
|
||||
testDB := "test_addresses.db"
|
||||
os.Remove(testDB)
|
||||
_ = os.Remove(testDB)
|
||||
database.ResetForTesting()
|
||||
|
||||
db, err := database.Connect(testDB)
|
||||
@@ -42,7 +42,7 @@ func setupAddressTestDB(t *testing.T) (string, models.User) {
|
||||
|
||||
func TestGetAddressesEmpty(t *testing.T) {
|
||||
testDB, user := setupAddressTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewAddressHandler("", "", "")
|
||||
@@ -74,7 +74,7 @@ func TestGetAddressesEmpty(t *testing.T) {
|
||||
|
||||
func TestGetAddressesIsolation(t *testing.T) {
|
||||
testDB, user1 := setupAddressTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -105,7 +105,7 @@ func TestGetAddressesIsolation(t *testing.T) {
|
||||
Addresses []models.Address `json:"addresses"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Addresses) != 1 {
|
||||
t.Fatalf("expexted 1 address for user1, got %d", len(resp.Data.Addresses))
|
||||
@@ -117,7 +117,7 @@ func TestGetAddressesIsolation(t *testing.T) {
|
||||
|
||||
func TestGetAddressesWithData(t *testing.T) {
|
||||
testDB, user := setupAddressTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
|
||||
@@ -22,7 +22,7 @@ const testClientSecret = "test-client-secret"
|
||||
func setupAuthHandlerTestDB(t *testing.T) (string, *auth.JWTManager, *AuthHandler) {
|
||||
t.Helper()
|
||||
testDB := "test_auth.db"
|
||||
os.Remove(testDB)
|
||||
_ = os.Remove(testDB)
|
||||
database.ResetForTesting()
|
||||
|
||||
db, err := database.Connect(testDB)
|
||||
@@ -62,7 +62,7 @@ func createAuthTestUser(t *testing.T, fingerprint string) models.User {
|
||||
|
||||
func TestGetTokenSuccess(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
user := createAuthTestUser(t, "SHA256:testfingerprint123")
|
||||
@@ -108,7 +108,7 @@ func TestGetTokenSuccess(t *testing.T) {
|
||||
|
||||
func TestGetTokenBadSecret(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
@@ -128,7 +128,7 @@ func TestGetTokenBadSecret(t *testing.T) {
|
||||
|
||||
func TestGetTokenMissingFingerprint(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
@@ -147,7 +147,7 @@ func TestGetTokenMissingFingerprint(t *testing.T) {
|
||||
|
||||
func TestGetTokenUnknownFingerprint(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
@@ -167,7 +167,7 @@ func TestGetTokenUnknownFingerprint(t *testing.T) {
|
||||
|
||||
func TestGetTokenInvalidJSON(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
req := httptest.NewRequest("POST", "/api/v1/auth/token", bytes.NewBufferString("{bad json"))
|
||||
@@ -185,7 +185,7 @@ func TestGetTokenInvalidJSON(t *testing.T) {
|
||||
|
||||
func TestRegisterWithSSHKeySuccess(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
@@ -207,7 +207,7 @@ func TestRegisterWithSSHKeySuccess(t *testing.T) {
|
||||
|
||||
func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
createAuthTestUser(t, "SHA256:existingfp")
|
||||
@@ -229,7 +229,7 @@ func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
||||
|
||||
func TestRegisterWithSSHKeyMissingFields(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
@@ -250,7 +250,7 @@ func TestRegisterWithSSHKeyMissingFields(t *testing.T) {
|
||||
|
||||
func TestGetUserBySSHKeySuccess(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
createAuthTestUser(t, "SHA256:lookupfp")
|
||||
@@ -267,7 +267,7 @@ func TestGetUserBySSHKeySuccess(t *testing.T) {
|
||||
|
||||
func TestGetUserBySSHKeyNotFound(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/auth/ssh?fingerprint=SHA256:nope", nil)
|
||||
@@ -282,7 +282,7 @@ func TestGetUserBySSHKeyNotFound(t *testing.T) {
|
||||
|
||||
func TestGetUserBySSHKeyMissingParam(t *testing.T) {
|
||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/auth/ssh", nil)
|
||||
|
||||
@@ -30,14 +30,16 @@ var (
|
||||
payRedirectsMu sync.Mutex
|
||||
)
|
||||
|
||||
func storeRedirect(stripeURL string) string {
|
||||
func storeRedirect(stripeURL string) (string, error) {
|
||||
b := make([]byte, 16)
|
||||
rand.Read(b)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
token := hex.EncodeToString(b)
|
||||
payRedirectsMu.Lock()
|
||||
payRedirects[token] = stripeURL
|
||||
payRedirectsMu.Unlock()
|
||||
return token
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// PayRedirect resolves a short token and redirects to the Stripe checkout URL.
|
||||
@@ -166,7 +168,11 @@ func (h *CardHandler) CollectCard(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
token := storeRedirect(sess.URL)
|
||||
token, err := storeRedirect(sess.URL)
|
||||
if err != nil {
|
||||
utils.RespondError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "failed to generate redirect token", nil)
|
||||
return
|
||||
}
|
||||
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
|
||||
"url": fmt.Sprintf("%s/pay/%s", h.appURL, token),
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
func setupCartTestDB(t *testing.T) (string, models.User) {
|
||||
t.Helper()
|
||||
testDB := "test_cart.db"
|
||||
os.Remove(testDB)
|
||||
_ = os.Remove(testDB)
|
||||
database.ResetForTesting()
|
||||
|
||||
db, err := database.Connect(testDB)
|
||||
@@ -62,7 +62,7 @@ func authRequest(method, url string, body []byte, userID uint) *http.Request {
|
||||
|
||||
func TestGetCartEmpty(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -101,7 +101,7 @@ func TestGetCartEmpty(t *testing.T) {
|
||||
|
||||
func TestSetItemAndGetCart(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -157,7 +157,7 @@ func TestSetItemAndGetCart(t *testing.T) {
|
||||
|
||||
func TestSetItemUpdateQuantity(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -190,7 +190,7 @@ func TestSetItemUpdateQuantity(t *testing.T) {
|
||||
} `json:"cart"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Cart.Items) != 1 {
|
||||
t.Fatalf("Expected 1 item after update, got %d", len(resp.Data.Cart.Items))
|
||||
@@ -202,7 +202,7 @@ func TestSetItemUpdateQuantity(t *testing.T) {
|
||||
|
||||
func TestSetItemRemove(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -232,7 +232,7 @@ func TestSetItemRemove(t *testing.T) {
|
||||
} `json:"cart"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Cart.Items) != 0 {
|
||||
t.Errorf("Expected 0 items after removal, got %d", len(resp.Data.Cart.Items))
|
||||
@@ -241,7 +241,7 @@ func TestSetItemRemove(t *testing.T) {
|
||||
|
||||
func TestSetItemInvalidProduct(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -258,7 +258,7 @@ func TestSetItemInvalidProduct(t *testing.T) {
|
||||
|
||||
func TestSetItemMissingCoffeeID(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -275,7 +275,7 @@ func TestSetItemMissingCoffeeID(t *testing.T) {
|
||||
|
||||
func TestClearCart(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -310,7 +310,7 @@ func TestClearCart(t *testing.T) {
|
||||
} `json:"cart"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Cart.Items) != 0 {
|
||||
t.Errorf("Expected 0 items after clear, got %d", len(resp.Data.Cart.Items))
|
||||
@@ -319,7 +319,7 @@ func TestClearCart(t *testing.T) {
|
||||
|
||||
func TestSetAddress(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -358,7 +358,7 @@ func TestSetAddress(t *testing.T) {
|
||||
} `json:"cart"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if resp.Data.Cart.AddressID == nil || *resp.Data.Cart.AddressID != addr.ID {
|
||||
t.Errorf("Expected address_id %d, got %v", addr.ID, resp.Data.Cart.AddressID)
|
||||
@@ -367,7 +367,7 @@ func TestSetAddress(t *testing.T) {
|
||||
|
||||
func TestSetAddressWrongUser(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -402,7 +402,7 @@ func TestSetAddressWrongUser(t *testing.T) {
|
||||
|
||||
func TestSetCard(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -440,7 +440,7 @@ func TestSetCard(t *testing.T) {
|
||||
} `json:"cart"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if resp.Data.Cart.CardID == nil || *resp.Data.Cart.CardID != card.ID {
|
||||
t.Errorf("Expected card_id %d, got %v", card.ID, resp.Data.Cart.CardID)
|
||||
@@ -449,7 +449,7 @@ func TestSetCard(t *testing.T) {
|
||||
|
||||
func TestSetCardWrongUser(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -482,7 +482,7 @@ func TestSetCardWrongUser(t *testing.T) {
|
||||
|
||||
func TestConvertCartMissingAddress(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -505,7 +505,7 @@ func TestConvertCartMissingAddress(t *testing.T) {
|
||||
|
||||
func TestConvertCartMissingCard(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -536,7 +536,7 @@ func TestConvertCartMissingCard(t *testing.T) {
|
||||
|
||||
func TestConvertCartEmpty(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -553,7 +553,7 @@ func TestConvertCartEmpty(t *testing.T) {
|
||||
|
||||
func TestMultipleItems(t *testing.T) {
|
||||
testDB, user := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewCartHandler("")
|
||||
@@ -583,7 +583,7 @@ func TestMultipleItems(t *testing.T) {
|
||||
} `json:"cart"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Cart.Items) != 3 {
|
||||
t.Errorf("Expected 3 items, got %d", len(resp.Data.Cart.Items))
|
||||
@@ -595,7 +595,7 @@ func TestMultipleItems(t *testing.T) {
|
||||
|
||||
func TestCartIsolation(t *testing.T) {
|
||||
testDB, user1 := setupCartTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -638,7 +638,7 @@ func TestCartIsolation(t *testing.T) {
|
||||
} `json:"cart"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Cart.Items) != 1 {
|
||||
t.Fatalf("User 1 expected 1 item, got %d", len(resp.Data.Cart.Items))
|
||||
@@ -655,7 +655,7 @@ func TestCartIsolation(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Cart.Items) != 1 {
|
||||
t.Fatalf("User 2 expected 1 item, got %d", len(resp.Data.Cart.Items))
|
||||
|
||||
@@ -16,7 +16,7 @@ func setupTestDB(t *testing.T) string {
|
||||
testDB := "test_products.db"
|
||||
|
||||
// Remove if exists
|
||||
os.Remove(testDB)
|
||||
_ = os.Remove(testDB)
|
||||
|
||||
// Reset singleton
|
||||
database.ResetForTesting()
|
||||
@@ -39,7 +39,7 @@ func setupTestDB(t *testing.T) string {
|
||||
|
||||
func TestGetProduct(t *testing.T) {
|
||||
testDB := setupTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewProductHandler()
|
||||
@@ -78,7 +78,7 @@ func TestGetProduct(t *testing.T) {
|
||||
|
||||
func TestGetProductNotFound(t *testing.T) {
|
||||
testDB := setupTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewProductHandler()
|
||||
@@ -98,7 +98,7 @@ func TestGetProductNotFound(t *testing.T) {
|
||||
|
||||
func TestGetProductsEmptyDatabase(t *testing.T) {
|
||||
testDB := "test_empty.db"
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
// Create database but don't seed
|
||||
|
||||
@@ -133,7 +133,7 @@ func (h *SlackHandler) handleBuyLabel(orderIDStr string, p slackInteractivePaylo
|
||||
notify.SlackPostToOrderThread(orderID, fmt.Sprintf(":x: Label purchase failed: %v", err))
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var result struct {
|
||||
Data struct {
|
||||
@@ -201,7 +201,7 @@ func (h *SlackHandler) handleMarkStatus(orderIDStr string, p slackInteractivePay
|
||||
notify.SlackPostToOrderThread(orderID, fmt.Sprintf(":x: Mark %s failed: %v", status, err))
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if resp.StatusCode >= 300 {
|
||||
notify.SlackPostToOrderThread(orderID, fmt.Sprintf(":x: Mark %s failed (HTTP %d)", status, resp.StatusCode))
|
||||
return
|
||||
@@ -245,7 +245,7 @@ func (h *SlackHandler) editClearActions(responseURL, status, actor string) {
|
||||
slog.Warn("slack mark_status: response_url edit failed", "error", err)
|
||||
return
|
||||
}
|
||||
resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
|
||||
func (h *SlackHandler) verifySignature(r *http.Request, body []byte) bool {
|
||||
@@ -264,7 +264,7 @@ func (h *SlackHandler) verifySignature(r *http.Request, body []byte) bool {
|
||||
}
|
||||
|
||||
mac := hmac.New(sha256.New, []byte(h.signingSecret))
|
||||
fmt.Fprintf(mac, "v0:%d:%s", ts, body)
|
||||
_, _ = fmt.Fprintf(mac, "v0:%d:%s", ts, body)
|
||||
expected := "v0=" + hex.EncodeToString(mac.Sum(nil))
|
||||
|
||||
return hmac.Equal([]byte(expected), []byte(sigHeader))
|
||||
|
||||
@@ -40,7 +40,8 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
defer database.Close()
|
||||
|
||||
defer func() { _ = database.Close() }()
|
||||
|
||||
// Run migrations
|
||||
if err := database.Migrate(db); err != nil {
|
||||
@@ -61,7 +62,7 @@ func main() {
|
||||
if err != nil {
|
||||
log.Printf("tracing init failed (non-fatal): %v", err)
|
||||
} else {
|
||||
defer shutdown(context.Background())
|
||||
defer func() { _ = shutdown(context.Background()) }()
|
||||
}
|
||||
|
||||
stripeclient.InitOTel()
|
||||
|
||||
@@ -59,7 +59,7 @@ func SetupRoutes(
|
||||
r.Get("/t/{id}", handlers.TrackingRedirect)
|
||||
r.Get("/card-added", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Write([]byte(`<!DOCTYPE html><html><body style="font-family:monospace;text-align:center;padding:4rem"><h2>Card added.</h2><p>You can close this tab.</p></body></html>`))
|
||||
_, _ = w.Write([]byte(`<!DOCTYPE html><html><body style="font-family:monospace;text-align:center;padding:4rem"><h2>Card added.</h2><p>You can close this tab.</p></body></html>`))
|
||||
})
|
||||
r.Handle("/metrics", middleware.MetricsHandler())
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ func (c *Client) GetOrCreateToken(fingerprint, pubKeyStr, clientSecret string) (
|
||||
if err != nil {
|
||||
return "", models.PublicUser{}, fmt.Errorf("token refresh request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", models.PublicUser{}, fmt.Errorf("token refresh returned status %d", resp.StatusCode)
|
||||
@@ -137,7 +137,7 @@ func (c *Client) GetProducts() ([]models.Coffee, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch products: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
|
||||
@@ -167,7 +167,7 @@ func (c *Client) Health() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("health check failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("API returned status %d", resp.StatusCode)
|
||||
@@ -217,7 +217,7 @@ func (c *Client) RegisterUser(username, sshPublicKey, sshKeyFingerprint string)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to register user: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var registerResp RegisterResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(®isterResp); err != nil {
|
||||
@@ -302,7 +302,7 @@ func (c *Client) GetCart() (*CartData, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch cart: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var cartResp CartResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cartResp); err != nil {
|
||||
@@ -342,7 +342,7 @@ func (c *Client) SetCartItem(coffeeID uint, quantity int) (*CartData, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set cart item: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var cartResp CartResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cartResp); err != nil {
|
||||
@@ -381,7 +381,7 @@ func (c *Client) SetCartAddress(addressID uint) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set cart address: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var result APIResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
@@ -420,7 +420,7 @@ func (c *Client) SetCartCard(cardID uint) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set cart card: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var result APIResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
@@ -450,7 +450,7 @@ func (c *Client) ClearCart() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to clear cart: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var result APIResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
@@ -480,7 +480,7 @@ func (c *Client) ConvertCart() (*models.Order, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert cart: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var convertResp ConvertCartResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&convertResp); err != nil {
|
||||
@@ -510,7 +510,7 @@ func (c *Client) GetCards() ([]models.Card, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch cards: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var cardsResp CardsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cardsResp); err != nil {
|
||||
@@ -553,7 +553,7 @@ func (c *Client) SaveCard(params SaveCardRequest) (*models.Card, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save card: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var cardResp CardResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cardResp); err != nil {
|
||||
@@ -590,7 +590,7 @@ func (c *Client) CollectCard() (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to collect card: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var collectResp CollectCardResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&collectResp); err != nil {
|
||||
@@ -619,7 +619,7 @@ func (c *Client) DeleteCard(id uint) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete card: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("delete card failed with status %d", resp.StatusCode)
|
||||
@@ -648,7 +648,7 @@ func (c *Client) GetOrders() ([]models.Order, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch orders: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var ordersResp OrderResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&ordersResp); err != nil {
|
||||
@@ -686,7 +686,7 @@ func (c *Client) GetAddresses() ([]models.Address, error) {
|
||||
return nil, fmt.Errorf("failed to fetch addresses: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var addrResp AddressResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&addrResp); err != nil {
|
||||
@@ -732,7 +732,7 @@ func (c *Client) CreateAddress(addrReq CreateAddressRequest) (*models.Address, e
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create address: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var result struct {
|
||||
Success bool `json:"success"`
|
||||
@@ -769,7 +769,7 @@ func (c *Client) DeleteAddress(id uint) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete address: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("delete address failed with status %d", resp.StatusCode)
|
||||
@@ -804,7 +804,7 @@ func (c *Client) GetViewInit() (ViewInitData, error) {
|
||||
if err != nil {
|
||||
return ViewInitData{}, fmt.Errorf("view init request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return ViewInitData{}, fmt.Errorf("view init returned status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
func setupAuthTestDB(t *testing.T) string {
|
||||
t.Helper()
|
||||
testDB := "test_ssh_auth.db"
|
||||
os.Remove(testDB)
|
||||
_ = os.Remove(testDB)
|
||||
database.ResetForTesting()
|
||||
|
||||
db, err := database.Connect(testDB)
|
||||
@@ -47,7 +47,7 @@ func newTestSSHKey(t *testing.T) gossh.PublicKey {
|
||||
|
||||
func TestAuthenticateSSHKeyNewUser(t *testing.T) {
|
||||
testDB := setupAuthTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -72,7 +72,7 @@ func TestAuthenticateSSHKeyNewUser(t *testing.T) {
|
||||
|
||||
func TestAuthenticateSSHKeyExistingUser(t *testing.T) {
|
||||
testDB := setupAuthTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -96,7 +96,7 @@ func TestAuthenticateSSHKeyExistingUser(t *testing.T) {
|
||||
|
||||
func TestRegisterWithSSHKey(t *testing.T) {
|
||||
testDB := setupAuthTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -121,7 +121,7 @@ func TestRegisterWithSSHKey(t *testing.T) {
|
||||
|
||||
func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
||||
testDB := setupAuthTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -145,7 +145,7 @@ func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
||||
|
||||
func TestIsSSHKeyRegistered(t *testing.T) {
|
||||
testDB := setupAuthTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
@@ -164,7 +164,7 @@ func TestIsSSHKeyRegistered(t *testing.T) {
|
||||
}
|
||||
|
||||
// After authentication (which auto-creates)
|
||||
svc.AuthenticateSSHKey(key)
|
||||
_, _, _ = svc.AuthenticateSSHKey(key)
|
||||
|
||||
registered, err = svc.IsSSHKeyRegistered(key)
|
||||
if err != nil {
|
||||
|
||||
@@ -197,7 +197,7 @@ func (c *BookingClient) doJSON(ctx context.Context, url string, body any, out an
|
||||
if err != nil {
|
||||
return fmt.Errorf("request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read response: %w", err)
|
||||
|
||||
@@ -110,7 +110,7 @@ func (c *Client) ValidateAddress(ctx context.Context, addr Address) (*Address, e
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bring request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
||||
@@ -101,7 +101,7 @@ func Close() error {
|
||||
// WARNING: Only use this in tests!
|
||||
func ResetForTesting() {
|
||||
if db != nil {
|
||||
Close()
|
||||
_ = Close()
|
||||
db = nil
|
||||
once = sync.Once{}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func TestConnect(t *testing.T) {
|
||||
// Use a temporary test database
|
||||
testDB := "test_terminalshop.db"
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer ResetForTesting()
|
||||
|
||||
db, err := Connect(testDB)
|
||||
@@ -35,7 +35,7 @@ func TestConnect(t *testing.T) {
|
||||
|
||||
func TestMigrate(t *testing.T) {
|
||||
testDB := "test_migrate.db"
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer ResetForTesting()
|
||||
|
||||
db, err := Connect(testDB)
|
||||
@@ -57,7 +57,7 @@ func TestMigrate(t *testing.T) {
|
||||
|
||||
func TestSeed(t *testing.T) {
|
||||
testDB := "test_seed.db"
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer ResetForTesting()
|
||||
|
||||
db, err := Connect(testDB)
|
||||
@@ -115,7 +115,7 @@ func TestSeed(t *testing.T) {
|
||||
|
||||
func TestGetDB(t *testing.T) {
|
||||
testDB := "test_getdb.db"
|
||||
defer os.Remove(testDB)
|
||||
defer func() { _ = os.Remove(testDB) }()
|
||||
defer ResetForTesting()
|
||||
|
||||
// Initialize connection
|
||||
|
||||
@@ -32,7 +32,7 @@ func postSlackMessage(token string, payload map[string]any) (string, bool) {
|
||||
slog.Warn("slack post failed", "error", err)
|
||||
return "", false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var parsed struct {
|
||||
Ok bool `json:"ok"`
|
||||
@@ -66,7 +66,7 @@ func slackPinCall(token, endpoint, channel, ts, benignErr string) bool {
|
||||
slog.Warn("slack pin call failed", "endpoint", endpoint, "error", err)
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var parsed struct {
|
||||
Ok bool `json:"ok"`
|
||||
|
||||
@@ -270,7 +270,7 @@ func (c *Client) doJSON(ctx context.Context, method, path string, body any, out
|
||||
if err != nil {
|
||||
return fmt.Errorf("request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read response: %w", err)
|
||||
@@ -432,7 +432,7 @@ func (c *Client) ValidateAddress(ctx context.Context, addr Address) (*Address, e
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("shippo request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
||||
@@ -324,7 +324,8 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) {
|
||||
switch selectedItem {
|
||||
case "order history", "active orders":
|
||||
if len(m.currentOrderList()) > 0 {
|
||||
if m.account.orderViewState == 0 {
|
||||
switch m.account.orderViewState {
|
||||
case 0:
|
||||
m.account.orderViewState = 1
|
||||
m.account.orderCursor = 0
|
||||
m.account.detailViewport.GotoTop()
|
||||
@@ -334,8 +335,7 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) {
|
||||
{key: "esc", value: "back"},
|
||||
{key: "q", value: "quit"},
|
||||
}
|
||||
} else if m.account.orderViewState == 1 {
|
||||
// Save scroll position before entering detail view
|
||||
case 1:
|
||||
m.account.orderYOffset = m.account.detailViewport.YOffset
|
||||
m.account.orderViewState = 2
|
||||
m.account.detailViewport.GotoTop()
|
||||
|
||||
@@ -248,4 +248,3 @@ func (m Model) displayStyle(k models.DisplayKind) lipgloss.Style {
|
||||
}
|
||||
return m.theme.TextHighlight()
|
||||
}
|
||||
|
||||
|
||||
@@ -145,18 +145,18 @@ func (m Model) generateReviewContent() string {
|
||||
if m.ShippingInfo.Street2 != "" {
|
||||
view.WriteString(m.ShippingInfo.Street2 + "\n")
|
||||
}
|
||||
view.WriteString(fmt.Sprintf("%s, %s %s, %s\n", m.ShippingInfo.City, m.ShippingInfo.State, m.ShippingInfo.Zip, m.ShippingInfo.Country))
|
||||
fmt.Fprintf(&view, "%s, %s %s, %s\n", m.ShippingInfo.City, m.ShippingInfo.State, m.ShippingInfo.Zip, m.ShippingInfo.Country)
|
||||
}
|
||||
view.WriteString("\n")
|
||||
|
||||
if m.SelectedCard != nil {
|
||||
view.WriteString(fmt.Sprintf("cc: **** **** **** %s\n", m.SelectedCard.Last4))
|
||||
fmt.Fprintf(&view, "cc: **** **** **** %s\n", m.SelectedCard.Last4)
|
||||
}
|
||||
view.WriteString("\n")
|
||||
|
||||
subtotal := m.CalculateSubtotal()
|
||||
|
||||
view.WriteString(fmt.Sprintf("subtotal: %s\n", formatUSD(subtotal)))
|
||||
fmt.Fprintf(&view, "subtotal: %s\n", formatUSD(subtotal))
|
||||
view.WriteString(m.theme.TextAccent().Render(fmt.Sprintf("total: %s", formatUSD(subtotal))) + "\n")
|
||||
view.WriteString("\n")
|
||||
view.WriteString(m.theme.TextBrand().Render("press enter to confirm") + "\n")
|
||||
|
||||
@@ -400,23 +400,6 @@ func (m Model) fetchCartCmd() tea.Msg {
|
||||
return CartFetchedMsg{Cart: cart, Err: err}
|
||||
}
|
||||
|
||||
// fetchProductsCmd fetches products from the API
|
||||
func (m Model) fetchProductsCmd() tea.Msg {
|
||||
if m.APIClient == nil {
|
||||
log.Println("[TUI] APIClient is nil, using fallback products")
|
||||
return ProductsMsg{Err: nil} // No API client, use fallback
|
||||
}
|
||||
|
||||
log.Printf("[TUI] Fetching products from API at %s", m.APIClient.BaseURL)
|
||||
products, err := m.APIClient.GetProducts()
|
||||
if err != nil {
|
||||
log.Printf("[TUI] Failed to fetch products: %v", err)
|
||||
} else {
|
||||
log.Printf("[TUI] Loaded %d products from API", len(products))
|
||||
}
|
||||
return ProductsMsg{Products: products, Err: err}
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
return tea.Batch(
|
||||
m.authCmd,
|
||||
@@ -670,58 +653,6 @@ func (m Model) syncCartItemCmd(coffeeID uint, quantity int) (Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
// saveCardAndConvert tokenizes the card locally using the Stripe publishable
|
||||
// key (raw card data never reaches the backend), saves the token, then converts the cart
|
||||
func (m Model) saveCardAndConvert(form PaymentFormCompleteMsg) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
if m.APIClient == nil {
|
||||
return CheckoutResultMsg{Err: fmt.Errorf("API client not available")}
|
||||
}
|
||||
|
||||
stripe.Key = m.StripePublicKey
|
||||
tok, err := stripetoken.New(&stripe.TokenParams{
|
||||
Card: &stripe.CardParams{
|
||||
Name: stripe.String(form.CardName),
|
||||
Number: stripe.String(form.CardNumber),
|
||||
ExpMonth: stripe.String(form.ExpiryMonth),
|
||||
ExpYear: stripe.String(form.ExpiryYear),
|
||||
CVC: stripe.String(form.CVC),
|
||||
AddressZip: stripe.String(form.BillingZip),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
if stripeErr, ok := err.(*stripe.Error); ok {
|
||||
return CheckoutResultMsg{Err: fmt.Errorf("%s", stripeErr.Msg)}
|
||||
}
|
||||
return CheckoutResultMsg{Err: fmt.Errorf("failed to tokenize card: %w", err)}
|
||||
}
|
||||
card, err := m.APIClient.SaveCard(api.SaveCardRequest{Token: tok.ID})
|
||||
if err != nil {
|
||||
return CheckoutResultMsg{Err: fmt.Errorf("failed to save card: %w", err)}
|
||||
}
|
||||
|
||||
if err := m.resolveCartAddress(); err != nil {
|
||||
return CheckoutResultMsg{Err: err}
|
||||
}
|
||||
|
||||
// 3. Set the card on the cart
|
||||
if err := m.APIClient.SetCartCard(card.ID); err != nil {
|
||||
return CheckoutResultMsg{Err: fmt.Errorf("failed to set card: %w", err)}
|
||||
}
|
||||
|
||||
// 4. Convert the cart to an order
|
||||
order, err := m.APIClient.ConvertCart()
|
||||
if err != nil {
|
||||
return CheckoutResultMsg{Err: err}
|
||||
}
|
||||
|
||||
return CheckoutResultMsg{
|
||||
OrderID: order.ID,
|
||||
Total: order.Total,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) saveCardOnlyCmd(form PaymentFormCompleteMsg) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
if m.APIClient == nil {
|
||||
|
||||
@@ -35,7 +35,7 @@ func RespondSuccess(w http.ResponseWriter, statusCode int, data interface{}) {
|
||||
Data: data,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
_ = json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// RespondError sends an error JSON response
|
||||
@@ -52,7 +52,7 @@ func RespondError(w http.ResponseWriter, statusCode int, code, message string, d
|
||||
},
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
_ = json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// HealthData represents health check data
|
||||
|
||||
@@ -66,7 +66,7 @@ func CcnValidator(cardNumber string) error {
|
||||
func IsDigits(name string) ErrorHandler {
|
||||
return func(str string) error {
|
||||
for _, c := range str {
|
||||
if !(c >= '0' && c <= '9') {
|
||||
if c < '0' || c > '9' {
|
||||
return fmt.Errorf("[%s] expected only digits but got %s", name, str)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user