mongodb
Signed-off-by: Omar Sánchez Pizarro <omar.sanchez@pistacero.net>
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
import express from 'express';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { getRedisClient } from '../services/redis.js';
|
||||
import { getDB, getUser, createUser, deleteUser as deleteUserFromDB, getAllUsers, updateUserPassword } from '../services/mongodb.js';
|
||||
import { basicAuthMiddleware, createSession, invalidateSession, invalidateUserSessions } from '../middlewares/auth.js';
|
||||
import { adminAuthMiddleware } from '../middlewares/adminAuth.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Endpoint de login (público)
|
||||
router.post('/login', async (req, res) => {
|
||||
try {
|
||||
const redisClient = getRedisClient();
|
||||
if (!redisClient) {
|
||||
return res.status(500).json({ error: 'Redis no está disponible' });
|
||||
const db = getDB();
|
||||
if (!db) {
|
||||
return res.status(500).json({ error: 'MongoDB no está disponible' });
|
||||
}
|
||||
|
||||
const { username, password } = req.body;
|
||||
@@ -19,17 +20,15 @@ router.post('/login', async (req, res) => {
|
||||
return res.status(400).json({ error: 'username y password son requeridos' });
|
||||
}
|
||||
|
||||
// Buscar usuario en Redis
|
||||
const userKey = `user:${username}`;
|
||||
const userExists = await redisClient.exists(userKey);
|
||||
// Buscar usuario en MongoDB
|
||||
const user = await getUser(username);
|
||||
|
||||
if (!userExists) {
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'Invalid credentials', message: 'Usuario o contraseña incorrectos' });
|
||||
}
|
||||
|
||||
// Obtener hash de la contraseña
|
||||
const userData = await redisClient.hGetAll(userKey);
|
||||
const passwordHash = userData.passwordHash;
|
||||
const passwordHash = user.passwordHash;
|
||||
|
||||
if (!passwordHash) {
|
||||
return res.status(401).json({ error: 'Invalid credentials', message: 'Usuario o contraseña incorrectos' });
|
||||
@@ -45,11 +44,15 @@ router.post('/login', async (req, res) => {
|
||||
// Crear sesión/token
|
||||
const token = await createSession(username);
|
||||
|
||||
console.log(`✅ Login exitoso: ${username}`);
|
||||
// Obtener rol del usuario
|
||||
const userRole = user.role || 'user';
|
||||
|
||||
console.log(`✅ Login exitoso: ${username} (${userRole})`);
|
||||
res.json({
|
||||
success: true,
|
||||
token,
|
||||
username,
|
||||
role: userRole,
|
||||
message: 'Login exitoso'
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -78,9 +81,11 @@ router.post('/logout', basicAuthMiddleware, async (req, res) => {
|
||||
// Verificar token (para validar si la sesión sigue activa)
|
||||
router.get('/me', basicAuthMiddleware, async (req, res) => {
|
||||
try {
|
||||
const user = await getUser(req.user.username);
|
||||
res.json({
|
||||
success: true,
|
||||
username: req.user.username,
|
||||
role: user?.role || 'user',
|
||||
authenticated: true
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -91,9 +96,9 @@ router.get('/me', basicAuthMiddleware, async (req, res) => {
|
||||
// Cambiar contraseña de usuario
|
||||
router.post('/change-password', basicAuthMiddleware, async (req, res) => {
|
||||
try {
|
||||
const redisClient = getRedisClient();
|
||||
if (!redisClient) {
|
||||
return res.status(500).json({ error: 'Redis no está disponible' });
|
||||
const db = getDB();
|
||||
if (!db) {
|
||||
return res.status(500).json({ error: 'MongoDB no está disponible' });
|
||||
}
|
||||
|
||||
const { currentPassword, newPassword } = req.body;
|
||||
@@ -107,26 +112,21 @@ router.post('/change-password', basicAuthMiddleware, async (req, res) => {
|
||||
return res.status(400).json({ error: 'La nueva contraseña debe tener al menos 6 caracteres' });
|
||||
}
|
||||
|
||||
const userKey = `user:${username}`;
|
||||
const userData = await redisClient.hGetAll(userKey);
|
||||
const user = await getUser(username);
|
||||
|
||||
if (!userData || !userData.passwordHash) {
|
||||
if (!user || !user.passwordHash) {
|
||||
return res.status(404).json({ error: 'Usuario no encontrado' });
|
||||
}
|
||||
|
||||
// Verificar contraseña actual
|
||||
const match = await bcrypt.compare(currentPassword, userData.passwordHash);
|
||||
const match = await bcrypt.compare(currentPassword, user.passwordHash);
|
||||
if (!match) {
|
||||
return res.status(401).json({ error: 'Contraseña actual incorrecta' });
|
||||
}
|
||||
|
||||
// Hashear nueva contraseña y actualizar
|
||||
const newPasswordHash = await bcrypt.hash(newPassword, 10);
|
||||
await redisClient.hSet(userKey, {
|
||||
...userData,
|
||||
passwordHash: newPasswordHash,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
await updateUserPassword(username, newPasswordHash);
|
||||
|
||||
// Invalidar todas las sesiones del usuario (requiere nuevo login)
|
||||
await invalidateUserSessions(username);
|
||||
@@ -139,40 +139,37 @@ router.post('/change-password', basicAuthMiddleware, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Obtener lista de usuarios (requiere autenticación admin)
|
||||
// Obtener lista de usuarios (requiere autenticación, admin ve todos, user ve solo suyo)
|
||||
router.get('/', basicAuthMiddleware, async (req, res) => {
|
||||
try {
|
||||
const redisClient = getRedisClient();
|
||||
if (!redisClient) {
|
||||
return res.status(500).json({ error: 'Redis no está disponible' });
|
||||
const db = getDB();
|
||||
if (!db) {
|
||||
return res.status(500).json({ error: 'MongoDB no está disponible' });
|
||||
}
|
||||
|
||||
// Obtener todas las claves de usuarios
|
||||
const userKeys = await redisClient.keys('user:*');
|
||||
const users = [];
|
||||
const users = await getAllUsers(req.user.username);
|
||||
|
||||
for (const key of userKeys) {
|
||||
const username = key.replace('user:', '');
|
||||
const userData = await redisClient.hGetAll(key);
|
||||
|
||||
if (userData && userData.username) {
|
||||
users.push({
|
||||
username: userData.username,
|
||||
createdAt: userData.createdAt || null,
|
||||
updatedAt: userData.updatedAt || null,
|
||||
createdBy: userData.createdBy || null,
|
||||
});
|
||||
// Convertir ObjectId a string y formatear fechas
|
||||
const formattedUsers = users.map(user => {
|
||||
const formatted = { ...user };
|
||||
formatted._id = user._id?.toString();
|
||||
if (user.createdAt && typeof user.createdAt === 'object') {
|
||||
formatted.createdAt = user.createdAt.toISOString();
|
||||
}
|
||||
}
|
||||
if (user.updatedAt && typeof user.updatedAt === 'object') {
|
||||
formatted.updatedAt = user.updatedAt.toISOString();
|
||||
}
|
||||
return formatted;
|
||||
});
|
||||
|
||||
// Ordenar por fecha de creación (más recientes primero)
|
||||
users.sort((a, b) => {
|
||||
formattedUsers.sort((a, b) => {
|
||||
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
||||
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
|
||||
res.json({ users, total: users.length });
|
||||
res.json({ users: formattedUsers, total: formattedUsers.length });
|
||||
} catch (error) {
|
||||
console.error('Error obteniendo usuarios:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
@@ -180,11 +177,11 @@ router.get('/', basicAuthMiddleware, async (req, res) => {
|
||||
});
|
||||
|
||||
// Crear nuevo usuario (requiere autenticación admin)
|
||||
router.post('/', basicAuthMiddleware, async (req, res) => {
|
||||
router.post('/', basicAuthMiddleware, adminAuthMiddleware, async (req, res) => {
|
||||
try {
|
||||
const redisClient = getRedisClient();
|
||||
if (!redisClient) {
|
||||
return res.status(500).json({ error: 'Redis no está disponible' });
|
||||
const db = getDB();
|
||||
if (!db) {
|
||||
return res.status(500).json({ error: 'MongoDB no está disponible' });
|
||||
}
|
||||
|
||||
const { username, password } = req.body;
|
||||
@@ -201,19 +198,17 @@ router.post('/', basicAuthMiddleware, async (req, res) => {
|
||||
return res.status(400).json({ error: 'La contraseña debe tener al menos 6 caracteres' });
|
||||
}
|
||||
|
||||
const userKey = `user:${username}`;
|
||||
const userExists = await redisClient.exists(userKey);
|
||||
|
||||
if (userExists) {
|
||||
// Verificar si el usuario ya existe
|
||||
const existingUser = await getUser(username);
|
||||
if (existingUser) {
|
||||
return res.status(409).json({ error: 'El usuario ya existe' });
|
||||
}
|
||||
|
||||
// Hashear contraseña y crear usuario
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
await redisClient.hSet(userKey, {
|
||||
await createUser({
|
||||
username,
|
||||
passwordHash,
|
||||
createdAt: new Date().toISOString(),
|
||||
createdBy: req.user.username,
|
||||
});
|
||||
|
||||
@@ -221,16 +216,20 @@ router.post('/', basicAuthMiddleware, async (req, res) => {
|
||||
res.json({ success: true, message: 'Usuario creado correctamente', username });
|
||||
} catch (error) {
|
||||
console.error('Error creando usuario:', error);
|
||||
// Manejar error de duplicado
|
||||
if (error.code === 11000) {
|
||||
return res.status(409).json({ error: 'El usuario ya existe' });
|
||||
}
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Eliminar usuario (requiere autenticación admin)
|
||||
router.delete('/:username', basicAuthMiddleware, async (req, res) => {
|
||||
router.delete('/:username', basicAuthMiddleware, adminAuthMiddleware, async (req, res) => {
|
||||
try {
|
||||
const redisClient = getRedisClient();
|
||||
if (!redisClient) {
|
||||
return res.status(500).json({ error: 'Redis no está disponible' });
|
||||
const db = getDB();
|
||||
if (!db) {
|
||||
return res.status(500).json({ error: 'MongoDB no está disponible' });
|
||||
}
|
||||
|
||||
const { username } = req.params;
|
||||
@@ -241,15 +240,19 @@ router.delete('/:username', basicAuthMiddleware, async (req, res) => {
|
||||
return res.status(400).json({ error: 'No puedes eliminar tu propio usuario' });
|
||||
}
|
||||
|
||||
const userKey = `user:${username}`;
|
||||
const userExists = await redisClient.exists(userKey);
|
||||
|
||||
if (!userExists) {
|
||||
// Verificar si el usuario existe
|
||||
const user = await getUser(username);
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'Usuario no encontrado' });
|
||||
}
|
||||
|
||||
// Eliminar usuario
|
||||
await redisClient.del(userKey);
|
||||
// Eliminar usuario y sus sesiones
|
||||
await deleteUserFromDB(username);
|
||||
await invalidateUserSessions(username);
|
||||
|
||||
// También eliminar sus workers
|
||||
const workersCollection = db.collection('workers');
|
||||
await workersCollection.deleteOne({ username });
|
||||
|
||||
console.log(`✅ Usuario eliminado: ${username} por ${currentUser}`);
|
||||
res.json({ success: true, message: `Usuario ${username} eliminado correctamente` });
|
||||
|
||||
Reference in New Issue
Block a user