├── helpers ├── cache.js ├── index.js ├── db-validator.js ├── obtener-usuario.js ├── calcular-distancia.js ├── google-verify-token.js ├── jwt.js ├── generar-aleatorio.js ├── subir-archivo.js └── enviar-notificacion.js ├── ejemplo.env ├── assets └── no-image.jpg ├── prueba ├── img │ ├── logo.png │ └── descarga.png └── public │ ├── descarga.png │ ├── index.html │ └── pdfTemplate.js ├── index.js ├── routes ├── buscar.js ├── comentarios.js ├── documents.js ├── mensajes.js ├── denuncias.js ├── notificaciones.js ├── ubicaciones.js ├── auth.js ├── publicaciones.js ├── usuarios.js ├── uploads.js ├── reportes.js └── salas.js ├── controllers ├── documents.js ├── denuncias.js ├── buscar.js ├── mensajes.js ├── ubicaciones.js ├── notificaciones.js ├── socket.js ├── auth.js ├── uploads.js ├── comentarios.js ├── usuarios.js ├── publicaciones.js ├── salas.js └── reportes.js ├── .gitignore ├── database └── config.js ├── middlewares ├── validar-campos.js ├── validar-archivo.js ├── validar-jwt.js └── express-validator.js ├── models ├── index.js ├── mensaje.js ├── denuncia.js ├── comentario.js ├── ubicacion.js ├── sala.js ├── notificacion.js ├── publicacion.js └── usuario.js ├── tests ├── usuarios.spec.js ├── notificaciones.spec.js ├── auth.spec.js ├── comentarios.spec.js ├── mensajes.spec.js ├── publicaciones.spec.js ├── denuncias.spec.js ├── salas.spec.js └── ubicaciones.spec.js ├── package.json ├── app.js ├── sockets └── socket.js └── README.md /helpers/cache.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /helpers/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ejemplo.env: -------------------------------------------------------------------------------- 1 | PORT= 2 | DB_CNN= 3 | JWT_KEY= 4 | TOKEN_NOTIFICAIONES= -------------------------------------------------------------------------------- /assets/no-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinici0/rest-server-movil-web/HEAD/assets/no-image.jpg -------------------------------------------------------------------------------- /prueba/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinici0/rest-server-movil-web/HEAD/prueba/img/logo.png -------------------------------------------------------------------------------- /prueba/img/descarga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinici0/rest-server-movil-web/HEAD/prueba/img/descarga.png -------------------------------------------------------------------------------- /prueba/public/descarga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinici0/rest-server-movil-web/HEAD/prueba/public/descarga.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | const { app,server } = require("./app"); 3 | 4 | server.listen(process.env.PORT, (err) => { 5 | if (err) throw new Error(err); 6 | console.log("Servidor corriendo en puerto", process.env.PORT); 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /routes/buscar.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { buscar } = require("../controllers/buscar"); 3 | 4 | const router = Router(); 5 | 6 | router.get("/:coleccion/:termino", buscar); 7 | 8 | module.exports = router; 9 | -------------------------------------------------------------------------------- /controllers/documents.js: -------------------------------------------------------------------------------- 1 | const pdf = require('html-pdf'); 2 | const pdfTemplate = require('../prueba/public/pdfTemplate'); 3 | const path = require("path"); 4 | 5 | const createDocument = async (req, res) => { 6 | res.send('PDF'); 7 | }; 8 | 9 | const getDocument = async (req, res) => { 10 | res.sendFile(path.join(__dirname, 'datos.pdf')); 11 | }; 12 | 13 | module.exports = { createDocument, getDocument }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | *.pid.lock 10 | 11 | # Dependencies 12 | node_modules/ 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Output of 'npm run build' 18 | build/ 19 | 20 | # Environment variables 21 | .env 22 | 23 | # IDE files 24 | .vscode/ 25 | .idea/ 26 | 27 | # OS generated files 28 | .DS_Store 29 | Thumbs.db 30 | 31 | uploads/ -------------------------------------------------------------------------------- /helpers/db-validator.js: -------------------------------------------------------------------------------- 1 | const { Usuario} = require('../models'); 2 | 3 | const coleccionesPermitidas = ( coleccion = '', colecciones = []) => { 4 | 5 | const incluida = colecciones.includes( coleccion ); 6 | if ( !incluida ) { 7 | throw new Error(`La colección ${ coleccion } no es permitida, ${ colecciones }`); 8 | } 9 | return true; 10 | } 11 | 12 | 13 | module.exports = { 14 | coleccionesPermitidas 15 | } -------------------------------------------------------------------------------- /database/config.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const dbConnection = async () => { 4 | try { 5 | await mongoose.connect(process.env.DB_CNN); 6 | 7 | console.log("DB Online"); 8 | } catch (error) { 9 | console.log(error); 10 | throw new Error("Error en la base de datos - Hable con el admin"); 11 | } 12 | }; 13 | 14 | module.exports = { 15 | dbConnection, 16 | }; 17 | 18 | 19 | -------------------------------------------------------------------------------- /middlewares/validar-campos.js: -------------------------------------------------------------------------------- 1 | const { validationResult } = require('express-validator'); 2 | 3 | 4 | const validarCampos = (req, res, next ) => { 5 | 6 | const errores = validationResult( req ); 7 | 8 | if( !errores.isEmpty() ) { 9 | return res.status(400).json({ 10 | ok: false, 11 | errors: errores.mapped() 12 | }); 13 | } 14 | 15 | next(); 16 | } 17 | 18 | 19 | module.exports = { 20 | validarCampos 21 | } -------------------------------------------------------------------------------- /routes/comentarios.js: -------------------------------------------------------------------------------- 1 | const { Router } = require('express'); 2 | const { validarJWT } = require('../middlewares/validar-jwt'); 3 | const { getComentariosByPublicacion, createComentario, toggleLikeComentario } = require('../controllers/comentarios'); 4 | 5 | const router = Router(); 6 | 7 | router.get("/:publicacionId", validarJWT, getComentariosByPublicacion); 8 | 9 | router.post("/", validarJWT, createComentario); 10 | 11 | router.put("/like/:id", validarJWT, toggleLikeComentario); 12 | 13 | module.exports = router; -------------------------------------------------------------------------------- /helpers/obtener-usuario.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const Sala = require("../models/"); 4 | 5 | const obtenerUsuariosSalaHelper = async (salaId) => { 6 | try { 7 | const usuariosEnSala = await Sala.findById(salaId).populate({ 8 | path: "usuarios", 9 | match: { online: false }, 10 | }); 11 | return usuariosEnSala.usuarios; 12 | } catch (error) { 13 | console.log(error); 14 | return []; 15 | } 16 | }; 17 | 18 | module.exports = { 19 | obtenerUsuariosSalaHelper 20 | } -------------------------------------------------------------------------------- /routes/documents.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { check } = require("express-validator"); 3 | const { validarJWT } = require("../middlewares/validar-jwt"); 4 | const { validarArchivoSubir } = require("../middlewares/validar-archivo"); 5 | const pdfTemplate = require('../prueba/public/pdfTemplate'); 6 | const router = Router(); 7 | 8 | const {createDocument,getDocument} = require('../controllers/documents'); 9 | 10 | router.post( "/", createDocument ); 11 | 12 | router.get( "/", getDocument ); 13 | 14 | module.exports = router; -------------------------------------------------------------------------------- /models/index.js: -------------------------------------------------------------------------------- 1 | const Comentario = require("./comentario"); 2 | const Publicacion = require("./publicacion"); 3 | const Usuario = require("./usuario"); 4 | const Ubicacion = require("./ubicacion"); 5 | const Mensaje = require("./mensaje"); 6 | const Sala = require("./sala"); 7 | const Notificacion = require("./notificacion"); 8 | const Denuncia = require("./denuncia"); 9 | 10 | module.exports = { 11 | Comentario, 12 | Publicacion, 13 | Usuario, 14 | Ubicacion, 15 | Mensaje, 16 | Sala, 17 | Notificacion, 18 | Denuncia 19 | } -------------------------------------------------------------------------------- /middlewares/validar-archivo.js: -------------------------------------------------------------------------------- 1 | const { response } = require("express") 2 | 3 | 4 | const validarArchivoSubir = (req, res = response, next ) => { 5 | 6 | //obtenr nomber del body 7 | 8 | console.log(req.body); 9 | 10 | if (!req.files || Object.keys(req.files).length === 0 || !req.files.archivo ) { 11 | return res.status(400).json({ 12 | msg: 'No hay archivos que subir - validarArchivoSubir' 13 | }); 14 | } 15 | 16 | next(); 17 | 18 | } 19 | 20 | 21 | module.exports = { 22 | validarArchivoSubir 23 | } -------------------------------------------------------------------------------- /models/mensaje.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const MensajeSchema = Schema( 4 | { 5 | mensaje: { 6 | type: String, 7 | required: true, 8 | }, 9 | usuario: { 10 | type: Schema.Types.ObjectId, 11 | ref: "Usuario", 12 | required: true, 13 | }, 14 | }, 15 | { 16 | timestamps: true, 17 | } 18 | ); 19 | 20 | MensajeSchema.method("toJSON", function () { 21 | const { __v, _id, ...object } = this.toObject(); 22 | return object; 23 | }); 24 | 25 | module.exports = model("Mensaje", MensajeSchema); -------------------------------------------------------------------------------- /routes/mensajes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Path: /api/mensajes 3 | */ 4 | const { Router } = require("express"); 5 | const { validarJWT } = require("../middlewares/validar-jwt"); 6 | 7 | const { 8 | getAllMessages, 9 | getMensajeByUser, 10 | getMensajeByRoom, 11 | } = require("../controllers/mensajes"); 12 | 13 | const router = Router(); 14 | 15 | router.get("/", validarJWT, getAllMessages); 16 | 17 | router.get("/get-mensaje-by-user", validarJWT, getMensajeByUser); 18 | 19 | router.get("/get-mensaje-by-room/:salaId", validarJWT, getMensajeByRoom); 20 | 21 | module.exports = router; 22 | -------------------------------------------------------------------------------- /routes/denuncias.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { check } = require("express-validator"); 3 | const { validarJWT } = require("../middlewares/validar-jwt"); 4 | 5 | const router = Router(); 6 | 7 | const { validarCampos } = require("../middlewares/validar-campos"); 8 | const { guardarDenuncia } = require("../controllers/denuncias"); 9 | 10 | router.post( "/", [ 11 | check("publicacionId", "El id de la publicación es obligatorio").not().isEmpty(), 12 | check("motivo", "El motivo es obligatorio").not().isEmpty(), 13 | validarCampos, 14 | validarJWT, 15 | ], guardarDenuncia ); 16 | 17 | module.exports = router; -------------------------------------------------------------------------------- /routes/notificaciones.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { check } = require("express-validator"); 3 | const { validarJWT } = require("../middlewares/validar-jwt"); 4 | const { obtenerNotificacionesUsuario, marcarNotificacionComoLeida, deleteAllNotifications, deleteNotificationById } = require("../controllers/notificaciones"); 5 | 6 | const router = Router(); 7 | 8 | router.get("/", validarJWT, obtenerNotificacionesUsuario); 9 | 10 | router.put("/:id", validarJWT, marcarNotificacionComoLeida); 11 | 12 | router.delete("/", validarJWT, deleteAllNotifications); 13 | 14 | router.delete("/:id", validarJWT, deleteNotificationById); 15 | 16 | module.exports = router; -------------------------------------------------------------------------------- /middlewares/validar-jwt.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | 3 | const validarJWT = (req, res, next) => { 4 | // Leer token 5 | const token = req.header("x-token"); 6 | 7 | if (!token) { 8 | return res.status(401).json({ 9 | ok: false, 10 | msg: "No hay token en la petición", 11 | }); 12 | } 13 | 14 | try { 15 | const { uid } = jwt.verify(token, process.env.JWT_KEY); 16 | console.log(uid); 17 | req.uid = uid; 18 | 19 | next(); 20 | } catch (error) { 21 | return res.status(401).json({ 22 | ok: false, 23 | msg: "Token no válido", 24 | }); 25 | } 26 | }; 27 | 28 | module.exports = { 29 | validarJWT, 30 | }; 31 | -------------------------------------------------------------------------------- /helpers/calcular-distancia.js: -------------------------------------------------------------------------------- 1 | // calcular la distancia entre dos puntos de latitud y longitud 2 | const calcularDistancia = (lat1, lon1, lat2, lon2) => { 3 | const R = 6371; // Radio de la Tierra en kilómetros 4 | 5 | const dLat = degToRad(lat2 - lat1); 6 | const dLon = degToRad(lon2 - lon1); 7 | 8 | const a = 9 | Math.sin(dLat / 2) * Math.sin(dLat / 2) + 10 | Math.cos(degToRad(lat1)) * 11 | Math.cos(degToRad(lat2)) * 12 | Math.sin(dLon / 2) * 13 | Math.sin(dLon / 2); 14 | 15 | const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 16 | 17 | const distancia = R * c; 18 | 19 | return distancia; 20 | }; 21 | 22 | // Función para convertir grados a radianes 23 | function degToRad(deg) { 24 | return deg * (Math.PI / 180); 25 | } 26 | 27 | module.exports = { 28 | calcularDistancia, 29 | }; 30 | -------------------------------------------------------------------------------- /models/denuncia.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const DenunciaSchema = Schema( 4 | { 5 | publicacion: { 6 | type: Schema.Types.ObjectId, 7 | ref: 'Publicacion', 8 | required: true, 9 | }, 10 | motivo: { 11 | type: String, 12 | required: true, 13 | }, 14 | detalles: { 15 | type: String, 16 | }, 17 | denunciante: { 18 | type: Schema.Types.ObjectId, 19 | ref: 'Usuario', 20 | required: true, 21 | }, 22 | fecha: { 23 | type: Date, 24 | default: Date.now, 25 | }, 26 | }, 27 | ); 28 | 29 | DenunciaSchema.method('toJSON', function () { 30 | const { __v, _id, ...object } = this.toObject(); 31 | object.uid = _id; 32 | return object; 33 | }); 34 | 35 | module.exports = model('Denuncia', DenunciaSchema); -------------------------------------------------------------------------------- /helpers/google-verify-token.js: -------------------------------------------------------------------------------- 1 | const { OAuth2Client } = require("google-auth-library"); 2 | 3 | const CLIENT_ID = 4 | "538224676274-66q03vper7rorc34ndtnb3g7sllclt8n.apps.googleusercontent.com"; 5 | 6 | const client = new OAuth2Client(CLIENT_ID); 7 | 8 | const validarGoogleIdToken = async (token) => { 9 | try { 10 | const ticket = await client.verifyIdToken({ 11 | idToken: token, 12 | audience: [ 13 | CLIENT_ID, 14 | "538224676274-6dm96dvsaknf5gf1uh7sp672demu4at1.apps.googleusercontent.com", 15 | ], 16 | }); 17 | 18 | const payload = ticket.getPayload(); 19 | 20 | return { 21 | name: payload["name"], 22 | picture: payload["picture"], 23 | email: payload["email"], 24 | }; 25 | } catch (error) { 26 | return null; 27 | } 28 | }; 29 | 30 | module.exports = { 31 | validarGoogleIdToken, 32 | }; 33 | -------------------------------------------------------------------------------- /prueba/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SocketServer 7 | 8 | 9 |

Acceso denegado

10 | 11 | 12 | 13 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /helpers/jwt.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | 3 | const generarJWT = (uid) => { 4 | return new Promise((resolve, reject) => { 5 | const payload = { uid }; 6 | 7 | jwt.sign( 8 | payload, 9 | process.env.JWT_KEY, 10 | { 11 | expiresIn: "1y", 12 | }, 13 | (err, token) => { 14 | if (err) { 15 | // no se pudo crear el token 16 | reject("No se pudo generar el JWT"); 17 | } else { 18 | // TOKEN! 19 | resolve(token); 20 | } 21 | } 22 | ); 23 | }); 24 | }; 25 | 26 | const comprobarJWT = (token = "") => { 27 | try { 28 | const { uid } = jwt.verify(token, process.env.JWT_KEY); 29 | 30 | return [true, uid]; 31 | } catch (error) { 32 | return [false, null]; 33 | } 34 | }; 35 | 36 | module.exports = { 37 | generarJWT, 38 | comprobarJWT, 39 | }; 40 | -------------------------------------------------------------------------------- /tests/usuarios.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Usuarios Router", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; // Adjust this to retrieve the token properly 6 | 7 | test("Should delete a phone number", async () => { 8 | const response = await request(app) 9 | .delete("/api/usuarios/delete-telefono") 10 | .set("x-token", token) 11 | .expect(400); 12 | 13 | // Verify the response, e.g., response.body 14 | }); 15 | 16 | test("Should get users", async () => { 17 | const response = await request(app) 18 | .get("/api/usuarios") 19 | .set("x-token", token) 20 | .expect(200); 21 | 22 | // Verify the response, e.g., response.body 23 | }); 24 | 25 | // Add more tests for other endpoints as needed 26 | }); 27 | -------------------------------------------------------------------------------- /routes/ubicaciones.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { check } = require("express-validator"); 3 | const { validarJWT } = require("../middlewares/validar-jwt"); 4 | const { validarCampos } = require("../middlewares/validar-campos"); 5 | const { 6 | obtenerUbicaciones, 7 | crearUbicacion, 8 | obtenerUbicacionesPorUsuario, 9 | agregarUbicacion, 10 | eliminarUbicacion, 11 | } = require("../controllers/ubicaciones"); 12 | const { validacionesUbicacion } = require("../middlewares/express-validator"); 13 | 14 | const router = Router(); 15 | 16 | router.get("/", obtenerUbicaciones); 17 | 18 | router.post("/", [validacionesUbicacion, validarCampos], crearUbicacion); 19 | 20 | router.get("/", validarJWT, obtenerUbicacionesPorUsuario); 21 | 22 | router.put("/:id", validarJWT, agregarUbicacion); 23 | 24 | router.delete("/:id", validarJWT, eliminarUbicacion); 25 | module.exports = router; 26 | -------------------------------------------------------------------------------- /tests/notificaciones.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Notificaciones Router", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; // Adjust this to retrieve the token properly 6 | 7 | test("Should get user's notifications", async () => { 8 | const response = await request(app) 9 | .get("/api/notificacion") 10 | .set("x-token", token) 11 | .expect(200); 12 | 13 | // Verify the response, e.g., response.body 14 | }); 15 | 16 | test("Should mark notification as read", async () => { 17 | const notificationId = "64bfef6c2f35690cfee892ff"; // Replace with an actual notification ID 18 | const response = await request(app) 19 | .put(`/api/notificacion/${notificationId}`) 20 | .set("x-token", token) 21 | .expect(404); 22 | 23 | // Verify the response, e.g., response.body 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/auth.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("GET api/auth", () => { 5 | const user = { 6 | nombre: "vinicio", 7 | email: "testadadaawdwdawdadw1@gmail.com", 8 | password: "123456", 9 | }; 10 | 11 | test("should return 200 for user login", async () => { 12 | await request(app) 13 | .post("/api/login") 14 | .send({ 15 | email: user.email, 16 | password: user.password 17 | }) 18 | .expect(200); 19 | }); 20 | 21 | test("should return 400", async () => { 22 | await request(app) 23 | .post("/api/login/new") 24 | .send(user) 25 | .expect(400); 26 | }); 27 | 28 | test("should return 200 for token renewal", async () => { 29 | const token = process.env.TOKEN_TEST_AUTH; 30 | await request(app) 31 | .get("/api/login/renew") 32 | .set("x-token", token) 33 | .expect(200); 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /models/comentario.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const ComentarioSchema = Schema( 4 | { 5 | contenido: { 6 | type: String, 7 | required: true, 8 | }, 9 | usuario: { 10 | type: Schema.Types.ObjectId, 11 | ref: "Usuario", 12 | required: true, 13 | }, 14 | publicacion: { 15 | type: Schema.Types.ObjectId, 16 | ref: "Publicacion", 17 | required: true, 18 | }, 19 | likes: [ 20 | { 21 | type: Schema.Types.ObjectId, 22 | ref: "Usuario", 23 | }, 24 | ], 25 | estado: { 26 | type: String, 27 | enum: ["publicado", "borrador"], 28 | required: true, 29 | }, 30 | }, 31 | { 32 | timestamps: true, 33 | } 34 | ); 35 | 36 | ComentarioSchema.method("toJSON", function () { 37 | const { __v, _id, ...object } = this.toObject(); 38 | object.uid = _id; 39 | return object; 40 | }); 41 | 42 | module.exports = model("Comentario", ComentarioSchema); 43 | -------------------------------------------------------------------------------- /models/ubicacion.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const UbicacionSchema = Schema( 4 | { 5 | latitud: { 6 | type: Number, 7 | required: true, 8 | }, 9 | longitud: { 10 | type: Number, 11 | required: true, 12 | }, 13 | barrio: { 14 | type: String, 15 | required: true, 16 | }, 17 | parroquia: { 18 | type: String, 19 | }, 20 | ciudad: { 21 | type: String, 22 | required: true, 23 | }, 24 | pais: { 25 | type: String, 26 | required: true, 27 | }, 28 | referencia : { 29 | type: String, 30 | }, 31 | estado: { 32 | type: Boolean, 33 | default: true, 34 | }, 35 | }, 36 | { 37 | timestamps: true, 38 | } 39 | ); 40 | 41 | UbicacionSchema.method("toJSON", function () { 42 | const { __v, _id, ...object } = this.toObject(); 43 | object.uid = _id; 44 | return object; 45 | }); 46 | 47 | module.exports = model("Ubicacion", UbicacionSchema); 48 | -------------------------------------------------------------------------------- /models/sala.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const SalaSchema = Schema( 4 | { 5 | nombre: { 6 | type: String, 7 | required: true, 8 | }, 9 | codigo: { 10 | type: String, 11 | required: true, 12 | unique: true, 13 | }, 14 | color: { 15 | type: String, 16 | required: true, 17 | }, 18 | isLike : { 19 | type: Boolean, 20 | default: false 21 | }, 22 | propietario: { 23 | type: Schema.Types.ObjectId, 24 | ref: "Usuario", 25 | }, 26 | isActivo: { 27 | type: Boolean, 28 | default: true, 29 | }, 30 | usuarios: [{ type: Schema.Types.ObjectId, ref: "Usuario" }], 31 | mensajes: [{ type: Schema.Types.ObjectId, ref: "Mensaje" }], 32 | }, 33 | 34 | { 35 | timestamps: true, 36 | } 37 | ); 38 | 39 | SalaSchema.method("toJSON", function () { 40 | const { __v, _id,...object } = this.toObject(); 41 | object.uid = _id; 42 | return object; 43 | }); 44 | 45 | module.exports = model("Sala", SalaSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-band_names_server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon index.js", 9 | "start:dev": "nodemon index.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "axios": "^1.4.0", 15 | "bcryptjs": "^2.4.3", 16 | "cors": "^2.8.5", 17 | "dotenv": "^16.0.3", 18 | "exceljs": "^4.3.0", 19 | "express": "^4.18.2", 20 | "express-fileupload": "^1.4.0", 21 | "express-validator": "^6.6.1", 22 | "google-auth-library": "^8.8.0", 23 | "html-pdf": "^3.0.1", 24 | "jsonwebtoken": "^8.5.1", 25 | "lodash": "^4.17.21", 26 | "moment": "^2.29.4", 27 | "mongoose": "^6.6.5", 28 | "pdfkit": "^0.13.0", 29 | "pdfmake": "^0.2.7", 30 | "pdfmake-unicode": "^0.0.1", 31 | "socket.io": "^4.7.1", 32 | "uuid": "^9.0.0" 33 | }, 34 | "devDependencies": { 35 | "jest": "^29.6.2", 36 | "supertest": "^6.3.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /helpers/generar-aleatorio.js: -------------------------------------------------------------------------------- 1 | const generarCodigoUnico = () => { 2 | const codigoNumerico = Math.floor(Math.random() * 999999); // Genera un número aleatorio entre 0 y 999999 3 | const codigo = codigoNumerico.toString().padStart(6, "0"); // Convierte el número en una cadena de 6 caracteres rellenada con ceros a la izquierda si es necesario 4 | const codigoConGuion = `${codigo.substr(0, 3)}-${codigo.substr(3)}`; // Agrega el guión al medio del código 5 | return codigoConGuion; 6 | }; 7 | 8 | const generarColorAleatorio = () => { 9 | const colorBase = "6165FA"; // El color principal de la aplicación en formato hexadecimal (sin el prefijo 0xFF) 10 | 11 | let color = "#"; 12 | 13 | // Generar dos valores hexadecimales aleatorios para cada uno de los tres componentes de color (rojo, verde y azul) 14 | for (let i = 0; i < 3; i++) { 15 | const componente = Math.floor(Math.random() * 256) 16 | .toString(16) 17 | .padStart(2, "0"); 18 | color += componente; 19 | } 20 | 21 | return color; 22 | }; 23 | 24 | module.exports = { 25 | generarCodigoUnico, 26 | generarColorAleatorio, 27 | }; 28 | -------------------------------------------------------------------------------- /tests/comentarios.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Comments API Testing api/comentarios", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; 6 | 7 | const commentData = { 8 | contenido:"sala1", 9 | publicacionId:"64c2dc5184c4d0f0c9062ef0" 10 | }; 11 | 12 | test("Should retrieve comments by publication ID", async () => { 13 | await request(app) 14 | .get("/api/comentarios/64d17088e27ea53f578d106c") 15 | .set("x-token", token) 16 | .expect(200); 17 | }); 18 | 19 | test("Should successfully create a new comment", async () => { 20 | const response = await request(app) 21 | .post("/api/comentarios") 22 | .set("x-token", token) 23 | .send(commentData) 24 | .expect(201); 25 | }); 26 | 27 | test("Should successfully like a comment", async () => { 28 | await request(app) 29 | .put("/api/comentarios/like/64d17088e27ea53f578d106c") 30 | .set("x-token", token) 31 | .expect(200); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/mensajes.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Mensajes Router", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; 6 | const salaId = "64c1ca50a9e34350e4b40c27"; 7 | test("Should get all messages", async () => { 8 | const response = await request(app) 9 | .get(`/api/mensajes/get-mensajes/${salaId}`) 10 | .set("x-token", token) 11 | .expect(404); 12 | 13 | // Verify the response, e.g., response.body 14 | }); 15 | 16 | test("Should get messages by user", async () => { 17 | const response = await request(app) 18 | .get("/api/mensajes/get-mensaje-by-user") 19 | .set("x-token", token) 20 | .expect(200); 21 | 22 | // Verify the response, e.g., response.body 23 | }); 24 | 25 | test("Should get messages by room", async () => { 26 | const response = await request(app) 27 | .get(`/api/mensajes/get-mensaje-by-room/${salaId}`) 28 | .set("x-token", token) 29 | .expect(200); 30 | 31 | // Verify the response, e.g., response.body 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/publicaciones.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Publicaciones Router", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; 6 | 7 | test("Should get user's publications", async () => { 8 | const response = await request(app) 9 | .get("/api/publicacion") 10 | .set("x-token", token) 11 | .expect(200); 12 | 13 | // Verify the response, e.g., response.body 14 | }); 15 | 16 | test("Should get nearby publications", async () => { 17 | const response = await request(app) 18 | .get("/api/publicacion/cercanas") 19 | .set("x-token", token) 20 | .expect(200); 21 | 22 | // Verify the response, e.g., response.body 23 | }); 24 | 25 | 26 | test("Should get nearby publications", async () => { 27 | const response = await request(app) 28 | .get("/api/publicacion/cercanas") 29 | .set("x-token", token) 30 | .expect(200); 31 | 32 | // Verify the response, e.g., response.body 33 | }); 34 | 35 | // Add more tests for other endpoints as needed 36 | }); 37 | -------------------------------------------------------------------------------- /tests/denuncias.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Denuncias Router", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; 6 | 7 | const denunciaData = { 8 | publicacionId: "64c2d5ec84c4d0f0c9062d14", 9 | motivo: "Inappropriate content", 10 | }; 11 | 12 | test("Should successfully submit a denuncia", async () => { 13 | const denunciaExistente = true; 14 | 15 | const response = await request(app) 16 | .post("/api/denuncias") 17 | .set("x-token", token) 18 | .send(denunciaData); 19 | 20 | expect(response.body.ok).toBe(false); 21 | expect(response.body.msg).toBe( 22 | "Ya has denunciado esta publicación anteriormente" 23 | ); 24 | }); 25 | 26 | test("Should return 'not found' error for non-existing publication", async () => { 27 | const publicacionId = "non_existing_id"; 28 | const denunciaData = { 29 | publicacionId, 30 | motivo: "Inappropriate content", 31 | }; 32 | 33 | const response = await request(app) 34 | .post("/api/denuncias") 35 | .set("x-token", token) 36 | .send(denunciaData) 37 | .expect(500); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /middlewares/express-validator.js: -------------------------------------------------------------------------------- 1 | const { check } = require("express-validator"); 2 | const validacionesCrearPublicacion = [ 3 | check("barrio", "El barrio es obligatorio").not().isEmpty(), 4 | check("ciudad", "La ciudad es obligatoria").not().isEmpty(), 5 | check("color", "El color es obligatorio").not().isEmpty(), 6 | check("contenido", "El contenido es obligatorio").not().isEmpty(), 7 | check("isPublic", "El estado de publicación es obligatorio").not().isEmpty(), 8 | check("latitud", "La latitud es obligatoria").not().isEmpty(), 9 | check("longitud", "La longitud es obligatoria").not().isEmpty(), 10 | check("titulo", "El título es obligatorio").not().isEmpty(), 11 | check("nombreUsuario", "El nombre de usuario es obligatorio").not().isEmpty(), 12 | ]; 13 | 14 | const validacionesUbicacion = [ 15 | check("barrio", "El barrio es obligatorio").not().isEmpty(), 16 | check("ciudad", "La ciudad es obligatoria").not().isEmpty(), 17 | check("latitud", "La latitud es obligatoria").not().isEmpty(), 18 | check("longitud", "La longitud es obligatoria").not().isEmpty(), 19 | check("pais", "El país es obligatorio").not().isEmpty(), 20 | ]; 21 | 22 | 23 | module.exports = { 24 | validacionesCrearPublicacion, 25 | validacionesUbicacion, 26 | }; 27 | -------------------------------------------------------------------------------- /models/notificacion.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const NotificacionSchema = Schema( 4 | { 5 | tipo: { 6 | type: String, 7 | enum: ['publicacion', 'sos', 'mensaje'], 8 | required: true, 9 | }, 10 | usuario: { 11 | type: Schema.Types.ObjectId, 12 | ref: 'Usuario', 13 | required: true, 14 | }, 15 | //usuario que envia la notificacion 16 | usuarioRemitente: { 17 | type: Schema.Types.ObjectId, 18 | ref: 'Usuario', 19 | }, 20 | 21 | publicacion: { 22 | type: Schema.Types.ObjectId, 23 | ref: 'Publicacion', 24 | }, 25 | telefonoUsuario: { 26 | type: String, 27 | }, 28 | mensaje: { 29 | type: String, 30 | required: true, 31 | }, 32 | latitud: { 33 | type: Number, 34 | required: true, 35 | }, 36 | longitud: { 37 | type: Number, 38 | required: true, 39 | }, 40 | isLeida: { 41 | type: Boolean, 42 | default: false, 43 | }, 44 | }, 45 | { 46 | timestamps: true, 47 | } 48 | ); 49 | 50 | 51 | NotificacionSchema.method('toJSON', function () { 52 | const { __v, _id,...object } = this.toObject(); 53 | object.uid = _id; 54 | return object; 55 | }); 56 | 57 | 58 | module.exports = model('Notificacion', NotificacionSchema); 59 | -------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | /* 2 | path: api/login 3 | TOUTER 4 | */ 5 | const { Router } = require("express"); 6 | const { check } = require("express-validator"); 7 | 8 | const { 9 | crearUsuario, 10 | login, 11 | renewToken, 12 | googleAuth, 13 | } = require("../controllers/auth"); 14 | const { validarCampos } = require("../middlewares/validar-campos"); 15 | const { validarJWT } = require("../middlewares/validar-jwt"); 16 | 17 | const router = Router(); 18 | 19 | router.post( 20 | "/new", 21 | [ 22 | check("nombre", "El nombre es obligatorio").not().isEmpty(), 23 | check("password", "La contraseña es obligatoria").not().isEmpty(), 24 | check("email", "El correo es obligatorio").isEmail(), 25 | validarCampos, 26 | ], 27 | crearUsuario 28 | ); 29 | 30 | router.post( 31 | "/", 32 | [ 33 | check("password", "La contraseña es obligatoria").not().isEmpty(), 34 | check("email", "El correo es obligatorio").isEmail(), 35 | validarCampos, 36 | ], 37 | login 38 | ); 39 | 40 | router.post( 41 | "/", 42 | [ 43 | check("password", "La contraseña es obligatoria").not().isEmpty(), 44 | check("email", "El correo es obligatorio").isEmail(), 45 | validarCampos, 46 | ], 47 | login 48 | ); 49 | 50 | router.get("/renew", validarJWT, renewToken); 51 | 52 | router.post("/google", googleAuth); 53 | 54 | module.exports = router; 55 | -------------------------------------------------------------------------------- /routes/publicaciones.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { check } = require("express-validator"); 3 | const { validarJWT } = require("../middlewares/validar-jwt"); 4 | const { 5 | obtenerPublicacionesUsuario, 6 | guardarPublicacion, 7 | getPublicacionesEnRadio, 8 | updatePublicacion, 9 | likePublicacion, 10 | guardarListArchivo, 11 | isPublicacionFinalizada, 12 | deletePublicacion, 13 | actualizarDescripcion, 14 | } = require("../controllers/publicaciones"); 15 | const { validarCampos } = require("../middlewares/validar-campos"); 16 | const { validacionesCrearPublicacion } = require("../middlewares/express-validator"); 17 | 18 | const router = Router(); 19 | 20 | router.get("/", validarJWT, obtenerPublicacionesUsuario); 21 | 22 | router.get("/cercanas", validarJWT, getPublicacionesEnRadio); 23 | 24 | router.put("/like2/:id", validarJWT, likePublicacion); 25 | 26 | router.post("/", [...validacionesCrearPublicacion, validarCampos, validarJWT], guardarPublicacion); 27 | 28 | router.put("/:id", validarJWT, updatePublicacion); 29 | 30 | router.post("/listaArchivos/:uid/:titulo",validarCampos, guardarListArchivo); 31 | 32 | router.put("/like", validarJWT, updatePublicacion); 33 | 34 | router.put("/marcar-publicacion-pendiente-false/:publicacionId", validarJWT, isPublicacionFinalizada); 35 | 36 | router.delete("/:id", validarJWT, deletePublicacion); 37 | 38 | router.put("/actualizarDescripcion/:id", validarJWT, actualizarDescripcion); 39 | 40 | module.exports = router; 41 | 42 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const cors = require("cors"); 4 | const fileUpload = require('express-fileupload'); 5 | require("dotenv").config(); 6 | 7 | require("./database/config").dbConnection(); 8 | 9 | const app = express(); 10 | app.use(cors()); 11 | 12 | app.use(express.json()); 13 | 14 | const server = require("http").createServer(app); 15 | module.exports.io = require("socket.io")(server); 16 | require("./sockets/socket"); 17 | const publicPath = path.resolve(__dirname, "./prueba/public"); 18 | app.use(express.static(publicPath)); 19 | 20 | app.use( 21 | fileUpload({ 22 | useTempFiles: true, 23 | tempFileDir: "/tmp/", 24 | createParentPath: true 25 | }) 26 | ); 27 | 28 | app.use("/api/buscar", require("./routes/buscar")); 29 | app.use("/api/comentarios", require("./routes/comentarios")); 30 | app.use("/api/login", require("./routes/auth")); 31 | app.use("/api/mensajes", require("./routes/mensajes")); 32 | app.use("/api/publicacion", require("./routes/publicaciones")); 33 | app.use("/api/salas", require("./routes/salas")); 34 | app.use("/api/ubicaciones", require("./routes/ubicaciones")); 35 | app.use("/api/uploads", require("./routes/uploads")); 36 | app.use("/api/usuarios", require("./routes/usuarios")); 37 | app.use("/api/reportes", require("./routes/reportes")); 38 | app.use("/api/notificacion", require("./routes/notificaciones")); 39 | app.use("/api/denuncias", require("./routes/denuncias")); 40 | app.use("/api/documents", require("./routes/documents")); 41 | 42 | module.exports = { 43 | app, 44 | server, 45 | } -------------------------------------------------------------------------------- /routes/usuarios.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { validarJWT } = require("../middlewares/validar-jwt"); 3 | 4 | const { 5 | actualizarUsuario, 6 | ageregarTelefonos, 7 | agregarDireccion, 8 | agregarTelefono, 9 | eliminarTelefono, 10 | enviarNotificacionesArrayTelefonos, 11 | getUsuarios, 12 | actualizarTelefonoOrNombre, 13 | actualizarIsOpenRoom, 14 | marcarPublicacionPendienteFalse, 15 | marcarSalaPendienteFalse, 16 | marcarNotificacionesPendienteFalse, 17 | eliminarTokenApp, 18 | } = require("../controllers/usuarios"); 19 | 20 | const router = Router(); 21 | router.delete("/delete-telefono", validarJWT, eliminarTelefono ); 22 | router.get("/", validarJWT, getUsuarios); 23 | router.post("/notificacion", validarJWT, enviarNotificacionesArrayTelefonos); 24 | router.put("/", validarJWT, actualizarUsuario); 25 | router.put("/actualizar-is-open-room", validarJWT, actualizarIsOpenRoom); 26 | router.put("/add-direccion", validarJWT, agregarDireccion); 27 | router.put("/add-telefono", validarJWT, agregarTelefono ); 28 | router.put("/add-telefonos", validarJWT, ageregarTelefonos ); 29 | router.put("/add-telefono-nombre", validarJWT, actualizarTelefonoOrNombre ); 30 | router.put("/marcar-publicacion-pendiente-false", validarJWT, marcarPublicacionPendienteFalse ); 31 | router.put("/marcar-sala-pendiente-false", validarJWT, marcarSalaPendienteFalse ); 32 | router.put("/marcar-notificaciones-pendiente-false", validarJWT, marcarNotificacionesPendienteFalse ); 33 | router.delete("/delete-token-app", validarJWT, eliminarTokenApp ); 34 | 35 | module.exports = router; 36 | -------------------------------------------------------------------------------- /routes/uploads.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { check } = require("express-validator"); 3 | const { validarJWT } = require("../middlewares/validar-jwt"); 4 | const { validarArchivoSubir } = require("../middlewares/validar-archivo"); 5 | const { 6 | cargarArchivo, 7 | mostrarImagen, 8 | mostrarAllImagenes, 9 | actualizarImagen, 10 | mostrarImagenUsuario, 11 | } = require("../controllers/uploads"); 12 | const { validarCampos } = require("../middlewares/validar-campos"); 13 | const { coleccionesPermitidas } = require("../helpers/db-validator"); 14 | 15 | const router = Router(); 16 | 17 | router.post("/", validarArchivoSubir, cargarArchivo); 18 | 19 | router.get( 20 | "/:coleccion/:id", 21 | [ 22 | check("id", "El id debe de ser de mongo").isMongoId(), 23 | // check('coleccion').custom( c => coleccionesPermitidas( c, ['usuarios','publicaciones'] ) ), 24 | validarCampos, 25 | ], 26 | mostrarImagen 27 | ); 28 | 29 | router.get( 30 | "/usuario/:coleccion/:id", 31 | [ 32 | check("id", "El id debe de ser de mongo").isMongoId(), 33 | // check('coleccion').custom( c => coleccionesPermitidas( c, ['usuarios','publicaciones'] ) ), 34 | validarCampos, 35 | ], 36 | mostrarImagenUsuario 37 | ); 38 | 39 | 40 | router.get("/imagenes/:coleccion/:id", mostrarImagen); 41 | 42 | router.put('/:coleccion/:id', [ 43 | validarArchivoSubir, 44 | check('id','El id debe de ser de mongo').isMongoId(), 45 | check('coleccion').custom( c => coleccionesPermitidas( c, ['usuarios','publicaciones'] ) ), 46 | validarCampos 47 | ], actualizarImagen ) 48 | 49 | module.exports = router; 50 | -------------------------------------------------------------------------------- /routes/reportes.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { 3 | obtenerCiudades,obtenerBarrios,obtenerEmergencias,obtenerReporteBarras,obtenerReportePastel,obtenerAnios,obtenerMapaCalor,obtenerDatosCards 4 | ,obtenerCoordenadas 5 | ,descargarXLSX,descargarPDF,descargarCSV 6 | } = require("../controllers/reportes"); 7 | 8 | const { validarJWT } = require("../middlewares/validar-jwt"); 9 | 10 | const router = Router(); 11 | router.get( 12 | "/obtenerCiudades", 13 | /* validarJWT, */ 14 | obtenerCiudades 15 | ); 16 | router.post( 17 | "/obtenerBarrios", 18 | /* validarJWT, */ 19 | obtenerBarrios 20 | ); 21 | router.post( 22 | "/obtenerEmergencias", 23 | /* validarJWT, */ 24 | obtenerEmergencias 25 | ); 26 | router.post( 27 | "/obtenerReporteBarras", 28 | /* validarJWT, */ 29 | obtenerReporteBarras 30 | ); 31 | router.post( 32 | "/obtenerReportePastel", 33 | /* validarJWT, */ 34 | obtenerReportePastel 35 | ); 36 | router.post( 37 | "/obtenerAnios", 38 | /* validarJWT, */ 39 | obtenerAnios 40 | ); 41 | router.post( 42 | "/obtenerMapaCalor", 43 | /* validarJWT, */ 44 | obtenerMapaCalor 45 | ); 46 | router.get( 47 | "/obtenerDatosCards", 48 | /* validarJWT, */ 49 | obtenerDatosCards 50 | ); 51 | 52 | router.post( 53 | "/obtenerCoordenadas", 54 | /* validarJWT, */ 55 | obtenerCoordenadas 56 | ); 57 | 58 | router.post( 59 | "/descargarXLSX", 60 | /* validarJWT, */ 61 | descargarXLSX 62 | ); 63 | 64 | router.post( 65 | "/descargarPDF", 66 | /* validarJWT, */ 67 | descargarPDF 68 | ); 69 | 70 | router.post( 71 | "/descargarCSV", 72 | /* validarJWT, */ 73 | descargarCSV 74 | ); 75 | 76 | module.exports = router; 77 | -------------------------------------------------------------------------------- /routes/salas.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { 3 | crearSala, 4 | getSalas, 5 | unirseSala, 6 | obtenerSalasMensajesUsuario, 7 | getMensajesBySala, 8 | getMensajesSala, 9 | updateSala, 10 | obtenerUsuariosSala, 11 | deleteSala, 12 | deleteUserById, 13 | abandonarSala, 14 | getSalasByUser, 15 | obtenerSalasConMensajesNoLeidos, 16 | cambiarEstadoSala, 17 | } = require("../controllers/salas"); 18 | 19 | const { validarJWT } = require("../middlewares/validar-jwt"); 20 | 21 | const router = Router(); 22 | 23 | router.post("/", validarJWT, crearSala); 24 | 25 | router.get("/", getSalas); 26 | 27 | router.put("/:salaId", updateSala); 28 | 29 | router.delete("/:salaId", validarJWT, deleteSala); 30 | 31 | router.post("/unir-sala", validarJWT, unirseSala); 32 | 33 | router.get( 34 | "/obtener-salas-mensajes-usuario", 35 | validarJWT, 36 | obtenerSalasConMensajesNoLeidos 37 | ); 38 | 39 | router.put("/cambiar-estado-sala/:salaId", validarJWT, cambiarEstadoSala); 40 | 41 | router.get( 42 | "/obtener-salas-mensajes-usuario", 43 | validarJWT, 44 | obtenerSalasMensajesUsuario 45 | ); 46 | 47 | router.get("/obtener-salas-usuario", validarJWT, getSalasByUser); 48 | 49 | router.get("/get-mensajes-by-sala/:salaId", validarJWT, getMensajesBySala); 50 | 51 | router.get("/get-mensajes-sala/:salaId", validarJWT, getMensajesSala); 52 | 53 | router.get("/obtener-usuarios-sala/:salaId", validarJWT, obtenerUsuariosSala); 54 | 55 | router.delete("/delete-user/:salaId/:usuarioId", validarJWT, deleteUserById); 56 | 57 | router.delete("/abandonar-sala/:salaId", validarJWT, abandonarSala); 58 | 59 | // router.get("/obtener-mensajes-no-leidos/:salaId", validarJWT, obtenerMensajesNoLeidosPorUsuario); 60 | 61 | module.exports = router; 62 | -------------------------------------------------------------------------------- /sockets/socket.js: -------------------------------------------------------------------------------- 1 | const { comprobarJWT } = require("../helpers/jwt"); 2 | const { io } = require("../app"); 3 | const { 4 | usuarioConectado, 5 | usuarioDesconectado, 6 | grabarMensajeSala, 7 | grabarComentarioPublicacion, 8 | } = require("../controllers/socket"); 9 | 10 | // Mensajes de Sockets 11 | io.on("connection", (client) => { 12 | console.log("Cliente conectado"); 13 | 14 | // console.log(client.handshake.headers); 15 | console.log(client.handshake.headers["x-token"], "token"); 16 | 17 | const [valido, uid] = comprobarJWT(client.handshake.headers["x-token"]); 18 | 19 | // Verificar autenticación 20 | 21 | if (!valido) { 22 | console.log("Cliente no autenticado"); 23 | return client.disconnect(); 24 | } 25 | 26 | // Cliente autenticado 27 | usuarioConectado(uid); 28 | console.log("Cliente autenticado"); 29 | client.on("join-room", async (payload) => { 30 | const { codigo } = payload; 31 | console.log(codigo, "codigo"); 32 | 33 | const [valido, uid] = comprobarJWT(client.handshake.headers["x-token"]); 34 | 35 | if (!valido) { 36 | console.log("Token inválido"); 37 | client.disconnect(); 38 | } else { 39 | client.join(codigo); 40 | } 41 | }); 42 | 43 | client.on("mensaje-grupal", async (payload) => { 44 | console.log(payload); 45 | grabarMensajeSala(payload); 46 | client.broadcast.to(payload.para).emit("mensaje-grupal", payload); 47 | }); 48 | 49 | client.on("comentario-publicacion", async (payload) => { 50 | console.log(payload); 51 | client.broadcast.to(payload.para).emit("comentario-publicacion", payload); 52 | }); 53 | 54 | 55 | client.on("disconnect", () => { 56 | usuarioDesconectado(uid); 57 | console.log("Cliente desconectado"); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /helpers/subir-archivo.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { v4: uuidv4 } = require("uuid"); 3 | 4 | const subirArchivoUsuario = ( 5 | archivo, 6 | extensionesValidas = ["png", "jpg", "jpeg", "gif"], 7 | carpeta = "" 8 | ) => { 9 | 10 | return new Promise((resolve, reject) => { 11 | const nombreCortado = archivo.archivo.name.split("."); 12 | const extension = nombreCortado[nombreCortado.length - 1]; 13 | 14 | // Validar la extension 15 | // if (!extensionesValidas.includes(extension)) { 16 | // return reject( 17 | // `La extensión ${extension} no es permitida - ${extensionesValidas}` 18 | // ); 19 | // } 20 | 21 | const nombreTemp = uuidv4() + "." + extension; 22 | const uploadPath = path.join(__dirname, "../uploads/", carpeta, nombreTemp); 23 | 24 | archivo.archivo.mv(uploadPath, (err) => { 25 | if (err) { 26 | reject(err); 27 | } 28 | 29 | resolve(nombreTemp); 30 | }); 31 | }); 32 | }; 33 | 34 | const subirArchivoPublicacion = ( 35 | archivo, 36 | extensionesValidas = ["png", "jpg", "jpeg", "gif"], 37 | carpeta = "" 38 | ) => { 39 | return new Promise((resolve, reject) => { 40 | const nombreCortado = archivo.name.split("."); 41 | const extension = nombreCortado[nombreCortado.length - 1]; 42 | 43 | // Validar la extensionssssss 44 | if (!extensionesValidas.includes(extension)) { 45 | return reject( 46 | `La extensión ${extension} no es permitida - ${extensionesValidas}` 47 | ); 48 | } 49 | 50 | const nombreTemp = uuidv4() + "." + extension; 51 | const uploadPath = path.join(__dirname, "../uploads/", carpeta, nombreTemp); 52 | 53 | archivo.mv(uploadPath, (err) => { 54 | if (err) { 55 | reject(err); 56 | } 57 | 58 | resolve(nombreTemp); 59 | }); 60 | }); 61 | }; 62 | 63 | module.exports = { 64 | subirArchivoUsuario, 65 | subirArchivoPublicacion 66 | }; 67 | -------------------------------------------------------------------------------- /models/publicacion.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const PublicacionSchema = Schema( 4 | { 5 | titulo: { 6 | type: String, 7 | required: true, 8 | }, 9 | contenido: { 10 | type: String, 11 | required: true, 12 | }, 13 | 14 | color: { 15 | type: String, 16 | required: true, 17 | }, 18 | ciudad: { 19 | type: String, 20 | required: true, 21 | }, 22 | barrio: { 23 | type: String, 24 | required: true, 25 | }, 26 | isPublic: { 27 | type: Boolean, 28 | default: true, 29 | }, 30 | usuario: { 31 | type: Schema.Types.ObjectId, 32 | ref: "Usuario", 33 | required: true, 34 | }, 35 | nombreUsuario: { 36 | type: String, 37 | required: true, 38 | }, 39 | likes: [ 40 | { 41 | type: Schema.Types.ObjectId, 42 | ref: "Usuario", 43 | }, 44 | ], 45 | //lista de imagenes 46 | imagenes: [ 47 | { 48 | type: String, 49 | }, 50 | ], 51 | latitud: { 52 | type: Number, 53 | }, 54 | longitud: { 55 | type: Number, 56 | }, 57 | comentarios: [ 58 | { 59 | type: Schema.Types.ObjectId, 60 | ref: "Comentario", 61 | }, 62 | ], 63 | imgAlerta: { 64 | type: String, 65 | required: true, 66 | }, 67 | 68 | isLiked: { 69 | type: Boolean, 70 | default: false, 71 | }, 72 | isActivo: { 73 | type: Boolean, 74 | default: true, 75 | }, 76 | //publicacion pendiente 77 | isPublicacionPendiente: { 78 | type: Boolean, 79 | default: false, 80 | }, 81 | }, 82 | { 83 | timestamps: true, 84 | } 85 | ); 86 | 87 | PublicacionSchema.method("toJSON", function () { 88 | const { __v, _id, ...object } = this.toObject(); 89 | object.uid = _id; 90 | return object; 91 | }); 92 | 93 | module.exports = model("Publicacion", PublicacionSchema); 94 | -------------------------------------------------------------------------------- /tests/salas.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Sala Router", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; 6 | let salaId = "64bfef6c2f35690cfee892ff"; 7 | 8 | // Prueba 1: Obtener todas las salas 9 | test("Should get salas", async () => { 10 | const response = await request(app) 11 | .get("/api/salas") 12 | .set("x-token", token) 13 | .expect(200); 14 | 15 | // Verificar la respuesta, por ejemplo: 16 | expect(response.body).toEqual(expect.arrayContaining([])); // Reemplaza [] con el formato esperado de las salas. 17 | }); 18 | 19 | // Prueba 2: Crear una sala 20 | test("Should create a sala", async () => { 21 | const nuevaSala = { nombre: "Sala de Prueba" }; 22 | const response = await request(app) 23 | .post("/api/salas") 24 | .set("x-token", token) 25 | .send(nuevaSala) 26 | .expect(200); 27 | }); 28 | 29 | // Prueba 5: Eliminar una sala 30 | // Prueba 5: Eliminar una sala - Usuario no autorizado 31 | test("Should not delete a sala for unauthorized user", async () => { 32 | const response = await request(app) 33 | .delete(`/api/salas/${salaId}`) 34 | .set("x-token", token) 35 | .expect(401); 36 | 37 | // Verificar la respuesta HTTP 401 (Unauthorized) 38 | expect(response.status).toBe(401); 39 | }); 40 | 41 | 42 | // Prueba 6: Unirse a una sala 43 | test("Should join a sala", async () => { 44 | const datosUnirseSala = { codigo: "299-427" }; 45 | const response = await request(app) 46 | .post("/api/salas/unir-sala") 47 | .set("x-token", token) 48 | .send(datosUnirseSala) 49 | .expect(400); 50 | 51 | 52 | }); 53 | 54 | 55 | 56 | // Prueba 8: Abandonar una sala 57 | test("Should leave a sala", async () => { 58 | const response = await request(app) 59 | .delete(`/api/salas/abandonar-sala/${salaId}`) 60 | .set("x-token", token) 61 | .expect(200); 62 | 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /models/usuario.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | const UsuarioSchema = Schema( 4 | { 5 | nombre: { 6 | type: String, 7 | required: true, 8 | }, 9 | email: { 10 | type: String, 11 | required: true, 12 | unique: true, 13 | }, 14 | password: { 15 | type: String, 16 | required: true, 17 | }, 18 | ubicaciones: [ 19 | { 20 | type: Schema.Types.ObjectId, 21 | ref: "Ubicacion", 22 | }, 23 | ], 24 | telefono: { 25 | type: String, 26 | }, 27 | telefonos: [ 28 | { 29 | type: String, 30 | }, 31 | ], 32 | img: { 33 | type: String, 34 | }, 35 | online: { 36 | type: Boolean, 37 | default: false, 38 | }, 39 | tokenApp: { 40 | type: String, 41 | default: null, 42 | }, 43 | google: { 44 | type: Boolean, 45 | default: false, 46 | }, 47 | isOpenRoom: { 48 | type: Boolean, 49 | default: false, 50 | }, 51 | isActivo: { 52 | type: Boolean, 53 | default: true, 54 | }, 55 | isPublicacionPendiente: { 56 | type: Boolean, 57 | default: false, 58 | }, 59 | isSalasPendiente: { 60 | type: Boolean, 61 | default: false, 62 | }, 63 | isNotificacionesPendiente: { 64 | type: Boolean, 65 | default: false, 66 | }, 67 | role: { 68 | type: String, 69 | required: true, 70 | default: "USER_ROLE", 71 | }, 72 | salas: [ 73 | { 74 | salaId: { type: Schema.Types.ObjectId, ref: "Sala" }, 75 | mensajesNoLeidos: { type: Number, default: 0 }, 76 | ultimaVezActivo: { type: Date, default: null }, 77 | isRoomOpen: { type: Boolean, default: false }, 78 | }, 79 | ], 80 | }, 81 | { 82 | timestamps: true, 83 | } 84 | ); 85 | 86 | UsuarioSchema.method("toJSON", function () { 87 | const { __v, _id, password, ...object } = this.toObject(); 88 | object.uid = _id; 89 | return object; 90 | }); 91 | 92 | module.exports = model("Usuario", UsuarioSchema); 93 | -------------------------------------------------------------------------------- /controllers/denuncias.js: -------------------------------------------------------------------------------- 1 | const { Usuario, Publicacion, Denuncia } = require("../models"); 2 | 3 | const guardarDenuncia = async (req, res) => { 4 | const usuarioId = req.uid; 5 | const { publicacionId, motivo, detalles } = req.body; 6 | 7 | try { 8 | // Verificar si la publicación existe 9 | const publicacion = await Publicacion.findById(publicacionId); 10 | if (!publicacion) { 11 | return res.status(404).json({ 12 | ok: false, 13 | msg: "La publicación no fue encontrada", 14 | }); 15 | } 16 | 17 | // Verificar si el usuario ya ha denunciado esta publicación 18 | const denunciaExistente = await Denuncia.findOne({ 19 | publicacion: publicacionId, 20 | denunciante: usuarioId, 21 | }); 22 | 23 | if (denunciaExistente) { 24 | return res.status(400).json({ 25 | ok: false, 26 | msg: "Ya has denunciado esta publicación anteriormente", 27 | }); 28 | } 29 | 30 | // Crear la denuncia 31 | const denuncia = new Denuncia({ 32 | publicacion: publicacionId, 33 | motivo, 34 | detalles, 35 | denunciante: usuarioId, 36 | }); 37 | 38 | await denuncia.save(); 39 | 40 | res.json({ 41 | ok: true, 42 | denuncia, 43 | }); 44 | } catch (error) { 45 | console.log(error); 46 | res.status(500).json({ 47 | ok: false, 48 | msg: "Por favor hable con el administrador", 49 | }); 50 | } 51 | }; 52 | 53 | 54 | const obtenerDenuncias = async (req, res) => { 55 | try { 56 | const denuncias = await Denuncia.find() 57 | .populate("publicacion", "titulo") 58 | .populate("denunciante", "nombre") 59 | .sort({ fecha: -1 }); 60 | 61 | res.json({ 62 | ok: true, 63 | denuncias, 64 | }); 65 | } catch (error) { 66 | console.log(error); 67 | res.status(500).json({ 68 | ok: false, 69 | msg: "Por favor hable con el administrador", 70 | }); 71 | } 72 | }; 73 | 74 | 75 | module.exports = { 76 | guardarDenuncia, 77 | }; 78 | -------------------------------------------------------------------------------- /tests/ubicaciones.spec.js: -------------------------------------------------------------------------------- 1 | const { app } = require("../app"); 2 | const request = require("supertest"); 3 | 4 | describe("Ubicaciones Router", () => { 5 | const token = process.env.TOKEN_TEST_AUTH; // Adjust this to retrieve the token properly 6 | 7 | const dataUbicacion = { 8 | "latitud": -0.252175, 9 | "longitud": -79.1881, 10 | "barrio": "Calle Cuba", 11 | "parroquia": "Luz de América", 12 | "ciudad": "Santo Domingo de los Tsáchilas", 13 | "pais": "Ecuador", 14 | "referencia": "Parroquia Luz De América", 15 | } 16 | 17 | test("Should get all locations", async () => { 18 | const response = await request(app) 19 | .get("/api/ubicaciones") 20 | .expect(200); 21 | 22 | // Verify the response, e.g., response.body 23 | }); 24 | 25 | test("Should create a new location", async () => { 26 | 27 | 28 | const response = await request(app) 29 | .post("/api/ubicaciones") 30 | .set("x-token", token) 31 | .send(dataUbicacion) 32 | .expect(200); 33 | 34 | // Verify the response, e.g., response.body 35 | }); 36 | 37 | test("Should get user's locations", async () => { 38 | const response = await request(app) 39 | .get("/api/ubicaciones") 40 | .set("x-token", token) 41 | .expect(200); 42 | }); 43 | 44 | test("Should add a location", async () => { 45 | const locationId = "64c3bb741d3d977b018a30ca"; // Replace with an actual location ID 46 | const response = await request(app) 47 | .put(`/api/ubicaciones/${locationId}`) 48 | .set("x-token", token) 49 | .send(dataUbicacion) 50 | .expect(200); 51 | }); 52 | 53 | test("Should delete a location", async () => { 54 | const locationId = "64c3bb741d3d977b018a30ca"; // Replace with an actual location ID 55 | const response = await request(app) 56 | .delete(`/api/ubicaciones/${locationId}`) 57 | .set("x-token", token) 58 | .expect(200); 59 | 60 | // Verify the response, e.g., response.body 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /controllers/buscar.js: -------------------------------------------------------------------------------- 1 | const { response, request } = require("express"); 2 | const { ObjectId } = require("mongoose").Types; 3 | 4 | const { 5 | Comentario, 6 | Mensaje, 7 | Publicacion, 8 | Sala, 9 | Ubicacion, 10 | Usuario, 11 | } = require("../models"); 12 | 13 | const coleccionesPermitidas = [ 14 | "usuarios", 15 | "publicaciones", 16 | "comentarios", 17 | "mensajes", 18 | "salas", 19 | "ubicaciones", 20 | ]; 21 | 22 | const buscarUsuarios = async (termino = "", res = response) => { 23 | const esMongoID = ObjectId.isValid(termino); 24 | 25 | if (esMongoID) { 26 | const usuario = await Usuario.findById(termino); 27 | return res.json({ 28 | results: usuario ? [usuario] : [], 29 | }); 30 | } 31 | 32 | const regex = new RegExp(termino, "i"); 33 | 34 | const usuarios = await Usuario.find({ 35 | $or: [{ nombre: regex }, { apellido: regex }, { email: regex }], 36 | $and: [{ estado: true }], 37 | }); 38 | 39 | res.json({ 40 | results: usuarios, 41 | }); 42 | }; 43 | 44 | const buscarUbicaciones = async ( 45 | termino = "", 46 | res = response, 47 | req = request 48 | ) => { 49 | const esMongoID = ObjectId.isValid(termino); 50 | if (esMongoID) { 51 | const ubicacion = await Ubicacion.findById(termino); 52 | return res.json({ 53 | ubicaciones: ubicacion ? [ubicacion] : [], 54 | }); 55 | } 56 | 57 | const { limite = 6, desde = 0 } = req.query; 58 | const regex = new RegExp(termino, "i"); 59 | 60 | const ubicaciones = await Ubicacion.find({ 61 | $or: [ 62 | { barrio: regex }, 63 | { ciudad: regex }, 64 | { parroquia: regex }, 65 | { pais: regex }, 66 | { referencia: regex }, 67 | ], 68 | $and: [{ estado: true }], 69 | }) 70 | .skip(Number(desde)) 71 | .limit(Number(limite)); 72 | 73 | res.json({ 74 | ok: true, 75 | ubicaciones: ubicaciones, 76 | }); 77 | }; 78 | 79 | const buscar = async (req = request, res = response) => { 80 | const { coleccion, termino } = req.params; 81 | if (!coleccionesPermitidas.includes(coleccion)) { 82 | return res.status(400).json({ 83 | msg: `Las colecciones permitidas son: ${coleccionesPermitidas}`, 84 | }); 85 | } 86 | 87 | switch (coleccion) { 88 | case "usuarios": 89 | buscarUsuarios(termino, res); 90 | break; 91 | case "ubicaciones": 92 | buscarUbicaciones(termino, res, req); 93 | break; 94 | default: 95 | res.status(500).json({ 96 | msg: "Se le olvidó hacer esta búsqueda", 97 | }); 98 | } 99 | }; 100 | 101 | module.exports = { buscar }; 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Backend de Seguridad ESPE 2 | El proyecto Backend de Seguridad ESPE es una parte fundamental del proyecto de vinculación con la sociedad denominado "Implementación de aplicaciones web y móvil para gestionar emergencias comunitarias en la provincia de Santo Domingo de los Tsáchilas". Su objetivo principal es fortalecer la seguridad de la comunidad a través de la comunicación, coordinación y respuesta ante situaciones de emergencia. Esta aplicación aprovecha el alto uso de dispositivos móviles e Internet para ofrecer una solución innovadora en el campo de la protección ciudadana. 3 | 4 | ## Pasos para ejecutar el proyecto 5 | A continuación, se detallan los pasos necesarios para ejecutar el proyecto Backend de Seguridad ESPE en su entorno local: 6 | 7 | ### Requisitos previos 8 | Asegúrese de tener instalado [Node.js](https://nodejs.org/en) en su sistema. 9 | 10 | ### Clonar el repositorio 11 | ``` 12 | git clone https://github.com/Vinici0/rest-server-movil-web.git 13 | cd rest-server-movil-web 14 | ``` 15 | ### Instalar dependencias 16 | Ejecute el siguiente comando para instalar todas las dependencias del proyecto: 17 | ``` 18 | npm install 19 | ``` 20 | ### Configurar variables de entorno 21 | 22 | El proyecto utiliza variables de entorno para configuraciones sensibles. Cree un archivo .env en el directorio raíz del proyecto y configure las siguientes variables: 23 | ``` 24 | PORT=3000 # Puerto en el que se ejecutará el servidor 25 | DB_CNN= # URI de la base de datos MongoDB 26 | JWT_KEY= # Clave secreta para JWT 27 | TOKEN_NOTIFICAIONES= # notificaciones 28 | ``` 29 | ### Ejecutar el servidor 30 | El servidor estará disponible en ```http://localhost:3000```. 31 | 32 | ### Dependencias del proyecto 33 | A continuación, se enumeran las principales dependencias utilizadas en este proyecto:
34 | 35 | - **axios:** Realizar solicitudes HTTP. 36 | - **bcryptjs:** Realizar el hash de contraseñas. 37 | - **cors:** Configuración de políticas de acceso de origen cruzado. 38 | - **dotenv:** Cargar variables de entorno desde un archivo. 39 | - **express:** Framework de aplicación web. 40 | - **express-fileupload:** Procesar cargas de archivos. 41 | - **express-validator:** Validación de datos en Express. 42 | - **google-auth-library:** Autenticación con Google. 43 | - **html-pdf:** Generación de archivos PDF a partir de HTML. 44 | - **jsonwebtoken:** Implementación de JSON Web Tokens. 45 | - **lodash:** Utilidades de manipulación de datos. 46 | - **moment:** Manipulación de fechas y horas. 47 | - **mongoose:** Modelado de datos para MongoDB. 48 | - **pdfkit y pdfmake:** Generación de documentos PDF. 49 | - **socket.io:** Comunicación en tiempo real mediante WebSocket. 50 | - **uuid:** Generación de identificadores únicos. 51 | 52 | ### Contacto 53 | Si tienes alguna pregunta o comentario sobre este proyecto, no dudes en contactarme a través de mi perfil de [LinkedIn](https://www.linkedin.com/in/vinicio-borja-tapia/).

54 | ¡Gracias por su interés en el proyecto Backend de Seguridad ESPE! 55 | 56 | -------------------------------------------------------------------------------- /helpers/enviar-notificacion.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | const { guardarNotificacion } = require("../controllers/notificaciones"); 3 | 4 | const enviarNotificacion = async (tokens, titulo, desc, data = {}) => { 5 | if (tokens.length === 0) { 6 | console.log("No hay tokens"); 7 | return; 8 | } 9 | 10 | try { 11 | const response = await axios.post( 12 | "https://fcm.googleapis.com/fcm/send", 13 | { 14 | notification: { 15 | title: titulo, 16 | body: desc, 17 | }, 18 | priority: "high", 19 | data: { 20 | data: JSON.stringify(data), 21 | }, 22 | registration_ids: tokens, 23 | }, 24 | { 25 | headers: { 26 | "Content-Type": "application/json", 27 | Authorization: "key=" + process.env.TOKEN_NOTIFICAIONES, 28 | }, 29 | } 30 | ); 31 | 32 | console.log(response.data); 33 | 34 | } catch (error) { 35 | console.log(error); 36 | throw new Error("Error al enviar la notificación."); 37 | } 38 | }; 39 | 40 | // Controlador para guardar una notificación relacionada con una publicación 41 | const guardarNotificacionPublicacion = async ( 42 | usuario, 43 | mensaje, 44 | publicacionId, 45 | latitud, 46 | longitud, 47 | usuarioRemitente 48 | ) => { 49 | try { 50 | const notificacion = await guardarNotificacion( 51 | "publicacion", 52 | usuario, 53 | mensaje, 54 | publicacionId, 55 | null, 56 | latitud, 57 | longitud, 58 | usuarioRemitente 59 | ); 60 | 61 | return notificacion; 62 | } catch (error) { 63 | console.log(error); 64 | throw new Error("Error al guardar la notificación de publicación"); 65 | } 66 | }; 67 | 68 | const guardarNotificacionPublicacionMensaje = async ( 69 | usuario, 70 | mensaje, 71 | publicacionId, 72 | latitud, 73 | longitud, 74 | usuarioRemitente 75 | ) => { 76 | try { 77 | const notificacion = await guardarNotificacion( 78 | "mensaje", 79 | usuario, 80 | mensaje, 81 | publicacionId, 82 | null, 83 | latitud, 84 | longitud, 85 | usuarioRemitente 86 | ); 87 | 88 | return notificacion; 89 | } catch (error) { 90 | console.log(error); 91 | throw new Error("Error al guardar la notificación de publicación"); 92 | } 93 | }; 94 | 95 | // Controlador para guardar una notificación relacionada con una solicitud 96 | const guardarNotificacionSOS = async ( 97 | usuario, 98 | mensaje, 99 | telefonoUsuario, 100 | latitud, 101 | longitud, 102 | usuarioRemitente 103 | ) => { 104 | try { 105 | const notificacion = await guardarNotificacion( 106 | "sos", 107 | usuario, 108 | mensaje, 109 | null, 110 | telefonoUsuario, 111 | latitud, 112 | longitud, 113 | usuarioRemitente 114 | ); 115 | 116 | return notificacion; 117 | } catch (error) { 118 | console.log(error); 119 | throw new Error("Error al guardar la notificación de solicitud"); 120 | } 121 | }; 122 | 123 | module.exports = { 124 | enviarNotificacion, 125 | guardarNotificacionPublicacion, 126 | guardarNotificacionSOS, 127 | guardarNotificacionPublicacionMensaje 128 | }; 129 | -------------------------------------------------------------------------------- /controllers/mensajes.js: -------------------------------------------------------------------------------- 1 | const { Usuario, Sala, Mensaje } = require("../models"); 2 | 3 | const getMensajeByUser = async (req, res) => { 4 | const miId = req.uid; 5 | 6 | const mensajes = await Mensaje.find({ 7 | usuario: miId, 8 | }); 9 | 10 | res.json({ 11 | ok: true, 12 | mensajes, 13 | }); 14 | }; 15 | 16 | const getAllMessages = async (req, res) => { 17 | const messages = await Mensaje.find(); 18 | res.json({ 19 | ok: true, 20 | messages, 21 | }); 22 | }; 23 | 24 | const getMensajeByRoom = async (req, res) => { 25 | const { salaId } = req.params; 26 | const { limite = 50, desde = 0 } = req.query; 27 | const usuarioId = req.uid; 28 | 29 | try { 30 | const sala = await Sala.findById(salaId) 31 | .populate({ 32 | path: "mensajes", 33 | options: { 34 | skip: Number(desde), 35 | limit: Number(limite), 36 | sort: { createdAt: -1 }, 37 | }, 38 | }) 39 | .lean(); 40 | 41 | const mensajesSala = await Promise.all( 42 | sala.mensajes.map(async (mensaje) => { 43 | 44 | const usuarioMensaje = await Usuario.findById(mensaje.usuario); 45 | 46 | mensaje = { ...mensaje, nombre: usuarioMensaje.nombre, img: usuarioMensaje.img, isGoogle: usuarioMensaje.google }; 47 | return mensaje; 48 | }) 49 | ); 50 | 51 | const usuario = await Usuario.findById(usuarioId); 52 | 53 | // Encontrar la entrada correspondiente en la lista de salas del usuario para la sala específica 54 | const salaUsuario = usuario.salas.find((salaUsuario) => salaUsuario.salaId.toString() === salaId); 55 | 56 | // Reiniciar el contador de mensajes no leídos (mensajesNoLeidos) a cero para esa entrada 57 | if (salaUsuario) { 58 | salaUsuario.mensajesNoLeidos = 0; 59 | await usuario.save(); 60 | } 61 | 62 | res.json({ 63 | ok: true, 64 | mensajesSala, 65 | }); 66 | } catch (error) { 67 | console.log(error); 68 | res.status(500).json({ 69 | ok: false, 70 | msg: "Por favor hable con el administrador", 71 | }); 72 | } 73 | }; 74 | 75 | 76 | 77 | const getMensajeByRoom2 = async (req, res) => { 78 | const { salaId } = req.params; 79 | const { limite = 50, desde = 0 } = req.query; 80 | const miId = req.uid; 81 | 82 | try { 83 | const sala = await Sala.findById(salaId) 84 | .populate({ 85 | path: "mensajes", 86 | options: { 87 | skip: Number(desde), 88 | limit: Number(limite), 89 | sort: { createdAt: -1 }, 90 | }, 91 | }) 92 | .lean(); 93 | 94 | const mensajesSala = await Promise.all( 95 | sala.mensajes.map(async (mensaje) => { 96 | const usuarioMensaje = await Usuario.findById(mensaje.usuario); 97 | mensaje = { ...mensaje, nombre: usuarioMensaje.nombre, img: usuarioMensaje.img, isGoogle: usuarioMensaje.google }; 98 | return mensaje; 99 | }) 100 | ); 101 | 102 | // Marcar los mensajes de la sala como leídos por el usuario 103 | const mensajesIds = mensajesSala.map((mensaje) => mensaje._id); 104 | await Mensaje.updateMany( 105 | { _id: { $in: mensajesIds }, leidoPor: { $ne: miId } }, 106 | { $addToSet: { leidoPor: miId } } 107 | ); 108 | 109 | res.json({ 110 | ok: true, 111 | mensajesSala, 112 | }); 113 | } catch (error) { 114 | console.log(error); 115 | res.status(500).json({ 116 | ok: false, 117 | msg: "Por favor hable con el administrador", 118 | }); 119 | } 120 | }; 121 | 122 | 123 | 124 | module.exports = { 125 | getAllMessages, 126 | getMensajeByUser, 127 | getMensajeByRoom, 128 | }; 129 | -------------------------------------------------------------------------------- /controllers/ubicaciones.js: -------------------------------------------------------------------------------- 1 | const { Ubicacion, Usuario } = require("../models"); 2 | 3 | const crearUbicacion = async (req, res = response) => { 4 | try { 5 | const ubicacion = new Ubicacion(req.body); 6 | await ubicacion.save(); 7 | res.json({ 8 | ok: true, 9 | ubicacion, 10 | }); 11 | } catch (error) { 12 | console.log(error); 13 | res.status(500).json({ 14 | ok: false, 15 | msg: "Error inesperado", 16 | }); 17 | } 18 | }; 19 | 20 | const obtenerUbicaciones = async (req, res = response) => { 21 | try { 22 | const ubicaciones = await Ubicacion.find(); 23 | res.json({ 24 | ok: true, 25 | ubicaciones, 26 | }); 27 | } catch (error) { 28 | console.log(error); 29 | res.status(500).json({ 30 | ok: false, 31 | msg: "Error inesperado", 32 | }); 33 | } 34 | }; 35 | 36 | const obtenerUbicacionesPorUsuario = async (req, res = response) => { 37 | const usuarioId = req.uid; 38 | try { 39 | const ubicaciones = await Ubicacion.find({ usuario: usuarioId }); 40 | res.json({ 41 | ok: true, 42 | ubicaciones, 43 | }); 44 | } catch (error) { 45 | console.log(error); 46 | res.status(500).json({ 47 | ok: false, 48 | msg: "Error inesperado", 49 | }); 50 | } 51 | }; 52 | 53 | const agregarUbicacion = async (req, res = response) => { 54 | const usuarioId = req.uid; 55 | const ubicacionId = req.params.id; 56 | 57 | try { 58 | const usuario = await Usuario.findById(usuarioId); 59 | 60 | if (!usuario) { 61 | return res.status(404).json({ 62 | ok: false, 63 | msg: "Usuario no encontrado", 64 | }); 65 | } 66 | 67 | const ubicacion = await Ubicacion.findById(ubicacionId); 68 | 69 | if (!ubicacion) { 70 | return res.status(404).json({ 71 | ok: false, 72 | msg: "Ubicación no encontrada", 73 | }); 74 | } 75 | 76 | // Verificar si la ubicación ya está asociada al usuario 77 | if (usuario.ubicaciones.includes(ubicacionId)) { 78 | return res.status(400).json({ 79 | ok: false, 80 | msg: "La ubicación ya está asociada al usuario", 81 | }); 82 | } 83 | 84 | // Agregar la ubicación al usuario 85 | usuario.ubicaciones.push(ubicacionId); 86 | await usuario.save(); 87 | 88 | res.json( 89 | ubicacion, 90 | ); 91 | } catch (error) { 92 | console.log(error); 93 | res.status(500).json({ 94 | ok: false, 95 | msg: "Error inesperado", 96 | }); 97 | } 98 | }; 99 | 100 | //delete ubicacion por id 101 | const eliminarUbicacion = async (req, res = response) => { 102 | const usuarioId = req.uid; 103 | const ubicacionId = req.params.id; 104 | 105 | try { 106 | const usuario = await Usuario.findById(usuarioId); 107 | 108 | if (!usuario) { 109 | return res.status(404).json({ 110 | ok: false, 111 | msg: "Usuario no encontrado", 112 | }); 113 | } 114 | 115 | // Verificar si la ubicación está asociada al usuario 116 | if (!usuario.ubicaciones.includes(ubicacionId)) { 117 | return res.status(400).json({ 118 | ok: false, 119 | msg: "La ubicación no está asociada al usuario", 120 | }); 121 | } 122 | 123 | // Eliminar la ubicación del usuario 124 | await Usuario.findByIdAndUpdate(usuarioId, { 125 | $pull: { ubicaciones: ubicacionId }, 126 | }); 127 | 128 | res.json({ 129 | ok: true, 130 | msg: "Ubicación eliminada del usuario exitosamente", 131 | }); 132 | } catch (error) { 133 | console.log(error); 134 | res.status(500).json({ 135 | ok: false, 136 | msg: "Error inesperado", 137 | }); 138 | } 139 | }; 140 | 141 | module.exports = { 142 | crearUbicacion, 143 | obtenerUbicaciones, 144 | obtenerUbicacionesPorUsuario, 145 | agregarUbicacion, 146 | eliminarUbicacion, 147 | }; 148 | -------------------------------------------------------------------------------- /controllers/notificaciones.js: -------------------------------------------------------------------------------- 1 | const { Notificacion } = require("../models"); 2 | 3 | const obtenerNotificacionesUsuario = async (req, res) => { 4 | const usuarioId = req.uid; 5 | const { limite = 10, desde = 0 } = req.query; 6 | 7 | try { 8 | const notificaciones = await Notificacion.find({ 9 | usuario: usuarioId, 10 | }) 11 | .populate( 12 | "publicacion", 13 | "titulo contenido color ciudad barrio isPublic usuario likes imagenes latitud longitud comentarios imgAlerta isLiked createdAt updatedAt nombreUsuario isPublicacionPendiente" 14 | ) 15 | .populate("usuarioRemitente", "nombre img telefono email google") 16 | .sort({ createdAt: -1 }) 17 | .skip(Number(desde)) 18 | .limit(Number(limite)); 19 | 20 | //que solo 21 | 22 | res.json({ 23 | ok: true, 24 | notificaciones, 25 | }); 26 | } catch (error) { 27 | console.log(error); 28 | res.status(500).json({ 29 | ok: false, 30 | msg: "Por favor hable con el administrador", 31 | }); 32 | } 33 | }; 34 | 35 | const marcarNotificacionComoLeida = async (req, res) => { 36 | const usuarioId = req.uid; 37 | const notificacionId = req.params.id; 38 | 39 | try { 40 | const notificacion = await Notificacion.findById(notificacionId); 41 | 42 | if (!notificacion) { 43 | return res.status(404).json({ mensaje: "Notificación no encontrada" }); 44 | } 45 | 46 | notificacion.isLeida = true; 47 | 48 | await notificacion.save(); 49 | 50 | res.json({ 51 | ok: true, 52 | notificacion, 53 | }); 54 | } catch (error) { 55 | console.log(error); 56 | res.status(500).json({ 57 | ok: false, 58 | msg: "Por favor hable con el administrador", 59 | }); 60 | } 61 | }; 62 | 63 | // Controlador para guardar una nueva notificación 64 | const guardarNotificacion = async ( 65 | tipo, 66 | usuario, 67 | mensaje, 68 | relacionadoId = null, 69 | telefonoUsuario = null, 70 | latitud, 71 | longitud, 72 | usuarioRemitente 73 | ) => { 74 | console.log("usuarioRemitente", usuarioRemitente); 75 | try { 76 | console.log(telefonoUsuario); 77 | const notificacion = new Notificacion({ 78 | tipo, 79 | usuario, 80 | publicacion: tipo === "publicacion" || tipo === "mensaje" ? relacionadoId : null, 81 | telefonoDestino: tipo === "sos" ? telefonoUsuario : null, 82 | mensaje, 83 | latitud, 84 | longitud, 85 | usuarioRemitente, 86 | }); 87 | 88 | await notificacion.save(); 89 | 90 | // Aquí puedes realizar acciones adicionales relacionadas con la notificación, si es necesario 91 | 92 | return notificacion; 93 | } catch (error) { 94 | console.log(error); 95 | throw new Error("Error al guardar la notificación"); 96 | } 97 | }; 98 | 99 | //deleteAllNotifications 100 | const deleteAllNotifications = async (req, res) => { 101 | const usuarioId = req.uid; 102 | 103 | try { 104 | const notificaciones = await Notificacion.deleteMany({ 105 | usuario: usuarioId, 106 | }); 107 | 108 | res.json({ 109 | ok: true, 110 | notificaciones, 111 | }); 112 | } catch (error) { 113 | console.log(error); 114 | res.status(500).json({ 115 | ok: false, 116 | msg: "Por favor hable con el administrador", 117 | }); 118 | } 119 | }; 120 | 121 | //deleteNotificationById 122 | const deleteNotificationById = async (req, res) => { 123 | const usuarioId = req.uid; 124 | const notificacionId = req.params.id; 125 | 126 | try { 127 | const notificacion = await Notificacion.findById(notificacionId); 128 | 129 | if (!notificacion) { 130 | return res.status(404).json({ mensaje: "Notificación no encontrada" }); 131 | } 132 | 133 | await notificacion.delete(); 134 | 135 | res.json({ 136 | ok: true, 137 | notificacion, 138 | }); 139 | } catch (error) { 140 | console.log(error); 141 | res.status(500).json({ 142 | ok: false, 143 | msg: "Por favor hable con el administrador", 144 | 145 | }); 146 | } 147 | }; 148 | 149 | module.exports = { 150 | obtenerNotificacionesUsuario, 151 | guardarNotificacion, 152 | marcarNotificacionComoLeida, 153 | deleteAllNotifications, 154 | deleteNotificationById, 155 | }; 156 | -------------------------------------------------------------------------------- /controllers/socket.js: -------------------------------------------------------------------------------- 1 | const { enviarNotificacion } = require("../helpers/enviar-notificacion"); 2 | const { 3 | Publicacion, 4 | Sala, 5 | Mensaje, 6 | Usuario, 7 | Comentario, 8 | } = require("../models"); 9 | 10 | const usuarioConectado = async (uid = "") => { 11 | if (!uid) return null; 12 | 13 | const usuario = await Usuario.findById(uid); 14 | if (!usuario) return null; 15 | 16 | usuario.online = true; 17 | await usuario.save(); 18 | return usuario; 19 | }; 20 | 21 | const usuarioDesconectado = async (uid = "") => { 22 | if (!uid) return null; 23 | 24 | const usuario = await Usuario.findById(uid); 25 | if (!usuario) return null; 26 | usuario.online = false; 27 | await usuario.save(); 28 | return usuario; 29 | }; 30 | 31 | const grabarMensaje = async (payload) => { 32 | // payload: { 33 | // de: '', 34 | // para: '', 35 | // texto: '' 36 | // } 37 | 38 | try { 39 | console.log(payload); 40 | const mensaje = new Mensaje(payload); 41 | await mensaje.save(); 42 | 43 | return true; 44 | } catch (error) { 45 | return false; 46 | } 47 | }; 48 | 49 | const grabarMensajeSala2 = async (payload) => { 50 | try { 51 | const { mensaje, de, para } = payload; 52 | const sala = await Sala.findById(para); 53 | 54 | const newMessage = new Mensaje({ mensaje, usuario: de }); 55 | await newMessage.save(); 56 | 57 | sala.mensajes.push(newMessage._id); 58 | await sala.save(); 59 | 60 | return true; 61 | } catch (error) { 62 | console.log(error); 63 | return false; 64 | } 65 | }; 66 | 67 | const grabarMensajeSala = async (payload) => { 68 | try { 69 | const { mensaje, de, para } = payload; 70 | 71 | // console.log(payload); 72 | const newMessage = new Mensaje({ mensaje, usuario: de }); 73 | await newMessage.save(); 74 | 75 | const sala = await Sala.findById(para); 76 | sala.mensajes.push(newMessage._id); 77 | await sala.save(); 78 | 79 | const usuariosEnGrupoOffline = await obtenerUsuariosSalaHelper(para, de); 80 | 81 | for (const usuario of usuariosEnGrupoOffline) { 82 | if (usuario._id.toString() === de) { 83 | continue; 84 | } 85 | 86 | //actualizar isSalasPendiente a true 87 | usuario.isSalasPendiente = true; 88 | usuario.isNotificacionesPendiente = true; 89 | 90 | usuario.salas = usuario.salas.map((sala) => { 91 | if (sala.salaId.toString() === para) { 92 | sala.mensajesNoLeidos++; 93 | sala.ultimaVezActivo = new Date(); 94 | } 95 | 96 | return sala; 97 | }); 98 | 99 | await usuario.save(); 100 | } 101 | 102 | const tokens = usuariosEnGrupoOffline.map((usuario) => usuario.tokenApp); 103 | const titulo = "Nuevo mensaje"; 104 | const desc = `Tienes un nuevo mensaje en el grupo ${sala.nombre}`; 105 | 106 | const data = { 107 | salaId: sala._id, 108 | nombre: sala.nombre, 109 | mensajesNoLeidos: sala.mensajesNoLeidos, 110 | ultimaVezActivo: sala.ultimaVezActivo, 111 | type: "sala", 112 | }; 113 | 114 | const allTokens = [].concat(...tokens); 115 | //TODO: enviar notificación 116 | await enviarNotificacion(allTokens, titulo, desc, data); 117 | return true; 118 | } catch (error) { 119 | console.log(error); 120 | return false; 121 | } 122 | }; 123 | 124 | const obtenerUsuariosSalaHelper = async (salaId, usuarioId) => { 125 | try { 126 | // El usuario que envía el mensaje 127 | const usuariosEnSala = await Usuario.find({ 128 | "salas.salaId": salaId, 129 | "salas.isRoomOpen": false, 130 | _id: { $ne: usuarioId }, 131 | }); 132 | 133 | return usuariosEnSala; 134 | } catch (error) { 135 | console.log(error); 136 | return []; 137 | } 138 | }; 139 | 140 | const grabarComentarioPublicacion = async (payload) => { 141 | console.log(payload); 142 | const usuarioId = payload.de; 143 | try { 144 | const { mensaje, para } = payload; 145 | 146 | const publicacion = await Publicacion.findById(para); 147 | if (!publicacion) { 148 | return res.status(404).json({ error: "Publicación no encontrada" }); 149 | } 150 | 151 | const comentario = new Comentario({ 152 | contenido: mensaje, 153 | usuario: usuarioId, 154 | publicacion: para, 155 | estado: "publicado", 156 | }); 157 | 158 | await comentario.save(); 159 | 160 | publicacion.comentarios.push(comentario._id); 161 | await publicacion.save(); 162 | 163 | return comentario._id.toString(); 164 | } catch (error) { 165 | console.error(error); 166 | return false; 167 | } 168 | }; 169 | 170 | module.exports = { 171 | usuarioConectado, 172 | usuarioDesconectado, 173 | grabarMensaje, 174 | grabarMensajeSala, 175 | grabarComentarioPublicacion, 176 | }; 177 | -------------------------------------------------------------------------------- /controllers/auth.js: -------------------------------------------------------------------------------- 1 | const { response } = require("express"); 2 | const bcrypt = require("bcryptjs"); 3 | 4 | const Usuario = require("../models/usuario"); 5 | const { generarJWT } = require("../helpers/jwt"); 6 | const { validarGoogleIdToken } = require("../helpers/google-verify-token"); 7 | 8 | const crearUsuario = async (req, res = response) => { 9 | const { email, password, tokenApp } = req.body; 10 | 11 | try { 12 | const existeEmail = await Usuario.findOne({ email }); 13 | if (existeEmail) { 14 | return res.status(400).json({ 15 | ok: false, 16 | msg: "El correo ya está registrado", 17 | }); 18 | } 19 | 20 | const usuario = new Usuario(req.body); 21 | 22 | // Encriptar contraseña 23 | const salt = bcrypt.genSaltSync(); 24 | usuario.password = bcrypt.hashSync(password, salt); 25 | usuario.tokenApp = tokenApp; 26 | await usuario.save(); 27 | 28 | // Generar mi JWT 29 | const token = await generarJWT(usuario.id); 30 | 31 | res.json({ 32 | ok: true, 33 | usuario, 34 | token, 35 | }); 36 | } catch (error) { 37 | console.log(error); 38 | res.status(500).json({ 39 | ok: false, 40 | msg: "Hable con el administrador", 41 | }); 42 | } 43 | }; 44 | const login = async (req, res = response) => { 45 | const { email, password, tokenApp } = req.body; 46 | console.log(req.body); 47 | try { 48 | const usuarioDB = await Usuario.findOne({ email }) 49 | .populate( 50 | "ubicaciones", 51 | "latitud longitud ciudad pais barrio updatedAt createdAt estado _id" 52 | ) 53 | .exec(); 54 | if (!usuarioDB) { 55 | return res.status(404).json({ 56 | ok: false, 57 | msg: "Email no encontrado", 58 | }); 59 | } 60 | 61 | // Validar el password 62 | const validPassword = bcrypt.compareSync(password, usuarioDB.password); 63 | if (!validPassword) { 64 | return res.status(400).json({ 65 | ok: false, 66 | msg: "La contraseña no es valida", 67 | }); 68 | } 69 | 70 | // Actualizar el token de dispositivo si se proporciona 71 | if (tokenApp) { 72 | await Usuario.findOneAndUpdate({ email }, { tokenApp }); 73 | } 74 | 75 | // Generar el JWT 76 | const token = await generarJWT(usuarioDB.id); 77 | 78 | res.json({ 79 | ok: true, 80 | usuario: usuarioDB, 81 | token, 82 | }); 83 | } catch (error) { 84 | console.log(error); 85 | return res.status(500).json({ 86 | ok: false, 87 | msg: "Hable con el administrador", 88 | }); 89 | } 90 | }; 91 | 92 | const googleAuth = async (req, res = response) => { 93 | const token = req.body.token; 94 | const { tokenApp } = req.body; 95 | if (!token) { 96 | return res.json({ 97 | ok: false, 98 | msg: "No hay token en la petición", 99 | }); 100 | } 101 | 102 | const googleUser = await validarGoogleIdToken(token); 103 | const { email } = googleUser; 104 | try { 105 | if (!googleUser) { 106 | return res.status(400).json({ 107 | ok: false, 108 | }); 109 | } 110 | 111 | let usuarioDB = await Usuario.findOne({ email }).populate( 112 | "ubicaciones", 113 | "latitud longitud ciudad pais barrio updatedAt createdAt estado _id" 114 | ); 115 | 116 | if (!usuarioDB) { 117 | // Si el usuario no existe, lo creamos 118 | const data = { 119 | nombre: googleUser.name, 120 | tokenApp: tokenApp, 121 | email: googleUser.email, 122 | password: "@@@", 123 | img: googleUser.picture, 124 | google: true, 125 | }; 126 | 127 | usuarioDB = new Usuario(data); 128 | 129 | await usuarioDB.save(); 130 | } 131 | 132 | // Actualizar el token de dispositivo si se proporciona 133 | if (tokenApp) { 134 | usuarioDB.tokenApp = tokenApp; 135 | await usuarioDB.save(); 136 | } 137 | 138 | // Generar el JWT 139 | const token = await generarJWT(usuarioDB.id); 140 | 141 | return res.json({ 142 | ok: true, 143 | usuario: usuarioDB, 144 | token, 145 | }); 146 | } catch (error) { 147 | console.log(error); 148 | return res.status(500).json({ 149 | ok: false, 150 | msg: "Hable con el administrador", 151 | }); 152 | } 153 | }; 154 | const renewToken = async (req, res = response) => { 155 | try { 156 | const uid = req.uid; 157 | 158 | // Generar un nuevo JWT usando el UID del usuario 159 | const token = await generarJWT(uid); 160 | 161 | // Obtener el usuario por el UID desde la base de datos 162 | const usuarioDB = await Usuario.findById(uid).populate( 163 | "ubicaciones", 164 | "latitud longitud ciudad pais barrio updatedAt createdAt estado _id" 165 | ); 166 | 167 | if (!usuarioDB) { 168 | return res.status(404).json({ 169 | ok: false, 170 | msg: "Usuario no encontrado", 171 | }); 172 | } 173 | 174 | // Obtener el token de dispositivo (tokenApp) desde la solicitud 175 | const { tokenApp } = req.body; 176 | 177 | // Actualizar el token de dispositivo si se proporciona en la solicitud 178 | if (tokenApp) { 179 | usuarioDB.tokenApp = tokenApp; 180 | await usuarioDB.save(); 181 | } 182 | 183 | // Devolver la respuesta con el nuevo token JWT y los datos del usuario 184 | res.json({ 185 | ok: true, 186 | usuario: usuarioDB, 187 | token, 188 | }); 189 | } catch (error) { 190 | console.log(error); 191 | return res.status(500).json({ 192 | ok: false, 193 | msg: "Hable con el administrador", 194 | }); 195 | } 196 | }; 197 | 198 | module.exports = { 199 | crearUsuario, 200 | login, 201 | renewToken, 202 | googleAuth, 203 | }; 204 | -------------------------------------------------------------------------------- /controllers/uploads.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | 4 | const { response } = require("express"); 5 | const { subirArchivoUsuario } = require("../helpers/subir-archivo"); 6 | 7 | const { Usuario, Publicacion } = require("../models"); 8 | 9 | const cargarArchivo = async (req, res = response) => { 10 | try { 11 | const nombres = []; 12 | for (const archivo of req.files.archivo) { 13 | const nombre = await subirArchivoUsuario(archivo, undefined, "imgs"); 14 | nombres.push(nombre); 15 | } 16 | res.json({ nombres }); 17 | } catch (msg) { 18 | res.status(400).json({ msg }); 19 | } 20 | }; 21 | 22 | const mostrarImagen = async (req, res = response) => { 23 | const { id, coleccion } = req.params; 24 | 25 | let modelo; 26 | 27 | switch (coleccion) { 28 | case "usuarios": 29 | modelo = await Usuario.findById(id); 30 | if (!modelo) { 31 | return res.status(400).json({ 32 | msg: `No existe un usuario con el id ${id}`, 33 | }); 34 | } 35 | 36 | break; 37 | 38 | case "publicaciones": 39 | modelo = await Publicacion.findById(id); 40 | if (!modelo) { 41 | return res.status(400).json({ 42 | msg: `No existe un producto con el id ${id}`, 43 | }); 44 | } 45 | 46 | break; 47 | 48 | default: 49 | return res.status(500).json({ msg: "Se me olvidó validar esto" }); 50 | } 51 | 52 | const idqury = req.query.imagenIndex; 53 | 54 | //si no se especifica el id de la imagen se muestra todas las imagenes 55 | 56 | // Limpiar imágenes previas 57 | if (modelo.imagenes) { 58 | //motrar imagen si concide con el id del arreglo de imagenes 59 | const pathImagen = path.join( 60 | __dirname, 61 | "../uploads", 62 | coleccion + "/" + modelo.titulo.replace(/\s/g, ""), 63 | modelo.imagenes.find((img) => img === idqury) 64 | ); 65 | 66 | if (fs.existsSync(pathImagen)) { 67 | //Sirve para verificar si existe el archivo en el path especificado 68 | return res.sendFile(pathImagen); 69 | } 70 | } 71 | 72 | const pathImagen = path.join(__dirname, "../assets/no-image.jpg"); 73 | res.sendFile(pathImagen); 74 | }; 75 | 76 | const mostrarAllImagenes = async (req, res = response) => { 77 | const { id, coleccion } = req.params; 78 | 79 | let modelo; 80 | 81 | switch (coleccion) { 82 | case "usuarios": 83 | modelo = await Usuario.findById(id); 84 | if (!modelo) { 85 | return res.status(400).json({ 86 | msg: `No existe un usuario con el id ${id}`, 87 | }); 88 | } 89 | break; 90 | 91 | case "publicaciones": 92 | modelo = await Publicacion.findById(id); 93 | if (!modelo) { 94 | return res.status(400).json({ 95 | msg: `No existe una publicación con el id ${id}`, 96 | }); 97 | } 98 | break; 99 | 100 | default: 101 | return res.status(500).json({ msg: "Se me olvidó validar esto" }); 102 | } 103 | 104 | // Limpiar imágenes previas 105 | if (modelo.imagenes && modelo.imagenes.length > 0) { 106 | const pathImagenes = modelo.imagenes.map((imagenId) => { 107 | const pathImagen = path.join( 108 | __dirname, 109 | "../uploads", 110 | coleccion, 111 | imagenId 112 | ); 113 | return fs.existsSync(pathImagen) ? pathImagen : null; 114 | }); 115 | 116 | // Filtrar las rutas de imagen válidas 117 | const rutasValidas = pathImagenes.filter((ruta) => ruta !== null); 118 | 119 | if (rutasValidas.length > 0) { 120 | return res.json({ imagenes: rutasValidas }); 121 | } 122 | } 123 | 124 | const pathImagen = path.join(__dirname, "../assets/no-image.jpg"); 125 | 126 | res.sendFile(pathImagen); 127 | }; 128 | 129 | const mostrarImagenUsuario = async (req, res = response) => { 130 | const { id, coleccion } = req.params; 131 | 132 | let modelo; 133 | 134 | switch (coleccion) { 135 | case "usuarios": 136 | modelo = await Usuario.findById(id); 137 | if (!modelo) { 138 | return res.status(400).json({ 139 | msg: `No existe un usuario con el id ${id}`, 140 | }); 141 | } 142 | 143 | break; 144 | 145 | default: 146 | return res.status(500).json({ msg: "Se me olvidó validar esto" }); 147 | } 148 | 149 | // Limpiar imágenes previas 150 | if (modelo.img) { 151 | // Hay que borrar la imagen del servidor 152 | const pathImagen = path.join( 153 | __dirname, 154 | "../uploads", 155 | coleccion, 156 | modelo.img 157 | ); 158 | 159 | if (fs.existsSync(pathImagen)) { 160 | return res.sendFile(pathImagen);//S 161 | } 162 | } 163 | 164 | const pathImagen = path.join(__dirname, "../assets/no-image.jpg"); 165 | res.sendFile(pathImagen); 166 | }; 167 | 168 | const actualizarImagen = async (req, res = response) => { 169 | const { id, coleccion } = req.params; 170 | 171 | let modelo; 172 | 173 | switch (coleccion) { 174 | case "usuarios": 175 | modelo = await Usuario.findById(id); 176 | if (!modelo) { 177 | return res.status(400).json({ 178 | msg: `No existe un usuario con el id ${id}`, 179 | }); 180 | } 181 | 182 | break; 183 | 184 | case "publicaciones": 185 | modelo = await Publicacion.findById(id); 186 | if (!modelo) { 187 | return res.status(400).json({ 188 | msg: `No existe una publicación con el id ${id}`, 189 | }); 190 | } 191 | 192 | break; 193 | 194 | default: 195 | return res.status(500).json({ msg: "Se me olvidó validar esto" }); 196 | } 197 | 198 | if (modelo.img) { 199 | // Hay que borrar la imagen del servidor 200 | const pathImagen = path.join( 201 | __dirname, 202 | "../uploads", 203 | coleccion, 204 | modelo.img 205 | ); 206 | if (fs.existsSync(pathImagen)) { 207 | fs.unlinkSync(pathImagen); 208 | } 209 | } 210 | 211 | const nombre = await subirArchivoUsuario(req.files, undefined, coleccion); 212 | modelo.img = nombre; 213 | 214 | await modelo.save(); 215 | 216 | res.json(modelo); 217 | }; 218 | 219 | module.exports = { 220 | cargarArchivo, 221 | mostrarImagen, 222 | mostrarAllImagenes, 223 | actualizarImagen, 224 | mostrarImagenUsuario, 225 | }; 226 | -------------------------------------------------------------------------------- /controllers/comentarios.js: -------------------------------------------------------------------------------- 1 | const { 2 | guardarNotificacionPublicacion, 3 | enviarNotificacion, 4 | guardarNotificacionPublicacionMensaje, 5 | } = require("../helpers/enviar-notificacion"); 6 | const { Publicacion, Comentario, Usuario } = require("../models"); 7 | 8 | const createComentario2 = async (req, res) => { 9 | const usuarioId = req.uid; 10 | try { 11 | const { contenido, publicacionId } = req.body; 12 | 13 | // Verificar si la publicación existe 14 | const publicacion = await Publicacion.findById(publicacionId); 15 | if (!publicacion) { 16 | return res.status(404).json({ error: "Publicación no encontrada" }); 17 | } 18 | 19 | // Crear el nuevo comentario 20 | const comentario = new Comentario({ 21 | contenido, 22 | usuario: usuarioId, 23 | publicacion: publicacionId, 24 | estado: "publicado", 25 | }); 26 | 27 | // Guardar el comentario en la base de datos 28 | await comentario.save(); 29 | 30 | // Agregar el comentario a la lista de comentarios de la publicación 31 | publicacion.comentarios.push(comentario._id); 32 | await publicacion.save(); 33 | res.status(201).json({ comentario }); 34 | 35 | publicacion.toObject(); 36 | publicacion.type = "publication"; 37 | delete publicacion.__v; 38 | // Obtener los IDs de los usuarios que han comentado en la publicación 39 | const usuariosQueComentaron = await Usuario.find({ 40 | _id: { $in: publicacion.comentarios }, 41 | }); 42 | 43 | console.log(usuariosQueComentaron); 44 | 45 | // Obtener los tokens de los usuarios para enviar notificaciones 46 | const tokens = usuariosQueComentaron.map((usuario) => usuario.tokenApp); 47 | console.log(tokens); 48 | const titulo = 49 | "Nuevo comentario en una publicación en la que has comentado"; 50 | 51 | // Envío de notificaciones a los usuarios 52 | // await enviarNotificacion(tokens, titulo, contenido, publicacion); 53 | 54 | // Guardar notificaciones de comentarios en la publicación 55 | // for (const usuario of usuariosQueComentaron) { 56 | // await guardarNotificacionPublicacion( 57 | // usuario._id.toString(), 58 | // contenido, 59 | // publicacion._id.toString(), 60 | // publicacion.latitud, 61 | // publicacion.longitud, 62 | // publicacion.usuario.toString() 63 | // ); 64 | // } 65 | } catch (error) { 66 | console.error(error); 67 | res.status(500).json({ error: "Error al crear el comentario" }); 68 | } 69 | }; 70 | 71 | const createComentario = async (req, res) => { 72 | const usuarioId = req.uid; 73 | try { 74 | const { contenido, publicacionId } = req.body; 75 | 76 | // Verificar si la publicación existe 77 | const publicacion = await Publicacion.findById(publicacionId); 78 | if (!publicacion) { 79 | return res.status(404).json({ error: "Publicación no encontrada" }); 80 | } 81 | 82 | // Crear el nuevo comentario 83 | const comentario = new Comentario({ 84 | contenido, 85 | usuario: usuarioId, 86 | publicacion: publicacionId, 87 | estado: "publicado", 88 | }); 89 | 90 | // Guardar el comentario en la base de datos 91 | await comentario.save(); 92 | 93 | // Agregar el comentario a la lista de comentarios de la publicación 94 | publicacion.comentarios.push(comentario._id); 95 | await publicacion.save(); 96 | res.status(201).json({ comentario }); 97 | 98 | const publicacion2 = publicacion.toObject(); // Asignar la nueva instancia 99 | publicacion2.type = "publication"; 100 | delete publicacion2.__v; 101 | console.log(publicacion2); 102 | 103 | // Obtener los comentarios relacionados con la publicación 104 | const comentariosDeLaPublicacion = await Comentario.find({ publicacion: publicacionId }); 105 | 106 | // Obtener los IDs de los usuarios que han comentado 107 | const usuariosQueComentaronIds = comentariosDeLaPublicacion.map((comentario) => comentario.usuario); 108 | 109 | // Obtener los usuarios que han comentado 110 | const usuariosQueComentaron = await Usuario.find({ _id: { $in: usuariosQueComentaronIds } }); 111 | 112 | // Obtener los tokens de los usuarios para enviar notificaciones 113 | const tokens = usuariosQueComentaron.map((usuario) => usuario.tokenApp); 114 | const titulo = "Comentaron en un reporte"; 115 | const subTitulo = "Comentario en un reporte en el que has comentado"; 116 | // Envío de notificaciones a los usuarios 117 | await enviarNotificacion(tokens, titulo, contenido, publicacion2); 118 | 119 | // Guardar notificaciones de comentarios en la publicación 120 | for (const usuario of usuariosQueComentaron) { 121 | await guardarNotificacionPublicacionMensaje( 122 | usuario._id.toString(), 123 | contenido, 124 | publicacion2._id.toString(), 125 | publicacion2.latitud, 126 | publicacion2.longitud, 127 | publicacion2.usuario.toString() 128 | ); 129 | } 130 | } catch (error) { 131 | console.error(error); 132 | res.status(500).json({ error: "Error al crear el comentario" }); 133 | } 134 | }; 135 | 136 | 137 | const getComentariosByPublicacion = async (req, res) => { 138 | try { 139 | const { publicacionId } = req.params; 140 | // Buscar los comentarios de la publicación en la base de datos 141 | const comentarios = await Comentario.find({ 142 | publicacion: publicacionId, 143 | }).populate("usuario", "nombre google img"); 144 | 145 | res.status(200).json({ 146 | ok: true, 147 | comentarios, 148 | }); 149 | } catch (error) { 150 | console.error(error); 151 | res.status(500).json({ error: "Error al obtener los comentarios" }); 152 | } 153 | }; 154 | 155 | const toggleLikeComentario = async (req, res) => { 156 | try { 157 | const comentarioId = req.params.id; 158 | 159 | const comentario = await Comentario.findById(comentarioId).populate( 160 | "usuario", 161 | "nombre img" 162 | ); 163 | if (!comentario) { 164 | return res.status(404).json({ error: "Comentario no encontrado" }); 165 | } 166 | 167 | // Verificar si el usuario ya ha dado like al comentario 168 | const usuarioId = req.uid; 169 | const usuarioIndex = comentario.likes.findIndex( 170 | (userId) => userId.toString() === usuarioId 171 | ); 172 | 173 | if (usuarioIndex === -1) { 174 | // El usuario no ha dado like al comentario, agregar el like 175 | comentario.likes.push(usuarioId); 176 | } else { 177 | // El usuario ya ha dado like al comentario, quitar el like 178 | comentario.likes.splice(usuarioIndex, 1); 179 | } 180 | 181 | await comentario.save(); 182 | 183 | res.status(200).json({ ok: true, comentario: comentario }); 184 | } catch (error) { 185 | console.error(error); 186 | res.status(500).json({ error: "Error al modificar el contador de likes" }); 187 | } 188 | }; 189 | 190 | module.exports = { 191 | createComentario, 192 | getComentariosByPublicacion, 193 | toggleLikeComentario, 194 | }; 195 | -------------------------------------------------------------------------------- /prueba/public/pdfTemplate.js: -------------------------------------------------------------------------------- 1 | module.exports = (dataInfo) => { 2 | console.log(dataInfo); 3 | const data = dataInfo.publicaciones; 4 | 5 | const today = new Date(); 6 | const groupedData = {}; 7 | 8 | data.forEach((item) => { 9 | if (!groupedData[item.titulo]) { 10 | groupedData[item.titulo] = []; 11 | } 12 | groupedData[item.titulo].push(item); 13 | }); 14 | 15 | return ` 16 | 17 | 18 | 19 | 20 | PDF Result Template 21 | 177 | 178 | 179 |
180 |
181 | 184 |
185 |
186 |

UNIVERSIDAD DE LAS FUERZAS ARMADAS - ESPE

187 |

REPORTE DE EMERGENCIAS COMUNITARIAS

188 |
189 |
190 | 193 |
194 |
195 | 196 |
197 |
198 |
Usuario registrado
199 |
${data.totalUsuarios}
200 |
201 |
202 |
Publicaciones registradas
203 |
${data.length}
204 |
205 |
206 |
Publicaciones por mes
207 |
${data.totalPublicacionesMes}
208 |
209 |
210 |
Publicaciones por día
211 |
${data.totalPublicacionesDia}
212 |
213 |
214 | 215 | 216 |
217 | 218 | 219 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | ${Object.entries(groupedData) 238 | .map( 239 | ([titulo, items]) => ` 240 | 241 | 242 | 243 | 244 | 247 | 248 | 251 | 252 | 255 | 256 | ` 257 | ) 258 | .join("")} 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 |
220 | 221 |
222 |
Reporte de Emergencias
223 |
${today.toLocaleDateString()}
224 |
225 |
TítuloCiudadFecha de creaciónFecha de actualizaciónHora de creaciónHora de actualizaciónCantidadPorcentaje
${titulo}${items[0].ciudad}${items[0].createdAt.toLocaleDateString()} 245 | ${items[items.length - 1].updatedAt.toLocaleDateString()} 246 | ${items[0].createdAt.toLocaleTimeString()} 249 | ${items[items.length - 1].updatedAt.toLocaleTimeString()} 250 | ${items.length} 253 | ${((items.length / data.length) * 100).toFixed(2)}% 254 |
Total${data.length}100%
271 |
272 | 273 | 274 | 275 | `; 276 | }; 277 | -------------------------------------------------------------------------------- /controllers/usuarios.js: -------------------------------------------------------------------------------- 1 | const { response } = require("express"); 2 | const { Usuario } = require("../models"); 3 | const { 4 | enviarNotificacion, 5 | guardarNotificacionSOS, 6 | } = require("../helpers/enviar-notificacion"); 7 | 8 | const getUsuarios = async (req, res = response) => { 9 | const desde = Number(req.query.desde) || 0; 10 | 11 | const usuarios = await Usuario.find({ _id: { $ne: req.uid } }) 12 | .sort("-online") 13 | .skip(desde) 14 | .limit(20); 15 | 16 | res.json({ 17 | ok: true, 18 | usuarios, 19 | }); 20 | }; 21 | 22 | const actualizarUsuario = async (req, res) => { 23 | const uid = req.uid; 24 | const { nombre, email, online, password, telefono, ...resto } = req.body; 25 | 26 | try { 27 | // Busca y actualiza el usuario por su ID 28 | const usuario = await Usuario.findByIdAndUpdate(uid, resto, { new: true }); 29 | 30 | res.json({ 31 | ok: true, 32 | usuario, 33 | }); 34 | } catch (error) { 35 | console.log(error); 36 | res.status(500).json({ 37 | ok: false, 38 | msg: "Por favor hable con el administrador", 39 | }); 40 | } 41 | }; 42 | 43 | const actualizarIsOpenRoom = async (req, res) => { 44 | const uid = req.uid; 45 | const { isOpenRoom } = req.body; 46 | 47 | try { 48 | // Busca y actualiza el usuario por su ID 49 | const usuario = await Usuario.findByIdAndUpdate( 50 | uid, 51 | { isOpenRoom }, 52 | { new: true } 53 | ); 54 | 55 | res.json({ 56 | ok: true, 57 | usuario, 58 | }); 59 | } catch (error) { 60 | console.log(error); 61 | res.status(500).json({ 62 | ok: false, 63 | msg: "Por favor hable con el administrador", 64 | }); 65 | } 66 | }; 67 | 68 | const actualizarTelefonoOrNombre = async (req, res) => { 69 | const uid = req.uid; 70 | const { nombre, telefono } = req.body; 71 | try { 72 | // Busca y actualiza el usuario por su ID 73 | const usuario = await Usuario.findByIdAndUpdate( 74 | uid, 75 | { nombre, telefono }, 76 | { new: true } 77 | ); 78 | 79 | res.json({ 80 | ok: true, 81 | usuario, 82 | }); 83 | } catch (error) { 84 | console.log(error); 85 | res.status(500).json({ 86 | ok: false, 87 | msg: "Por favor hable con el administrador", 88 | }); 89 | } 90 | }; 91 | 92 | // Controlador para agregar una nueva dirección a un usuario 93 | const agregarDireccion = async (req, res) => { 94 | const idUsuario = req.uid; 95 | const { latitud, longitud } = req.body; 96 | 97 | try { 98 | const usuario = await Usuario.findById(idUsuario); 99 | 100 | if (!usuario) { 101 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 102 | } 103 | 104 | const nuevaDireccion = { 105 | latitud, 106 | longitud, 107 | }; 108 | 109 | usuario.direcciones.push(nuevaDireccion); 110 | 111 | await usuario.save(); 112 | 113 | res.status(201).json({ mensaje: "Dirección agregada", usuario }); 114 | } catch (error) { 115 | console.error(error); 116 | res.status(500).json({ mensaje: "Error al agregar la dirección" }); 117 | } 118 | }; 119 | 120 | const ageregarTelefonos = async (req, res) => { 121 | const idUsuario = req.uid; 122 | 123 | const { telefono } = req.body; 124 | 125 | try { 126 | const usuario = await Usuario.findById(idUsuario); 127 | 128 | if (!usuario) { 129 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 130 | } 131 | 132 | // Verificar si el teléfono ya está asociado al usuario 133 | if (usuario.telefonos.includes(telefono)) { 134 | return res.status(400).json({ 135 | ok: false, 136 | msg: "El teléfono ya está asociado al usuario", 137 | }); 138 | } 139 | 140 | usuario.telefonos.push(telefono); 141 | await usuario.save(); 142 | 143 | res.status(201).json({ mensaje: "Teléfono agregado", usuario }); 144 | } catch (error) { 145 | console.error(error); 146 | res.status(500).json({ mensaje: "Error al agregar el teléfono" }); 147 | } 148 | }; 149 | 150 | const eliminarTelefono = async (req, res) => { 151 | const idUsuario = req.uid; 152 | const { telefono } = req.body; 153 | 154 | try { 155 | const usuario = await Usuario.findById(idUsuario); 156 | 157 | if (!usuario) { 158 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 159 | } 160 | 161 | // Verificar si el teléfono está asociado al usuario 162 | if (!usuario.telefonos.includes(telefono)) { 163 | return res.status(400).json({ 164 | ok: false, 165 | msg: "El teléfono no está asociado al usuario", 166 | }); 167 | } 168 | 169 | // Eliminar el teléfono del arreglo 170 | usuario.telefonos = usuario.telefonos.filter((tel) => tel !== telefono); 171 | await usuario.save(); 172 | 173 | res.status(200).json({ mensaje: "Teléfono eliminado", usuario }); 174 | } catch (error) { 175 | console.error(error); 176 | res.status(500).json({ mensaje: "Error al eliminar el teléfono" }); 177 | } 178 | }; 179 | 180 | const agregarTelefono = async (req, res) => { 181 | const idUsuario = req.uid; 182 | const { telefono } = req.body; 183 | 184 | try { 185 | const usuario = await Usuario.findById(idUsuario); 186 | 187 | if (!usuario) { 188 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 189 | } 190 | 191 | usuario.telefono = telefono; 192 | await usuario.save(); 193 | 194 | res.status(201).json({ mensaje: "Teléfono agregado", usuario }); 195 | } catch (error) { 196 | console.error(error); 197 | res.status(500).json({ mensaje: "Error al agregar el teléfono" }); 198 | } 199 | }; 200 | 201 | const enviarNotificacionesArrayTelefonos = async (req, res) => { 202 | const idUsuario = req.uid; 203 | const { lat, lng } = req.body; 204 | 205 | try { 206 | const usuario = await Usuario.findById(idUsuario).populate( 207 | "ubicaciones", 208 | "latitud longitud" 209 | ); 210 | 211 | if (!usuario) { 212 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 213 | } 214 | 215 | const telefonos = usuario.telefonos; // Obtener el arreglo de teléfonos del usuario 216 | 217 | const usuariosConTelefono = await Usuario.find({ 218 | telefono: { $in: telefonos }, 219 | }); 220 | const tokens = usuariosConTelefono.map((usuario) => usuario.tokenApp); 221 | 222 | const titulo = `${usuario.nombre} necesita ayuda`; 223 | const contenido = "Presiona para ver la ubicación"; 224 | 225 | //TODO: Notificación SOS 226 | const data = { 227 | nombre: usuario.nombre, 228 | latitud: lat, 229 | longitud: lng, 230 | img: usuario.img, 231 | google: usuario.google, 232 | type: "sos", 233 | }; 234 | 235 | await enviarNotificacion(tokens, titulo, contenido, data); 236 | 237 | for (const usuarioDestino of usuariosConTelefono) { 238 | await guardarNotificacionSOS( 239 | usuarioDestino._id, 240 | contenido, 241 | usuario.telefono, 242 | lat, 243 | lng, 244 | idUsuario 245 | ); 246 | //TODO: Verificar si el usuario tiene la app abierta 247 | usuarioDestino.isNotificacionesPendiente = true; 248 | await usuarioDestino.save(); 249 | } 250 | res 251 | .status(200) 252 | .json({ mensaje: "Notificación enviada", usuarios: usuariosConTelefono }); 253 | } catch (error) { 254 | console.error(error); 255 | res.status(500).json({ mensaje: "Error al enviar la notificación" }); 256 | } 257 | }; 258 | 259 | const marcarPublicacionPendienteFalse = async (req, res) => { 260 | const idUsuario = req.uid; 261 | 262 | try { 263 | const usuario = await Usuario.findById(idUsuario); 264 | 265 | if (!usuario) { 266 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 267 | } 268 | usuario.isPublicacionPendiente = false; 269 | await usuario.save(); 270 | 271 | res.status(200).json({ 272 | mensaje: "Campo isPublicacionPendiente actualizado a false", 273 | usuario, 274 | }); 275 | } catch (error) { 276 | console.error(error); 277 | res 278 | .status(500) 279 | .json({ mensaje: "Error al actualizar el campo isPublicacionPendiente" }); 280 | } 281 | }; 282 | 283 | const marcarSalaPendienteFalse = async (req, res) => { 284 | const idUsuario = req.uid; 285 | 286 | try { 287 | const usuario = await Usuario.findById(idUsuario); 288 | 289 | if (!usuario) { 290 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 291 | } 292 | 293 | usuario.isSalasPendiente = false; 294 | await usuario.save(); 295 | 296 | res 297 | .status(200) 298 | .json({ mensaje: "Campo isSalaPendiente actualizado a false", usuario }); 299 | } catch (error) { 300 | console.error(error); 301 | res 302 | 303 | .status(500) 304 | .json({ mensaje: "Error al actualizar el campo isSalaPendiente" }); 305 | } 306 | }; 307 | 308 | //isNotificacionesPendiente 309 | const marcarNotificacionesPendienteFalse = async (req, res) => { 310 | const idUsuario = req.uid; 311 | 312 | try { 313 | const usuario = await Usuario.findById(idUsuario); 314 | 315 | if (!usuario) { 316 | return res.status(404).json({ mensaje: "Usuario no encontrado" }); 317 | } 318 | 319 | usuario.isNotificacionesPendiente = false; 320 | await usuario.save(); 321 | 322 | res.status(200).json({ 323 | mensaje: "Campo isNotificacionesPendiente actualizado a false", 324 | usuario, 325 | }); 326 | } catch (error) { 327 | console.error(error); 328 | res 329 | .status(500) 330 | .json({ 331 | mensaje: "Error al actualizar el campo isNotificacionesPendiente", 332 | }); 333 | } 334 | }; 335 | 336 | const eliminarTokenApp = async (req, res) => { 337 | const uid = req.uid; 338 | 339 | try { 340 | // Busca y actualiza el usuario por su ID para eliminar el tokenApp 341 | const usuario = await Usuario.findByIdAndUpdate( 342 | uid, 343 | { $unset: { tokenApp: "" } }, 344 | { new: true } 345 | ); 346 | 347 | res.json({ 348 | ok: true, 349 | usuario, 350 | msg: "El tokenApp ha sido eliminado correctamente.", 351 | }); 352 | } catch (error) { 353 | console.log(error); 354 | res.status(500).json({ 355 | ok: false, 356 | msg: "Por favor hable con el administrador", 357 | }); 358 | } 359 | }; 360 | 361 | module.exports = { 362 | getUsuarios, 363 | actualizarUsuario, 364 | agregarDireccion, 365 | ageregarTelefonos, 366 | agregarTelefono, 367 | eliminarTelefono, 368 | enviarNotificacionesArrayTelefonos, 369 | actualizarTelefonoOrNombre, 370 | actualizarIsOpenRoom, 371 | marcarPublicacionPendienteFalse, 372 | marcarSalaPendienteFalse, 373 | marcarNotificacionesPendienteFalse, 374 | eliminarTokenApp, 375 | }; 376 | -------------------------------------------------------------------------------- /controllers/publicaciones.js: -------------------------------------------------------------------------------- 1 | const { calcularDistancia } = require("../helpers/calcular-distancia"); 2 | const { 3 | enviarNotificacion, 4 | guardarNotificacionPublicacion, 5 | } = require("../helpers/enviar-notificacion"); 6 | const { subirArchivoPublicacion } = require("../helpers/subir-archivo"); 7 | const { Usuario, Publicacion } = require("../models"); 8 | 9 | const obtenerPublicacionesUsuario = async (req, res) => { 10 | const usuarioId = req.uid; 11 | try { 12 | const publicaciones = await Publicacion.find({ usuario: usuarioId }); 13 | 14 | res.json({ 15 | ok: true, 16 | publicaciones, 17 | }); 18 | } catch (error) { 19 | console.log(error); 20 | res.status(500).json({ 21 | ok: false, 22 | msg: "Por favor hable con el administrador", 23 | }); 24 | } 25 | }; 26 | const guardarPublicacion = async (req, res) => { 27 | const radio = 2; 28 | const usuarioId = req.uid; 29 | const nombres = []; 30 | const { 31 | titulo, 32 | contenido, 33 | color, 34 | ciudad, 35 | barrio, 36 | isPublic, 37 | imagenes, 38 | imgAlerta, 39 | latitud, 40 | longitud, 41 | nombreUsuario, 42 | } = req.body; 43 | 44 | try { 45 | const publicacion = new Publicacion({ 46 | titulo, 47 | contenido, 48 | color, 49 | ciudad, 50 | barrio, 51 | isPublic, 52 | usuario: usuarioId, 53 | imagenes, 54 | imgAlerta, 55 | latitud, 56 | longitud, 57 | nombreUsuario, 58 | }); 59 | await publicacion.save(); 60 | 61 | res.json({ 62 | ok: true, 63 | publicacion, 64 | }); 65 | 66 | const usuarios = await Usuario.find().populate( 67 | "ubicaciones", 68 | "latitud longitud tokenApp" 69 | ); 70 | 71 | const usuariosEnRadio = usuarios.filter((usuario) => { 72 | return usuario.ubicaciones.some((ubicacion) => { 73 | const distancia = calcularDistancia( 74 | ubicacion.latitud, 75 | ubicacion.longitud, 76 | latitud, 77 | longitud 78 | ); 79 | return distancia <= radio; 80 | }); 81 | }); 82 | 83 | const tokens = usuariosEnRadio 84 | .filter((usuario) => usuario._id.toString() !== usuarioId.toString()) 85 | .map((usuario) => usuario.tokenApp); 86 | 87 | // Actualizar el campo isPublicacionPendiente a true para todos los usuarios en usuariosEnRadio 88 | for (const usuario of usuariosEnRadio) { 89 | usuario.isPublicacionPendiente = true; 90 | usuario.isNotificacionesPendiente = true; 91 | await usuario.save(); 92 | } 93 | 94 | //TODO: Notificar a los usuarios en el radio 95 | const publicacion2 = publicacion.toObject(); 96 | publicacion2.type = "publication"; 97 | console.log(publicacion2); 98 | delete publicacion2.__v; 99 | await enviarNotificacion(tokens, titulo, contenido, publicacion2); 100 | 101 | const usuariosEnRadio2 = usuariosEnRadio.filter((usuario) => usuario._id.toString() !== usuarioId.toString()); 102 | 103 | for (const usuario of usuariosEnRadio2) { 104 | await guardarNotificacionPublicacion( 105 | usuario._id, 106 | contenido, 107 | publicacion._id, 108 | latitud, 109 | longitud, 110 | usuarioId 111 | ); 112 | } 113 | } catch (error) { 114 | console.log(error); 115 | res.status(500).json({ 116 | ok: false, 117 | msg: "Por favor hable con el administrador", 118 | }); 119 | } 120 | }; 121 | 122 | const guardarListArchivo = async (req, res) => { 123 | const nombres = []; 124 | const { titulo, uid } = req.params; 125 | 126 | const archivo = req.files?.archivo; 127 | 128 | try { 129 | const publicacion = await Publicacion.findById(uid); 130 | 131 | if (!publicacion) { 132 | return res.status(404).json({ mensaje: "Publicación no encontrada" }); 133 | } 134 | if (archivo !== undefined && archivo !== null) { 135 | if (Array.isArray(archivo)) { 136 | for (const file of archivo) { 137 | const nombre = await subirArchivoPublicacion( 138 | file, 139 | undefined, 140 | "publicaciones/" + titulo.replace(/\s/g, "") 141 | ); 142 | if (!publicacion.imagenes) { 143 | publicacion.imagenes = []; // Inicializar como un array vacío si es nulo 144 | } 145 | publicacion.imagenes.push(nombre); 146 | nombres.push(nombre); 147 | } 148 | } else { 149 | const nombre = await subirArchivoPublicacion( 150 | archivo, 151 | undefined, 152 | "publicaciones/" + titulo.replace(/\s/g, "") 153 | ); 154 | if (!publicacion.imagenes) { 155 | publicacion.imagenes = []; // Inicializar como un array vacío si es nulo 156 | } 157 | publicacion.imagenes.push(nombre); 158 | nombres.push(nombre); 159 | } 160 | } 161 | 162 | await publicacion.save(); 163 | 164 | res.json({ 165 | ok: true, 166 | publicacion, 167 | nombres, 168 | }); 169 | } catch (error) { 170 | console.log(error); 171 | res.status(500).json({ 172 | ok: false, 173 | msg: "Por favor hable con el administrador", 174 | }); 175 | } 176 | }; 177 | 178 | const getPublicacionesEnRadio = async (req, res) => { 179 | const radio = 2; // Radio en kilómetros 180 | const { limite = 15, desde = 0 } = req.query; 181 | try { 182 | const usuario = await Usuario.findById(req.uid).populate( 183 | "ubicaciones", 184 | "latitud longitud" 185 | ); 186 | 187 | if (!usuario) { 188 | return res.status(404).json({ mensaje: "Usuario no encontrado." }); 189 | } 190 | 191 | let publicacionesEnRadio; 192 | 193 | if (usuario.ubicaciones.length > 0) { 194 | publicacionesEnRadio = await Publicacion.find({ 195 | latitud: { $exists: true }, 196 | longitud: { $exists: true }, 197 | isActivo: true, 198 | }) 199 | .sort({ createdAt: -1 }) 200 | .skip(Number(desde)); 201 | 202 | publicacionesEnRadio = publicacionesEnRadio.filter((publicacion) => { 203 | return usuario.ubicaciones.some((direccion) => { 204 | const distancia = calcularDistancia( 205 | publicacion.latitud, 206 | publicacion.longitud, 207 | direccion.latitud, 208 | direccion.longitud 209 | ); 210 | 211 | return distancia <= radio; 212 | }); 213 | }); 214 | } else { 215 | publicacionesEnRadio = await Publicacion.find({ isActivo: true }) 216 | .sort({ createdAt: -1 }) 217 | .skip(Number(desde)); 218 | } 219 | 220 | publicacionesEnRadio = publicacionesEnRadio.slice(0, Number(limite)); 221 | 222 | res.json({ 223 | ok: true, 224 | publicaciones: publicacionesEnRadio, 225 | }); 226 | } catch (error) { 227 | console.error(error); 228 | res.status(500).json({ mensaje: "Error al obtener las publicaciones." }); 229 | } 230 | }; 231 | 232 | const dislikePublicacion = async (req, res) => { 233 | try { 234 | const publicacionId = req.params.id; 235 | 236 | // Verificar si la publicación existe 237 | const publicacion = await Publicacion.findById(publicacionId); 238 | if (!publicacion) { 239 | return res.status(404).json({ error: "Publicación no encontrada" }); 240 | } 241 | 242 | // Verificar si el contador de likes es mayor a cero antes de decrementar 243 | if (publicacion.likes > 0) { 244 | publicacion.likes -= 1; 245 | await publicacion.save(); 246 | } 247 | 248 | res.status(200).json({ likes: publicacion.likes }); 249 | } catch (error) { 250 | console.error(error); 251 | res 252 | .status(500) 253 | .json({ error: "Error al decrementar el contador de likes" }); 254 | } 255 | }; 256 | 257 | //update publicacion 258 | const updatePublicacion = async (req, res) => { 259 | const { id } = req.params; 260 | const { isLiked, likes } = req.body; 261 | 262 | try { 263 | const publicacion = await Publicacion.findById(id); 264 | 265 | if (!publicacion) { 266 | return res.status(404).json({ mensaje: "Publicacion no encontrada" }); 267 | } 268 | 269 | const nuevaPublicacion = { 270 | isLiked, 271 | likes, 272 | usuario: req.uid, 273 | }; 274 | 275 | const publicacionActualizada = await Publicacion.findByIdAndUpdate( 276 | id, 277 | nuevaPublicacion, 278 | { new: true } 279 | ); 280 | 281 | res.json({ 282 | ok: true, 283 | publicacion: publicacionActualizada, 284 | }); 285 | } catch (error) { 286 | console.log(error); 287 | res.status(500).json({ 288 | ok: false, 289 | mensaje: "Error inesperado", 290 | }); 291 | } 292 | }; 293 | 294 | const updatePublicacion2 = async (req, res) => { 295 | const { id } = req.params; 296 | const { likes } = req.body; 297 | 298 | try { 299 | const publicacion = await Publicacion.findById(id); 300 | 301 | if (!publicacion) { 302 | return res.status(404).json({ mensaje: "Publicacion no encontrada" }); 303 | } 304 | 305 | const nuevaPublicacion = { 306 | likes, 307 | usuario: req.uid, 308 | }; 309 | 310 | const publicacionActualizada = await Publicacion.findByIdAndUpdate( 311 | id, 312 | nuevaPublicacion, 313 | { new: true } 314 | ); 315 | 316 | res.json({ 317 | ok: true, 318 | publicacion: publicacionActualizada, 319 | }); 320 | } catch (error) { 321 | console.log(error); 322 | res.status(500).json({ 323 | ok: false, 324 | mensaje: "Error inesperado", 325 | }); 326 | } 327 | }; 328 | 329 | const obtenerPublicacionesUsuarioConLikes = async (req, res) => { 330 | const usuarioId = req.uid; // ID del usuario obtenido del token de autenticación 331 | 332 | try { 333 | const publicaciones = await Publicacion.find({ usuario: usuarioId }); 334 | 335 | const publicacionesConLikes = publicaciones.filter( 336 | (publicacion) => publicacion.isLiked 337 | ); 338 | 339 | res.json({ 340 | ok: true, 341 | publicacionesConLikes, 342 | }); 343 | } catch (error) { 344 | console.log(error); 345 | res.status(500).json({ 346 | ok: false, 347 | msg: "Por favor hable con el administrador", 348 | }); 349 | } 350 | }; 351 | 352 | //update like publicacion 353 | const likePublicacion = async (req, res) => { 354 | try { 355 | const publicacionId = req.params.id; 356 | const usuarioId = req.uid; 357 | 358 | // Verificar si la publicación existe 359 | const publicacion = await Publicacion.findById(publicacionId); 360 | if (!publicacion) { 361 | return res.status(404).json({ error: "Publicación no encontrada" }); 362 | } 363 | 364 | // Verificar si el usuario ya ha dado like a la publicación 365 | const usuarioYaDioLike = publicacion.likes.includes(usuarioId.toString()); 366 | 367 | if (!usuarioYaDioLike) { 368 | // Agregar el ID del usuario a la lista de likes 369 | publicacion.likes.push(usuarioId); 370 | } else { 371 | // Eliminar el ID del usuario de la lista de likes 372 | publicacion.likes = publicacion.likes.filter( 373 | (id) => id.toString() !== usuarioId.toString() 374 | ); 375 | } 376 | 377 | await publicacion.save(); 378 | 379 | res.status(200).json({ publicacion }); 380 | } catch (error) { 381 | console.error(error); 382 | res.status(500).json({ error: "Error al gestionar el like" }); 383 | } 384 | }; 385 | 386 | //isPublicacionPendiente a true 387 | const isPublicacionFinalizada = async (req, res) => { 388 | const usuarioId = req.uid; 389 | 390 | try { 391 | const { publicacionId } = req.params; 392 | 393 | // Verificar si la publicación existe 394 | const publicacion = await Publicacion.findById(publicacionId); 395 | if (!publicacion) { 396 | return res.status(404).json({ error: "Publicación no encontrada" }); 397 | } 398 | 399 | // Verificar si el usuario es el dueño de la publicación 400 | if (publicacion.usuario.toString() !== usuarioId) { 401 | return res.status(401).json({ error: "No autorizado para modificar esta publicación" }); 402 | } 403 | 404 | // Cambiar el estado de isPublicacionPendiente a true 405 | publicacion.isPublicacionPendiente = true; 406 | await publicacion.save(); 407 | 408 | res.status(200).json({ message: "La publicación se ha finalizado" }); 409 | } catch (error) { 410 | console.error(error); 411 | res.status(500).json({ error: "Error al marcar la publicación como pendiente" }); 412 | } 413 | }; 414 | 415 | //delete publicacion 416 | const deletePublicacion = async (req, res) => { 417 | const { id } = req.params; 418 | try { 419 | await Publicacion.findByIdAndDelete(id); 420 | // await Publicacion.findByIdAndUpdate(id, { isActivo: false }); 421 | res.status(200).json({ message: "Publicación eliminada con éxito" }); 422 | } catch (error) { 423 | res.status(500).json({ message: "Error al eliminar la publicación" }); 424 | } 425 | }; 426 | 427 | const actualizarDescripcion = async (req, res) => { 428 | const { id } = req.params; 429 | const { descripcion } = req.body; 430 | 431 | try { 432 | if (!descripcion) { 433 | return res.status(400).json({ mensaje: "La descripción es obligatoria" }); 434 | } 435 | 436 | //actualizar 437 | await Publicacion.findByIdAndUpdate(id, { contenido: descripcion }); 438 | 439 | res.json({ 440 | ok: true, 441 | mensaje: "Descripción actualizada con éxito", 442 | }); 443 | } catch (error) { 444 | console.log(error); 445 | res.status(500).json({ 446 | ok: false, 447 | mensaje: "Error inesperado", 448 | }); 449 | } 450 | }; 451 | 452 | 453 | module.exports = { 454 | obtenerPublicacionesUsuario, 455 | guardarPublicacion, 456 | getPublicacionesEnRadio, 457 | likePublicacion, 458 | dislikePublicacion, 459 | updatePublicacion, 460 | updatePublicacion2, 461 | obtenerPublicacionesUsuarioConLikes, 462 | guardarListArchivo, 463 | isPublicacionFinalizada, 464 | deletePublicacion, 465 | actualizarDescripcion 466 | }; 467 | -------------------------------------------------------------------------------- /controllers/salas.js: -------------------------------------------------------------------------------- 1 | const { Mensaje, Sala, Usuario } = require("../models"); 2 | 3 | const { generarCodigoUnico } = require("../helpers/generar-aleatorio"); 4 | const _ = require("lodash"); 5 | const { enviarNotificacion } = require("../helpers/enviar-notificacion"); 6 | // const { obtenerUsuariosSalaHelper } = require("../helpers/obtener-usuario"); 7 | 8 | const obtenerMensajesSala = async (req, res) => { 9 | const { codigo } = req.params; 10 | 11 | try { 12 | const sala = await Sala.findOne({ codigo }).populate("mensajes"); 13 | if (!sala) { 14 | return res.status(404).json({ 15 | ok: false, 16 | msg: "Sala no encontrada", 17 | }); 18 | } 19 | 20 | res.json({ 21 | ok: true, 22 | mensajes: sala.mensajes, 23 | }); 24 | } catch (error) { 25 | console.log(error); 26 | res.status(500).json({ 27 | ok: false, 28 | msg: "Por favor hable con el administrador", 29 | }); 30 | } 31 | }; 32 | 33 | // Paso 2: Modificar el controlador crearSala 34 | 35 | const crearSala = async (req, res) => { 36 | const { nombre } = req.body; 37 | let codigo; 38 | let salaExistente; 39 | 40 | do { 41 | codigo = generarCodigoUnico(); 42 | salaExistente = await Sala.findOne({ codigo }); 43 | } while (salaExistente); 44 | 45 | const colorRandom = Array.from({ length: 3 }, () => 46 | Math.floor(Math.random() * 256) 47 | ); 48 | const color = colorRandom.reduce( 49 | (acc, curr) => acc + curr.toString(16).padStart(2, "0") 50 | ); 51 | 52 | try { 53 | const sala = new Sala({ nombre, codigo, color, propietario: req.uid }); 54 | 55 | // Agregar el uid del creador a la lista de usuarios de la sala 56 | sala.usuarios.push(req.uid); 57 | 58 | await sala.save(); 59 | 60 | // Agregar la sala creada a la lista de salas del usuario 61 | const usuario = await Usuario.findById(req.uid); 62 | usuario.salas.push({ 63 | salaId: sala._id, 64 | mensajesNoLeidos: 0, 65 | ultimaVezActivo: null, 66 | isRoomOpen: false, 67 | }); 68 | await usuario.save(); 69 | 70 | const uid = req.uid; 71 | const salaResponse = _.pick(sala.toObject(), [ 72 | "nombre", 73 | "codigo", 74 | "color", 75 | "mensajes", 76 | "usuarios", 77 | "propietario", 78 | "_id", 79 | ]); 80 | salaResponse.totalUsuarios = sala.usuarios.length; 81 | salaResponse.idUsuario = uid; 82 | res.json({ 83 | ok: true, 84 | sala: salaResponse, 85 | }); 86 | } catch (error) { 87 | console.log(error); 88 | res.status(500).json({ 89 | ok: false, 90 | msg: "Por favor hable con el administrador", 91 | }); 92 | } 93 | }; 94 | 95 | const unirseSala = async (req, res) => { 96 | const { codigo } = req.body; 97 | const uid = req.uid; 98 | 99 | try { 100 | const sala = await Sala.findOne({ codigo }); 101 | 102 | if (!sala) { 103 | return res.status(404).json({ 104 | ok: false, 105 | msg: "Sala no encontrada", 106 | }); 107 | } 108 | 109 | // Verificar si el usuario ya está en la sala 110 | if (sala.usuarios.includes(uid)) { 111 | return res.status(400).json({ 112 | ok: false, 113 | msg: "El usuario ya está en la sala", 114 | }); 115 | } 116 | 117 | // Agregar al usuario a la lista de usuarios de la sala 118 | sala.usuarios.push(uid); 119 | await sala.save(); 120 | 121 | // Agregar la sala a la lista de salas del usuario 122 | const usuario = await Usuario.findById(uid); 123 | usuario.salas.push({ 124 | salaId: sala._id, 125 | mensajesNoLeidos: 0, // Inicializar a cero mensajes no leídos al unirse a la sala 126 | ultimaVezActivo: null, // Inicializar como null ya que aún no ha enviado mensajes en esta sala 127 | }); 128 | await usuario.save(); 129 | 130 | const salaResponse = _.pick(sala.toObject(), [ 131 | "nombre", 132 | "codigo", 133 | "color", 134 | "usuarios", 135 | "propietario", 136 | "_id", 137 | ]); 138 | salaResponse.idUsuario = uid; 139 | salaResponse.totalUsuarios = sala.usuarios.length; 140 | res.json({ 141 | ok: true, 142 | sala: salaResponse, 143 | }); 144 | } catch (error) { 145 | console.log(error); 146 | res.status(500).json({ 147 | ok: false, 148 | msg: "Por favor hable con el administrador", 149 | }); 150 | } 151 | }; 152 | 153 | const grabarMensajeSala = async (req, res) => { 154 | try { 155 | const { mensaje, salaId } = req.body; 156 | const usuarioId = req.uid; 157 | 158 | const newMessage = new Mensaje({ mensaje, usuario: usuarioId }); 159 | await newMessage.save(); 160 | 161 | const sala = await Sala.findById(salaId); 162 | sala.mensajes.push(newMessage._id); 163 | await sala.save(); 164 | 165 | const usuariosEnGrupoOffline = await obtenerUsuariosSalaHelper(salaId); 166 | 167 | // Actualizar la cantidad de mensajes no leídos solo para los usuarios offline en el grupo 168 | for (const usuario of usuariosEnGrupoOffline) { 169 | usuario.salas = usuario.salas.map((sala) => { 170 | if (sala.salaId.toString() === salaId) { 171 | sala.mensajesNoLeidos++; 172 | sala.ultimaVezActivo = new Date(); 173 | } 174 | 175 | return sala; 176 | }); 177 | } 178 | 179 | await Promise.all(usuariosEnGrupoOffline.map((usuario) => usuario.save())); 180 | 181 | res.json({ 182 | ok: true, 183 | sala: sala, 184 | }); 185 | } catch (error) { 186 | console.log(error); 187 | res.status(500).json({ 188 | ok: false, 189 | msg: "Por favor hable con el administrador", 190 | }); 191 | } 192 | }; 193 | 194 | /* 195 | obtenerUsuariosSalaHelper 196 | Esta función obtiene los usuarios de una sala que están offline y que no han abierto la sala en la aplicación. 197 | */ 198 | const obtenerUsuariosSalaHelper = async (salaId) => { 199 | try { 200 | const usuariosEnSala = await Usuario.find({ 201 | "salas.salaId": salaId, 202 | "salas.isRoomOpen": false, 203 | }); 204 | return usuariosEnSala; 205 | } catch (error) { 206 | console.log(error); 207 | return []; 208 | } 209 | }; 210 | 211 | const cambiarEstadoSala = async (req, res) => { 212 | try { 213 | const { isRoomOpen } = req.body; 214 | const userId = req.uid; 215 | 216 | // Encuentra el usuario por su ID 217 | const usuario = await Usuario.findById(userId); 218 | 219 | if (!usuario) { 220 | return res.status(404).json({ 221 | ok: false, 222 | msg: "Usuario no encontrado", 223 | }); 224 | } 225 | 226 | // Find the sala with the given salaId in the salas array 227 | const salaToUpdate = usuario.salas.find( 228 | (sala) => sala.salaId.toString() === req.params.salaId 229 | ); 230 | 231 | if (!salaToUpdate) { 232 | return res.status(404).json({ 233 | ok: false, 234 | msg: "Sala no encontrada para el usuario", 235 | }); 236 | } 237 | 238 | // Update the isRoomOpen property for the found sala 239 | salaToUpdate.isRoomOpen = isRoomOpen; 240 | 241 | await usuario.save(); 242 | 243 | return res.json({ 244 | ok: true, 245 | msg: "Estado de la sala actualizado exitosamente", 246 | usuario: usuario, 247 | }); 248 | } catch (error) { 249 | console.log(error); 250 | res.status(500).json({ 251 | ok: false, 252 | msg: "Por favor hable con el administradorrr", 253 | }); 254 | } 255 | }; 256 | 257 | const obtenerSalasConMensajesNoLeidos = async (req, res) => { 258 | const usuarioId = req.uid; 259 | 260 | try { 261 | const usuario = await Usuario.findById(usuarioId); 262 | const salas = usuario.salas; 263 | const salasConMensajesNoLeidos = []; 264 | for (const sala of salas) { 265 | // Obtener la información de la sala 266 | const salaInfo = await Sala.findById(sala.salaId); 267 | // Obtener el total de usuarios en la sala 268 | const totalUsuariosSala = salaInfo.usuarios.length; 269 | // Si la sala tiene mensajes no leídos, se agrega al array con la cantidad de mensajes no leídos y el total de usuarios 270 | if (sala.mensajesNoLeidos > 0) { 271 | salasConMensajesNoLeidos.push({ 272 | uid: salaInfo._id, 273 | nombre: salaInfo.nombre, 274 | color: salaInfo.color, 275 | propietario: salaInfo.propietario, 276 | codigo: salaInfo.codigo, 277 | mensajesNoLeidos: sala.mensajesNoLeidos, 278 | totalUsuarios: totalUsuariosSala, 279 | }); 280 | } else { 281 | // Si la sala no tiene mensajes no leídos, se agrega al array con 0 mensajes no leídos y el total de usuarios 282 | salasConMensajesNoLeidos.push({ 283 | uid: salaInfo._id, 284 | nombre: salaInfo.nombre, 285 | color: salaInfo.color, 286 | codigo: salaInfo.codigo, 287 | propietario: salaInfo.propietario, 288 | mensajesNoLeidos: 0, 289 | totalUsuarios: totalUsuariosSala, 290 | }); 291 | } 292 | } 293 | 294 | res.json({ 295 | ok: true, 296 | salas: salasConMensajesNoLeidos, 297 | }); 298 | } catch (error) { 299 | console.log(error); 300 | res.status(500).json({ 301 | ok: false, 302 | msg: "Por favor hable con el administrador", 303 | }); 304 | } 305 | }; 306 | 307 | const getSalas = async (req, res) => { 308 | const uid = req.uid; 309 | const salas = await Sala.find( 310 | {}, 311 | { nombre: 1, codigo: 1, _id: 1, usuarios: 1, color: 1, propietario: 1 } 312 | ); 313 | 314 | res.json({ 315 | ok: true, 316 | salas, 317 | }); 318 | }; 319 | 320 | const obtenerSalasMensajesUsuario = async (req, res) => { 321 | const uid = req.uid; 322 | 323 | try { 324 | const salas = await Sala.find({ usuarios: uid }).populate("mensajes"); 325 | res.json({ 326 | ok: true, 327 | salas, 328 | }); 329 | } catch (error) { 330 | console.log(error); 331 | res.status(500).json({ 332 | ok: false, 333 | msg: "Por favor hable con el administrador", 334 | }); 335 | } 336 | }; 337 | 338 | const getSalasByUser = async (req, res) => { 339 | const uid = req.uid; 340 | try { 341 | const salas = await Sala.find( 342 | { usuarios: uid, isActivo: true }, 343 | { nombre: 1, _id: 1, color: 1, codigo: 1, propietario: 1 } 344 | ); 345 | 346 | const totalUsuarios = await Sala.find({ usuarios: uid }).countDocuments(); 347 | res.json({ 348 | ok: true, 349 | salas, 350 | totalUsuarios, 351 | }); 352 | } catch (error) { 353 | console.log(error); 354 | res.status(500).json({ 355 | ok: false, 356 | msg: "Por favor hable con el administrador", 357 | }); 358 | } 359 | }; 360 | 361 | const getMensajesBySala = async (req, res) => { 362 | const { salaId } = req.params; 363 | try { 364 | const sala = await Sala.findById(salaId) 365 | .populate("mensajes") 366 | .populate("usuarios"); 367 | 368 | res.json({ 369 | ok: true, 370 | sala, 371 | }); 372 | } catch (error) { 373 | console.log(error); 374 | res.status(500).json({ 375 | ok: false, 376 | msg: "Por favor hable con el administrador", 377 | }); 378 | } 379 | }; 380 | 381 | const getMensajesSala = async (req, res) => { 382 | try { 383 | const { salaId } = req.params; 384 | const sala = await Sala.findById(salaId).populate("mensajes"); 385 | 386 | res.json({ 387 | ok: true, 388 | sala, 389 | }); 390 | } catch (error) { 391 | console.log(error); 392 | res.status(500).json({ 393 | ok: false, 394 | msg: "Por favor hable con el administrador", 395 | }); 396 | } 397 | }; 398 | 399 | const updateSala = async (req, res) => { 400 | try { 401 | const { salaId } = req.params; 402 | const { nombre, codigo, color } = req.body; 403 | 404 | const sala = await Sala.findByIdAndUpdate( 405 | salaId, 406 | { nombre, codigo, color }, 407 | { new: true } 408 | ); 409 | 410 | res.json({ 411 | ok: true, 412 | sala, 413 | }); 414 | } catch (error) { 415 | console.log(error); 416 | res.status(500).json({ 417 | ok: false, 418 | msg: "Por favor hable con el administrador", 419 | }); 420 | } 421 | }; 422 | 423 | const deleteSala = async (req, res) => { 424 | try { 425 | const { salaId } = req.params; 426 | 427 | // Find the room by its ID 428 | const sala = await Sala.findById(salaId); 429 | 430 | if (!sala) { 431 | return res.status(404).json({ 432 | ok: false, 433 | msg: "Sala no encontrada", 434 | }); 435 | } 436 | 437 | // Check if the user is the owner of the room 438 | if (sala.propietario.toString() !== req.uid) { 439 | return res.status(401).json({ 440 | ok: false, 441 | msg: "No estás autorizado para eliminar esta sala", 442 | }); 443 | } 444 | 445 | // Delete the room from the Sala collection 446 | await Sala.findByIdAndDelete(salaId); 447 | 448 | // Delete the room from the list of rooms for all users 449 | await Usuario.updateMany( 450 | { "salas.salaId": salaId }, 451 | { 452 | $pull: { 453 | salas: { salaId: salaId }, 454 | mensajes: { _id: { $in: sala.mensajes } }, // Remove messages associated with the room 455 | }, 456 | } 457 | ); 458 | 459 | res.json({ 460 | ok: true, 461 | msg: "Sala eliminada exitosamente", 462 | }); 463 | } catch (error) { 464 | console.log(error); 465 | res.status(500).json({ 466 | ok: false, 467 | msg: "Por favor hable con el administrador", 468 | }); 469 | } 470 | }; 471 | 472 | const obtenerUsuariosSala = async (req, res) => { 473 | const { salaId } = req.params; 474 | 475 | console.log(salaId); 476 | try { 477 | const sala = await Sala.findById(salaId).populate({ 478 | path: "usuarios", 479 | populate: { 480 | path: "ubicaciones", 481 | model: "Ubicacion", // Reemplaza "Ubicacion" con el nombre de tu modelo de ubicaciones 482 | }, 483 | }); 484 | 485 | console.log(sala); 486 | 487 | if (!sala) { 488 | return res.status(404).json({ 489 | ok: false, 490 | msg: "Sala no encontrada", 491 | }); 492 | } 493 | 494 | 495 | 496 | res.json({ 497 | ok: true, 498 | usuarios: sala.usuarios, 499 | }); 500 | } catch (error) { 501 | console.log(error); 502 | res.status(500).json({ 503 | ok: false, 504 | msg: "Por favor hable con el administrador", 505 | }); 506 | } 507 | }; 508 | 509 | const deleteUserById = async (req, res) => { 510 | const { salaId, usuarioId } = req.params; 511 | const uid = req.uid; 512 | 513 | try { 514 | // Buscar la sala por su ID 515 | const sala = await Sala.findById(salaId); 516 | 517 | //eliminar de la sala el usuario 518 | 519 | if (!sala) { 520 | return res.status(404).json({ 521 | ok: false, 522 | msg: "Sala no encontrada", 523 | }); 524 | } 525 | 526 | // Verificar si el usuario es el propietario de la sala 527 | if (sala.propietario.toString() !== uid) { 528 | return res.status(401).json({ 529 | ok: false, 530 | msg: "No estás autorizado para realizar esta acción", 531 | }); 532 | } 533 | 534 | // Verificar si el usuario a eliminar existe en la sala 535 | if (!sala.usuarios.includes(usuarioId)) { 536 | return res.status(404).json({ 537 | ok: false, 538 | msg: "Usuario no encontrado en la sala", 539 | }); 540 | } 541 | 542 | // Eliminar al usuario de la sala 543 | sala.usuarios.pull(usuarioId); 544 | await sala.save(); 545 | 546 | //eliminar de la sala el usuario 547 | const usuario = await Usuario.findById(usuarioId); 548 | usuario.salas = usuario.salas.filter((sala) => !sala.salaId.equals(salaId)); 549 | await usuario.save(); 550 | 551 | res.json({ 552 | ok: true, 553 | msg: "Usuario eliminado exitosamente de la sala", 554 | }); 555 | 556 | //notificar al miemro eliminado que fue eliminado 557 | const usuarioEliminado = await Usuario.findById(usuarioId); 558 | const usuarioEliminador = await Usuario.findById(uid); 559 | const mensaje = { 560 | titulo: "Eliminado de sala", 561 | cuerpo: `El usuario ${usuarioEliminador.nombre} te ha eliminado de la sala ${sala.nombre}`, 562 | }; 563 | 564 | // await enviarNotificacion( usuarioEliminado.tokenApp, mensaje.titulo, mensaje.cuerpo,sala); 565 | //tokens, titulo, desc, data = {} 566 | 567 | res.status(500).json({ 568 | ok: false, 569 | msg: "Por favor hable con el administrador", 570 | }); 571 | 572 | } catch (error) { 573 | console.log(error); 574 | res.status(500).json({ 575 | ok: false, 576 | msg: "Por favor hable con el administradorrr", 577 | }); 578 | } 579 | }; 580 | 581 | 582 | const abandonarSala = async (req, res) => { 583 | const { salaId } = req.params; 584 | const uid = req.uid; 585 | 586 | try { 587 | // Buscar al usuario por su ID 588 | const usuario = await Usuario.findById(uid); 589 | 590 | // Filtrar las salas del usuario y guardar los cambios 591 | usuario.salas = usuario.salas.filter((sala) => !sala.salaId.equals(salaId)); 592 | //eliminar de la sala 593 | const sala = await Sala.findById(salaId); 594 | sala.usuarios.pull(uid); 595 | 596 | await sala.save(); 597 | 598 | await usuario.save(); 599 | 600 | res.json({ 601 | ok: true, 602 | msg: "Usuario abandonó la sala exitosamente", 603 | }); 604 | } catch (error) { 605 | console.log(error); 606 | res.status(500).json({ 607 | ok: false, 608 | msg: "Por favor hable con el administrador", 609 | }); 610 | } 611 | }; 612 | 613 | module.exports = { 614 | abandonarSala, 615 | deleteSala, 616 | deleteUserById, 617 | crearSala, 618 | getMensajesBySala, 619 | getMensajesSala, 620 | getSalas, 621 | getSalasByUser, 622 | grabarMensajeSala, 623 | obtenerMensajesSala, 624 | obtenerSalasMensajesUsuario, 625 | obtenerUsuariosSala, 626 | unirseSala, 627 | updateSala, 628 | obtenerSalasConMensajesNoLeidos, 629 | cambiarEstadoSala, 630 | }; 631 | -------------------------------------------------------------------------------- /controllers/reportes.js: -------------------------------------------------------------------------------- 1 | const { Publicacion, Usuario } = require("../models"); 2 | const fs = require("fs"); 3 | const PDFDocument = require("pdfkit"); 4 | const pdfMakePrinter = require("pdfmake/src/printer"); 5 | const pdfMakeUni = require("pdfmake-unicode"); 6 | const ExcelJS = require("exceljs"); 7 | const publicacion = require("../models/publicacion"); 8 | process.env.OPENSSL_CONF = '/dev/null'; 9 | //Importaciones 10 | const pdf = require("html-pdf"); 11 | const pdfTemplate = require("../prueba/public/pdfTemplate"); 12 | const path = require("path"); 13 | 14 | const obtenerCiudades = async (req, res) => { 15 | let ciudades = []; 16 | try { 17 | let publicaciones = await Publicacion.find(); 18 | ciudades = Array.from( 19 | new Set(publicaciones.map((publicacion) => publicacion["ciudad"])) 20 | ); 21 | 22 | res.json({ 23 | ok: true, 24 | msg: "Ciudades obtenidas correctamente", 25 | data: ciudades, 26 | }); 27 | } catch (error) { 28 | console.log(error); 29 | res.status(500).json({ 30 | ok: false, 31 | msg: "Por favor hable con el administrador", 32 | }); 33 | } 34 | }; 35 | 36 | const obtenerBarrios = async (req, res) => { 37 | let barrios = []; 38 | let consulta = {}; 39 | try { 40 | const parametrosBusqueda = req.body; 41 | Object.keys(parametrosBusqueda).forEach((key) => { 42 | if ( 43 | parametrosBusqueda[key] !== "" && 44 | parametrosBusqueda[key] !== undefined 45 | ) { 46 | consulta[key] = parametrosBusqueda[key]; 47 | } 48 | }); 49 | let publicaciones = await Publicacion.find(consulta); 50 | if ( 51 | parametrosBusqueda.horaFin != undefined && 52 | parametrosBusqueda.horaFin.includes(":") 53 | ) { 54 | const FechahoraFin = convertToEcuadorTimeZone( 55 | new Date(parametrosBusqueda.horaFin) 56 | ); 57 | const horaFin = FechahoraFin.getHours(); 58 | const minutosFin = FechahoraFin.getMinutes(); 59 | const documentosHoraFin = publicaciones.filter((publicacion) => { 60 | const hora = publicacion.createdAt.getHours(); 61 | const minutos = publicacion.createdAt.getMinutes(); 62 | 63 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 64 | }); 65 | publicaciones = documentosHoraFin; 66 | } 67 | 68 | if ( 69 | parametrosBusqueda.horaInicio != undefined && 70 | parametrosBusqueda.horaInicio.includes(":") 71 | ) { 72 | const FechahoraInicio = convertToEcuadorTimeZone( 73 | new Date(parametrosBusqueda.horaInicio) 74 | ); 75 | const horaInicio = FechahoraInicio.getHours(); 76 | const minutosInicio = FechahoraInicio.getMinutes(); 77 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 78 | const hora = publicacion.createdAt.getHours(); 79 | const minutos = publicacion.createdAt.getMinutes(); 80 | return ( 81 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 82 | ); 83 | }); 84 | publicaciones = documentosHoraInicio; 85 | } 86 | 87 | barrios = Array.from( 88 | new Set(publicaciones.map((publicacion) => publicacion["barrio"])) 89 | ); 90 | res.json({ 91 | ok: true, 92 | msg: "Barrios obtenidos correctamente", 93 | data: barrios, 94 | }); 95 | } catch (error) { 96 | console.log(error); 97 | res.status(500).json({ 98 | ok: false, 99 | msg: "Por favor hable con el administrador", 100 | }); 101 | } 102 | }; 103 | 104 | const obtenerDatosCards = async (req, res) => { 105 | let publicacionesRegistradas = 0; 106 | let usuariosRegistros = 0; 107 | let publicacionesDelMes = 0; 108 | let publicacionesDelDia = 0; 109 | try { 110 | let publicaciones = await Publicacion.find(); 111 | const usuarios = await Usuario.find(); 112 | publicacionesRegistradas = publicaciones.length; 113 | usuariosRegistros = usuarios.length; 114 | const fechaActual = new Date(); 115 | 116 | // Calcula el primer día del mes actual 117 | const primerDiaMesActual = new Date( 118 | fechaActual.getFullYear(), 119 | fechaActual.getMonth(), 120 | 1 121 | ); 122 | 123 | // Calcula el último día del mes actual 124 | const ultimoDiaMesActual = new Date( 125 | fechaActual.getFullYear(), 126 | fechaActual.getMonth() + 1, 127 | 0 128 | ); 129 | 130 | // Consulta las publicaciones que se encuentran dentro del rango del mes actual 131 | publicacionesDelMes = await Publicacion.countDocuments({ 132 | createdAt: { 133 | $gte: primerDiaMesActual, 134 | $lte: ultimoDiaMesActual, 135 | }, 136 | }); 137 | 138 | const fechaInicioDiaActual = new Date(); 139 | fechaInicioDiaActual.setHours(0, 0, 0, 0); 140 | 141 | // Obtenemos la fecha de fin del día actual 142 | const fechaFinDiaActual = new Date(); 143 | fechaFinDiaActual.setHours(23, 59, 59, 999); 144 | 145 | // Realizamos la consulta a la base de datos para obtener el conteo 146 | publicacionesDelDia = await Publicacion.countDocuments({ 147 | createdAt: { 148 | $gte: fechaInicioDiaActual, 149 | $lte: fechaFinDiaActual, 150 | }, 151 | }); 152 | res.json({ 153 | ok: true, 154 | msg: "Barrios obtenidos correctamente", 155 | data: { 156 | publicacionesRegistradas, 157 | usuariosRegistros, 158 | publicacionesDelMes, 159 | publicacionesDelDia, 160 | }, 161 | }); 162 | } catch (error) { 163 | console.log(error); 164 | res.status(500).json({ 165 | ok: false, 166 | msg: "Por favor hable con el administrador", 167 | }); 168 | } 169 | }; 170 | 171 | const obtenerAnios = async (req, res) => { 172 | let anios = []; 173 | let consulta = {}; 174 | try { 175 | const parametrosBusqueda = req.body; 176 | Object.keys(parametrosBusqueda).forEach((key) => { 177 | if (parametrosBusqueda[key] != "") { 178 | consulta[key] = parametrosBusqueda[key]; 179 | } 180 | }); 181 | let publicaciones = await Publicacion.find(consulta); 182 | if ( 183 | parametrosBusqueda.horaFin != undefined && 184 | parametrosBusqueda.horaFin.includes(":") 185 | ) { 186 | const FechahoraFin = convertToEcuadorTimeZone( 187 | new Date(parametrosBusqueda.horaFin) 188 | ); 189 | const horaFin = FechahoraFin.getHours(); 190 | const minutosFin = FechahoraFin.getMinutes(); 191 | const documentosHoraFin = publicaciones.filter((publicacion) => { 192 | const hora = publicacion.createdAt.getHours(); 193 | const minutos = publicacion.createdAt.getMinutes(); 194 | 195 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 196 | }); 197 | publicaciones = documentosHoraFin; 198 | } 199 | 200 | if ( 201 | parametrosBusqueda.horaInicio != undefined && 202 | parametrosBusqueda.horaInicio.includes(":") 203 | ) { 204 | const FechahoraInicio = convertToEcuadorTimeZone( 205 | new Date(parametrosBusqueda.horaInicio) 206 | ); 207 | const horaInicio = FechahoraInicio.getHours(); 208 | const minutosInicio = FechahoraInicio.getMinutes(); 209 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 210 | const hora = publicacion.createdAt.getHours(); 211 | const minutos = publicacion.createdAt.getMinutes(); 212 | return ( 213 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 214 | ); 215 | }); 216 | publicaciones = documentosHoraInicio; 217 | } 218 | 219 | anios = Array.from( 220 | new Set( 221 | publicaciones.map((publicacion) => 222 | new Date(publicacion.createdAt).getFullYear() 223 | ) 224 | ) 225 | ); 226 | res.json({ 227 | ok: true, 228 | msg: "Años obtenidos correctamente", 229 | data: anios, 230 | }); 231 | } catch (error) { 232 | console.log(error); 233 | res.status(500).json({ 234 | ok: false, 235 | msg: "Por favor hable con el administrador", 236 | }); 237 | } 238 | }; 239 | 240 | const obtenerEmergencias = async (req, res) => { 241 | let emergencias = []; 242 | let consulta = {}; 243 | try { 244 | const parametrosBusqueda = req.body; 245 | Object.keys(parametrosBusqueda).forEach((key) => { 246 | if ( 247 | parametrosBusqueda[key] !== "" && 248 | parametrosBusqueda[key] !== undefined 249 | ) { 250 | consulta[key] = parametrosBusqueda[key]; 251 | } 252 | }); 253 | let publicaciones = await Publicacion.find(consulta); 254 | if ( 255 | parametrosBusqueda.horaFin != undefined && 256 | parametrosBusqueda.horaFin.includes(":") 257 | ) { 258 | const FechahoraFin = convertToEcuadorTimeZone( 259 | new Date(parametrosBusqueda.horaFin) 260 | ); 261 | const horaFin = FechahoraFin.getHours(); 262 | const minutosFin = FechahoraFin.getMinutes(); 263 | const documentosHoraFin = publicaciones.filter((publicacion) => { 264 | const hora = publicacion.createdAt.getHours(); 265 | const minutos = publicacion.createdAt.getMinutes(); 266 | 267 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 268 | }); 269 | publicaciones = documentosHoraFin; 270 | } 271 | 272 | if ( 273 | parametrosBusqueda.horaInicio != undefined && 274 | parametrosBusqueda.horaInicio.includes(":") 275 | ) { 276 | const FechahoraInicio = convertToEcuadorTimeZone( 277 | new Date(parametrosBusqueda.horaInicio) 278 | ); 279 | const horaInicio = FechahoraInicio.getHours(); 280 | const minutosInicio = FechahoraInicio.getMinutes(); 281 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 282 | const hora = publicacion.createdAt.getHours(); 283 | const minutos = publicacion.createdAt.getMinutes(); 284 | return ( 285 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 286 | ); 287 | }); 288 | publicaciones = documentosHoraInicio; 289 | } 290 | 291 | emergencias = Array.from( 292 | new Set(publicaciones.map((publicacion) => publicacion["titulo"])) 293 | ); 294 | res.json({ 295 | ok: true, 296 | msg: "Emergencias obtenidas correctamente", 297 | data: emergencias, 298 | }); 299 | } catch (error) { 300 | console.log(error); 301 | res.status(500).json({ 302 | ok: false, 303 | msg: "Por favor hable con el administrador", 304 | }); 305 | } 306 | }; 307 | const convertToEcuadorTimeZone = (date) => { 308 | const ecuadorTimeZoneOffset = -5 * 60; // -5 horas en minutos 309 | const userTimeZoneOffset = date.getTimezoneOffset(); // Obtener el offset del huso horario del usuario en minutos 310 | const gmtTime = date.getTime() + userTimeZoneOffset * 60 * 1000; // Convertir la hora a GMT 311 | const ecuadorTime = gmtTime + ecuadorTimeZoneOffset * 60 * 1000; // Agregar el offset de GMT-5 312 | return new Date(ecuadorTime); 313 | }; 314 | const obtenerReporteBarras = async (req, res) => { 315 | let emergencias = []; 316 | let consulta = {}; 317 | try { 318 | const parametrosBusqueda = req.body; 319 | 320 | Object.keys(parametrosBusqueda).forEach((key) => { 321 | if ( 322 | parametrosBusqueda[key] !== "" && 323 | parametrosBusqueda[key] !== undefined 324 | ) { 325 | consulta[key] = parametrosBusqueda[key]; 326 | } 327 | }); 328 | 329 | if (parametrosBusqueda.fechaFin) { 330 | const fechaFin = new Date(parametrosBusqueda.fechaFin); 331 | fechaFin.setHours(23, 59, 59); // Establecer la hora de finalización a las 23:59:59 332 | consulta.createdAt = consulta.createdAt || {}; 333 | consulta.createdAt.$lte = fechaFin; 334 | } 335 | 336 | if (parametrosBusqueda.fechaInicio) { 337 | const fechaInicio = new Date(parametrosBusqueda.fechaInicio); 338 | consulta.createdAt = consulta.createdAt || {}; 339 | consulta.createdAt.$gte = fechaInicio; 340 | } 341 | 342 | let publicaciones = await Publicacion.find(consulta); 343 | 344 | if ( 345 | parametrosBusqueda.horaFin != undefined && 346 | parametrosBusqueda.horaFin.includes(":") 347 | ) { 348 | const FechahoraFin = convertToEcuadorTimeZone( 349 | new Date(parametrosBusqueda.horaFin) 350 | ); 351 | const horaFin = FechahoraFin.getHours(); 352 | const minutosFin = FechahoraFin.getMinutes(); 353 | const documentosHoraFin = publicaciones.filter((publicacion) => { 354 | const hora = publicacion.createdAt.getHours(); 355 | const minutos = publicacion.createdAt.getMinutes(); 356 | 357 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 358 | }); 359 | publicaciones = documentosHoraFin; 360 | } 361 | 362 | if ( 363 | parametrosBusqueda.horaInicio != undefined && 364 | parametrosBusqueda.horaInicio.includes(":") 365 | ) { 366 | const FechahoraInicio = convertToEcuadorTimeZone( 367 | new Date(parametrosBusqueda.horaInicio) 368 | ); 369 | const horaInicio = FechahoraInicio.getHours(); 370 | const minutosInicio = FechahoraInicio.getMinutes(); 371 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 372 | const hora = publicacion.createdAt.getHours(); 373 | const minutos = publicacion.createdAt.getMinutes(); 374 | return ( 375 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 376 | ); 377 | }); 378 | publicaciones = documentosHoraInicio; 379 | } 380 | 381 | const conteoPorMes = {}; 382 | for ( 383 | let index = new Date(parametrosBusqueda.fechaInicio).getMonth(); 384 | index <= new Date(parametrosBusqueda.fechaFin).getMonth(); 385 | index++ 386 | ) { 387 | conteoPorMes[index] = 0; 388 | } 389 | 390 | publicaciones.forEach((publicacion) => { 391 | const fecha = new Date(publicacion.createdAt); 392 | const mes = fecha.getMonth() + 1; // Los meses en JavaScript se representan del 0 al 11, por eso sumamos 1. 393 | 394 | if (conteoPorMes[mes]) { 395 | conteoPorMes[mes]++; 396 | } else { 397 | conteoPorMes[mes] = 1; 398 | } 399 | }); 400 | 401 | res.json({ 402 | ok: true, 403 | msg: "Datos para barras obtenidas correctamente", 404 | data: conteoPorMes, 405 | }); 406 | } catch (error) { 407 | console.log(error); 408 | res.status(500).json({ 409 | ok: false, 410 | msg: "Por favor hable con el administrador", 411 | }); 412 | } 413 | }; 414 | const obtenerReportePastel = async (req, res) => { 415 | const conteoEmergencias = {}; 416 | let consulta = {}; 417 | try { 418 | const parametrosBusqueda = req.body; 419 | Object.keys(parametrosBusqueda).forEach((key) => { 420 | if ( 421 | parametrosBusqueda[key] !== "" && 422 | parametrosBusqueda[key] !== undefined 423 | ) { 424 | consulta[key] = parametrosBusqueda[key]; 425 | } 426 | }); 427 | 428 | if (parametrosBusqueda.fechaFin) { 429 | const fechaFin = new Date(parametrosBusqueda.fechaFin); 430 | fechaFin.setHours(23, 59, 59); // Establecer la hora de finalización a las 23:59:59 431 | consulta.createdAt = consulta.createdAt || {}; 432 | consulta.createdAt.$lte = fechaFin; 433 | } 434 | 435 | if (parametrosBusqueda.fechaInicio) { 436 | const fechaInicio = new Date(parametrosBusqueda.fechaInicio); 437 | consulta.createdAt = consulta.createdAt || {}; 438 | consulta.createdAt.$gte = fechaInicio; 439 | } 440 | 441 | let publicaciones = await Publicacion.find(consulta); 442 | if ( 443 | parametrosBusqueda.horaFin != undefined && 444 | parametrosBusqueda.horaFin.includes(":") 445 | ) { 446 | const FechahoraFin = convertToEcuadorTimeZone( 447 | new Date(parametrosBusqueda.horaFin) 448 | ); 449 | const horaFin = FechahoraFin.getHours(); 450 | const minutosFin = FechahoraFin.getMinutes(); 451 | const documentosHoraFin = publicaciones.filter((publicacion) => { 452 | const hora = publicacion.createdAt.getHours(); 453 | const minutos = publicacion.createdAt.getMinutes(); 454 | 455 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 456 | }); 457 | publicaciones = documentosHoraFin; 458 | } 459 | 460 | if ( 461 | parametrosBusqueda.horaInicio != undefined && 462 | parametrosBusqueda.horaInicio.includes(":") 463 | ) { 464 | const FechahoraInicio = convertToEcuadorTimeZone( 465 | new Date(parametrosBusqueda.horaInicio) 466 | ); 467 | const horaInicio = FechahoraInicio.getHours(); 468 | const minutosInicio = FechahoraInicio.getMinutes(); 469 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 470 | const hora = publicacion.createdAt.getHours(); 471 | const minutos = publicacion.createdAt.getMinutes(); 472 | return ( 473 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 474 | ); 475 | }); 476 | publicaciones = documentosHoraInicio; 477 | } 478 | 479 | emergencias = publicaciones.forEach((publicacion) => { 480 | const { titulo } = publicacion; 481 | if (conteoEmergencias[titulo]) { 482 | conteoEmergencias[titulo]++; 483 | } else { 484 | conteoEmergencias[titulo] = 1; 485 | } 486 | }); 487 | res.json({ 488 | ok: true, 489 | msg: "Datos de pastel", 490 | data: conteoEmergencias, 491 | }); 492 | } catch (error) { 493 | console.log(error); 494 | res.status(500).json({ 495 | ok: false, 496 | msg: "Por favor hable con el administrador", 497 | }); 498 | } 499 | }; 500 | const obtenerMapaCalor = async (req, res) => { 501 | let consulta = {}; 502 | try { 503 | const parametrosBusqueda = req.body; 504 | Object.keys(parametrosBusqueda).forEach((key) => { 505 | if ( 506 | parametrosBusqueda[key] !== "" && 507 | parametrosBusqueda[key] !== undefined 508 | ) { 509 | consulta[key] = parametrosBusqueda[key]; 510 | } 511 | }); 512 | 513 | if (parametrosBusqueda.fechaFin) { 514 | const fechaFin = new Date(parametrosBusqueda.fechaFin); 515 | fechaFin.setHours(23, 59, 59); // Establecer la hora de finalización a las 23:59:59 516 | consulta.createdAt = consulta.createdAt || {}; 517 | consulta.createdAt.$lte = fechaFin; 518 | } 519 | 520 | if (parametrosBusqueda.fechaInicio) { 521 | const fechaInicio = new Date(parametrosBusqueda.fechaInicio); 522 | consulta.createdAt = consulta.createdAt || {}; 523 | consulta.createdAt.$gte = fechaInicio; 524 | } 525 | 526 | let publicaciones = await Publicacion.find(consulta); 527 | if ( 528 | parametrosBusqueda.horaFin != undefined && 529 | parametrosBusqueda.horaFin.includes(":") 530 | ) { 531 | const FechahoraFin = convertToEcuadorTimeZone( 532 | new Date(parametrosBusqueda.horaFin) 533 | ); 534 | const horaFin = FechahoraFin.getHours(); 535 | const minutosFin = FechahoraFin.getMinutes(); 536 | const documentosHoraFin = publicaciones.filter((publicacion) => { 537 | const hora = publicacion.createdAt.getHours(); 538 | const minutos = publicacion.createdAt.getMinutes(); 539 | 540 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 541 | }); 542 | publicaciones = documentosHoraFin; 543 | } 544 | 545 | if ( 546 | parametrosBusqueda.horaInicio != undefined && 547 | parametrosBusqueda.horaInicio.includes(":") 548 | ) { 549 | const FechahoraInicio = convertToEcuadorTimeZone( 550 | new Date(parametrosBusqueda.horaInicio) 551 | ); 552 | const horaInicio = FechahoraInicio.getHours(); 553 | const minutosInicio = FechahoraInicio.getMinutes(); 554 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 555 | const hora = publicacion.createdAt.getHours(); 556 | const minutos = publicacion.createdAt.getMinutes(); 557 | return ( 558 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 559 | ); 560 | }); 561 | publicaciones = documentosHoraInicio; 562 | } 563 | 564 | const diasSemana = [ 565 | "Domingo", 566 | "Lunes", 567 | "Martes", 568 | "Miércoles", 569 | "Jueves", 570 | "Viernes", 571 | "Sábado", 572 | ]; 573 | 574 | const heatmapData = Array.from( 575 | { 576 | length: 7, 577 | }, 578 | () => ({ 579 | name: "", 580 | data: Array(24).fill(0), 581 | }) 582 | ); 583 | 584 | diasSemana.map((diaSemana, index) => { 585 | heatmapData[index].name = diasSemana[index]; 586 | }); 587 | 588 | publicaciones.forEach((publicacion) => { 589 | const fecha = new Date(publicacion.createdAt); 590 | const diaSemana = fecha.getDay(); 591 | const hora = fecha.getHours(); 592 | 593 | heatmapData[diaSemana].name = diasSemana[diaSemana]; 594 | heatmapData[diaSemana].data[hora] += 1; 595 | }); 596 | 597 | let maxCount = 0; 598 | 599 | heatmapData.forEach((data, index) => { 600 | const maxInDay = Math.max(...data.data); 601 | if (maxInDay > maxCount) { 602 | maxCount = maxInDay; 603 | } 604 | }); 605 | const segmentSize = Math.ceil(maxCount / 3); 606 | const ranges = [ 607 | { 608 | from: 1, 609 | to: segmentSize, 610 | name: "Bajo", 611 | color: "#008FFB", 612 | }, 613 | { 614 | from: segmentSize + 1, 615 | to: segmentSize * 2, 616 | name: "Medio", 617 | color: "#efa94a", 618 | }, 619 | { 620 | from: segmentSize * 2 + 1, 621 | to: maxCount, 622 | name: "Alto", 623 | color: "#FF4560", 624 | }, 625 | ]; 626 | 627 | res.json({ 628 | ok: true, 629 | msg: "Datos de mapa de calor", 630 | data: { 631 | heatmapData, 632 | ranges, 633 | total: publicaciones.length, 634 | }, 635 | }); 636 | } catch (error) { 637 | console.log(error); 638 | res.status(500).json({ 639 | ok: false, 640 | msg: "Por favor hable con el administrador", 641 | }); 642 | } 643 | }; 644 | const obtenerCoordenadas = async (req, res) => { 645 | let consulta = {}; 646 | try { 647 | const parametrosBusqueda = req.body; 648 | Object.keys(parametrosBusqueda).forEach((key) => { 649 | if ( 650 | parametrosBusqueda[key] !== "" && 651 | parametrosBusqueda[key] !== undefined 652 | ) { 653 | consulta[key] = parametrosBusqueda[key]; 654 | } 655 | }); 656 | 657 | if (parametrosBusqueda.fechaFin) { 658 | const fechaFin = new Date(parametrosBusqueda.fechaFin); 659 | fechaFin.setHours(23, 59, 59); // Establecer la hora de finalización a las 23:59:59 660 | consulta.createdAt = consulta.createdAt || {}; 661 | consulta.createdAt.$lte = fechaFin; 662 | } 663 | 664 | if (parametrosBusqueda.fechaInicio) { 665 | const fechaInicio = new Date(parametrosBusqueda.fechaInicio); 666 | consulta.createdAt = consulta.createdAt || {}; 667 | consulta.createdAt.$gte = fechaInicio; 668 | } 669 | 670 | let publicaciones = await Publicacion.find(consulta); 671 | if ( 672 | parametrosBusqueda.horaFin != undefined && 673 | parametrosBusqueda.horaFin.includes(":") 674 | ) { 675 | const FechahoraFin = convertToEcuadorTimeZone( 676 | new Date(parametrosBusqueda.horaFin) 677 | ); 678 | const horaFin = FechahoraFin.getHours(); 679 | const minutosFin = FechahoraFin.getMinutes(); 680 | const documentosHoraFin = publicaciones.filter((publicacion) => { 681 | const hora = publicacion.createdAt.getHours(); 682 | const minutos = publicacion.createdAt.getMinutes(); 683 | 684 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 685 | }); 686 | publicaciones = documentosHoraFin; 687 | } 688 | 689 | if ( 690 | parametrosBusqueda.horaInicio != undefined && 691 | parametrosBusqueda.horaInicio.includes(":") 692 | ) { 693 | const FechahoraInicio = convertToEcuadorTimeZone( 694 | new Date(parametrosBusqueda.horaInicio) 695 | ); 696 | const horaInicio = FechahoraInicio.getHours(); 697 | const minutosInicio = FechahoraInicio.getMinutes(); 698 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 699 | const hora = publicacion.createdAt.getHours(); 700 | const minutos = publicacion.createdAt.getMinutes(); 701 | return ( 702 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 703 | ); 704 | }); 705 | publicaciones = documentosHoraInicio; 706 | } 707 | 708 | const coordenadas = publicaciones.map((publicacion) => { 709 | return { 710 | titulo: publicacion.titulo, 711 | position: [publicacion.latitud, publicacion.longitud], 712 | }; 713 | }); 714 | 715 | res.json({ 716 | ok: true, 717 | msg: "Datos de mapa de calor", 718 | data: coordenadas, 719 | }); 720 | } catch (error) { 721 | console.log(error); 722 | res.status(500).json({ 723 | ok: false, 724 | msg: "Por favor hable con el administrador", 725 | }); 726 | } 727 | }; 728 | 729 | const descargarXLSX = async (req, res) => { 730 | let consulta = {}; 731 | try { 732 | const parametrosBusqueda = req.body; 733 | Object.keys(parametrosBusqueda).forEach((key) => { 734 | if ( 735 | parametrosBusqueda[key] !== "" && 736 | parametrosBusqueda[key] !== undefined 737 | ) { 738 | consulta[key] = parametrosBusqueda[key]; 739 | } 740 | }); 741 | 742 | if (parametrosBusqueda.fechaFin) { 743 | const fechaFin = new Date(parametrosBusqueda.fechaFin); 744 | fechaFin.setHours(23, 59, 59); // Establecer la hora de finalización a las 23:59:59 745 | consulta.fechaPublicacion = consulta.fechaPublicacion || {}; 746 | consulta.fechaPublicacion.$lte = fechaFin; 747 | } 748 | 749 | if (parametrosBusqueda.fechaInicio) { 750 | const fechaInicio = new Date(parametrosBusqueda.fechaInicio); 751 | consulta.fechaPublicacion = consulta.fechaPublicacion || {}; 752 | consulta.fechaPublicacion.$gte = fechaInicio; 753 | } 754 | 755 | // Cambiar la propiedad de createdAt por fechaPublicacion 756 | let publicaciones = await Publicacion.find(consulta, { nombreUsuario: 0 }); 757 | 758 | if ( 759 | parametrosBusqueda.horaFin != undefined && 760 | parametrosBusqueda.horaFin.includes(":") 761 | ) { 762 | const FechahoraFin = convertToEcuadorTimeZone( 763 | new Date(parametrosBusqueda.horaFin) 764 | ); 765 | const horaFin = FechahoraFin.getHours(); 766 | const minutosFin = FechahoraFin.getMinutes(); 767 | const documentosHoraFin = publicaciones.filter((publicacion) => { 768 | const hora = publicacion.fechaPublicacion.getHours(); 769 | const minutos = publicacion.fechaPublicacion.getMinutes(); 770 | 771 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 772 | }); 773 | publicaciones = documentosHoraFin; 774 | } 775 | 776 | if ( 777 | parametrosBusqueda.horaInicio != undefined && 778 | parametrosBusqueda.horaInicio.includes(":") 779 | ) { 780 | const FechahoraInicio = convertToEcuadorTimeZone( 781 | new Date(parametrosBusqueda.horaInicio) 782 | ); 783 | const horaInicio = FechahoraInicio.getHours(); 784 | const minutosInicio = FechahoraInicio.getMinutes(); 785 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 786 | const hora = publicacion.fechaPublicacion.getHours(); 787 | const minutos = publicacion.fechaPublicacion.getMinutes(); 788 | return ( 789 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 790 | ); 791 | }); 792 | publicaciones = documentosHoraInicio; 793 | } 794 | 795 | // ... 796 | 797 | //eliminar de publicaciones nombreUsuario 798 | 799 | //eliminar nombreUsuario de todos las publicaciones 800 | 801 | exportToExcel(publicaciones) 802 | .then((buffer) => { 803 | // Configurar las cabeceras para la descarga del archivo 804 | res.setHeader("Content-Disposition", "attachment; filename=datos.xlsx"); 805 | res.setHeader( 806 | "Content-Type", 807 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" 808 | ); 809 | 810 | // Enviar el archivo de Excel como respuesta 811 | res.send(buffer); 812 | }) 813 | .catch((error) => { 814 | console.error("Error al generar el archivo de Excel:", error); 815 | res.status(500).send("Error al generar el archivo de Excel."); 816 | }); 817 | } catch (error) { 818 | console.log(error); 819 | res.status(500).json({ 820 | ok: false, 821 | msg: "Por favor hable con el administrador", 822 | }); 823 | } 824 | }; 825 | 826 | // Resto del código sin cambios 827 | 828 | const descargarCSV = async (req, res) => { 829 | let consulta = {}; 830 | try { 831 | const parametrosBusqueda = req.body; 832 | Object.keys(parametrosBusqueda).forEach((key) => { 833 | if ( 834 | parametrosBusqueda[key] !== "" && 835 | parametrosBusqueda[key] !== undefined 836 | ) { 837 | consulta[key] = parametrosBusqueda[key]; 838 | } 839 | }); 840 | 841 | if (parametrosBusqueda.fechaFin) { 842 | const fechaFin = new Date(parametrosBusqueda.fechaFin); 843 | fechaFin.setHours(23, 59, 59); // Establecer la hora de finalización a las 23:59:59 844 | consulta.createdAt = consulta.createdAt || {}; 845 | consulta.createdAt.$lte = fechaFin; 846 | } 847 | 848 | if (parametrosBusqueda.fechaInicio) { 849 | const fechaInicio = new Date(parametrosBusqueda.fechaInicio); 850 | consulta.createdAt = consulta.createdAt || {}; 851 | consulta.createdAt.$gte = fechaInicio; 852 | } 853 | 854 | 855 | 856 | let publicaciones = await Publicacion.find(consulta, { nombreUsuario: 0 }); 857 | if ( 858 | parametrosBusqueda.horaFin != undefined && 859 | parametrosBusqueda.horaFin.includes(":") 860 | ) { 861 | const FechahoraFin = convertToEcuadorTimeZone( 862 | new Date(parametrosBusqueda.horaFin) 863 | ); 864 | const horaFin = FechahoraFin.getHours(); 865 | const minutosFin = FechahoraFin.getMinutes(); 866 | const documentosHoraFin = publicaciones.filter((publicacion) => { 867 | const hora = publicacion.createdAt.getHours(); 868 | const minutos = publicacion.createdAt.getMinutes(); 869 | 870 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 871 | }); 872 | publicaciones = documentosHoraFin; 873 | } 874 | 875 | if ( 876 | parametrosBusqueda.horaInicio != undefined && 877 | parametrosBusqueda.horaInicio.includes(":") 878 | ) { 879 | const FechahoraInicio = convertToEcuadorTimeZone( 880 | new Date(parametrosBusqueda.horaInicio) 881 | ); 882 | const horaInicio = FechahoraInicio.getHours(); 883 | const minutosInicio = FechahoraInicio.getMinutes(); 884 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 885 | const hora = publicacion.createdAt.getHours(); 886 | const minutos = publicacion.createdAt.getMinutes(); 887 | return ( 888 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 889 | ); 890 | }); 891 | publicaciones = documentosHoraInicio; 892 | } 893 | 894 | exportToCSV(publicaciones) 895 | .then((buffer) => { 896 | // Configurar las cabeceras para la descarga del archivo 897 | res.setHeader("Content-Disposition", "attachment; filename=datos.xlsx"); 898 | res.setHeader( 899 | "Content-Type", 900 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" 901 | ); 902 | 903 | // Enviar el archivo de Excel como respuesta 904 | res.send(buffer); 905 | }) 906 | .catch((error) => { 907 | console.error("Error al generar el archivo de Excel:", error); 908 | res.status(500).send("Error al generar el archivo de Excel."); 909 | }); 910 | } catch (error) { 911 | console.log(error); 912 | res.status(500).json({ 913 | ok: false, 914 | msg: "Por favor hable con el administrador", 915 | }); 916 | } 917 | }; 918 | 919 | process.env.OPENSSL_CONF = '/dev/null'; 920 | 921 | const descargarPDF = async (req, res) => { 922 | let consulta = {}; 923 | try { 924 | const parametrosBusqueda = req.body; 925 | 926 | Object.keys(parametrosBusqueda).forEach((key) => { 927 | if ( 928 | parametrosBusqueda[key] !== "" && 929 | parametrosBusqueda[key] !== undefined 930 | ) { 931 | consulta[key] = parametrosBusqueda[key]; 932 | } 933 | }); 934 | 935 | if (parametrosBusqueda.fechaFin) { 936 | const fechaFin = new Date(parametrosBusqueda.fechaFin); 937 | fechaFin.setHours(23, 59, 59); // Establecer la hora de finalización a las 23:59:59 938 | consulta.createdAt = consulta.createdAt || {}; 939 | consulta.createdAt.$lte = fechaFin; 940 | } 941 | 942 | if (parametrosBusqueda.fechaInicio) { 943 | const fechaInicio = new Date(parametrosBusqueda.fechaInicio); 944 | consulta.createdAt = consulta.createdAt || {}; 945 | consulta.createdAt.$gte = fechaInicio; 946 | } 947 | 948 | let publicaciones = await Publicacion.find(consulta); 949 | if ( 950 | parametrosBusqueda.horaFin != undefined && 951 | parametrosBusqueda.horaFin.includes(":") 952 | ) { 953 | const FechahoraFin = convertToEcuadorTimeZone( 954 | new Date(parametrosBusqueda.horaFin) 955 | ); 956 | const horaFin = FechahoraFin.getHours(); 957 | const minutosFin = FechahoraFin.getMinutes(); 958 | const documentosHoraFin = publicaciones.filter((publicacion) => { 959 | const hora = publicacion.createdAt.getHours(); 960 | const minutos = publicacion.createdAt.getMinutes(); 961 | 962 | return hora < horaFin || (hora === horaFin && minutos <= minutosFin); 963 | }); 964 | publicaciones = documentosHoraFin; 965 | } 966 | 967 | if ( 968 | parametrosBusqueda.horaInicio != undefined && 969 | parametrosBusqueda.horaInicio.includes(":") 970 | ) { 971 | const FechahoraInicio = convertToEcuadorTimeZone( 972 | new Date(parametrosBusqueda.horaInicio) 973 | ); 974 | const horaInicio = FechahoraInicio.getHours(); 975 | const minutosInicio = FechahoraInicio.getMinutes(); 976 | const documentosHoraInicio = publicaciones.filter((publicacion) => { 977 | const hora = publicacion.createdAt.getHours(); 978 | const minutos = publicacion.createdAt.getMinutes(); 979 | return ( 980 | hora > horaInicio || (hora === horaInicio && minutos >= minutosInicio) 981 | ); 982 | }); 983 | publicaciones = documentosHoraInicio; 984 | } 985 | 986 | 987 | 988 | const data = path.join( __dirname, 'aaaaaaaaaaa.txt'); 989 | console.log(data); 990 | fs.writeFile(data, 'Hola mundo', (err) => { 991 | if (err) throw err; 992 | 993 | console.log('The file has been saved!'); 994 | }); 995 | 996 | //TODO: Generar el PDF 997 | console.log(publicaciones); 998 | const pathPDF = path.join( __dirname, 'datos.pdf'); 999 | const pdfOptions = { 1000 | childProcessOptions: { 1001 | env: { 1002 | OPENSSL_CONF: '/dev/null', // Configuración para evitar problemas SSL 1003 | }, 1004 | }, 1005 | }; 1006 | 1007 | //TODO: total usuarios 1008 | const totalUsuarios = await Usuario.countDocuments(); 1009 | console.log(totalUsuarios);//46 1010 | 1011 | //TODO: total publicaciones por dia, dia acutal 1012 | const fechaActual = new Date(); 1013 | 1014 | const fechaInicio = new Date(fechaActual.getFullYear(), fechaActual.getMonth(), fechaActual.getDate(), 0, 0, 0); 1015 | const fechaFin = new Date(fechaActual.getFullYear(), fechaActual.getMonth(), fechaActual.getDate(), 23, 59, 59); 1016 | 1017 | const totalPublicacionesDia = await Publicacion.countDocuments({ 1018 | createdAt: { 1019 | $gte: fechaInicio, 1020 | $lte: fechaFin 1021 | } 1022 | }); 1023 | console.log(totalPublicacionesDia);//29 1024 | 1025 | //TODO: total publicaciones por MES, mes actual 1026 | const fechaInicioMes = new Date(fechaActual.getFullYear(), fechaActual.getMonth(), 1, 0, 0, 0); 1027 | const fechaFinMes = new Date(fechaActual.getFullYear(), fechaActual.getMonth() + 1, 0, 23, 59, 59); 1028 | 1029 | const totalPublicacionesMes = await Publicacion.countDocuments({ 1030 | createdAt: { 1031 | $gte: fechaInicioMes, 1032 | $lte: fechaFinMes 1033 | } 1034 | }); 1035 | 1036 | console.log(publicaciones);//29 1037 | 1038 | //taotal publicaciones registradas en el sistema 1039 | const totalPublicacionesCoutn = await Publicacion.countDocuments(); 1040 | 1041 | 1042 | //objeto de persona con nombre y edad 1043 | const dataInfo = { 1044 | totalUsuarios: totalUsuarios, 1045 | totalPublicacionesDia: totalPublicacionesDia, 1046 | totalPublicacionesMes: totalPublicacionesMes, 1047 | publicaciones: publicaciones, 1048 | totalPublicacionesCoutn: totalPublicacionesCoutn 1049 | } 1050 | 1051 | 1052 | // console.log(conteoPorMes); 1053 | const pdfFilePath = path.join(__dirname, 'datos.pdf'); // Ruta donde deseas guardar el PDF 1054 | 1055 | pdf.create(pdfTemplate(dataInfo), pdfOptions).toFile(pdfFilePath, (err) => { 1056 | if (err) { 1057 | console.log('Error creating PDF:', err); 1058 | // Maneja el error de acuerdo a tus necesidades 1059 | // res.send(Promise.reject()); 1060 | } else { 1061 | console.log('PDF has been successfully created and saved.'); 1062 | // Realiza acciones adicionales si es necesario 1063 | // res.send(Promise.resolve()); 1064 | } 1065 | }); 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | // Configurar los encabezados de la respuesta para descargar el archivo PDF 1072 | const filename = "archivo.pdf"; 1073 | res.setHeader("Content-Disposition", `attachment; filename="${filename}"`); 1074 | res.setHeader("Content-Type", "application/pdf"); 1075 | 1076 | // Obtener la ruta absoluta de las fuentes Roboto 1077 | const fonts = { 1078 | Roboto: { 1079 | normal: require.resolve( 1080 | "pdfmake-unicode/src/fonts/Arial GEO/Roboto-Regular.ttf" 1081 | ), 1082 | bold: require.resolve( 1083 | "pdfmake-unicode/src/fonts/Arial GEO/Roboto-Medium.ttf" 1084 | ), 1085 | }, 1086 | }; 1087 | 1088 | // Crear un objeto de definición de PDF utilizando pdfmake 1089 | const printer = new pdfMakePrinter(fonts); 1090 | const docDefinition = { 1091 | content: [ 1092 | { text: "Lista de Publicaciones", style: "header" }, 1093 | { 1094 | table: { 1095 | headerRows: 1, 1096 | widths: ["auto", "auto", "auto", "auto", "auto", "auto", "auto"], 1097 | body: [ 1098 | [ 1099 | { text: "Título", style: "header2" }, 1100 | { text: "Contenido", style: "header2" }, 1101 | { text: "Ciudad", style: "header2" }, 1102 | { text: "Barrio", style: "header2" }, 1103 | { text: "Nombre de Usuario", style: "header2" }, 1104 | { text: "Latitud", style: "header2" }, 1105 | { text: "Longitud", style: "header2" }, 1106 | ], 1107 | ...publicaciones.map((publicacion) => [ 1108 | publicacion.titulo, 1109 | publicacion.contenido, 1110 | publicacion.ciudad, 1111 | publicacion.barrio, 1112 | publicacion.nombreUsuario, 1113 | publicacion.latitud.toString(), 1114 | publicacion.longitud.toString(), 1115 | ]), 1116 | ], 1117 | }, 1118 | }, 1119 | ], 1120 | styles: { 1121 | header: { 1122 | fontSize: 18, 1123 | bold: true, 1124 | alignment: "center", 1125 | }, 1126 | header2: { 1127 | fontSize: 12, 1128 | bold: true, 1129 | alignment: "center", 1130 | }, 1131 | }, 1132 | }; 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | // Crear el documento PDF utilizando pdfmake 1139 | const pdfDoc = printer.createPdfKitDocument(docDefinition); 1140 | pdfDoc.pipe(res); 1141 | pdfDoc.end(); 1142 | } catch (error) { 1143 | console.log(error); 1144 | res.status(500).json({ 1145 | ok: false, 1146 | msg: "Por favor hable con el administrador", 1147 | }); 1148 | } 1149 | }; 1150 | // Función para exportar un array de objetos a Excel 1151 | async function exportToExcel(dataArray) { 1152 | const workbook = new ExcelJS.Workbook(); 1153 | const worksheet = workbook.addWorksheet("Datos"); 1154 | 1155 | // Definir los encabezados de las columnas en la hoja de cálculo 1156 | console.log(dataArray); 1157 | let objeto = dataArray[0]._doc; 1158 | delete objeto._id; 1159 | delete objeto.color; 1160 | delete objeto.isPublic; 1161 | delete objeto.usuario; 1162 | delete objeto.likes; 1163 | delete objeto.imagenes; 1164 | delete objeto.comentarios; 1165 | delete objeto.__v; 1166 | delete objeto.isActivo; 1167 | delete objeto.isLiked; 1168 | delete objeto.imgAlerta; 1169 | delete objeto.isPublicacionPendiente; 1170 | delete objeto.updatedAt; 1171 | delete objeto.fechaPublicacion; 1172 | 1173 | // Cambiar encabezado de createdAt por FechaCreacion 1174 | objeto.FechaCreacion = objeto.createdAt; 1175 | delete objeto.createdAt; 1176 | 1177 | // Cambiar los encabezados de las columnas a español y mayúsculas 1178 | const headerTranslations = { 1179 | titulo: "Tipo emergencia comunitaria", 1180 | contenido: "Descripción de la emergencia", 1181 | color: "Color", 1182 | ciudad: "Ciudad", 1183 | barrio: "Barrio", 1184 | isPublic: "Es Público", 1185 | usuario: "Usuario", 1186 | nombreUsuario: "Nombre de Usuario", 1187 | likes: "Likes", 1188 | imagenes: "Imágenes", 1189 | latitud: "Latitud", 1190 | longitud: "Longitud", 1191 | comentarios: "Comentarios", 1192 | imgAlerta: "Imagen de Alerta", 1193 | isLiked: "Es Favorito", 1194 | isActivo: "Es Activo", 1195 | FechaCreacion: "Fecha de publicación", 1196 | isPublicacionPendiente: "Publicación Pendiente", 1197 | }; 1198 | 1199 | const columnHeaders = Object.keys(objeto).map(header => { 1200 | if (headerTranslations.hasOwnProperty(header)) { 1201 | return headerTranslations[header]; 1202 | } else { 1203 | return header.charAt(0).toUpperCase() + header.slice(1); 1204 | } 1205 | }); 1206 | 1207 | console.log(columnHeaders); 1208 | worksheet.addRow(columnHeaders); 1209 | 1210 | // Llenar la hoja de cálculo con los datos de los objetos 1211 | dataArray.forEach((dataObj) => { 1212 | let objeto = Object.values(dataObj)[2]; 1213 | delete objeto._id; 1214 | delete objeto.color; 1215 | delete objeto.isPublic; 1216 | delete objeto.usuario; 1217 | delete objeto.likes; 1218 | delete objeto.imagenes; 1219 | delete objeto.comentarios; 1220 | delete objeto.__v; 1221 | delete objeto.isActivo; 1222 | delete objeto.isLiked; 1223 | delete objeto.imgAlerta; 1224 | delete objeto.isPublicacionPendiente; 1225 | delete objeto.fechaPublicacion; 1226 | delete objeto.updatedAt; 1227 | 1228 | worksheet.addRow(Object.values(objeto)); 1229 | }); 1230 | 1231 | worksheet.columns.forEach((column, index) => { 1232 | let maxLength = 0; 1233 | worksheet.eachRow((row, rowNumber) => { 1234 | if (rowNumber > 1) { 1235 | const cellValue = row.getCell(index + 1).value; 1236 | if (cellValue && cellValue.toString().length > maxLength) { 1237 | maxLength = cellValue.toString().length; 1238 | } 1239 | } 1240 | }); 1241 | // Limitar el ancho de las celdas al máximo permitido (16384) 1242 | column.width = Math.min(maxLength < 12 ? 12 : maxLength, 16384); 1243 | }); 1244 | 1245 | // Devolver el archivo de Excel como un buffer 1246 | const buffer = await workbook.xlsx.writeBuffer(); 1247 | return buffer; 1248 | } 1249 | 1250 | 1251 | // Función para exportar un array de objetos a Excel 1252 | async function exportToCSV(dataArray) { 1253 | const workbook = new ExcelJS.Workbook(); 1254 | const worksheet = workbook.addWorksheet('Datos'); 1255 | 1256 | // Definir los encabezados de las columnas en la hoja de cálculo 1257 | let objeto = dataArray[0]._doc; 1258 | 1259 | // Agrega aquí los campos que deseas eliminar 1260 | const camposEliminar = ['_id', 'color', 'isPublic', 'usuario', 'likes', 'imagenes', 'comentarios', '__v', 'isActivo', 'isLiked', 'imgAlerta', 'isPublicacionPendiente', 'fechaPublicacion', 'updatedAt']; 1261 | 1262 | camposEliminar.forEach(campos => { 1263 | delete objeto[campos]; 1264 | }); 1265 | 1266 | // Cambiar encabezado de createdAt por FechaCreacion 1267 | objeto.FechaCreacion = objeto.createdAt; 1268 | delete objeto.createdAt; 1269 | 1270 | // Cambiar los encabezados de las columnas a español y mayúsculas 1271 | const headerTranslations = { 1272 | titulo: "Tipo emergencia comunitaria", 1273 | contenido: "Descripción de la emergencia", 1274 | color: "Color", 1275 | ciudad: "Ciudad", 1276 | barrio: "Barrio", 1277 | isPublic: "Es Público", 1278 | usuario: "Usuario", 1279 | nombreUsuario: "Nombre de Usuario", 1280 | likes: "Likes", 1281 | imagenes: "Imágenes", 1282 | latitud: "Latitud", 1283 | longitud: "Longitud", 1284 | comentarios: "Comentarios", 1285 | imgAlerta: "Imagen de Alerta", 1286 | isLiked: "Es Favorito", 1287 | isActivo: "Es Activo", 1288 | FechaCreacion: "Fecha de publicación", 1289 | isPublicacionPendiente: "Publicación Pendiente", 1290 | }; 1291 | 1292 | const columnHeaders = Object.keys(objeto).map(header => { 1293 | if (headerTranslations.hasOwnProperty(header)) { 1294 | return headerTranslations[header]; 1295 | } else { 1296 | return header.charAt(0).toUpperCase() + header.slice(1); 1297 | } 1298 | }); 1299 | 1300 | worksheet.addRow(columnHeaders); 1301 | 1302 | // Llenar la hoja de cálculo con los datos de los objetos 1303 | dataArray.forEach((dataObj) => { 1304 | let objeto = Object.values(dataObj)[2]; 1305 | 1306 | camposEliminar.forEach(campos => { 1307 | delete objeto[campos]; 1308 | }); 1309 | 1310 | worksheet.addRow(Object.values(objeto)); 1311 | }); 1312 | 1313 | worksheet.columns.forEach((column, index) => { 1314 | let maxLength = 0; 1315 | worksheet.eachRow((row, rowNumber) => { 1316 | if (rowNumber > 1) { 1317 | const cellValue = row.getCell(index + 1).value; 1318 | if (cellValue && cellValue.toString().length > maxLength) { 1319 | maxLength = cellValue.toString().length; 1320 | } 1321 | } 1322 | }); 1323 | // Limitar el ancho de las celdas al máximo permitido (16384) 1324 | column.width = Math.min(maxLength < 12 ? 12 : maxLength, 16384); 1325 | }); 1326 | 1327 | // Devolver el archivo de Excel como un buffer 1328 | const buffer = await workbook.xlsx.writeBuffer(); 1329 | return buffer; 1330 | } 1331 | 1332 | 1333 | module.exports = { 1334 | obtenerCiudades, 1335 | obtenerBarrios, 1336 | obtenerEmergencias, 1337 | obtenerAnios, 1338 | obtenerReporteBarras, 1339 | obtenerReportePastel, 1340 | obtenerMapaCalor, 1341 | obtenerDatosCards, 1342 | obtenerCoordenadas, 1343 | descargarXLSX, 1344 | descargarPDF, 1345 | descargarCSV, 1346 | }; 1347 | --------------------------------------------------------------------------------