184 lines
4.8 KiB
Go
184 lines
4.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"time"
|
|
|
|
"dealroom/templates"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func (h *Handler) handleLoginPage(w http.ResponseWriter, r *http.Request) {
|
|
// If already logged in, redirect to dashboard
|
|
cookie, err := r.Cookie("session")
|
|
if err == nil && cookie.Value != "" {
|
|
var count int
|
|
h.db.QueryRow("SELECT COUNT(*) FROM sessions WHERE token = ? AND expires_at > datetime('now')", cookie.Value).Scan(&count)
|
|
if count > 0 {
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
return
|
|
}
|
|
}
|
|
templates.Login().Render(r.Context(), w)
|
|
}
|
|
|
|
func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
email := r.FormValue("email")
|
|
password := r.FormValue("password")
|
|
|
|
var userID string
|
|
var passHash string
|
|
err := h.db.QueryRow("SELECT id, password_hash FROM profiles WHERE email = ?", email).Scan(&userID, &passHash)
|
|
if err != nil || bcrypt.CompareHashAndPassword([]byte(passHash), []byte(password)) != nil {
|
|
http.Redirect(w, r, "/login?error=invalid", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
h.createSession(w, userID)
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
}
|
|
|
|
func hashPassword(password string) (string, error) {
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(hash), nil
|
|
}
|
|
|
|
func (h *Handler) handleLogout(w http.ResponseWriter, r *http.Request) {
|
|
cookie, err := r.Cookie("session")
|
|
if err == nil {
|
|
h.db.Exec("DELETE FROM sessions WHERE token = ?", cookie.Value)
|
|
}
|
|
http.SetCookie(w, &http.Cookie{Name: "session", Value: "", MaxAge: -1, Path: "/"})
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
}
|
|
|
|
func (h *Handler) createSession(w http.ResponseWriter, userID string) {
|
|
token := generateToken()
|
|
expires := time.Now().Add(24 * time.Hour)
|
|
h.db.Exec("INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)", token, userID, expires)
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: "session",
|
|
Value: token,
|
|
Path: "/",
|
|
Expires: expires,
|
|
HttpOnly: true,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) handleViewToggle(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
profile := getProfile(r.Context())
|
|
if profile.Role != "owner" && profile.Role != "admin" {
|
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
|
return
|
|
}
|
|
|
|
// Toggle the cookie
|
|
current := false
|
|
if c, err := r.Cookie("view_as_buyer"); err == nil && c.Value == "1" {
|
|
current = true
|
|
}
|
|
|
|
value := "1"
|
|
if current {
|
|
value = ""
|
|
}
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: "view_as_buyer",
|
|
Value: value,
|
|
Path: "/",
|
|
HttpOnly: true,
|
|
SameSite: http.SameSiteLaxMode,
|
|
})
|
|
|
|
// Redirect back to referrer or dashboard
|
|
ref := r.Header.Get("Referer")
|
|
if ref == "" {
|
|
ref = "/"
|
|
}
|
|
http.Redirect(w, r, ref, http.StatusSeeOther)
|
|
}
|
|
|
|
func generateToken() string {
|
|
b := make([]byte, 32)
|
|
rand.Read(b)
|
|
return hex.EncodeToString(b)
|
|
}
|
|
|
|
func (h *Handler) handleSignupPage(w http.ResponseWriter, r *http.Request) {
|
|
// If already logged in, redirect to dashboard
|
|
cookie, err := r.Cookie("session")
|
|
if err == nil && cookie.Value != "" {
|
|
var count int
|
|
h.db.QueryRow("SELECT COUNT(*) FROM sessions WHERE token = ? AND expires_at > datetime('now')", cookie.Value).Scan(&count)
|
|
if count > 0 {
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
return
|
|
}
|
|
}
|
|
templates.Signup().Render(r.Context(), w)
|
|
}
|
|
|
|
func (h *Handler) handleSignup(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Redirect(w, r, "/signup", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
// Parse form
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
http.Redirect(w, r, "/signup", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
orgName := r.FormValue("org_name")
|
|
orgType := r.FormValue("org_type")
|
|
if orgType == "" {
|
|
orgType = "company"
|
|
}
|
|
name := r.FormValue("name")
|
|
email := r.FormValue("email")
|
|
password := r.FormValue("password")
|
|
|
|
// Create organization
|
|
orgID := generateID("org")
|
|
_, err = h.db.Exec(`INSERT INTO organizations (id, name, slug, org_type) VALUES (?, ?, ?, ?)`, orgID, orgName, orgName, orgType)
|
|
if err != nil {
|
|
http.Redirect(w, r, "/signup", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
// Create user
|
|
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
userID := generateID("user")
|
|
_, err = h.db.Exec(`INSERT INTO profiles (id, email, full_name, role, organization_id, password_hash) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
userID, email, name, "owner", orgID, string(hashedPassword))
|
|
if err != nil {
|
|
http.Redirect(w, r, "/signup", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
// Auto login
|
|
h.createSession(w, userID)
|
|
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
}
|
|
|