122 lines
3.4 KiB
Go
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)
|
|
}
|