From e0fc4ca3d6bde61a92e58bd9be49f7a5e2556cf9 Mon Sep 17 00:00:00 2001 From: Erick Muke Date: Fri, 4 Jul 2025 03:41:21 -0600 Subject: [PATCH] feat: Creacion de la funcion para refrescar tokens --- server.js | 4 +- .../token/functions/token.controller.js | 6 ++ src/controllers/token/index.js | 1 + ...deleteUser.js => deleteUser.controller.js} | 0 .../{editUser.js => editUser.controller.js} | 0 ...sertUsers.js => insertUsers.controller.js} | 0 .../{listUsers.js => listUsers.controller.js} | 0 .../{login.js => login.controller.js} | 0 ...ginGoogle.js => loginGoogle.controller.js} | 0 ...sterUser.js => registerUser.controller.js} | 0 ...searchUser.js => searchUser.controller.js} | 0 src/controllers/users/index.js | 17 ++-- src/helpers/jwt.js | 34 +++++++- src/middleware/errorHandler.js | 11 +-- src/middleware/loadChalk.js | 5 -- src/middleware/rateLimitRequest.js | 16 ++-- src/middleware/verificarToken.js | 43 +++++----- src/router/index.js | 2 + src/router/token.routes.js | 81 +++++++++++++++++++ 19 files changed, 170 insertions(+), 50 deletions(-) create mode 100644 src/controllers/token/functions/token.controller.js create mode 100644 src/controllers/token/index.js rename src/controllers/users/functions/{deleteUser.js => deleteUser.controller.js} (100%) rename src/controllers/users/functions/{editUser.js => editUser.controller.js} (100%) rename src/controllers/users/functions/{insertUsers.js => insertUsers.controller.js} (100%) rename src/controllers/users/functions/{listUsers.js => listUsers.controller.js} (100%) rename src/controllers/users/functions/{login.js => login.controller.js} (100%) rename src/controllers/users/functions/{loginGoogle.js => loginGoogle.controller.js} (100%) rename src/controllers/users/functions/{registerUser.js => registerUser.controller.js} (100%) rename src/controllers/users/functions/{searchUser.js => searchUser.controller.js} (100%) delete mode 100644 src/middleware/loadChalk.js create mode 100644 src/router/token.routes.js diff --git a/server.js b/server.js index cacde75..9a2b812 100644 --- a/server.js +++ b/server.js @@ -11,7 +11,7 @@ import { router } from "./src/router/index.js"; // Datos del proyecto const projectInfo = { - name: "CRM Kinder Garden", + name: "CRM Kinder Garden Backend", description: "CRM para Gestión y Administración de una escuela", version: "1.0.0", authorName: "Erick Gonzalez", @@ -72,6 +72,6 @@ server.on("error", (error) => { server.listen(currentPort, () => { console.log( - `🟢 Server is listening on port localhost:${server.address().port}`, + `🟢 API funcionando correctamente, servidor corriendo en el puerto localhost:${server.address().port}`, ); }); diff --git a/src/controllers/token/functions/token.controller.js b/src/controllers/token/functions/token.controller.js new file mode 100644 index 0000000..2ca0613 --- /dev/null +++ b/src/controllers/token/functions/token.controller.js @@ -0,0 +1,6 @@ +import { refreshToken } from "../../../helpers/jwt.js"; + +export const RefreshToken = async (token) => { + const refresh = await refreshToken(token); + return refresh; +}; diff --git a/src/controllers/token/index.js b/src/controllers/token/index.js new file mode 100644 index 0000000..22b03d7 --- /dev/null +++ b/src/controllers/token/index.js @@ -0,0 +1 @@ +export * from "./functions/token.controller.js"; diff --git a/src/controllers/users/functions/deleteUser.js b/src/controllers/users/functions/deleteUser.controller.js similarity index 100% rename from src/controllers/users/functions/deleteUser.js rename to src/controllers/users/functions/deleteUser.controller.js diff --git a/src/controllers/users/functions/editUser.js b/src/controllers/users/functions/editUser.controller.js similarity index 100% rename from src/controllers/users/functions/editUser.js rename to src/controllers/users/functions/editUser.controller.js diff --git a/src/controllers/users/functions/insertUsers.js b/src/controllers/users/functions/insertUsers.controller.js similarity index 100% rename from src/controllers/users/functions/insertUsers.js rename to src/controllers/users/functions/insertUsers.controller.js diff --git a/src/controllers/users/functions/listUsers.js b/src/controllers/users/functions/listUsers.controller.js similarity index 100% rename from src/controllers/users/functions/listUsers.js rename to src/controllers/users/functions/listUsers.controller.js diff --git a/src/controllers/users/functions/login.js b/src/controllers/users/functions/login.controller.js similarity index 100% rename from src/controllers/users/functions/login.js rename to src/controllers/users/functions/login.controller.js diff --git a/src/controllers/users/functions/loginGoogle.js b/src/controllers/users/functions/loginGoogle.controller.js similarity index 100% rename from src/controllers/users/functions/loginGoogle.js rename to src/controllers/users/functions/loginGoogle.controller.js diff --git a/src/controllers/users/functions/registerUser.js b/src/controllers/users/functions/registerUser.controller.js similarity index 100% rename from src/controllers/users/functions/registerUser.js rename to src/controllers/users/functions/registerUser.controller.js diff --git a/src/controllers/users/functions/searchUser.js b/src/controllers/users/functions/searchUser.controller.js similarity index 100% rename from src/controllers/users/functions/searchUser.js rename to src/controllers/users/functions/searchUser.controller.js diff --git a/src/controllers/users/index.js b/src/controllers/users/index.js index 00a25e1..364b769 100644 --- a/src/controllers/users/index.js +++ b/src/controllers/users/index.js @@ -1,9 +1,8 @@ -export * from "./functions/deleteUser.js"; -export * from "./functions/editUser.js"; -export * from "./functions/insertUsers.js"; -export * from "./functions/listUsers.js"; -export * from "./functions/login.js"; -export * from "./functions/loginGoogle.js"; -export * from "./functions/registerUser.js"; -export * from "./functions/searchUser.js"; -export * from "./index.js"; +export * from "./functions/deleteUser.controller.js"; +export * from "./functions/editUser.controller.js"; +export * from "./functions/insertUsers.controller.js"; +export * from "./functions/listUsers.controller.js"; +export * from "./functions/login.controller.js"; +export * from "./functions/loginGoogle.controller.js"; +export * from "./functions/registerUser.controller.js"; +export * from "./functions/searchUser.controller.js"; diff --git a/src/helpers/jwt.js b/src/helpers/jwt.js index 760d62f..4271afb 100644 --- a/src/helpers/jwt.js +++ b/src/helpers/jwt.js @@ -1,4 +1,4 @@ -import { addHour } from "@formkit/tempo"; +import { addDay, addHour } from "@formkit/tempo"; import dotenv from "dotenv"; import jwt from "jsonwebtoken"; @@ -28,3 +28,35 @@ export const createToken = (user) => { }; return jwt.sign(payload, secret); }; + +export const refreshToken = (token) => { + const decoded = jwt.verify(token, secret); + + if (decoded.accountStatus === "Inactivo") { + throw { + statusCode: 403, + message: "Cuenta inactiva, porfavor contacta al administrador.", + code: "ACCOUNT_INACTIVE", + details: + "La cuenta esta desactivada, no se puede generar un nuevo token.", + }; + } + + const expirationDate = addDay(new Date(), 20); + + const expirationTime = Math.floor(expirationDate.getTime() / 1000); + + const payload = { + id: decoded.id, + nameUser: decoded.nameUser, + email: decoded.email, + profilePicture: decoded.profilePicture, + role: decoded.role, + accountType: decoded.accountType, + lastLogin: decoded.lastLogin, + accountStatus: decoded.accountStatus, + iat: Math.floor(Date.now() / 1000), + exp: expirationTime, + }; + return jwt.sign(payload, secret); +}; diff --git a/src/middleware/errorHandler.js b/src/middleware/errorHandler.js index 70e1058..8cdb7f1 100644 --- a/src/middleware/errorHandler.js +++ b/src/middleware/errorHandler.js @@ -1,11 +1,11 @@ import crypto from "node:crypto"; -export const errorHandler = (err, req, res, next) => { +export const errorHandler = (err, request, response, next) => { const status = err.statusCode || 500; const timestamp = new Date().toISOString(); const errorId = crypto.randomUUID(); - - res.status(status).json({ + console.log(request); + response.status(status).json({ success: false, error: { message: err.message || "Se produjo un error inesperado.", @@ -16,8 +16,9 @@ export const errorHandler = (err, req, res, next) => { timestamp, errorId, stack: process.env.NODE_ENV === "production" ? undefined : err.stack, - path: req.originalUrl, - method: req.method, + path: request.originalUrl, + method: request.method, + query: request.query, }, }); }; diff --git a/src/middleware/loadChalk.js b/src/middleware/loadChalk.js deleted file mode 100644 index 01d2cab..0000000 --- a/src/middleware/loadChalk.js +++ /dev/null @@ -1,5 +0,0 @@ -// Función para cargar chalk dinámicamente -export const loadChalk = async () => { - const chalk = await import("chalk"); - return chalk.default; -}; diff --git a/src/middleware/rateLimitRequest.js b/src/middleware/rateLimitRequest.js index 5bc212d..03c84aa 100644 --- a/src/middleware/rateLimitRequest.js +++ b/src/middleware/rateLimitRequest.js @@ -1,17 +1,17 @@ +import { request, response } from "express"; import { rateLimit } from "express-rate-limit"; -import { methodTooManyRequests } from "../server/serverMethods.js"; - export const rateLimitRequest = (time, limit, messageRequest) => { return rateLimit({ windowMs: time * 60 * 1000, limit: limit, - handler: (req, res) => { - methodTooManyRequests( - req, - res, - messageRequest + `inténtelo nuevamente después de ${time} minuto(s)`, - ); + handler: (request, response, next) => { + next({ + statusCode: 429, + message: `${messageRequest}. Inténtelo nuevamente después de ${time} minuto(s).`, + code: "TOO_MANY_REQUESTS", + details: "Has excedido el número máximo de solicitudes permitidas.", + }); }, standardHeaders: true, legacyHeaders: false, diff --git a/src/middleware/verificarToken.js b/src/middleware/verificarToken.js index 4ac3c0f..7314698 100644 --- a/src/middleware/verificarToken.js +++ b/src/middleware/verificarToken.js @@ -1,40 +1,43 @@ import dotenv from "dotenv"; import jwt from "jsonwebtoken"; -import { methodUnauthorized } from "../server/serverMethods.js"; - dotenv.config(); -export const verificarToken = (req, res, next) => { - const token = req.header("Authorization"); +export const verificarToken = (request, response, next) => { + const token = request.header("Authorization"); if (!token) { - return methodUnauthorized( - req, - res, - "Acceso no autorizado. Token no proporcionado.", - ); + throw { + statusCode: 401, + message: "Acceso no autorizado, token no proporcionado", + code: "TOKEN_NOT_FOUND", + details: + "El token no se mando o no esta autorizado para realizar esta peticion", + }; } const bearerToken = token.split(" ")[1]; if (!bearerToken) { - return methodUnauthorized( - req, - res, - "Acceso no autorizado. Token no proporcionado.", - ); + throw { + statusCode: 401, + message: "Acceso no autorizado, token no proporcionado", + code: "TOKEN_NOT_FOUND", + details: + "El token no se mando o no esta autorizado para realizar esta peticion", + }; } try { const secretKey = process.env.JWT_SECRET; const decoded = jwt.verify(bearerToken, secretKey); - req.usuario = decoded; + request.usuario = decoded; next(); } catch (error) { - return methodUnauthorized( - req, - res, - `Acceso no autorizado. Token inválido ${error}`, - ); + throw { + statusCode: 401, + message: "Acceso no autorizado: token inválido", + code: "TOKEN_INVALID", + details: error.message || "El token no pudo ser verificado", + }; } }; diff --git a/src/router/index.js b/src/router/index.js index 87e28cd..4071923 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -7,6 +7,7 @@ import { apiGoogle } from "./google.route.js"; import { apiMaestros } from "./maestrosRouter.js"; import { apiPadres } from "./padresRouter.js"; import { apiEstudiantes } from "./studentsRouter.js"; +import { apiToken } from "./token.routes.js"; import { apiUsuarios } from "./users.routes.js"; const router = express.Router(); @@ -23,5 +24,6 @@ const router = express.Router(); // ); router.use("/api/v1/users", apiUsuarios, apiGoogle); +router.use("/api/v1/token", apiToken); export { router }; diff --git a/src/router/token.routes.js b/src/router/token.routes.js new file mode 100644 index 0000000..0d68c4b --- /dev/null +++ b/src/router/token.routes.js @@ -0,0 +1,81 @@ +import express from "express"; + +import { RefreshToken } from "../controllers/token/index.js"; +import { verificarToken } from "../middleware/verificarToken.js"; +import { methodOK } from "../server/serverMethods.js"; + +const apiToken = express.Router(); + +/** + * @swagger + * /token/refresh: + * post: + * summary: Refresca el token de sesión + * description: Genera un nuevo token de acceso si el token anterior es válido y aún no ha expirado. Se recomienda usar este endpoint desde el frontend cuando el token está por expirar. + * tags: + * - Token + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - token + * properties: + * token: + * type: string + * description: Token de acceso actual que está por expirar. + * example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + * responses: + * 200: + * description: Token actualizado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: true + * message: + * type: string + * example: Consulta realizada correctamente + * data: + * type: string + * description: Nuevo token generado + * example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + * metadata: + * type: object + * properties: + * timestamp: + * type: string + * format: date-time + * example: 2025-07-04T08:06:58.280Z + * requestId: + * type: string + * format: uuid + * example: baeea511-fa16-437a-a122-b99a35f76bd8 + * dataCount: + * type: string + * example: "1" + * 403: + * description: El usuario está inactivo. Por favor contacte al administrador. + * 500: + * description: Error interno del servidor. + */ + +//POST /api/token/refresh +apiToken.post("/refresh", verificarToken, async (request, response, next) => { + try { + const { token } = request.body; + console.log(token); + const newToken = await RefreshToken(token); + console.log(newToken); + methodOK(request, response, newToken); + } catch (error) { + next(error); + } +}); + +export { apiToken };