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) }