quite a lot of things

This commit is contained in:
2025-12-06 03:05:44 +05:30
parent 39c61b7790
commit 28733e22d3
42 changed files with 4214 additions and 204 deletions

138
internal/web/auth.go Normal file
View File

@@ -0,0 +1,138 @@
package web
import (
"billit/internal/auth"
"net/http"
"net/url"
"strings"
"github.com/labstack/echo/v4"
)
// AuthHandlers holds auth service reference
type AuthHandlers struct {
auth *auth.Service
}
// NewAuthHandlers creates handlers with auth service
func NewAuthHandlers(authService *auth.Service) *AuthHandlers {
return &AuthHandlers{auth: authService}
}
// LoginPageHandler renders the login page (home page)
func (h *AuthHandlers) LoginPageHandler(c echo.Context) error {
// Check if already logged in
cookie, err := c.Cookie("auth_token")
if err == nil && cookie.Value != "" {
_, err := h.auth.ValidateToken(cookie.Value)
if err == nil {
// Already logged in, redirect to home
return c.Redirect(http.StatusFound, "/home")
}
}
// Capture redirect URL from query param
redirectURL := c.QueryParam("redirect")
return Render(c, LoginPage("", "", redirectURL))
}
// LoginHandler handles login form submission
func (h *AuthHandlers) LoginHandler(c echo.Context) error {
email := strings.TrimSpace(c.FormValue("email"))
password := c.FormValue("password")
redirectURL := c.FormValue("redirect")
if email == "" || password == "" {
return Render(c, LoginPage("Email and password are required", email, redirectURL))
}
token, err := h.auth.Login(email, password)
if err != nil {
return Render(c, LoginPage("Invalid email or password", email, redirectURL))
}
// Set HTTP-only cookie
cookie := h.auth.CreateAuthCookie(token)
c.SetCookie(cookie)
// Redirect to original URL or home page
if redirectURL != "" && strings.HasPrefix(redirectURL, "/") {
return c.Redirect(http.StatusFound, redirectURL)
}
return c.Redirect(http.StatusFound, "/home")
}
// RegisterPageHandler renders the registration page
func (h *AuthHandlers) RegisterPageHandler(c echo.Context) error {
return Render(c, RegisterPage("", ""))
}
// RegisterHandler handles registration form submission
func (h *AuthHandlers) RegisterHandler(c echo.Context) error {
email := strings.TrimSpace(c.FormValue("email"))
password := c.FormValue("password")
confirmPassword := c.FormValue("confirm_password")
if email == "" || password == "" {
return Render(c, RegisterPage("Email and password are required", email))
}
if password != confirmPassword {
return Render(c, RegisterPage("Passwords do not match", email))
}
if len(password) < 8 {
return Render(c, RegisterPage("Password must be at least 8 characters", email))
}
_, err := h.auth.Register(email, password)
if err != nil {
if err == auth.ErrUserExists {
return Render(c, RegisterPage("An account with this email already exists", email))
}
return Render(c, RegisterPage(err.Error(), email))
}
// Auto-login after registration
token, err := h.auth.Login(email, password)
if err != nil {
return c.Redirect(http.StatusFound, "/")
}
cookie := h.auth.CreateAuthCookie(token)
c.SetCookie(cookie)
return c.Redirect(http.StatusFound, "/home")
}
// LogoutHandler clears the auth cookie and redirects to login
func (h *AuthHandlers) LogoutHandler(c echo.Context) error {
cookie := h.auth.ClearAuthCookie()
c.SetCookie(cookie)
return c.Redirect(http.StatusFound, "/")
}
// AuthMiddleware protects routes that require authentication
func (h *AuthHandlers) AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
cookie, err := c.Cookie("auth_token")
if err != nil || cookie.Value == "" {
// No cookie - redirect to login with original URL for post-login redirect
redirectPath := url.QueryEscape(c.Request().URL.RequestURI())
return c.Redirect(http.StatusFound, "/?redirect="+redirectPath)
}
claims, err := h.auth.ValidateToken(cookie.Value)
if err != nil {
// Invalid/expired token - show session expired dialog
c.SetCookie(h.auth.ClearAuthCookie())
redirectPath := url.QueryEscape(c.Request().URL.RequestURI())
return Render(c, SessionExpiredPage(redirectPath))
}
// Store user info in context
c.Set("user_id", claims.UserID)
c.Set("user_email", claims.Email)
return next(c)
}
}