Created
August 3, 2025 18:08
-
-
Save alcarazolabs/bdbfacc4efe58baaefb3b6342ba3a9df to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // routes/devices.js | |
| import express from 'express'; | |
| import dayjs from 'dayjs'; | |
| import { getRedisClient } from '../redisClient.js'; | |
| import { Device, DeviceLocation, DeviceStatus, Secret } from '../models/initModels.js'; | |
| import jwt from 'jsonwebtoken'; | |
| import dotenv from 'dotenv'; | |
| import { ValidationError } from 'sequelize'; // validator de sequelize | |
| import { generateApiKeyV1 } from '../utils/generateApiKey.js' | |
| dotenv.config(); // Cargar variables del .env | |
| // Obtener valor JWT_SECRET desde el .env | |
| const JWT_SECRET = process.env.JWT_SECRET; | |
| const router = express.Router(); | |
| // Función helper para formatear fecha con zona horaria | |
| function formatearFechaHoraLima(fechaUTC) { | |
| return dayjs(fechaUTC) | |
| .tz('America/Lima') | |
| .format('YYYY-MM-DD HH:mm:ss'); | |
| } | |
| // Middleware para verificar el JWToken. | |
| function verifyToken(req, res, next){ | |
| const token = req.header("Authorization"); | |
| if (!token){ | |
| return res.status(401).json({ | |
| message: "Acceso denegado!" | |
| }) | |
| } | |
| try { | |
| const decoded = jwt.verify( | |
| token.split(" ")[1], | |
| JWT_SECRET | |
| ); | |
| req.user = decoded; | |
| next(); | |
| } catch (error) { | |
| console.log("Error al verificar el token: ", error); | |
| res.status(401).json({ | |
| message: "Token invalido!" | |
| }) | |
| } | |
| } | |
| router.post("/devices/apikey", verifyToken, async (req, res) => { | |
| try { | |
| const { id } = req.body; | |
| var message = ""; | |
| var key = generateApiKeyV1() | |
| if (id == null){ | |
| // Registrar por primera vez | |
| await Secret.create({ devices_api_key: key }); | |
| // Setear la key en la BD Cache | |
| // Obtener cliente-Redis | |
| const clientRedis = await getRedisClient(); | |
| await clientRedis.set("devices_api_key", key); | |
| message = "Key registrada correctamente." | |
| }else{ | |
| // Actualizar la key | |
| await Secret.upsert({ id: id, devices_api_key: key }); | |
| // Setear la key en la BD Cache | |
| // Obtener cliente-Redis | |
| const clientRedis = await getRedisClient(); | |
| await clientRedis.set("devices_api_key", key); | |
| message = "Key actualizada correctamente." | |
| } | |
| // Enviar las keys | |
| const keys = await Secret.findAll({ | |
| limit: 1, // Limitar a 1 resultados | |
| order: [['id', 'DESC']], | |
| }) | |
| res.status(201).json({ | |
| success: true, | |
| message: message, | |
| keys | |
| }); | |
| } catch (error) { | |
| // Primero, verificamos si el error es una instancia de ValidationError de Sequelize | |
| if (error instanceof ValidationError) { | |
| console.log("Error de validación:", error.errors); | |
| // Extraemos los mensajes de error personalizados que definimos en el modelo | |
| const errorMessages = error.errors.map(err => err.message); | |
| return res.status(400).json({ // 400 Bad Request es el código ideal para errores de validación | |
| message: "Error de validación", | |
| success: false, | |
| errors: errorMessages // Enviamos un array con todos los mensajes de error | |
| }); | |
| } | |
| // Si es otro tipo de error (ej. error de conexión a la BD), lo manejamos como un error del servidor | |
| console.log("Ocurrió un error en el registro del api key: ", error); | |
| res.status(500).json({ | |
| message: "Error en el servidor.", | |
| success: false | |
| }); | |
| } | |
| }); | |
| router.get("/devices/apikey", verifyToken, async (req, res) => { | |
| try { | |
| const keys = await Secret.findAll({ | |
| limit: 1, // Limitar a 1 resultados | |
| order: [['id', 'DESC']], | |
| }) | |
| res.json( { | |
| success: true, | |
| keys | |
| }) | |
| } catch (error) { | |
| res.status(500).json({ | |
| message: "Error del Servidor", | |
| success: false, | |
| }) | |
| } | |
| }); | |
| router.post("/devices", verifyToken, async (req, res) => { | |
| try { | |
| const { nombre, descripcion } = req.body; | |
| // Registrar el Device | |
| await Device.create({ nombre, descripcion }); | |
| // Aquí hacemos un nuevo SELECT para retornar todo actualizado | |
| const devices = await Device.findAll(); | |
| res.status(201).json({ | |
| success: true, | |
| message: "Dispositivo registrado correctamente.", | |
| devices | |
| }); | |
| } catch (error) { | |
| // Primero, verificamos si el error es una instancia de ValidationError de Sequelize | |
| if (error instanceof ValidationError) { | |
| console.log("Error de validación:", error.errors); | |
| // Extraemos los mensajes de error personalizados que definimos en el modelo | |
| const errorMessages = error.errors.map(err => err.message); | |
| return res.status(400).json({ // 400 Bad Request es el código ideal para errores de validación | |
| message: "Error de validación", | |
| success: false, | |
| errors: errorMessages // Enviamos un array con todos los mensajes de error | |
| }); | |
| } | |
| // Si es otro tipo de error (ej. error de conexión a la BD), lo manejamos como un error del servidor | |
| console.log("Ocurrió un error en el registro del dispositivo: ", error); | |
| res.status(500).json({ | |
| message: "Error en el servidor.", | |
| success: false | |
| }); | |
| } | |
| }); | |
| router.put("/devices", verifyToken, async (req, res) => { | |
| try { | |
| const { id, nombre, descripcion } = req.body; | |
| console.log(req.body) | |
| // Registrar el Device | |
| await Device.update( | |
| { nombre, descripcion }, | |
| { where: { id } } | |
| ); | |
| // retornar todo los devices actualizados | |
| const devices = await Device.findAll(); | |
| res.status(201).json({ | |
| success: true, | |
| message: "Dispositivo actualizado correctamente.", | |
| devices | |
| }); | |
| } catch (error) { | |
| // Primero, verificamos si el error es una instancia de ValidationError de Sequelize | |
| if (error instanceof ValidationError) { | |
| console.log("Error de validación:", error.errors); | |
| // Extraemos los mensajes de error personalizados que definimos en el modelo | |
| const errorMessages = error.errors.map(err => err.message); | |
| return res.status(400).json({ // 400 Bad Request es el código ideal para errores de validación | |
| message: "Error de validación", | |
| success: false, | |
| errors: errorMessages // Enviamos un array con todos los mensajes de error | |
| }); | |
| } | |
| // Si es otro tipo de error (ej. error de conexión a la BD), lo manejamos como un error del servidor | |
| console.log("Ocurrió un error en la actualizacion del dispositivo: ", error); | |
| res.status(500).json({ | |
| message: "Error en el servidor.", | |
| success: false | |
| }); | |
| } | |
| }); | |
| router.delete("/devices", verifyToken, async (req, res) => { | |
| try { | |
| const { id } = req.body; | |
| // Validar que se haya enviado un id | |
| if (!id) { | |
| return res.status(400).json({ | |
| success: false, | |
| message: "El ID del dispositivo es obligatorio." | |
| }); | |
| } | |
| // Verificar si el dispositivo existe | |
| const device = await Device.findByPk(id); | |
| if (!device) { | |
| return res.status(404).json({ | |
| success: false, | |
| message: `No se encontró un dispositivo con ID ${id}.` | |
| }); | |
| } | |
| // Eliminar el dispositivo | |
| await device.destroy(); // o Device.destroy({ where: { id } }) | |
| // Obtener lista actualizada | |
| const devices = await Device.findAll(); | |
| res.status(200).json({ | |
| success: true, | |
| message: "Dispositivo eliminado correctamente.", | |
| devices | |
| }); | |
| } catch (error) { | |
| if (error instanceof ValidationError) { | |
| const errorMessages = error.errors.map(err => err.message); | |
| return res.status(400).json({ | |
| message: "Error de validación", | |
| success: false, | |
| errors: errorMessages | |
| }); | |
| } | |
| console.error("Error en la eliminación del dispositivo: ", error); | |
| res.status(500).json({ | |
| message: "Error en el servidor.", | |
| success: false | |
| }); | |
| } | |
| }); | |
| router.get("/devices", verifyToken, async (req, res) => { | |
| try { | |
| const devices = await Device.findAll(); | |
| // Enviar datos del usuario | |
| res.json( { | |
| success: true, | |
| devices | |
| }) | |
| } catch (error) { | |
| res.status(500).json({ | |
| message: "Error del Servidor", | |
| success: false, | |
| }) | |
| } | |
| }); | |
| router.post("/devices/log-location", async (req, res) => { | |
| try { | |
| //Nota aqui falta recibir el api_key y validar!! | |
| const { id_dispositivo, latitud, longitud } = req.body; | |
| if (!id_dispositivo || !latitud || !longitud) { | |
| return res.status(400).json({ message: "Faltan campos requeridos.", success: false }); | |
| } | |
| // Obtener la fecha-hora de acuerdo a la zona horaria de Lima-Perú | |
| const fecha_hora = dayjs().tz("America/Lima").toDate(); | |
| const device = await Device.findByPk(id_dispositivo); | |
| if (!device) { | |
| return res.status(404).json({ message: "Dispositivo no encontrado.", success: false }); | |
| } | |
| // Logear la ubicación | |
| await DeviceLocation.create({ id_dispositivo, latitud, longitud, fecha_hora }); | |
| // Actualizar o Registrar en DeviceStatuses | |
| await DeviceStatus.upsert({ id_dispositivo, latitud, longitud, fecha_hora }); | |
| const allStatuses = await DeviceStatus.findAll({ | |
| include: [{ model: Device }] // Obtener los datos del Device (Relación) | |
| }); | |
| // Obtener cliente-Redis | |
| const clientRedis = await getRedisClient(); | |
| // Guardar los devices status en la cache-redis | |
| await clientRedis.set("devices_status", JSON.stringify(allStatuses)); | |
| // Emitir evento por WebSocket | |
| const io = req.app.get('socketio'); | |
| io.emit("locationUpdated", { | |
| id_dispositivo, | |
| latitud, | |
| longitud, | |
| fecha_hora: formatearFechaHoraLima(fecha_hora) | |
| }); | |
| return res.status(200).json({ message: "Ubicación registrada correctamente.", success: true }); | |
| } catch (error) { | |
| console.error("Error en /log-location:", error); | |
| res.status(500).json({ message: "Error interno del servidor.", success: false }); | |
| } | |
| }); | |
| router.get("/devices/status", async (req, res) => { | |
| try { | |
| const clientRedis = await getRedisClient(); | |
| const cacheData = await clientRedis.get("devices_status"); | |
| if (cacheData) { | |
| const dataOriginal = JSON.parse(cacheData); | |
| const data = dataOriginal.map(device => ({ | |
| ...device, | |
| fecha_hora: formatearFechaHoraLima(device.fecha_hora) //Obtener la fecha a la zona horaria de America/Lima | |
| })); | |
| return res.json({ source: "Redis", data }); | |
| } else { | |
| return res.status(404).json({ message: "No hay datos en caché.", success: false }); | |
| } | |
| } catch (error) { | |
| console.error("Error en /devices-status:", error); | |
| res.status(500).json({ message: "Error interno del servidor.", success: false }); | |
| } | |
| }); | |
| export default router; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment