215 lines
5.5 KiB
Go
215 lines
5.5 KiB
Go
package routes
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"dk/internal/auth"
|
|
"dk/internal/components"
|
|
"dk/internal/models/users"
|
|
"dk/internal/password"
|
|
"dk/internal/router"
|
|
"dk/internal/session"
|
|
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// RegisterAuthRoutes sets up authentication routes
|
|
func RegisterAuthRoutes(r *router.Router) {
|
|
guests := r.Group("")
|
|
guests.Use(auth.RequireGuest())
|
|
|
|
guests.Get("/login", showLogin)
|
|
guests.Post("/login", processLogin)
|
|
guests.Get("/register", showRegister)
|
|
guests.Post("/register", processRegister)
|
|
|
|
authed := r.Group("")
|
|
authed.Use(auth.RequireAuth())
|
|
|
|
authed.Post("/logout", processLogout)
|
|
}
|
|
|
|
// showLogin displays the login form
|
|
func showLogin(ctx router.Ctx, _ []string) {
|
|
sess := ctx.UserValue("session").(*session.Session)
|
|
var id string
|
|
|
|
if formData, exists := sess.Get("form_data"); exists {
|
|
if data, ok := formData.(map[string]string); ok {
|
|
id = data["id"]
|
|
}
|
|
}
|
|
sess.Delete("form_data")
|
|
session.Store(sess)
|
|
|
|
components.RenderPage(ctx, "Log In", "auth/login.html", map[string]any{
|
|
"id": id,
|
|
})
|
|
}
|
|
|
|
// processLogin handles login form submission
|
|
func processLogin(ctx router.Ctx, _ []string) {
|
|
email := strings.TrimSpace(string(ctx.PostArgs().Peek("id")))
|
|
userPassword := string(ctx.PostArgs().Peek("password"))
|
|
|
|
if email == "" || userPassword == "" {
|
|
setFlashAndFormData(ctx, "Email and password are required", map[string]string{"id": email})
|
|
ctx.Redirect("/login", fasthttp.StatusFound)
|
|
return
|
|
}
|
|
|
|
user, err := authenticate(email, userPassword)
|
|
if err != nil {
|
|
setFlashAndFormData(ctx, "Invalid email or password", map[string]string{"id": email})
|
|
ctx.Redirect("/login", fasthttp.StatusFound)
|
|
return
|
|
}
|
|
|
|
auth.Login(ctx, user)
|
|
|
|
// CSRF token is already in session, no need to transfer from cookie
|
|
|
|
ctx.Redirect("/", fasthttp.StatusFound)
|
|
}
|
|
|
|
// showRegister displays the registration form
|
|
func showRegister(ctx router.Ctx, _ []string) {
|
|
sess := ctx.UserValue("session").(*session.Session)
|
|
var username, email string
|
|
|
|
if formData, exists := sess.Get("form_data"); exists {
|
|
if data, ok := formData.(map[string]string); ok {
|
|
username = data["username"]
|
|
email = data["email"]
|
|
}
|
|
}
|
|
sess.Delete("form_data")
|
|
session.Store(sess)
|
|
|
|
components.RenderPage(ctx, "Register", "auth/register.html", map[string]any{
|
|
"username": username,
|
|
"email": email,
|
|
})
|
|
}
|
|
|
|
// processRegister handles registration form submission
|
|
func processRegister(ctx router.Ctx, _ []string) {
|
|
username := strings.TrimSpace(string(ctx.PostArgs().Peek("username")))
|
|
email := strings.TrimSpace(string(ctx.PostArgs().Peek("email")))
|
|
userPassword := string(ctx.PostArgs().Peek("password"))
|
|
confirmPassword := string(ctx.PostArgs().Peek("confirm_password"))
|
|
|
|
formData := map[string]string{
|
|
"username": username,
|
|
"email": email,
|
|
}
|
|
|
|
if err := validateRegistration(username, email, userPassword, confirmPassword); err != nil {
|
|
setFlashAndFormData(ctx, err.Error(), formData)
|
|
ctx.Redirect("/register", fasthttp.StatusFound)
|
|
return
|
|
}
|
|
|
|
if _, err := users.ByUsername(username); err == nil {
|
|
setFlashAndFormData(ctx, "Username already exists", formData)
|
|
ctx.Redirect("/register", fasthttp.StatusFound)
|
|
return
|
|
}
|
|
|
|
if _, err := users.ByEmail(email); err == nil {
|
|
setFlashAndFormData(ctx, "Email already registered", formData)
|
|
ctx.Redirect("/register", fasthttp.StatusFound)
|
|
return
|
|
}
|
|
|
|
user := users.New()
|
|
user.Username = username
|
|
user.Email = email
|
|
user.Password = password.Hash(userPassword)
|
|
user.ClassID = 1
|
|
user.Auth = 1
|
|
|
|
if err := user.Insert(); err != nil {
|
|
setFlashAndFormData(ctx, "Failed to create account", formData)
|
|
ctx.Redirect("/register", fasthttp.StatusFound)
|
|
return
|
|
}
|
|
|
|
// Auto-login after registration (this will update the current session)
|
|
auth.Login(ctx, user)
|
|
|
|
// Update success message (Login already sets a message, so override it)
|
|
if sess := ctx.UserValue("session").(*session.Session); sess != nil {
|
|
sess.SetFlash("success", fmt.Sprintf("Greetings, %s!", user.Username))
|
|
session.Store(sess)
|
|
}
|
|
|
|
// CSRF token is already in session, no need to transfer from cookie
|
|
|
|
ctx.Redirect("/", fasthttp.StatusFound)
|
|
}
|
|
|
|
// processLogout handles logout
|
|
func processLogout(ctx router.Ctx, params []string) {
|
|
auth.Logout(ctx)
|
|
ctx.Redirect("/", fasthttp.StatusFound)
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
func validateRegistration(username, email, password, confirmPassword string) error {
|
|
if username == "" {
|
|
return fmt.Errorf("username is required")
|
|
}
|
|
if len(username) < 3 {
|
|
return fmt.Errorf("username must be at least 3 characters")
|
|
}
|
|
if email == "" {
|
|
return fmt.Errorf("email is required")
|
|
}
|
|
if !strings.Contains(email, "@") {
|
|
return fmt.Errorf("invalid email address")
|
|
}
|
|
if password == "" {
|
|
return fmt.Errorf("password is required")
|
|
}
|
|
if len(password) < 6 {
|
|
return fmt.Errorf("password must be at least 6 characters")
|
|
}
|
|
if password != confirmPassword {
|
|
return fmt.Errorf("passwords do not match")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setFlashAndFormData(ctx router.Ctx, message string, formData map[string]string) {
|
|
sess := ctx.UserValue("session").(*session.Session)
|
|
sess.SetFlash("error", message)
|
|
sess.Set("form_data", formData)
|
|
session.Store(sess)
|
|
}
|
|
|
|
func authenticate(usernameOrEmail, plainPassword string) (*users.User, error) {
|
|
var user *users.User
|
|
var err error
|
|
|
|
user, err = users.ByUsername(usernameOrEmail)
|
|
if err != nil {
|
|
user, err = users.ByEmail(usernameOrEmail)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
isValid, err := password.Verify(plainPassword, user.Password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !isValid {
|
|
return nil, fmt.Errorf("invalid username/email or password")
|
|
}
|
|
|
|
return user, nil
|
|
}
|