package handlers import ( "context" "fmt" "log" "net/http" "strings" "time" "tankstopp/internal/auth" "tankstopp/internal/currency" "tankstopp/internal/models" "tankstopp/internal/views/pages" ) // RootHandler redirects to appropriate page based on authentication status func (h *Handler) RootHandler(w http.ResponseWriter, r *http.Request) { // Check if user is authenticated sessionID, err := auth.GetSessionCookie(r) if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } _, exists := h.sessionManager.GetSession(sessionID) if !exists { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // User is authenticated, redirect to dashboard http.Redirect(w, r, "/dashboard", http.StatusSeeOther) } // LoginHandler handles user authentication func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": component := pages.LoginPage("") w.Header().Set("Content-Type", "text/html") err := component.Render(r.Context(), w) if err != nil { log.Printf("Error rendering template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } case "POST": h.handleLogin(w, r) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } // handleLogin processes login form submission func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { log.Printf("Error parsing form: %v", err) h.renderLoginWithError(w, "Invalid form data") return } username := strings.TrimSpace(r.FormValue("username")) password := r.FormValue("password") if username == "" || password == "" { h.renderLoginWithError(w, "Username and password are required") return } // Get user from database user, err := h.db.GetUserByUsername(username) if err != nil { log.Printf("Error getting user: %v", err) h.renderLoginWithError(w, "Invalid username or password") return } // Check if user exists if user == nil { h.renderLoginWithError(w, "Invalid username or password") return } // Check password if !user.CheckPassword(password) { h.renderLoginWithError(w, "Invalid username or password") return } // Create session session := h.sessionManager.CreateSession(int(user.ID), user.Username) // Set session cookie cookie := &http.Cookie{ Name: "session_id", Value: session.ID, Path: "/", HttpOnly: true, Secure: false, // Set to true in production with HTTPS SameSite: http.SameSiteStrictMode, Expires: session.ExpiresAt, } http.SetCookie(w, cookie) // Redirect to dashboard http.Redirect(w, r, "/dashboard", http.StatusSeeOther) } // renderLoginWithError renders the login page with an error message func (h *Handler) renderLoginWithError(w http.ResponseWriter, errorMsg string) { component := pages.LoginPage(errorMsg) w.Header().Set("Content-Type", "text/html") err := component.Render(context.Background(), w) if err != nil { log.Printf("Error rendering template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } } // RegisterHandler handles user registration func (h *Handler) RegisterHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": component := pages.RegisterPage("") w.Header().Set("Content-Type", "text/html") err := component.Render(r.Context(), w) if err != nil { log.Printf("Error rendering template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } case "POST": h.handleRegister(w, r) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } // handleRegister processes registration form submission func (h *Handler) handleRegister(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { log.Printf("Error parsing form: %v", err) h.renderRegisterWithError(w, "Invalid form data") return } // Parse form data form := models.UserRegistrationForm{ Username: strings.TrimSpace(r.FormValue("username")), Email: strings.TrimSpace(r.FormValue("email")), Password: r.FormValue("password"), ConfirmPassword: r.FormValue("confirm_password"), BaseCurrency: r.FormValue("base_currency"), } // Validate form if err := h.validateRegistrationForm(&form); err != nil { h.renderRegisterWithError(w, err.Error()) return } // Convert form to user user, err := form.ToUser() if err != nil { log.Printf("Error converting form to user: %v", err) h.renderRegisterWithError(w, "Failed to create user") return } // Create user in database err = h.db.CreateUser(user) if err != nil { log.Printf("Error creating user: %v", err) if strings.Contains(err.Error(), "UNIQUE constraint failed") { h.renderRegisterWithError(w, "Username or email already exists") } else { h.renderRegisterWithError(w, "Failed to create account") } return } // Redirect to login with success message http.Redirect(w, r, "/login?success=Account+created+successfully", http.StatusSeeOther) } // validateRegistrationForm validates the registration form data func (h *Handler) validateRegistrationForm(form *models.UserRegistrationForm) error { if form.Username == "" { return fmt.Errorf("Username is required") } if len(form.Username) < 3 { return fmt.Errorf("Username must be at least 3 characters long") } if form.Email == "" { return fmt.Errorf("Email is required") } if !strings.Contains(form.Email, "@") { return fmt.Errorf("Invalid email address") } if form.Password == "" { return fmt.Errorf("Password is required") } if len(form.Password) < 8 { return fmt.Errorf("Password must be at least 8 characters long") } if form.Password != form.ConfirmPassword { return fmt.Errorf("Passwords do not match") } if form.BaseCurrency == "" { return fmt.Errorf("Base currency is required") } if !currency.IsValidCurrency(form.BaseCurrency) { return fmt.Errorf("Invalid currency") } return nil } // renderRegisterWithError renders the registration page with an error message func (h *Handler) renderRegisterWithError(w http.ResponseWriter, errorMsg string) { component := pages.RegisterPage(errorMsg) w.Header().Set("Content-Type", "text/html") err := component.Render(context.Background(), w) if err != nil { log.Printf("Error rendering template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } } // LogoutHandler handles user logout func (h *Handler) LogoutHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Get session cookie sessionID, err := auth.GetSessionCookie(r) if err == nil { // Remove session from session manager h.sessionManager.DeleteSession(sessionID) } // Clear session cookie cookie := &http.Cookie{ Name: "session_id", Value: "", Path: "/", HttpOnly: true, Secure: false, // Set to true in production with HTTPS SameSite: http.SameSiteStrictMode, Expires: time.Unix(0, 0), // Expire immediately } http.SetCookie(w, cookie) // Redirect to login page http.Redirect(w, r, "/login", http.StatusSeeOther) }