auth.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package handler
  2. import (
  3. "net/http"
  4. "goflare/internal/database/queries"
  5. mw "goflare/internal/middleware"
  6. "github.com/gorilla/sessions"
  7. "github.com/labstack/echo/v4"
  8. "golang.org/x/crypto/bcrypt"
  9. )
  10. type AuthHandler struct {
  11. q *queries.Queries
  12. store sessions.Store
  13. }
  14. func NewAuthHandler(q *queries.Queries, store sessions.Store) *AuthHandler {
  15. return &AuthHandler{q: q, store: store}
  16. }
  17. type credentials struct {
  18. Username string `json:"username"`
  19. Password string `json:"password"`
  20. }
  21. func (h *AuthHandler) Setup(c echo.Context) error {
  22. count, err := h.q.CountUsers(c.Request().Context())
  23. if err != nil {
  24. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "db error"})
  25. }
  26. if count > 0 {
  27. return c.JSON(http.StatusBadRequest, map[string]string{"error": "user already exists"})
  28. }
  29. var cred credentials
  30. if err := c.Bind(&cred); err != nil {
  31. return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})
  32. }
  33. if cred.Username == "" || cred.Password == "" {
  34. return c.JSON(http.StatusBadRequest, map[string]string{"error": "username and password required"})
  35. }
  36. hash, err := bcrypt.GenerateFromPassword([]byte(cred.Password), bcrypt.DefaultCost)
  37. if err != nil {
  38. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to hash password"})
  39. }
  40. user, err := h.q.CreateUser(c.Request().Context(), queries.CreateUserParams{
  41. Username: cred.Username,
  42. PasswordHash: string(hash),
  43. })
  44. if err != nil {
  45. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to create user"})
  46. }
  47. return c.JSON(http.StatusCreated, map[string]string{
  48. "message": "user created",
  49. "username": user.Username,
  50. })
  51. }
  52. func (h *AuthHandler) Login(c echo.Context) error {
  53. var cred credentials
  54. if err := c.Bind(&cred); err != nil {
  55. return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})
  56. }
  57. user, err := h.q.GetUserByUsername(c.Request().Context(), cred.Username)
  58. if err != nil {
  59. return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid credentials"})
  60. }
  61. if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(cred.Password)); err != nil {
  62. return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid credentials"})
  63. }
  64. sess, err := mw.GetSession(c, h.store)
  65. if err != nil {
  66. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "session error"})
  67. }
  68. sess.Values[mw.UserIDKey] = user.ID
  69. if err := sess.Save(c.Request(), c.Response()); err != nil {
  70. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to save session"})
  71. }
  72. return c.JSON(http.StatusOK, map[string]string{"message": "logged in"})
  73. }
  74. func (h *AuthHandler) Logout(c echo.Context) error {
  75. sess, err := mw.GetSession(c, h.store)
  76. if err != nil {
  77. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "session error"})
  78. }
  79. sess.Options.MaxAge = -1
  80. if err := sess.Save(c.Request(), c.Response()); err != nil {
  81. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to clear session"})
  82. }
  83. return c.JSON(http.StatusOK, map[string]string{"message": "logged out"})
  84. }
  85. func (h *AuthHandler) NeedsSetup(c echo.Context) error {
  86. count, err := h.q.CountUsers(c.Request().Context())
  87. if err != nil {
  88. return c.JSON(http.StatusInternalServerError, map[string]string{"error": "db error"})
  89. }
  90. return c.JSON(http.StatusOK, map[string]bool{"needs_setup": count == 0})
  91. }