package handler import ( "net/http" "goflare/internal/database/queries" mw "goflare/internal/middleware" "github.com/gorilla/sessions" "github.com/labstack/echo/v4" "golang.org/x/crypto/bcrypt" ) type AuthHandler struct { q *queries.Queries store sessions.Store } func NewAuthHandler(q *queries.Queries, store sessions.Store) *AuthHandler { return &AuthHandler{q: q, store: store} } type credentials struct { Username string `json:"username"` Password string `json:"password"` } func (h *AuthHandler) Setup(c echo.Context) error { count, err := h.q.CountUsers(c.Request().Context()) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "db error"}) } if count > 0 { return c.JSON(http.StatusBadRequest, map[string]string{"error": "user already exists"}) } var cred credentials if err := c.Bind(&cred); err != nil { return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"}) } if cred.Username == "" || cred.Password == "" { return c.JSON(http.StatusBadRequest, map[string]string{"error": "username and password required"}) } hash, err := bcrypt.GenerateFromPassword([]byte(cred.Password), bcrypt.DefaultCost) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to hash password"}) } user, err := h.q.CreateUser(c.Request().Context(), queries.CreateUserParams{ Username: cred.Username, PasswordHash: string(hash), }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to create user"}) } return c.JSON(http.StatusCreated, map[string]string{ "message": "user created", "username": user.Username, }) } func (h *AuthHandler) Login(c echo.Context) error { var cred credentials if err := c.Bind(&cred); err != nil { return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"}) } user, err := h.q.GetUserByUsername(c.Request().Context(), cred.Username) if err != nil { return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid credentials"}) } if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(cred.Password)); err != nil { return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid credentials"}) } sess, err := mw.GetSession(c, h.store) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "session error"}) } sess.Values[mw.UserIDKey] = user.ID if err := sess.Save(c.Request(), c.Response()); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to save session"}) } return c.JSON(http.StatusOK, map[string]string{"message": "logged in"}) } func (h *AuthHandler) Logout(c echo.Context) error { sess, err := mw.GetSession(c, h.store) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "session error"}) } sess.Options.MaxAge = -1 if err := sess.Save(c.Request(), c.Response()); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to clear session"}) } return c.JSON(http.StatusOK, map[string]string{"message": "logged out"}) } func (h *AuthHandler) NeedsSetup(c echo.Context) error { count, err := h.q.CountUsers(c.Request().Context()) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": "db error"}) } return c.JSON(http.StatusOK, map[string]bool{"needs_setup": count == 0}) }