Files
workspace/apps/go-crm/internal/handlers/auth.go
gabspereira 5b8107c169 Add GOTH stack CRM platform skeleton
- Go module with chi router, bcrypt
- SQLite schema for 9 tables
- Auth, Clients, Customers, Services, Scheduling, Payments, Q&A handlers
- HTMX template layouts
2026-04-21 15:56:40 +00:00

166 lines
4.4 KiB
Go

package handlers
import (
"database/sql"
"net/http"
"time"
"go-crm/internal/db"
"github.com/go-chi/chi/v5"
"golang.org/x/crypto/bcrypt"
)
func SignupPage(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
</head>
<body>
<h1>Sign Up</h1>
<form method="POST" action="/auth/signup">
<input type="email" name="email" placeholder="Email" required>
<input type="text" name="name" placeholder="Name" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Sign Up</button>
</form>
<p>Already have an account? <a href="/auth/login">Login</a></p>
</body>
</html>`))
}
func Signup(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
email := r.FormValue("email")
name := r.FormValue("name")
password := r.FormValue("password")
if email == "" || name == "" || password == "" {
http.Error(w, "All fields required", http.StatusBadRequest)
return
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
http.Error(w, "Failed to hash password", http.StatusInternalServerError)
return
}
account := struct {
Email string
Name string
Password string
CreatedAt int64
}{
Email: email,
Name: name,
Password: string(hashedPassword),
CreatedAt: time.Now().Unix(),
}
_, err = db.DB.Exec(
"INSERT INTO accounts (email, name, password, created_at) VALUES (?, ?, ?, ?)",
account.Email, account.Name, account.Password, account.CreatedAt,
)
if err != nil {
http.Error(w, "Email already exists", http.StatusBadRequest)
return
}
http.Redirect(w, r, "/auth/login", http.StatusFound)
}
func LoginPage(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form method="POST" action="/auth/login">
<input type="email" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
<p>Don't have an account? <a href="/auth/signup">Sign Up</a></p>
</body>
</html>`))
}
var DB *sql.DB
func Login(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
email := r.FormValue("email")
password := r.FormValue("password")
var accountID int64
var hashedPassword string
err := DB.QueryRow("SELECT account_id, password FROM accounts WHERE email = ?", email).Scan(&accountID, &hashedPassword)
if err != nil {
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
return
}
if err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)); err != nil {
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
return
}
sessionID := generateSessionID()
expires := time.Now().Add(24 * time.Hour).Unix()
_, err = DB.Exec("INSERT INTO sessions (session_id, account_id, expires) VALUES (?, ?, ?)", sessionID, accountID, expires)
if err != nil {
http.Error(w, "Failed to create session", http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{Name: "session", Value: sessionID, Path: "/"})
http.Redirect(w, r, "/clients", http.StatusFound)
}
func Logout(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session")
if err == nil {
DB.Exec("DELETE FROM sessions WHERE session_id = ?", cookie.Value)
}
http.SetCookie(w, &http.Cookie{Name: "session", Value: "", Path: "/", MaxAge: -1})
http.Redirect(w, r, "/auth/login", http.StatusFound)
}
func generateSessionID() string {
return time.Now().Format("20060102150405") + "-" + randomString(32)
}
func randomString(n int) string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, n)
for i := range b {
b[i] = letters[time.Now().UnixNano()%int64(len(letters))]
}
return string(b)
}
func getSession(r *http.Request) (int64, error) {
cookie, err := r.Cookie("session")
if err != nil {
return 0, err
}
var accountID int64
err = DB.QueryRow("SELECT account_id FROM sessions WHERE session_id = ? AND expires > ?", cookie.Value, time.Now().Unix()).Scan(&accountID)
return accountID, err
}
func SetupAuthHandlers(db *sql.DB) {
DB = db
chi.RegisterMethod("GET")
}