mirror of
https://github.com/marcusDenslow/terminalShop.git
synced 2026-10-25 10:02:21 +01:00
refactor: move SSH auth and initial data loading to splash screen
previously main.go handled auth directly, it connected to the database, ran migrations, created a JWT manager and SSHAuthService, and authenticated the SSH key before the TUI even started. this commit rips all of that out Auth now happens inside the TUI during the splash screen via splashAuthCmd, which calls the /auth/token endpoint. if no user exists or the fingerprint it auto-creates one using the SSH public ke, so there is no longer a separate registartion flow. The token and user com back in the response. once authed splashViewInitCmd fires a single /view/init request that loads everything needed to start the app, products, cart, addresses, cards, and orders, before switching to the shop page on the model side, User/*models.user and gossh.PublicKey were replaced with plain Fingerprint and SSHPublicKeyStr strings, and NewModelWithAuth was updated too
This commit is contained in:
@@ -8,6 +8,8 @@ import (
|
||||
"terminalShop/pkg/database"
|
||||
"terminalShop/pkg/models"
|
||||
"terminalShop/pkg/utils"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AuthHandler handles authentication-related requests
|
||||
@@ -27,14 +29,16 @@ func NewAuthHandler(jwtManager *auth.JWTManager, authFingerprintKey string) *Aut
|
||||
// Type to represent a request to exchange SSH fingerprint for JWT
|
||||
type TokenRequest struct {
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
SSHPublicKey string `json:"ssh_public_key"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
}
|
||||
|
||||
// Type to represent the JWT token response
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
User models.PublicUser `json:"user"`
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -60,8 +64,22 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// look up user by his fingies
|
||||
var user models.User
|
||||
if err := db.Where("ssh_key_fingerprint = ?", req.Fingerprint).First(&user).Error; err != nil {
|
||||
utils.RespondError(w, http.StatusUnauthorized, "USER_NOT_FOUND", "no user found for this fingerprint", nil)
|
||||
err := db.Where("ssh_key_fingerprint = ?", req.Fingerprint).First(&user).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
if req.SSHPublicKey == "" {
|
||||
utils.RespondError(w, http.StatusUnauthorized, "USER_NOT_FOUND", "no user found for this fingerprint", nil)
|
||||
return
|
||||
}
|
||||
user = models.User{
|
||||
SSHKeyFingerprint: req.Fingerprint,
|
||||
SSHPublicKey: req.SSHPublicKey,
|
||||
}
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to create user", nil)
|
||||
return
|
||||
}
|
||||
} else if err != nil {
|
||||
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "database error", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,6 +93,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
||||
"access_token": token,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 1800, // 30 minutes in seconds
|
||||
"user": user.ToPublic(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
51
api/handlers/view.go
Normal file
51
api/handlers/view.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"terminalShop/api/middleware"
|
||||
"terminalShop/pkg/database"
|
||||
"terminalShop/pkg/models"
|
||||
"terminalShop/pkg/utils"
|
||||
)
|
||||
|
||||
type ViewHandler struct{}
|
||||
|
||||
func NewViewHandler() *ViewHandler {
|
||||
return &ViewHandler{}
|
||||
}
|
||||
|
||||
func (h *ViewHandler) GetViewInit(w http.ResponseWriter, r *http.Request) {
|
||||
db := database.GetDB()
|
||||
userID := middleware.UserIDFromContext(r.Context())
|
||||
|
||||
var user models.User
|
||||
if err := db.First(&user, userID).Error; err != nil {
|
||||
utils.RespondError(w, http.StatusUnauthorized, "USER_NOT_FOUND", "user not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
var products []models.Coffee
|
||||
db.Find(&products)
|
||||
|
||||
var cartItems []models.CartItem
|
||||
db.Where("user_id = ?", userID).Preload("Coffee").Find(&cartItems)
|
||||
|
||||
var addresses []models.Address
|
||||
db.Where("user_id = ?", userID).Find(&addresses)
|
||||
|
||||
var cards []models.Card
|
||||
db.Where("user_id = ?", userID).Find(&cards)
|
||||
|
||||
var orders []models.Order
|
||||
db.Where("user_id = ?", userID).Preload("Items").Order("created_at desc").Find(&orders)
|
||||
|
||||
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
|
||||
"user": user.ToPublic(),
|
||||
"products": products,
|
||||
"cart": cartItems,
|
||||
"addresses": addresses,
|
||||
"cards": cards,
|
||||
"orders": orders,
|
||||
})
|
||||
}
|
||||
@@ -26,6 +26,7 @@ func SetupRoutes(version string, stripeSecretKey string, jwtManager *auth.JWTMan
|
||||
cardHandler := handlers.NewCardHandler(stripeSecretKey)
|
||||
orderHandler := handlers.NewOrderHandler()
|
||||
addressHandler := handlers.NewAddressHandler(shippoAPIKey, bringAPIUID, bringAPIKey)
|
||||
viewHandler := handlers.NewViewHandler()
|
||||
|
||||
// API v1 routes
|
||||
r.Route("/api/v1", func(r chi.Router) {
|
||||
@@ -46,6 +47,8 @@ func SetupRoutes(version string, stripeSecretKey string, jwtManager *auth.JWTMan
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireAuth)
|
||||
|
||||
r.Get("/view/init", viewHandler.GetViewInit)
|
||||
|
||||
// Cart
|
||||
r.Route("/cart", func(r chi.Router) {
|
||||
r.Get("/", cartHandler.GetCart)
|
||||
|
||||
Reference in New Issue
Block a user