Compare commits
4 Commits
documentat
...
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) {
|
func setupAddressTestDB(t *testing.T) (string, models.User) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
testDB := "test_addresses.db"
|
testDB := "test_addresses.db"
|
||||||
os.Remove(testDB)
|
_ = os.Remove(testDB)
|
||||||
database.ResetForTesting()
|
database.ResetForTesting()
|
||||||
|
|
||||||
db, err := database.Connect(testDB)
|
db, err := database.Connect(testDB)
|
||||||
@@ -42,7 +42,7 @@ func setupAddressTestDB(t *testing.T) (string, models.User) {
|
|||||||
|
|
||||||
func TestGetAddressesEmpty(t *testing.T) {
|
func TestGetAddressesEmpty(t *testing.T) {
|
||||||
testDB, user := setupAddressTestDB(t)
|
testDB, user := setupAddressTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewAddressHandler("", "", "")
|
handler := NewAddressHandler("", "", "")
|
||||||
@@ -74,7 +74,7 @@ func TestGetAddressesEmpty(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetAddressesIsolation(t *testing.T) {
|
func TestGetAddressesIsolation(t *testing.T) {
|
||||||
testDB, user1 := setupAddressTestDB(t)
|
testDB, user1 := setupAddressTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -105,7 +105,7 @@ func TestGetAddressesIsolation(t *testing.T) {
|
|||||||
Addresses []models.Address `json:"addresses"`
|
Addresses []models.Address `json:"addresses"`
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
json.NewDecoder(w.Body).Decode(&resp)
|
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||||
|
|
||||||
if len(resp.Data.Addresses) != 1 {
|
if len(resp.Data.Addresses) != 1 {
|
||||||
t.Fatalf("expexted 1 address for user1, got %d", len(resp.Data.Addresses))
|
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) {
|
func TestGetAddressesWithData(t *testing.T) {
|
||||||
testDB, user := setupAddressTestDB(t)
|
testDB, user := setupAddressTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const testClientSecret = "test-client-secret"
|
|||||||
func setupAuthHandlerTestDB(t *testing.T) (string, *auth.JWTManager, *AuthHandler) {
|
func setupAuthHandlerTestDB(t *testing.T) (string, *auth.JWTManager, *AuthHandler) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
testDB := "test_auth.db"
|
testDB := "test_auth.db"
|
||||||
os.Remove(testDB)
|
_ = os.Remove(testDB)
|
||||||
database.ResetForTesting()
|
database.ResetForTesting()
|
||||||
|
|
||||||
db, err := database.Connect(testDB)
|
db, err := database.Connect(testDB)
|
||||||
@@ -62,7 +62,7 @@ func createAuthTestUser(t *testing.T, fingerprint string) models.User {
|
|||||||
|
|
||||||
func TestGetTokenSuccess(t *testing.T) {
|
func TestGetTokenSuccess(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
user := createAuthTestUser(t, "SHA256:testfingerprint123")
|
user := createAuthTestUser(t, "SHA256:testfingerprint123")
|
||||||
@@ -108,7 +108,7 @@ func TestGetTokenSuccess(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetTokenBadSecret(t *testing.T) {
|
func TestGetTokenBadSecret(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
body, _ := json.Marshal(map[string]string{
|
body, _ := json.Marshal(map[string]string{
|
||||||
@@ -128,7 +128,7 @@ func TestGetTokenBadSecret(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetTokenMissingFingerprint(t *testing.T) {
|
func TestGetTokenMissingFingerprint(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
body, _ := json.Marshal(map[string]string{
|
body, _ := json.Marshal(map[string]string{
|
||||||
@@ -147,7 +147,7 @@ func TestGetTokenMissingFingerprint(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetTokenUnknownFingerprint(t *testing.T) {
|
func TestGetTokenUnknownFingerprint(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
body, _ := json.Marshal(map[string]string{
|
body, _ := json.Marshal(map[string]string{
|
||||||
@@ -167,7 +167,7 @@ func TestGetTokenUnknownFingerprint(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetTokenInvalidJSON(t *testing.T) {
|
func TestGetTokenInvalidJSON(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
req := httptest.NewRequest("POST", "/api/v1/auth/token", bytes.NewBufferString("{bad json"))
|
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) {
|
func TestRegisterWithSSHKeySuccess(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
body, _ := json.Marshal(map[string]string{
|
body, _ := json.Marshal(map[string]string{
|
||||||
@@ -207,7 +207,7 @@ func TestRegisterWithSSHKeySuccess(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
createAuthTestUser(t, "SHA256:existingfp")
|
createAuthTestUser(t, "SHA256:existingfp")
|
||||||
@@ -229,7 +229,7 @@ func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegisterWithSSHKeyMissingFields(t *testing.T) {
|
func TestRegisterWithSSHKeyMissingFields(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
body, _ := json.Marshal(map[string]string{
|
body, _ := json.Marshal(map[string]string{
|
||||||
@@ -250,7 +250,7 @@ func TestRegisterWithSSHKeyMissingFields(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetUserBySSHKeySuccess(t *testing.T) {
|
func TestGetUserBySSHKeySuccess(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
createAuthTestUser(t, "SHA256:lookupfp")
|
createAuthTestUser(t, "SHA256:lookupfp")
|
||||||
@@ -267,7 +267,7 @@ func TestGetUserBySSHKeySuccess(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetUserBySSHKeyNotFound(t *testing.T) {
|
func TestGetUserBySSHKeyNotFound(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
req := httptest.NewRequest("GET", "/api/v1/auth/ssh?fingerprint=SHA256:nope", nil)
|
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) {
|
func TestGetUserBySSHKeyMissingParam(t *testing.T) {
|
||||||
testDB, _, handler := setupAuthHandlerTestDB(t)
|
testDB, _, handler := setupAuthHandlerTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
req := httptest.NewRequest("GET", "/api/v1/auth/ssh", nil)
|
req := httptest.NewRequest("GET", "/api/v1/auth/ssh", nil)
|
||||||
|
|||||||
@@ -30,14 +30,16 @@ var (
|
|||||||
payRedirectsMu sync.Mutex
|
payRedirectsMu sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
func storeRedirect(stripeURL string) string {
|
func storeRedirect(stripeURL string) (string, error) {
|
||||||
b := make([]byte, 16)
|
b := make([]byte, 16)
|
||||||
rand.Read(b)
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
token := hex.EncodeToString(b)
|
token := hex.EncodeToString(b)
|
||||||
payRedirectsMu.Lock()
|
payRedirectsMu.Lock()
|
||||||
payRedirects[token] = stripeURL
|
payRedirects[token] = stripeURL
|
||||||
payRedirectsMu.Unlock()
|
payRedirectsMu.Unlock()
|
||||||
return token
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayRedirect resolves a short token and redirects to the Stripe checkout URL.
|
// 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
|
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{}{
|
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
|
||||||
"url": fmt.Sprintf("%s/pay/%s", h.appURL, token),
|
"url": fmt.Sprintf("%s/pay/%s", h.appURL, token),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
func setupCartTestDB(t *testing.T) (string, models.User) {
|
func setupCartTestDB(t *testing.T) (string, models.User) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
testDB := "test_cart.db"
|
testDB := "test_cart.db"
|
||||||
os.Remove(testDB)
|
_ = os.Remove(testDB)
|
||||||
database.ResetForTesting()
|
database.ResetForTesting()
|
||||||
|
|
||||||
db, err := database.Connect(testDB)
|
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) {
|
func TestGetCartEmpty(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -101,7 +101,7 @@ func TestGetCartEmpty(t *testing.T) {
|
|||||||
|
|
||||||
func TestSetItemAndGetCart(t *testing.T) {
|
func TestSetItemAndGetCart(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -157,7 +157,7 @@ func TestSetItemAndGetCart(t *testing.T) {
|
|||||||
|
|
||||||
func TestSetItemUpdateQuantity(t *testing.T) {
|
func TestSetItemUpdateQuantity(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -190,7 +190,7 @@ func TestSetItemUpdateQuantity(t *testing.T) {
|
|||||||
} `json:"cart"`
|
} `json:"cart"`
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
json.NewDecoder(w.Body).Decode(&resp)
|
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||||
|
|
||||||
if len(resp.Data.Cart.Items) != 1 {
|
if len(resp.Data.Cart.Items) != 1 {
|
||||||
t.Fatalf("Expected 1 item after update, got %d", len(resp.Data.Cart.Items))
|
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) {
|
func TestSetItemRemove(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -232,7 +232,7 @@ func TestSetItemRemove(t *testing.T) {
|
|||||||
} `json:"cart"`
|
} `json:"cart"`
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
json.NewDecoder(w.Body).Decode(&resp)
|
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||||
|
|
||||||
if len(resp.Data.Cart.Items) != 0 {
|
if len(resp.Data.Cart.Items) != 0 {
|
||||||
t.Errorf("Expected 0 items after removal, got %d", len(resp.Data.Cart.Items))
|
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) {
|
func TestSetItemInvalidProduct(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -258,7 +258,7 @@ func TestSetItemInvalidProduct(t *testing.T) {
|
|||||||
|
|
||||||
func TestSetItemMissingCoffeeID(t *testing.T) {
|
func TestSetItemMissingCoffeeID(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -275,7 +275,7 @@ func TestSetItemMissingCoffeeID(t *testing.T) {
|
|||||||
|
|
||||||
func TestClearCart(t *testing.T) {
|
func TestClearCart(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -310,7 +310,7 @@ func TestClearCart(t *testing.T) {
|
|||||||
} `json:"cart"`
|
} `json:"cart"`
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
json.NewDecoder(w.Body).Decode(&resp)
|
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||||
|
|
||||||
if len(resp.Data.Cart.Items) != 0 {
|
if len(resp.Data.Cart.Items) != 0 {
|
||||||
t.Errorf("Expected 0 items after clear, got %d", len(resp.Data.Cart.Items))
|
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) {
|
func TestSetAddress(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -358,7 +358,7 @@ func TestSetAddress(t *testing.T) {
|
|||||||
} `json:"cart"`
|
} `json:"cart"`
|
||||||
} `json:"data"`
|
} `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 {
|
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)
|
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) {
|
func TestSetAddressWrongUser(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -402,7 +402,7 @@ func TestSetAddressWrongUser(t *testing.T) {
|
|||||||
|
|
||||||
func TestSetCard(t *testing.T) {
|
func TestSetCard(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -440,7 +440,7 @@ func TestSetCard(t *testing.T) {
|
|||||||
} `json:"cart"`
|
} `json:"cart"`
|
||||||
} `json:"data"`
|
} `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 {
|
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)
|
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) {
|
func TestSetCardWrongUser(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -482,7 +482,7 @@ func TestSetCardWrongUser(t *testing.T) {
|
|||||||
|
|
||||||
func TestConvertCartMissingAddress(t *testing.T) {
|
func TestConvertCartMissingAddress(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -505,7 +505,7 @@ func TestConvertCartMissingAddress(t *testing.T) {
|
|||||||
|
|
||||||
func TestConvertCartMissingCard(t *testing.T) {
|
func TestConvertCartMissingCard(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -536,7 +536,7 @@ func TestConvertCartMissingCard(t *testing.T) {
|
|||||||
|
|
||||||
func TestConvertCartEmpty(t *testing.T) {
|
func TestConvertCartEmpty(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -553,7 +553,7 @@ func TestConvertCartEmpty(t *testing.T) {
|
|||||||
|
|
||||||
func TestMultipleItems(t *testing.T) {
|
func TestMultipleItems(t *testing.T) {
|
||||||
testDB, user := setupCartTestDB(t)
|
testDB, user := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewCartHandler("")
|
handler := NewCartHandler("")
|
||||||
@@ -583,7 +583,7 @@ func TestMultipleItems(t *testing.T) {
|
|||||||
} `json:"cart"`
|
} `json:"cart"`
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
json.NewDecoder(w.Body).Decode(&resp)
|
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||||
|
|
||||||
if len(resp.Data.Cart.Items) != 3 {
|
if len(resp.Data.Cart.Items) != 3 {
|
||||||
t.Errorf("Expected 3 items, got %d", len(resp.Data.Cart.Items))
|
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) {
|
func TestCartIsolation(t *testing.T) {
|
||||||
testDB, user1 := setupCartTestDB(t)
|
testDB, user1 := setupCartTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -638,7 +638,7 @@ func TestCartIsolation(t *testing.T) {
|
|||||||
} `json:"cart"`
|
} `json:"cart"`
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
json.NewDecoder(w.Body).Decode(&resp)
|
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||||
|
|
||||||
if len(resp.Data.Cart.Items) != 1 {
|
if len(resp.Data.Cart.Items) != 1 {
|
||||||
t.Fatalf("User 1 expected 1 item, got %d", len(resp.Data.Cart.Items))
|
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()
|
w = httptest.NewRecorder()
|
||||||
r.ServeHTTP(w, req)
|
r.ServeHTTP(w, req)
|
||||||
|
|
||||||
json.NewDecoder(w.Body).Decode(&resp)
|
_ = json.NewDecoder(w.Body).Decode(&resp)
|
||||||
|
|
||||||
if len(resp.Data.Cart.Items) != 1 {
|
if len(resp.Data.Cart.Items) != 1 {
|
||||||
t.Fatalf("User 2 expected 1 item, got %d", len(resp.Data.Cart.Items))
|
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"
|
testDB := "test_products.db"
|
||||||
|
|
||||||
// Remove if exists
|
// Remove if exists
|
||||||
os.Remove(testDB)
|
_ = os.Remove(testDB)
|
||||||
|
|
||||||
// Reset singleton
|
// Reset singleton
|
||||||
database.ResetForTesting()
|
database.ResetForTesting()
|
||||||
@@ -39,7 +39,7 @@ func setupTestDB(t *testing.T) string {
|
|||||||
|
|
||||||
func TestGetProduct(t *testing.T) {
|
func TestGetProduct(t *testing.T) {
|
||||||
testDB := setupTestDB(t)
|
testDB := setupTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewProductHandler()
|
handler := NewProductHandler()
|
||||||
@@ -78,7 +78,7 @@ func TestGetProduct(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetProductNotFound(t *testing.T) {
|
func TestGetProductNotFound(t *testing.T) {
|
||||||
testDB := setupTestDB(t)
|
testDB := setupTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
handler := NewProductHandler()
|
handler := NewProductHandler()
|
||||||
@@ -98,7 +98,7 @@ func TestGetProductNotFound(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetProductsEmptyDatabase(t *testing.T) {
|
func TestGetProductsEmptyDatabase(t *testing.T) {
|
||||||
testDB := "test_empty.db"
|
testDB := "test_empty.db"
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
// Create database but don't seed
|
// 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))
|
notify.SlackPostToOrderThread(orderID, fmt.Sprintf(":x: Label purchase failed: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var result struct {
|
var result struct {
|
||||||
Data 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))
|
notify.SlackPostToOrderThread(orderID, fmt.Sprintf(":x: Mark %s failed: %v", status, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
if resp.StatusCode >= 300 {
|
if resp.StatusCode >= 300 {
|
||||||
notify.SlackPostToOrderThread(orderID, fmt.Sprintf(":x: Mark %s failed (HTTP %d)", status, resp.StatusCode))
|
notify.SlackPostToOrderThread(orderID, fmt.Sprintf(":x: Mark %s failed (HTTP %d)", status, resp.StatusCode))
|
||||||
return
|
return
|
||||||
@@ -245,7 +245,7 @@ func (h *SlackHandler) editClearActions(responseURL, status, actor string) {
|
|||||||
slog.Warn("slack mark_status: response_url edit failed", "error", err)
|
slog.Warn("slack mark_status: response_url edit failed", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SlackHandler) verifySignature(r *http.Request, body []byte) bool {
|
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))
|
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))
|
expected := "v0=" + hex.EncodeToString(mac.Sum(nil))
|
||||||
|
|
||||||
return hmac.Equal([]byte(expected), []byte(sigHeader))
|
return hmac.Equal([]byte(expected), []byte(sigHeader))
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to connect to database: %v", err)
|
log.Fatalf("Failed to connect to database: %v", err)
|
||||||
}
|
}
|
||||||
defer database.Close()
|
|
||||||
|
defer func() { _ = database.Close() }()
|
||||||
|
|
||||||
// Run migrations
|
// Run migrations
|
||||||
if err := database.Migrate(db); err != nil {
|
if err := database.Migrate(db); err != nil {
|
||||||
@@ -61,7 +62,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("tracing init failed (non-fatal): %v", err)
|
log.Printf("tracing init failed (non-fatal): %v", err)
|
||||||
} else {
|
} else {
|
||||||
defer shutdown(context.Background())
|
defer func() { _ = shutdown(context.Background()) }()
|
||||||
}
|
}
|
||||||
|
|
||||||
stripeclient.InitOTel()
|
stripeclient.InitOTel()
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func SetupRoutes(
|
|||||||
r.Get("/t/{id}", handlers.TrackingRedirect)
|
r.Get("/t/{id}", handlers.TrackingRedirect)
|
||||||
r.Get("/card-added", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/card-added", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/html")
|
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())
|
r.Handle("/metrics", middleware.MetricsHandler())
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func (c *Client) GetOrCreateToken(fingerprint, pubKeyStr, clientSecret string) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", models.PublicUser{}, fmt.Errorf("token refresh request failed: %w", err)
|
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 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return "", models.PublicUser{}, fmt.Errorf("token refresh returned status %d", resp.StatusCode)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch products: %w", err)
|
return nil, fmt.Errorf("failed to fetch products: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
|
return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
|
||||||
@@ -167,7 +167,7 @@ func (c *Client) Health() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("health check failed: %w", err)
|
return fmt.Errorf("health check failed: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("API returned status %d", resp.StatusCode)
|
return fmt.Errorf("API returned status %d", resp.StatusCode)
|
||||||
@@ -217,7 +217,7 @@ func (c *Client) RegisterUser(username, sshPublicKey, sshKeyFingerprint string)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to register user: %w", err)
|
return nil, fmt.Errorf("failed to register user: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var registerResp RegisterResponse
|
var registerResp RegisterResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(®isterResp); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(®isterResp); err != nil {
|
||||||
@@ -302,7 +302,7 @@ func (c *Client) GetCart() (*CartData, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch cart: %w", err)
|
return nil, fmt.Errorf("failed to fetch cart: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var cartResp CartResponse
|
var cartResp CartResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&cartResp); err != nil {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to set cart item: %w", err)
|
return nil, fmt.Errorf("failed to set cart item: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var cartResp CartResponse
|
var cartResp CartResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&cartResp); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&cartResp); err != nil {
|
||||||
@@ -381,7 +381,7 @@ func (c *Client) SetCartAddress(addressID uint) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set cart address: %w", err)
|
return fmt.Errorf("failed to set cart address: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var result APIResponse
|
var result APIResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||||
@@ -420,7 +420,7 @@ func (c *Client) SetCartCard(cardID uint) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set cart card: %w", err)
|
return fmt.Errorf("failed to set cart card: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var result APIResponse
|
var result APIResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||||
@@ -450,7 +450,7 @@ func (c *Client) ClearCart() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to clear cart: %w", err)
|
return fmt.Errorf("failed to clear cart: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var result APIResponse
|
var result APIResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||||
@@ -480,7 +480,7 @@ func (c *Client) ConvertCart() (*models.Order, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to convert cart: %w", err)
|
return nil, fmt.Errorf("failed to convert cart: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var convertResp ConvertCartResponse
|
var convertResp ConvertCartResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&convertResp); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&convertResp); err != nil {
|
||||||
@@ -510,7 +510,7 @@ func (c *Client) GetCards() ([]models.Card, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch cards: %w", err)
|
return nil, fmt.Errorf("failed to fetch cards: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var cardsResp CardsResponse
|
var cardsResp CardsResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&cardsResp); err != nil {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to save card: %w", err)
|
return nil, fmt.Errorf("failed to save card: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var cardResp CardResponse
|
var cardResp CardResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&cardResp); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&cardResp); err != nil {
|
||||||
@@ -590,7 +590,7 @@ func (c *Client) CollectCard() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to collect card: %w", err)
|
return "", fmt.Errorf("failed to collect card: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var collectResp CollectCardResponse
|
var collectResp CollectCardResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&collectResp); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&collectResp); err != nil {
|
||||||
@@ -619,7 +619,7 @@ func (c *Client) DeleteCard(id uint) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to delete card: %w", err)
|
return fmt.Errorf("failed to delete card: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("delete card failed with status %d", resp.StatusCode)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch orders: %w", err)
|
return nil, fmt.Errorf("failed to fetch orders: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var ordersResp OrderResponse
|
var ordersResp OrderResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&ordersResp); err != nil {
|
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)
|
return nil, fmt.Errorf("failed to fetch addresses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var addrResp AddressResponse
|
var addrResp AddressResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&addrResp); err != nil {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create address: %w", err)
|
return nil, fmt.Errorf("failed to create address: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var result struct {
|
var result struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
@@ -769,7 +769,7 @@ func (c *Client) DeleteAddress(id uint) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to delete address: %w", err)
|
return fmt.Errorf("failed to delete address: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("delete address failed with status %d", resp.StatusCode)
|
return fmt.Errorf("delete address failed with status %d", resp.StatusCode)
|
||||||
@@ -804,7 +804,7 @@ func (c *Client) GetViewInit() (ViewInitData, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ViewInitData{}, fmt.Errorf("view init request failed: %w", err)
|
return ViewInitData{}, fmt.Errorf("view init request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return ViewInitData{}, fmt.Errorf("view init returned status: %d", resp.StatusCode)
|
return ViewInitData{}, fmt.Errorf("view init returned status: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
func setupAuthTestDB(t *testing.T) string {
|
func setupAuthTestDB(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
testDB := "test_ssh_auth.db"
|
testDB := "test_ssh_auth.db"
|
||||||
os.Remove(testDB)
|
_ = os.Remove(testDB)
|
||||||
database.ResetForTesting()
|
database.ResetForTesting()
|
||||||
|
|
||||||
db, err := database.Connect(testDB)
|
db, err := database.Connect(testDB)
|
||||||
@@ -47,7 +47,7 @@ func newTestSSHKey(t *testing.T) gossh.PublicKey {
|
|||||||
|
|
||||||
func TestAuthenticateSSHKeyNewUser(t *testing.T) {
|
func TestAuthenticateSSHKeyNewUser(t *testing.T) {
|
||||||
testDB := setupAuthTestDB(t)
|
testDB := setupAuthTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -72,7 +72,7 @@ func TestAuthenticateSSHKeyNewUser(t *testing.T) {
|
|||||||
|
|
||||||
func TestAuthenticateSSHKeyExistingUser(t *testing.T) {
|
func TestAuthenticateSSHKeyExistingUser(t *testing.T) {
|
||||||
testDB := setupAuthTestDB(t)
|
testDB := setupAuthTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -96,7 +96,7 @@ func TestAuthenticateSSHKeyExistingUser(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegisterWithSSHKey(t *testing.T) {
|
func TestRegisterWithSSHKey(t *testing.T) {
|
||||||
testDB := setupAuthTestDB(t)
|
testDB := setupAuthTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -121,7 +121,7 @@ func TestRegisterWithSSHKey(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
||||||
testDB := setupAuthTestDB(t)
|
testDB := setupAuthTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -145,7 +145,7 @@ func TestRegisterWithSSHKeyDuplicate(t *testing.T) {
|
|||||||
|
|
||||||
func TestIsSSHKeyRegistered(t *testing.T) {
|
func TestIsSSHKeyRegistered(t *testing.T) {
|
||||||
testDB := setupAuthTestDB(t)
|
testDB := setupAuthTestDB(t)
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer database.ResetForTesting()
|
defer database.ResetForTesting()
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -164,7 +164,7 @@ func TestIsSSHKeyRegistered(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// After authentication (which auto-creates)
|
// After authentication (which auto-creates)
|
||||||
svc.AuthenticateSSHKey(key)
|
_, _, _ = svc.AuthenticateSSHKey(key)
|
||||||
|
|
||||||
registered, err = svc.IsSSHKeyRegistered(key)
|
registered, err = svc.IsSSHKeyRegistered(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ func (c *BookingClient) doJSON(ctx context.Context, url string, body any, out an
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("request: %w", err)
|
return fmt.Errorf("request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("read response: %w", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bring request failed: %w", err)
|
return nil, fmt.Errorf("bring request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func Close() error {
|
|||||||
// WARNING: Only use this in tests!
|
// WARNING: Only use this in tests!
|
||||||
func ResetForTesting() {
|
func ResetForTesting() {
|
||||||
if db != nil {
|
if db != nil {
|
||||||
Close()
|
_ = Close()
|
||||||
db = nil
|
db = nil
|
||||||
once = sync.Once{}
|
once = sync.Once{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
func TestConnect(t *testing.T) {
|
func TestConnect(t *testing.T) {
|
||||||
// Use a temporary test database
|
// Use a temporary test database
|
||||||
testDB := "test_terminalshop.db"
|
testDB := "test_terminalshop.db"
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer ResetForTesting()
|
defer ResetForTesting()
|
||||||
|
|
||||||
db, err := Connect(testDB)
|
db, err := Connect(testDB)
|
||||||
@@ -35,7 +35,7 @@ func TestConnect(t *testing.T) {
|
|||||||
|
|
||||||
func TestMigrate(t *testing.T) {
|
func TestMigrate(t *testing.T) {
|
||||||
testDB := "test_migrate.db"
|
testDB := "test_migrate.db"
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer ResetForTesting()
|
defer ResetForTesting()
|
||||||
|
|
||||||
db, err := Connect(testDB)
|
db, err := Connect(testDB)
|
||||||
@@ -57,7 +57,7 @@ func TestMigrate(t *testing.T) {
|
|||||||
|
|
||||||
func TestSeed(t *testing.T) {
|
func TestSeed(t *testing.T) {
|
||||||
testDB := "test_seed.db"
|
testDB := "test_seed.db"
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer ResetForTesting()
|
defer ResetForTesting()
|
||||||
|
|
||||||
db, err := Connect(testDB)
|
db, err := Connect(testDB)
|
||||||
@@ -115,7 +115,7 @@ func TestSeed(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetDB(t *testing.T) {
|
func TestGetDB(t *testing.T) {
|
||||||
testDB := "test_getdb.db"
|
testDB := "test_getdb.db"
|
||||||
defer os.Remove(testDB)
|
defer func() { _ = os.Remove(testDB) }()
|
||||||
defer ResetForTesting()
|
defer ResetForTesting()
|
||||||
|
|
||||||
// Initialize connection
|
// Initialize connection
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func postSlackMessage(token string, payload map[string]any) (string, bool) {
|
|||||||
slog.Warn("slack post failed", "error", err)
|
slog.Warn("slack post failed", "error", err)
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var parsed struct {
|
var parsed struct {
|
||||||
Ok bool `json:"ok"`
|
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)
|
slog.Warn("slack pin call failed", "endpoint", endpoint, "error", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var parsed struct {
|
var parsed struct {
|
||||||
Ok bool `json:"ok"`
|
Ok bool `json:"ok"`
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ func (c *Client) doJSON(ctx context.Context, method, path string, body any, out
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("request: %w", err)
|
return fmt.Errorf("request: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("read response: %w", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("shippo request failed: %w", err)
|
return nil, fmt.Errorf("shippo request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
respBody, err := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -324,7 +324,8 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) {
|
|||||||
switch selectedItem {
|
switch selectedItem {
|
||||||
case "order history", "active orders":
|
case "order history", "active orders":
|
||||||
if len(m.currentOrderList()) > 0 {
|
if len(m.currentOrderList()) > 0 {
|
||||||
if m.account.orderViewState == 0 {
|
switch m.account.orderViewState {
|
||||||
|
case 0:
|
||||||
m.account.orderViewState = 1
|
m.account.orderViewState = 1
|
||||||
m.account.orderCursor = 0
|
m.account.orderCursor = 0
|
||||||
m.account.detailViewport.GotoTop()
|
m.account.detailViewport.GotoTop()
|
||||||
@@ -334,8 +335,7 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) {
|
|||||||
{key: "esc", value: "back"},
|
{key: "esc", value: "back"},
|
||||||
{key: "q", value: "quit"},
|
{key: "q", value: "quit"},
|
||||||
}
|
}
|
||||||
} else if m.account.orderViewState == 1 {
|
case 1:
|
||||||
// Save scroll position before entering detail view
|
|
||||||
m.account.orderYOffset = m.account.detailViewport.YOffset
|
m.account.orderYOffset = m.account.detailViewport.YOffset
|
||||||
m.account.orderViewState = 2
|
m.account.orderViewState = 2
|
||||||
m.account.detailViewport.GotoTop()
|
m.account.detailViewport.GotoTop()
|
||||||
|
|||||||
@@ -248,4 +248,3 @@ func (m Model) displayStyle(k models.DisplayKind) lipgloss.Style {
|
|||||||
}
|
}
|
||||||
return m.theme.TextHighlight()
|
return m.theme.TextHighlight()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,18 +145,18 @@ func (m Model) generateReviewContent() string {
|
|||||||
if m.ShippingInfo.Street2 != "" {
|
if m.ShippingInfo.Street2 != "" {
|
||||||
view.WriteString(m.ShippingInfo.Street2 + "\n")
|
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")
|
view.WriteString("\n")
|
||||||
|
|
||||||
if m.SelectedCard != nil {
|
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")
|
view.WriteString("\n")
|
||||||
|
|
||||||
subtotal := m.CalculateSubtotal()
|
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(m.theme.TextAccent().Render(fmt.Sprintf("total: %s", formatUSD(subtotal))) + "\n")
|
||||||
view.WriteString("\n")
|
view.WriteString("\n")
|
||||||
view.WriteString(m.theme.TextBrand().Render("press enter to confirm") + "\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}
|
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 {
|
func (m Model) Init() tea.Cmd {
|
||||||
return tea.Batch(
|
return tea.Batch(
|
||||||
m.authCmd,
|
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 {
|
func (m Model) saveCardOnlyCmd(form PaymentFormCompleteMsg) tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
if m.APIClient == nil {
|
if m.APIClient == nil {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func RespondSuccess(w http.ResponseWriter, statusCode int, data interface{}) {
|
|||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(response)
|
_ = json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RespondError sends an error JSON 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
|
// HealthData represents health check data
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func CcnValidator(cardNumber string) error {
|
|||||||
func IsDigits(name string) ErrorHandler {
|
func IsDigits(name string) ErrorHandler {
|
||||||
return func(str string) error {
|
return func(str string) error {
|
||||||
for _, c := range str {
|
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)
|
return fmt.Errorf("[%s] expected only digits but got %s", name, str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user