const db = require('../config/db'); const {sendVerificationEmail, sendResetEmail} = require('../utils/sendEmail'); const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); const nodemailer = require('nodemailer'); const crypto = require('crypto'); const transporter = nodemailer.createTransport({ service: 'gmail', auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS } }); exports.registerUser = async (req, res) => { try { const {username, email, password} = req.body; // Verifica si ya existe un usuario con ese username o email const [existing] = await db.execute( 'SELECT * FROM users WHERE username = ? OR email = ?', [username, email] ); if (existing.length > 0) { return res.status(400).json({success: false, message: "Usuario ya registrado."}); } const hashedPassword = await bcrypt.hash(password, 10); const token = crypto.randomBytes(32).toString('hex'); // Inserta el nuevo usuario con el token await db.execute( 'INSERT INTO users (username, email, password, is_verified, verificationToken) VALUES (?, ?, ?, 0, ?)', [username, email, hashedPassword, token] ); await sendVerificationEmail(email, token); res.status(201).json({success: true, message: "Usuario registrado. Verifica tu correo."}); } catch (err) { console.error("❌ Error en registerUser:", err); res.status(500).json({success: false, message: "Error en el servidor."}); } }; exports.verifyEmail = async (req, res) => { const token = req.params.token; try { const [rows] = await db.execute( 'SELECT * FROM users WHERE verificationToken = ?', [token] ); if (rows.length === 0) { return res.redirect('/verify-failed.html'); } await db.execute( 'UPDATE users SET is_verified = 1, verificationToken = NULL WHERE verificationToken = ?', [token] ); return res.redirect('/verify-success.html'); } catch (err) { console.error("❌ Error en verifyEmail:", err); return res.redirect('/verify-failed.html'); } }; exports.loginUser = async (req, res) => { const {username, password} = req.body; if (!username || !password) { return res.status(400).json({success: false, message: 'Usuario y contraseña requeridos'}); } try { const [results] = await db.execute('SELECT * FROM users WHERE username = ? LIMIT 1', [username]); if (results.length === 0) { return res.status(401).json({success: false, message: 'Usuario no encontrado'}); } const user = results[0]; const passwordMatch = await bcrypt.compare(password, user.password); if (!passwordMatch) { return res.status(401).json({success: false, message: 'Contraseña incorrecta'}); } if (!user.is_verified) { return res.status(403).json({success: false, message: 'Tu correo no ha sido verificado'}); } res.json({ success: true, user: {id: user.id, username: user.username}, message: 'Inicio de sesión exitoso' }); } catch (err) { res.status(500).json({success: false, message: 'Error en el servidor', error: err.message}); } }; exports.forgotPassword = async (req, res) => { const {email} = req.body; try { const [users] = await db.execute('SELECT id FROM users WHERE email = ?', [email]); if (users.length === 0) return res.status(404).json({message: 'Correo no encontrado'}); const token = crypto.randomBytes(32).toString('hex'); await db.execute('INSERT INTO password_resets (user_id, token) VALUES (?, ?)', [users[0].id, token]); const resetLink = `http://localhost:3000/reset-password/${token}`; // ✔️ Usa la función reutilizable await sendResetEmail(email, resetLink); res.json({message: 'Correo enviado con instrucciones'}); } catch (err) { console.error('Error al enviar token de recuperación:', err); res.status(500).json({message: 'Error interno del servidor'}); } }; exports.resetPassword = async (req, res) => { const {email, token, newPassword} = req.body; if (!email || !token || !newPassword) { return res.status(400).json({message: 'Todos los campos son obligatorios.'}); } try { const [rows] = await db.execute( 'SELECT user_id FROM password_resets WHERE token = ?', [token] ); if (rows.length === 0) { return res.status(400).json({message: 'Token inválido o expirado'}); } const userId = rows[0].user_id; const hashedPassword = await bcrypt.hash(newPassword, 10); await db.execute( 'UPDATE users SET password = ? WHERE id = ?', [hashedPassword, userId] ); await db.execute( 'DELETE FROM password_resets WHERE token = ?', [token] ); res.json({message: 'Contraseña actualizada correctamente'}); } catch (err) { console.error('❌ Error al restablecer contraseña:', err); res.status(500).json({message: 'Error interno del servidor'}); } }; exports.getUserStats = async (req, res) => { const userId = req.params.id; try { const [animeStatsRows] = await db.execute( 'SELECT status, COUNT(*) as count FROM anime WHERE userId = ? GROUP BY status', [userId] ); const [mangaStatsRows] = await db.execute( 'SELECT status, COUNT(*) as count FROM manga WHERE userId = ? GROUP BY status', [userId] ); const formatStats = (rows) => { const stats = {}; rows.forEach(row => { stats[row.status] = row.count; }); return stats; }; res.json({ animeStats: animeStatsRows.length ? formatStats(animeStatsRows) : {}, mangaStats: mangaStatsRows.length ? formatStats(mangaStatsRows) : {} }); } catch (err) { res.status(500).json({ success: false, message: 'Error al obtener estadísticas', error: err.message }); } };