241 lines
6.4 KiB
Go
241 lines
6.4 KiB
Go
package routes
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"dk/internal/components"
|
|
"dk/internal/models/users"
|
|
|
|
sushi "git.sharkk.net/Sharkk/Sushi"
|
|
"git.sharkk.net/Sharkk/Sushi/auth"
|
|
"git.sharkk.net/Sharkk/Sushi/password"
|
|
)
|
|
|
|
// RegisterAuthRoutes sets up authentication routes
|
|
func RegisterAuthRoutes(app *sushi.App) {
|
|
// Public routes (no auth required)
|
|
app.Get("/login", showLogin)
|
|
app.Post("/login", processLogin)
|
|
app.Get("/register", showRegister)
|
|
app.Post("/register", processRegister)
|
|
|
|
// Protected routes
|
|
authed := app.Group("")
|
|
authed.Use(auth.RequireAuth("/login"))
|
|
authed.Post("/logout", processLogout)
|
|
}
|
|
|
|
// showLogin displays the login form
|
|
func showLogin(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
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")
|
|
|
|
components.RenderPage(ctx, "Log In", "auth/login.html", map[string]any{
|
|
"id": id,
|
|
})
|
|
}
|
|
|
|
// processLogin handles login form submission
|
|
func processLogin(ctx sushi.Ctx) {
|
|
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")
|
|
return
|
|
}
|
|
|
|
user, err := authenticate(email, userPassword)
|
|
if err != nil {
|
|
setFlashAndFormData(ctx, "Invalid email or password", map[string]string{"id": email})
|
|
ctx.Redirect("/login")
|
|
return
|
|
}
|
|
|
|
// Update last online time when logging in
|
|
user.UpdateLastOnline()
|
|
|
|
ctx.Login(user.ID, user)
|
|
|
|
// Set success message
|
|
sess := ctx.GetCurrentSession()
|
|
sess.SetFlash("success", fmt.Sprintf("Welcome back, %s!", user.Username))
|
|
|
|
ctx.Redirect("/")
|
|
}
|
|
|
|
// showRegister displays the registration form
|
|
func showRegister(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
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")
|
|
|
|
components.RenderPage(ctx, "Register", "auth/register.html", map[string]any{
|
|
"username": username,
|
|
"email": email,
|
|
"error_message": sess.GetFlashMessage("error"),
|
|
})
|
|
}
|
|
|
|
// processRegister handles registration form submission
|
|
func processRegister(ctx sushi.Ctx) {
|
|
fmt.Println("DEBUG: Starting registration process")
|
|
|
|
username := strings.TrimSpace(ctx.Form("username").String())
|
|
email := strings.TrimSpace(ctx.Form("email").String())
|
|
userPassword := ctx.Form("password").String()
|
|
confirmPassword := ctx.Form("confirm_password").String()
|
|
|
|
fmt.Printf("DEBUG: Form data - username: '%s', email: '%s', password length: %d\n",
|
|
username, email, len(userPassword))
|
|
|
|
formData := map[string]string{
|
|
"username": username,
|
|
"email": email,
|
|
}
|
|
|
|
if err := validateRegistration(username, email, userPassword, confirmPassword); err != nil {
|
|
fmt.Printf("DEBUG: Validation failed: %v\n", err)
|
|
setFlashAndFormData(ctx, err.Error(), formData)
|
|
ctx.Redirect("/register")
|
|
return
|
|
}
|
|
fmt.Println("DEBUG: Validation passed")
|
|
|
|
// Check if username already exists
|
|
if _, err := users.ByUsername(username); err == nil {
|
|
fmt.Printf("DEBUG: Username '%s' already exists\n", username)
|
|
setFlashAndFormData(ctx, "Username already exists", formData)
|
|
ctx.Redirect("/register")
|
|
return
|
|
}
|
|
fmt.Printf("DEBUG: Username '%s' is available\n", username)
|
|
|
|
// Check if email already exists
|
|
if _, err := users.ByEmail(email); err == nil {
|
|
fmt.Printf("DEBUG: Email '%s' already exists\n", email)
|
|
setFlashAndFormData(ctx, "Email already registered", formData)
|
|
ctx.Redirect("/register")
|
|
return
|
|
}
|
|
fmt.Printf("DEBUG: Email '%s' is available\n", email)
|
|
|
|
// Create new user
|
|
user := users.New()
|
|
user.Username = username
|
|
user.Email = email
|
|
user.Password = password.HashPassword(userPassword)
|
|
user.ClassID = 1
|
|
user.Auth = 1
|
|
|
|
fmt.Printf("DEBUG: Created user struct with ID: %d\n", user.ID)
|
|
|
|
// Validate before inserting
|
|
if err := user.Validate(); err != nil {
|
|
fmt.Printf("DEBUG: User validation failed: %v\n", err)
|
|
setFlashAndFormData(ctx, fmt.Sprintf("Invalid user data: %s", err.Error()), formData)
|
|
ctx.Redirect("/register")
|
|
return
|
|
}
|
|
fmt.Println("DEBUG: User validation passed")
|
|
|
|
if err := user.Insert(); err != nil {
|
|
fmt.Printf("DEBUG: User insert failed: %v\n", err)
|
|
setFlashAndFormData(ctx, "Failed to create account", formData)
|
|
ctx.Redirect("/register")
|
|
return
|
|
}
|
|
fmt.Printf("DEBUG: User inserted successfully with ID: %d\n", user.ID)
|
|
|
|
// Auto-login after registration
|
|
ctx.Login(user.ID, user)
|
|
fmt.Printf("DEBUG: User logged in with ID: %d\n", user.ID)
|
|
|
|
// Set success message
|
|
sess := ctx.GetCurrentSession()
|
|
sess.SetFlash("success", fmt.Sprintf("Greetings, %s!", user.Username))
|
|
|
|
fmt.Println("DEBUG: Registration completed successfully")
|
|
ctx.Redirect("/")
|
|
}
|
|
|
|
// processLogout handles logout
|
|
func processLogout(ctx sushi.Ctx) {
|
|
ctx.Logout()
|
|
ctx.Redirect("/")
|
|
}
|
|
|
|
// 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 sushi.Ctx, message string, formData map[string]string) {
|
|
sess := ctx.GetCurrentSession()
|
|
sess.SetFlash("error", message)
|
|
sess.Set("form_data", formData)
|
|
}
|
|
|
|
func authenticate(usernameOrEmail, plainPassword string) (*users.User, error) {
|
|
var user *users.User
|
|
var err error
|
|
|
|
// Try username first
|
|
user, err = users.ByUsername(usernameOrEmail)
|
|
if err != nil {
|
|
// If username not found, try email
|
|
user, err = users.ByEmail(usernameOrEmail)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user not found")
|
|
}
|
|
}
|
|
|
|
isValid, err := password.VerifyPassword(plainPassword, user.Password)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("password verification error: %w", err)
|
|
}
|
|
if !isValid {
|
|
return nil, fmt.Errorf("invalid password")
|
|
}
|
|
|
|
return user, nil
|
|
}
|