Servidor/controllers/userController.js

205 lines
6.3 KiB
JavaScript

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