diff --git a/backend/internal/server/auth.go b/backend/internal/server/auth.go index 979001a9..69707d68 100644 --- a/backend/internal/server/auth.go +++ b/backend/internal/server/auth.go @@ -34,6 +34,7 @@ func (s *Server) setupAuthRoutes() { auth.POST("/signin", s.handleSignin, mw.ValidateRequestBody(reflect.TypeOf(SigninRequest{}))) auth.GET("/verify-email", s.handleVerifyEmail) auth.GET("/ami-verified", s.handleEmailVerifiedStatus) + auth.POST("/resend-verification", s.handleResendVerificationEmail, mw.ValidateRequestBody(reflect.TypeOf(ResendVerificationEmailRequest{}))) auth.POST("/refresh", s.handleRefreshToken) auth.POST("/signout", s.handleSignout) } @@ -245,6 +246,55 @@ func (s *Server) handleVerifyEmail(c echo.Context) error { }) } +func (s *Server) handleResendVerificationEmail(c echo.Context) error { + req, ok := c.Get(mw.REQUEST_BODY_KEY).(*ResendVerificationEmailRequest) + if !ok { + return echo.NewHTTPError(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + ctx := c.Request().Context() + user, err := s.queries.GetUserByEmail(ctx, req.Email) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "user not found") + } + + if user.EmailVerified { + return echo.NewHTTPError(http.StatusBadRequest, "email already verified") + } + + err = s.queries.DeleteVerifyEmailTokenByEmail(ctx, req.Email) + if err != nil { + log.Error().Err(err).Str("email", req.Email).Msg("Failed to delete existing verification tokens") + return echo.NewHTTPError(http.StatusInternalServerError, "failed to process request") + } + + exp := time.Now().Add(time.Minute * 30) + token, err := s.queries.CreateVerifyEmailToken(ctx, db.CreateVerifyEmailTokenParams{ + Email: req.Email, + ExpiresAt: exp, + }) + if err != nil { + log.Error().Err(err).Str("email", req.Email).Msg("Failed to verify email token in db") + return echo.NewHTTPError(http.StatusInternalServerError, "failed to create verification token") + } + + tokenStr, err := jwt.GenerateVerifyEmailToken(req.Email, token.ID, exp) + if err != nil { + log.Error().Err(err).Str("email", req.Email).Msg("Failed to generate signed verify email token") + return echo.NewHTTPError(http.StatusInternalServerError, "failed to generate verification token") + } + + err = service.SendVerficationEmail(ctx, req.Email, tokenStr) + if err != nil { + log.Error().Err(err).Str("email", req.Email).Msg("Failed to send verification email") + return echo.NewHTTPError(http.StatusInternalServerError, "failed to send verification email") + } + + return c.JSON(http.StatusOK, map[string]bool{ + "success": true, + }) +} + /* handleEmailVerifiedStatus checks for the email_verified column of the given email. If the email does not exist in the users table, it returns false. The same goes diff --git a/backend/internal/server/types.go b/backend/internal/server/types.go index 5e5b0d56..7acf600f 100644 --- a/backend/internal/server/types.go +++ b/backend/internal/server/types.go @@ -206,6 +206,10 @@ type EmailVerifiedStatusResponse struct { Verified bool `json:"verified"` } +type ResendVerificationEmailRequest struct { + Email string `json:"email" validate:"required,email"` +} + type CreateProjectFileRequest struct { FileType string `json:"file_type" validate:"required"` FileURL string `json:"file_url" validate:"required,url,s3_url"`