Skip to content

Commit

Permalink
Merge pull request #58 from Saca-la-Bici/feature/0.2.0/pushNotifications
Browse files Browse the repository at this point in the history
Base para enviar push notifications con Firebase Cloud Messaging
  • Loading branch information
MariaJoseGaytan authored Sep 30, 2024
2 parents 0528631 + c790d83 commit 271bfe7
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 1 deletion.
1 change: 1 addition & 0 deletions models/otros/anuncio.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ async function postAnnouncement(firebaseUID, titulo, contenido, imagen){
imagen: imagen
});
await announcement.save();
return announcement;
}

async function getAnnouncements(){
Expand Down
5 changes: 5 additions & 0 deletions models/perfil/usuario.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ const usuarioSchema = new mongoose.Schema(
unique: true,
required: true
},
fcmTokens: {
type: [String],
default: [],
required: false
}
},
{
collection: "Usuario",
Expand Down
26 changes: 25 additions & 1 deletion modules/anuncios/controllers/registrarAnuncio.controller.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@

const Announcement = require('../../../models/otros/anuncio.model');
const {
Usuario
} = require('../../../models/perfil/usuario.model');

const sendNotification = require('../../../util/sendNotification');

exports.postAnnouncement = async (request, response) => {
const firebaseUID = request.userUID.uid;
const titulo = request.body.titulo;
const contenido = request.body.contenido;
const imagen = request.body.imagen;
try {
await Announcement.postAnnouncement(firebaseUID, titulo, contenido, imagen);
const announcement = await Announcement.postAnnouncement(firebaseUID, titulo, contenido, imagen);

// Obtener todos los tokens de FCM de los usuarios
const usuarios = await Usuario.find({
fcmTokens: {
$exists: true,
$ne: []
}
});
const tokens = usuarios.reduce((acc, usuario) => acc.concat(usuario.fcmTokens), []);

// Definir el contenido de la notificación
const tituloNotificacion = titulo;
const cuerpoNotificacion = contenido;

// Enviar la notificación
await sendNotification(tokens, tituloNotificacion, cuerpoNotificacion, {
anuncioID: announcement._id.toString(),
});

return response.status(201).json({ message: 'Anuncio creado correctamente' });
} catch (error) {
return response.status(500).json({ message: 'Error al crear el anuncio', error: error.message });
Expand Down
48 changes: 48 additions & 0 deletions modules/session/controllers/actualizarFCM.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const {
Usuario
} = require('../../../models/perfil/usuario.model');

exports.actualizarFCM = async (request, response) => {
const {
fcmToken
} = request.body;

if (!fcmToken) {
return response.status(400).json({
message: 'fcmToken es requerido'
});
}

try {
// Eliminar el token de cualquier otra cuenta que lo tenga registrado
await Usuario.updateMany({
fcmTokens: fcmToken
}, {
$pull: {
fcmTokens: fcmToken
}
});

// Buscar al usuario por UID
const user = await Usuario.findOne({
firebaseUID: request.userUID.uid
});

if (user) {
// Agregar el token si no existe
if (!user.fcmTokens.includes(fcmToken)) {
user.fcmTokens.push(fcmToken);
await user.save();
}
}

response.status(200).json({
message: 'Token de FCM actualizado exitosamente'
});
} catch (error) {
console.error('Error al actualizar el token de FCM:', error);
response.status(500).json({
message: 'Error al actualizar el token de FCM'
});
}
}
10 changes: 10 additions & 0 deletions modules/session/routes/actualizarFCM.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const express = require('express');
const router = express.Router();

const verifyToken = require('../../../util/verifyUserToken')

const actualizarFCMController = require('../controllers/actualizarFCM.controller');

router.post('/', verifyToken, actualizarFCMController.actualizarFCM);

module.exports = router;
2 changes: 2 additions & 0 deletions modules/session/routes/sessionIndex.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ const rolPrivilegioRoute = require('./rolPrivilegio.routes');
const getUserEmailRoute = require('./getUserEmail.routes');
const perfilCompletoRoute = require('./perfilCompleto.routes');
const getUsernameRoute = require('./getUsername.routes');
const actualizarFCMRoute = require('./actualizarFCM.routes');

router.use('/registrarUsuario', registrarUsuarioRoute);
router.use('/rolPrivilegio', rolPrivilegioRoute);
router.use('/getUserEmail', getUserEmailRoute);
router.use('/perfilCompleto', perfilCompletoRoute);
router.use('/getUsername', getUsernameRoute);
router.use('/actualizarTokenNotificacion', actualizarFCMRoute);

module.exports = router;
78 changes: 78 additions & 0 deletions util/sendNotification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const admin = require('./firebase-admin-config');
const {
Usuario
} = require('../models/perfil/usuario.model');

const chunkArray = (array, size) => {
const result = [];
for (let i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size));
}
return result;
};

const sendNotification = async (tokens, titulo, cuerpo, data = {}) => {
if (!tokens || tokens.length === 0) {
console.log('No hay tokens de FCM para enviar notificaciones.');
return;
}

// Dividir los tokens en grupos de 500 como máximo
const tokenChunks = chunkArray(tokens, 500);

try {
for (const chunk of tokenChunks) {
const responses = await admin.messaging().sendEachForMulticast({
tokens: chunk,
notification: {
title: titulo,
body: cuerpo,
},
data: data
});

let successCount = 0;
let failureCount = 0;
const failedTokens = [];

responses.responses.forEach((response, index) => {
if (response.success) {
successCount++;
} else {
failureCount++;
const failedToken = chunk[index];
console.error(`Error al enviar a ${failedToken}: ${response.error.message}`);
if (
response.error.code === 'messaging/invalid-registration-token' ||
response.error.code === 'messaging/registration-token-not-registered'
) {
failedTokens.push(failedToken);
}
}
});

console.log(`${successCount} mensajes enviados con éxito`);
console.log(`${failureCount} mensajes fallaron`);

// Si hay tokens fallidos, eliminarlos de la base de datos
if (failedTokens.length > 0) {
await Usuario.updateMany({
fcmTokens: {
$in: failedTokens
}
}, {
$pull: {
fcmTokens: {
$in: failedTokens
}
}
});
console.log('Tokens inválidos eliminados de la base de datos.');
}
}
} catch (error) {
console.error('Error al enviar notificaciones:', error);
}
};

module.exports = sendNotification;

0 comments on commit 271bfe7

Please sign in to comment.