diff --git a/api/handlers/auth.go b/api/handlers/auth.go index db053f3..acc09e3 100644 --- a/api/handlers/auth.go +++ b/api/handlers/auth.go @@ -84,17 +84,30 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) { } if req.SSHPublicKey != "" { - sshKey := models.SSHKey{ - UserID: user.ID, - Fingerprint: req.Fingerprint, - PublicKey: req.SSHPublicKey, - } - if err := db.Where(models.SSHKey{Fingerprint: req.Fingerprint}).FirstOrCreate(&sshKey).Error; err != nil { - // we specifically want to block users whos ssh key failed to save. this is to prevent downstream errors which i dont want to deal with now - // !TODO add a way to just log the errror and app continue without trying to save carts, cards and addresses. this way the users wouldnt - // just be kicked, they would shop normally but on their next login they would need to add all their data again. - utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to save ssh key", nil) - return + 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 + } } } diff --git a/pkg/tui/update.go b/pkg/tui/update.go index 7f4c97a..b302e85 100644 --- a/pkg/tui/update.go +++ b/pkg/tui/update.go @@ -58,7 +58,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case SplashAuthMsg: if msg.Err != nil { m.ErrorMsg = fmt.Sprintf("authentication failed: %v", msg.Err) - return m, nil + // Still attempt to load view data (products may not require auth) + // so the splash screen doesn't get permanently stuck. + return m, m.splashViewInitCmd } m.AccessToken = msg.Token m.APIClient.Token = msg.Token @@ -93,9 +95,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.OrdersLoaded = true m.splash.dataReady = true if m.splash.delayDone { - m = m.SwitchPage(shopPage) - m = m.updateShopViewports() - return m, nil + return m.ShopSwitch() } return m, nil case CartSyncedMsg: