frontend-foundation: GOAT front-end Phase 1 — auth, tag management, public tag page (22/22 spec scenarios pass)
This commit is contained in:
98
frontend/internal/handlers/auth.go
Normal file
98
frontend/internal/handlers/auth.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
"wherewoof/frontend/internal/auth"
|
||||
"wherewoof/frontend/internal/db"
|
||||
)
|
||||
|
||||
func (a *App) RegisterPage(w http.ResponseWriter, r *http.Request) {
|
||||
a.render(w, r, "register", "Create account", nil, "")
|
||||
}
|
||||
|
||||
func (a *App) Register(w http.ResponseWriter, r *http.Request) {
|
||||
email := strings.ToLower(strings.TrimSpace(r.FormValue("email")))
|
||||
password := r.FormValue("password")
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
|
||||
if email == "" || password == "" {
|
||||
a.render(w, r, "register", "Create account", nil, "Email and password are required.")
|
||||
return
|
||||
}
|
||||
if len(password) < 8 {
|
||||
a.render(w, r, "register", "Create account", nil, "Password must be at least 8 characters.")
|
||||
return
|
||||
}
|
||||
if len([]byte(password)) > 72 {
|
||||
a.render(w, r, "register", "Create account", nil, "Password must be 72 bytes or fewer.")
|
||||
return
|
||||
}
|
||||
|
||||
hash, err := auth.HashPassword(password)
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := a.Queries.CreateUser(r.Context(), db.CreateUserParams{
|
||||
Email: email,
|
||||
PasswordHash: hash,
|
||||
Name: pgtype.Text{String: name, Valid: name != ""},
|
||||
})
|
||||
if err != nil {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
|
||||
a.render(w, r, "register", "Create account", nil, "That email is already registered.")
|
||||
return
|
||||
}
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.SetUserID(w, r, user.ID); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/account", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (a *App) LoginPage(w http.ResponseWriter, r *http.Request) {
|
||||
a.render(w, r, "login", "Log in", nil, "")
|
||||
}
|
||||
|
||||
func (a *App) Login(w http.ResponseWriter, r *http.Request) {
|
||||
email := strings.ToLower(strings.TrimSpace(r.FormValue("email")))
|
||||
password := r.FormValue("password")
|
||||
|
||||
user, err := a.Queries.GetUserByEmail(r.Context(), email)
|
||||
if err != nil {
|
||||
if !errors.Is(err, pgx.ErrNoRows) {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
a.render(w, r, "login", "Log in", nil, "Invalid email or password.")
|
||||
return
|
||||
}
|
||||
if !auth.CheckPassword(user.PasswordHash, password) {
|
||||
a.render(w, r, "login", "Log in", nil, "Invalid email or password.")
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.SetUserID(w, r, user.ID); err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/account", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (a *App) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
_ = auth.Clear(w, r)
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
Reference in New Issue
Block a user