From c78f5eb12be1954e3891d17448886f91e10ec19f Mon Sep 17 00:00:00 2001 From: marcusDenslow Date: Sat, 25 Apr 2026 10:50:19 +0200 Subject: [PATCH] refactor: remove putting ssh key in memory at all, just use it for auth changed the ssh key to just do fingerprinting and not store it at all, and not be able to edit it either in the account page --- api/handlers/auth.go | 28 ------------------ api/handlers/view.go | 4 --- api/routes/routes.go | 9 ------ pkg/api/client.go | 59 -------------------------------------- pkg/database/migrate.go | 1 - pkg/models/coffee.go | 1 - pkg/tui/account.go | 39 +------------------------ pkg/tui/account_sshkeys.go | 41 -------------------------- pkg/tui/model.go | 36 ----------------------- pkg/tui/update.go | 13 --------- 10 files changed, 1 insertion(+), 230 deletions(-) delete mode 100644 pkg/tui/account_sshkeys.go diff --git a/api/handlers/auth.go b/api/handlers/auth.go index acc09e3..1941ab5 100644 --- a/api/handlers/auth.go +++ b/api/handlers/auth.go @@ -83,34 +83,6 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) { return } - if req.SSHPublicKey != "" { - var sshKey models.SSHKey - // Use Unscoped to include soft-deleted rows, avoiding the - // UNIQUE constraint conflict when a key is re-registered. - err := db.Unscoped().Where("fingerprint = ?", req.Fingerprint).First(&sshKey).Error - if err != nil { - // Key doesn't exist at all — create it - sshKey = models.SSHKey{ - UserID: user.ID, - Fingerprint: req.Fingerprint, - PublicKey: req.SSHPublicKey, - } - if err := db.Create(&sshKey).Error; err != nil { - utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to save ssh key", nil) - return - } - } else if sshKey.DeletedAt.Valid { - // Key was soft-deleted — restore it - sshKey.DeletedAt = gorm.DeletedAt{} - sshKey.PublicKey = req.SSHPublicKey - sshKey.UserID = user.ID - if err := db.Unscoped().Save(&sshKey).Error; err != nil { - utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to restore ssh key", nil) - return - } - } - } - token, err := h.jwtManager.GenerateToken(user.ID, user.Email, user.Name) if err != nil { utils.RespondError(w, http.StatusInternalServerError, "TOKEN_ERROR", "failed to generate token", nil) diff --git a/api/handlers/view.go b/api/handlers/view.go index 3245eb2..9727379 100644 --- a/api/handlers/view.go +++ b/api/handlers/view.go @@ -37,9 +37,6 @@ func (h *ViewHandler) GetViewInit(w http.ResponseWriter, r *http.Request) { var cards []models.Card db.Where("user_id = ?", userID).Find(&cards) - var sshKeys []models.SSHKey - db.Where("user_id = ?", userID).Order("created_at ASC").Find(&sshKeys) - var orders []models.Order db.Where("user_id = ?", userID).Preload("Items").Order("created_at desc").Find(&orders) var cartItems []models.CartItem @@ -53,7 +50,6 @@ func (h *ViewHandler) GetViewInit(w http.ResponseWriter, r *http.Request) { "cart": cartItems, "addresses": addresses, "cards": cards, - "ssh_keys": sshKeys, "orders": orders, }) } diff --git a/api/routes/routes.go b/api/routes/routes.go index bcc05c3..e57c00c 100644 --- a/api/routes/routes.go +++ b/api/routes/routes.go @@ -43,7 +43,6 @@ func SetupRoutes( addressHandler := handlers.NewAddressHandler(shippoAPIKey, bringAPIUID, bringAPIKey) viewHandler := handlers.NewViewHandler() webhookHandler := handlers.NewWebhookHandler(stripeWebhookSecret, stripeSecretKey) - sshKeyHandler := handlers.NewSSHKeyHandler() // Short payment redirect and success page — no auth required. r.Get("/pay/{token}", handlers.PayRedirect) @@ -115,14 +114,6 @@ func SetupRoutes( r.Delete("/{id}", addressHandler.DeleteAddress) r.Put("/{id}/default", addressHandler.SetDefaultAddress) }) - - // SSH Keys - r.Route("/ssh-keys", func(r chi.Router) { - // GET doesnt need id because it gets every ssh key - r.Get("/", sshKeyHandler.GetSSHKeys) - // Delete needs an id to delete only one key - r.Delete("/{id}", sshKeyHandler.DeleteSSHKeys) - }) }) }) diff --git a/pkg/api/client.go b/pkg/api/client.go index 8f6e55f..7898ae0 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -628,64 +628,6 @@ func (c *Client) DeleteCard(id uint) error { return nil } -type SSHKeyResponse struct { - Success bool `json:"success"` - Data struct { - SSHKeys []models.SSHKey `json:"ssh_keys"` - } `json:"data"` - Error *APIError `json:"error,omitempty"` -} - -func (c *Client) GetSSHKeys() ([]models.SSHKey, error) { - url := fmt.Sprintf("%s/api/v1/ssh-keys", c.BaseURL) - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - resp, err := c.doRequest(req) - if err != nil { - return nil, fmt.Errorf("failed to fetch ssh keys: %w", err) - } - defer resp.Body.Close() - - var keysResp SSHKeyResponse - if err := json.NewDecoder(resp.Body).Decode(&keysResp); err != nil { - return nil, fmt.Errorf("failed to decode ssh keys response: %w", err) - } - - if !keysResp.Success { - if keysResp.Error != nil { - return nil, fmt.Errorf("%s: %s", keysResp.Error.Code, keysResp.Error.Message) - } - return nil, fmt.Errorf("failed to fetch ssh keys") - } - return keysResp.Data.SSHKeys, nil - -} - -func (c *Client) DeleteSSHKey(id uint) error { - url := fmt.Sprintf("%s/api/v1/ssh-keys/%d", c.BaseURL, id) - - req, err := http.NewRequest("DELETE", url, nil) - if err != nil { - return fmt.Errorf("failed to create delete request: %w", err) - } - - resp, err := c.doRequest(req) - if err != nil { - return fmt.Errorf("failed to delete ssh key: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("delete ssh key failed with status %d", resp.StatusCode) - } - - return nil -} - type OrderResponse struct { Success bool `json:"success"` Data struct { @@ -842,7 +784,6 @@ type ViewInitData struct { Cart []CartItemData `json:"cart"` Addresses []models.Address `json:"addresses"` Cards []models.Card `json:"cards"` - SSHKeys []models.SSHKey `json:"ssh_keys"` Orders []models.Order `json:"orders"` } diff --git a/pkg/database/migrate.go b/pkg/database/migrate.go index dc7b2a4..44a838e 100644 --- a/pkg/database/migrate.go +++ b/pkg/database/migrate.go @@ -23,7 +23,6 @@ func Migrate(db *gorm.DB) error { &models.Order{}, // Completed purchases &models.OrderItem{}, // Line items within orders &models.Address{}, // Saved addresses - &models.SSHKey{}, // One of the users ssh keys ); err != nil { return fmt.Errorf("migration failed: %w", err) } diff --git a/pkg/models/coffee.go b/pkg/models/coffee.go index 7ee5340..d8ec852 100644 --- a/pkg/models/coffee.go +++ b/pkg/models/coffee.go @@ -31,7 +31,6 @@ var AccountMenuItems = []string{ "order history", "addresses", "cards", - "ssh keys", "faq", "about", } diff --git a/pkg/tui/account.go b/pkg/tui/account.go index 0f6fde0..846a032 100644 --- a/pkg/tui/account.go +++ b/pkg/tui/account.go @@ -49,8 +49,6 @@ func (m Model) BuildAccountView(availableHeight int) string { detailView = m.AddressesView(detailContentWidth) case "cards": detailView = m.CardsView(detailContentWidth) - case "ssh keys": - detailView = m.SSHKeysView(detailContentWidth) case "faq": questionStyle := m.theme.TextHighlight().Bold(true) faqContent := "" @@ -222,10 +220,6 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) { if m.account.cardCursor > 0 { m.account.cardCursor-- } - } else if m.account.sshKeyListFocused && m.account.sshKeyDeleting == nil { - if m.account.sshKeyCursor > 0 { - m.account.sshKeyCursor-- - } } else if m.account.faqFocused { m.account.scrollOffset -= 3 if m.account.scrollOffset < 0 { @@ -255,10 +249,6 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) { if m.account.cardCursor < len(m.SavedCards)-1 { m.account.cardCursor++ } - } else if m.account.sshKeyListFocused && m.account.sshKeyDeleting == nil { - if m.account.sshKeyCursor < len(m.SSHKeys)-1 { - m.account.sshKeyCursor++ - } } else if m.account.faqFocused { m.account.scrollOffset += 3 maxScroll := m.computeFaqScrollMax() @@ -282,7 +272,7 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) { if targetBottom > m.account.scrollOffset+viewportHeight { m.account.scrollOffset = targetBottom - viewportHeight } - } else if m.account.orderViewState == 0 && !m.account.addressListFocused && !m.account.cardListFocused && !m.account.sshKeyListFocused && m.account.cursor < len(models.AccountMenuItems)-1 { + } else if m.account.orderViewState == 0 && !m.account.addressListFocused && !m.account.cardListFocused && m.account.cursor < len(models.AccountMenuItems)-1 { m.account.cursor++ m.account.scrollOffset = 0 } @@ -309,8 +299,6 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) { m.account.addressDeleting = &m.account.addressCursor } else if m.account.cardListFocused && m.account.cardDeleting == nil && m.account.cardCursor < len(m.SavedCards) { m.account.cardDeleting = &m.account.cardCursor - } else if m.account.sshKeyListFocused && m.account.sshKeyDeleting == nil && m.account.sshKeyCursor < len(m.SSHKeys) { - m.account.sshKeyDeleting = &m.account.sshKeyCursor } case "y": @@ -332,21 +320,11 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) { m.account.cardCursor-- } return m, m.deleteCardCmd(card.ID) - } else if m.account.sshKeyDeleting != nil { - cursor := *m.account.sshKeyDeleting - key := m.SSHKeys[cursor] - m.account.sshKeyDeleting = nil - m.SSHKeys = append(m.SSHKeys[:cursor], m.SSHKeys[cursor+1:]...) - if m.account.sshKeyCursor >= len(m.SSHKeys) && m.account.sshKeyCursor > 0 { - m.account.sshKeyCursor-- - } - return m, m.deleteSSHKeyCmd(key.ID) } case "n": m.account.addressDeleting = nil m.account.cardDeleting = nil - m.account.sshKeyDeleting = nil case "p", "enter": switch selectedItem { @@ -372,16 +350,6 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) { } } } - case "ssh keys": - if !m.account.sshKeyListFocused { - m.account.sshKeyListFocused = true - m.account.sshKeyCursor = 0 - m.footer = []footerCommand{ - {key: "j/k", value: "keys"}, - {key: "x", value: "delete"}, - {key: "esc", value: "back"}, - } - } case "addresses": if !m.account.addressListFocused { m.account.addressListFocused = true @@ -437,11 +405,6 @@ func (m Model) AccountUpdate(msg tea.Msg) (Model, tea.Cmd) { m.account.cardListFocused = false m.account.cardCursor = 0 m.footer = accountDefaultFooter - } else if m.account.sshKeyDeleting != nil { - m.account.sshKeyDeleting = nil - } else if m.account.sshKeyListFocused { - m.account.sshKeyListFocused = false - m.account.sshKeyCursor = 0 m.footer = accountDefaultFooter } else if m.account.faqFocused { m.account.faqFocused = false diff --git a/pkg/tui/account_sshkeys.go b/pkg/tui/account_sshkeys.go deleted file mode 100644 index dbb6ba6..0000000 --- a/pkg/tui/account_sshkeys.go +++ /dev/null @@ -1,41 +0,0 @@ - -package tui - -func (m Model) SSHKeysView(width int) string { - titleSTyle := m.theme.TextAccent().Bold(true).MarginBottom(2) - contentStyle := m.theme.TextBody().Width(width) - activeStyle := m.theme.TextAccent().Bold(true) - - - lines := titleSTyle.Render("SSH keys") + "\n\n" - if !m.SSHKeysLoaded { - lines += contentStyle.Render("Loading ssh keys...") - } else if len(m.SSHKeys) == 0 { - lines += contentStyle.Render("No saved ssh keys") - } else { - for i, key := range m.SSHKeys { - isSelected := m.account.sshKeyListFocused && i == m.account.sshKeyCursor - label := key.Fingerprint - if key.Comment != "" { - label += " " + key.Comment - } - if len(label) > width-4 { - label += label[:width-4] - } - if m.account.sshKeyDeleting != nil && *m.account.sshKeyDeleting == i { - lines += m.theme.TextError().Bold(true).Render(" delete? (y/n)") + "\n" - } else if isSelected { - lines += activeStyle.Render("> "+label) + "\n" - } else { - lines += contentStyle.Render(" "+label) + "\n" - } - } - } - if m.account.sshKeyListFocused { - lines += "\n" + m.theme.TextDim().Render("x: delete esc: back") - } else { - lines += "\n" + m.theme.TextDim().Render("enter: manage") - } - - return lines -} diff --git a/pkg/tui/model.go b/pkg/tui/model.go index ebd4f2b..e120faa 100644 --- a/pkg/tui/model.go +++ b/pkg/tui/model.go @@ -77,8 +77,6 @@ type Model struct { SavedCards []models.Card Orders []models.Order OrdersLoaded bool - SSHKeys []models.SSHKey - SSHKeysLoaded bool FAQs []FAQ // Page navigation @@ -180,9 +178,6 @@ type accountState struct { cardCursor int addressDeleting *int cardDeleting *int - sshKeyListFocused bool - sshKeyCursor int - sshKeyDeleting *int } type reviewState struct { @@ -519,17 +514,6 @@ type CardDeletedMsg struct { Err error } -// SSHKeysMsg is sent when ssh keys are fetched from the API -type SSHKeysMsg struct { - Keys []models.SSHKey - Err error -} - -// SSHKeyDeletedMsg is sent when an ssh key is deleted -type SSHKeyDeletedMsg struct { - Err error -} - func (m Model) fetchAddressesCmd() tea.Cmd { return func() tea.Msg { if m.APIClient == nil || m.User == nil { @@ -592,26 +576,6 @@ func (m Model) deleteCardCmd(id uint) tea.Cmd { } } -func (m Model) fetchSSHKeysCmd() tea.Cmd { - return func() tea.Msg { - if m.APIClient == nil || m.User == nil { - return SSHKeysMsg{Err: fmt.Errorf("not authenticated")} - } - keys, err := m.APIClient.GetSSHKeys() - return SSHKeysMsg{Keys: keys, Err: err} - } -} - -func (m Model) deleteSSHKeyCmd(id uint) tea.Cmd { - return func() tea.Msg { - if m.APIClient == nil || m.User == nil { - return SSHKeyDeletedMsg{Err: fmt.Errorf("not authenticated")} - } - err := m.APIClient.DeleteSSHKey(id) - return SSHKeyDeletedMsg{Err: err} - } -} - // checkoutWithSavedCard sets address and saved card on the cart, then converts. // Used when the user selects an existing card from the list (no tokenization needed). func (m Model) checkoutWithSavedCard() tea.Cmd { diff --git a/pkg/tui/update.go b/pkg/tui/update.go index b302e85..5cbd56d 100644 --- a/pkg/tui/update.go +++ b/pkg/tui/update.go @@ -89,8 +89,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.loadCartFromAPI(cart) m.SavedAddresses = msg.Data.Addresses m.SavedCards = msg.Data.Cards - m.SSHKeys = msg.Data.SSHKeys - m.SSHKeysLoaded = true m.Orders = msg.Data.Orders m.OrdersLoaded = true m.splash.dataReady = true @@ -133,17 +131,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.ErrorMsg = fmt.Sprintf("failed to delete card: %v", msg.Err) } return m, nil - case SSHKeysMsg: - if msg.Err == nil { - m.SSHKeys = msg.Keys - m.SSHKeysLoaded = true - } - return m, nil - case SSHKeyDeletedMsg: - if msg.Err != nil { - m.ErrorMsg = fmt.Sprintf("failed to delete ssh key: %v", msg.Err) - } - return m, nil case DelayCompleteMsg: m.splash.delayDone = true if m.currentPage == splashPage && m.splash.dataReady {