package handlers import ( "database/sql" "net/http" "time" "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(`
Already have an account? Login
`)) } 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.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 } var accountID int64 err = DB.QueryRow("SELECT account_id FROM accounts WHERE email = ?", email).Scan(&accountID) if err != nil { http.Error(w, "Failed to create account", http.StatusInternalServerError) return } client := struct { AccountID int64 Name string CreatedAt int64 }{ AccountID: accountID, Name: name, CreatedAt: time.Now().Unix(), } _, err = DB.Exec( "INSERT INTO clients (account_id, name, created_at) VALUES (?, ?, ?)", client.AccountID, client.Name, client.CreatedAt, ) if err != nil { http.Error(w, "Failed to create client", http.StatusInternalServerError) 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, "/", http.StatusFound) } func LoginPage(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.Write([]byte(`Don't have an account? Sign Up
`)) } 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, "/", 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 GetAccountID(r *http.Request) (int64, error) { return getSession(r) } func requireAuth(w http.ResponseWriter, r *http.Request) (int64, bool) { accountID, err := getSession(r) if err != nil { http.Redirect(w, r, "/auth/login", http.StatusFound) return 0, false } return accountID, true } func SetupAuthHandlers(db *sql.DB) { DB = db chi.RegisterMethod("GET") } func AccountPage(w http.ResponseWriter, r *http.Request) { accountID, ok := requireAuth(w, r) if !ok { return } var name, email string err := DB.QueryRow("SELECT name, email FROM accounts WHERE account_id = ?", accountID).Scan(&name, &email) if err != nil { http.Error(w, "Account not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "text/html") w.Write([]byte(`