Files

122 lines
3.4 KiB
Go

package handlers
import (
"crypto/rand"
"database/sql"
"encoding/hex"
"encoding/json"
"net/http"
"strings"
"time"
"github.com/alexedwards/scs/v2"
"github.com/weltonmoura/initpage/internal/auth"
"github.com/weltonmoura/initpage/internal/config"
)
type Auth struct {
DB *sql.DB
SM *scs.SessionManager
Cfg *config.Config
}
type authReq struct {
Email string `json:"email"`
Password string `json:"password"`
Honeypot string `json:"website"`
Token string `json:"cf_turnstile_response"`
}
func (h *Auth) Register(w http.ResponseWriter, r *http.Request) {
var req authReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"dados inválidos"}`))
return
}
if req.Honeypot != "" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"rejeitado"}`))
return
}
email := strings.ToLower(strings.TrimSpace(req.Email))
if !strings.Contains(email, "@") || len(req.Password) < 6 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"email ou senha inválidos"}`))
return
}
// Turnstile opcional
if h.Cfg.TurnstileSecret != "" && req.Token != "" {
// validação simples - implementar se necessário
}
hash, err := auth.HashPasswordSafe(req.Password)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(`{"error":"erro ao criar conta"}`))
return
}
id := randHex(16)
_, err = h.DB.Exec(`INSERT INTO users(id,email,password_hash,created_at) VALUES(?,?,?,?)`, id, email, hash, time.Now())
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"email já cadastrado ou inválido"}`))
return
}
h.SM.Put(r.Context(), "user_id", id)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"ok":true}`))
}
func (h *Auth) Login(w http.ResponseWriter, r *http.Request) {
var req authReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":"dados inválidos"}`))
return
}
email := strings.ToLower(strings.TrimSpace(req.Email))
var id, ph string
err := h.DB.QueryRow(`SELECT id,password_hash FROM users WHERE email=?`, email).Scan(&id, &ph)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"error":"email ou senha inválidos"}`))
return
}
if !auth.VerifyPassword(ph, req.Password) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"error":"email ou senha inválidos"}`))
return
}
_, _ = h.DB.Exec(`UPDATE users SET last_login_at=? WHERE id=?`, time.Now(), id)
h.SM.Put(r.Context(), "user_id", id)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"ok":true}`))
}
func (h *Auth) Logout(w http.ResponseWriter, r *http.Request) {
h.SM.Destroy(r.Context())
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"ok":true}`))
}
func (h *Auth) Me(w http.ResponseWriter, r *http.Request) {
if !h.SM.Exists(r.Context(), "user_id") {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"logged":false}`))
return
}
uid := h.SM.Get(r.Context(), "user_id").(string)
var email string
_ = h.DB.QueryRow(`SELECT email FROM users WHERE id=?`, uid).Scan(&email)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{"logged": true, "email": email})
}
func randHex(n int) string {
b := make([]byte, n)
rand.Read(b)
return hex.EncodeToString(b)
}