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 } 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) { 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") return } if _, err := users.ByUsername(username); err == nil { setFlashAndFormData(ctx, "Username already exists", formData) ctx.Redirect("/register") return } if _, err := users.ByEmail(email); err == nil { setFlashAndFormData(ctx, "Email already registered", formData) ctx.Redirect("/register") return } user := users.New() user.Username = username user.Email = email user.Password = password.HashPassword(userPassword) user.ClassID = 1 user.Auth = 1 if err := user.Insert(); err != nil { setFlashAndFormData(ctx, "Failed to create account", formData) ctx.Redirect("/register") return } // Auto-login after registration ctx.Login(user.ID, user) // Set success message sess := ctx.GetCurrentSession() sess.SetFlash("success", fmt.Sprintf("Greetings, %s!", user.Username)) 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 user, err = users.ByUsername(usernameOrEmail) if err != nil { fmt.Println(err.Error()) user, err = users.ByEmail(usernameOrEmail) if err != nil { fmt.Println(err.Error()) return nil, err } } isValid, err := password.VerifyPassword(plainPassword, user.Password) if err != nil { return nil, err } if !isValid { return nil, fmt.Errorf("invalid username/email or password") } return user, nil }