created endpoint for notifying clients and owners when a trainer updates his profile
This commit is contained in:
parent
ba0f28d1db
commit
d8686f3b8b
@ -1,6 +1,7 @@
|
|||||||
import { onRequest } from "firebase-functions/v2/https";
|
import { onRequest } from "firebase-functions/v2/https";
|
||||||
import { Request } from "firebase-functions/v2/https";
|
import { Request } from "firebase-functions/v2/https";
|
||||||
import * as admin from 'firebase-admin';
|
import * as admin from 'firebase-admin';
|
||||||
|
import { Message } from "firebase-admin/messaging";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import * as logger from "firebase-functions/logger";
|
import * as logger from "firebase-functions/logger";
|
||||||
import { onDocumentCreated } from "firebase-functions/firestore";
|
import { onDocumentCreated } from "firebase-functions/firestore";
|
||||||
@ -103,7 +104,7 @@ export const notifyInvitation = onDocumentCreated({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message: admin.messaging.Message = {
|
const message: Message = {
|
||||||
notification: {
|
notification: {
|
||||||
title: 'New Gym Invitation',
|
title: 'New Gym Invitation',
|
||||||
body: `${invitation.invitedByName} has invited you to join ${invitation.gymName}`,
|
body: `${invitation.invitedByName} has invited you to join ${invitation.gymName}`,
|
||||||
@ -136,3 +137,227 @@ export const notifyInvitation = onDocumentCreated({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const notifyTrainerUpdate = onRequest(
|
||||||
|
{
|
||||||
|
region: '#{SERVICES_RGN}#'
|
||||||
|
},
|
||||||
|
async (request: Request, response: express.Response) => {
|
||||||
|
try {
|
||||||
|
const { trainerId, section, trainerName, changedFields} = request.body;
|
||||||
|
|
||||||
|
if (!trainerId || !section || !trainerName || !changedFields) {
|
||||||
|
response.status(400).json({ success: false, error: 'Missing required parameters' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let changesTable = '<table border="1" cellpadding="5" style="border-collapse: collapse;">';
|
||||||
|
changesTable += '<tr><th>Field</th><th>New Value</th></tr>';
|
||||||
|
|
||||||
|
if (changedFields && typeof changedFields === 'object') {
|
||||||
|
for (const [field, value] of Object.entries(changedFields)) {
|
||||||
|
changesTable += '<tr>';
|
||||||
|
changesTable += `<td>${field}</td>`;
|
||||||
|
|
||||||
|
// Handle different types of values (strings, arrays, objects)
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
changesTable += `<td>${value.join(', ')}</td>`;
|
||||||
|
} else if (typeof value === 'object' && value !== null) {
|
||||||
|
changesTable += `<td>${JSON.stringify(value)}</td>`;
|
||||||
|
} else {
|
||||||
|
changesTable += `<td>${value}</td>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
changesTable += '</tr>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changesTable += '</table>';
|
||||||
|
|
||||||
|
const memberships = await admin
|
||||||
|
.firestore()
|
||||||
|
.collection('memberships')
|
||||||
|
.where('trainerId', '==', trainerId)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
const userIds = memberships.docs.map((doc) => doc.data().userId);
|
||||||
|
|
||||||
|
// if (clients.empty) {
|
||||||
|
// logger.info(`No clients found for trainer: ${trainerId}`);
|
||||||
|
// response.json({ success: true, message: 'No clients to notify' });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
const acceptedGyms = await admin
|
||||||
|
.firestore()
|
||||||
|
.collection('traineracceptedgyms')
|
||||||
|
.where('trainerProfileId', '==', trainerId)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
const gymIds = acceptedGyms.docs.map((doc) => doc.data().gymId);
|
||||||
|
|
||||||
|
const notificationPromises = [];
|
||||||
|
const emailPromises = [];
|
||||||
|
|
||||||
|
for (const userId of userIds) {
|
||||||
|
|
||||||
|
const clientQuerySnapshot = await admin
|
||||||
|
.firestore()
|
||||||
|
.collection('client_profiles')
|
||||||
|
.where('uid', '==',userId)
|
||||||
|
.limit(1)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
if (clientQuerySnapshot.empty) {
|
||||||
|
console.log(`No client found with uid: ${userId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientDoc = clientQuerySnapshot.docs[0];
|
||||||
|
const clientData = clientDoc.data();
|
||||||
|
const fcmToken = clientData.fcmToken;
|
||||||
|
const clientEmail = clientData.email;
|
||||||
|
|
||||||
|
if (fcmToken) {
|
||||||
|
const message : Message ={
|
||||||
|
notification: {
|
||||||
|
title: 'Trainer Profile Update',
|
||||||
|
body: `${trainerName} has updated their ${section}`,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: 'trainer_profile_update',
|
||||||
|
trainerId: trainerId,
|
||||||
|
section: section,
|
||||||
|
trainerName: trainerName,
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
priority: 'high',
|
||||||
|
notification: {
|
||||||
|
channelId: 'trainer_updates_channel',
|
||||||
|
priority: 'high',
|
||||||
|
defaultSound: true,
|
||||||
|
defaultVibrateTimings: true,
|
||||||
|
icon: '@mipmap/ic_launcher',
|
||||||
|
clickAction: 'FLUTTER_NOTIFICATION_CLICK',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
token: fcmToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
notificationPromises.push(admin.messaging().send(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientEmail) {
|
||||||
|
const emailContent = `
|
||||||
|
<h2>Trainer Profile Update</h2>
|
||||||
|
<p>Your trainer ${trainerName} has updated their ${section}.</p>
|
||||||
|
<h3>Changes Made:</h3>
|
||||||
|
${changesTable}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const mailgun = new Mailgun(formData);
|
||||||
|
const mailGunClient = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY });
|
||||||
|
|
||||||
|
const options = { wordwrap: 130 };
|
||||||
|
const textMessage = convert(emailContent, options);
|
||||||
|
|
||||||
|
const emailPromise = mailGunClient.messages.create(process.env.MAILGUN_SERVER, {
|
||||||
|
from: process.env.MAILGUN_FROM_ADDRESS,
|
||||||
|
to: clientEmail,
|
||||||
|
subject: `Your trainer ${trainerName} has updated their profile`,
|
||||||
|
text: textMessage,
|
||||||
|
html: emailContent
|
||||||
|
});
|
||||||
|
|
||||||
|
emailPromises.push(emailPromise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const gymId of gymIds) {
|
||||||
|
|
||||||
|
const gymQuerySnapshot = await admin
|
||||||
|
.firestore()
|
||||||
|
.collection('gyms')
|
||||||
|
.where('id', '==',gymId)
|
||||||
|
.limit(1)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
if (gymQuerySnapshot.empty) {
|
||||||
|
console.log(`No gym found with id: ${gymId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gymDoc = gymQuerySnapshot.docs[0];
|
||||||
|
const gymData = gymDoc.data();
|
||||||
|
const fcmToken = gymData.fcmToken;
|
||||||
|
const gymEmail = gymData.email;
|
||||||
|
|
||||||
|
if (fcmToken) {
|
||||||
|
const message :Message={
|
||||||
|
notification: {
|
||||||
|
title: 'Trainer Profile Update',
|
||||||
|
body: `${trainerName} has updated their ${section}`,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: 'trainer_profile_update',
|
||||||
|
trainerId: trainerId,
|
||||||
|
section: section,
|
||||||
|
trainerName: trainerName,
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
priority: 'high',
|
||||||
|
notification: {
|
||||||
|
channelId: 'trainer_updates_channel',
|
||||||
|
priority: 'high',
|
||||||
|
defaultSound: true,
|
||||||
|
defaultVibrateTimings: true,
|
||||||
|
icon: '@mipmap/ic_launcher',
|
||||||
|
clickAction: 'FLUTTER_NOTIFICATION_CLICK',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
token: fcmToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
notificationPromises.push(admin.messaging().send(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gymEmail) {
|
||||||
|
const emailContent = `
|
||||||
|
<h2>Trainer Profile Update</h2>
|
||||||
|
<p>Your trainer ${trainerName} has updated their ${section}.</p>
|
||||||
|
<h3>Changes Made:</h3>
|
||||||
|
${changesTable}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const mailgun = new Mailgun(formData);
|
||||||
|
const mailGunClient = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY });
|
||||||
|
|
||||||
|
const options = { wordwrap: 130 };
|
||||||
|
const textMessage = convert(emailContent, options);
|
||||||
|
|
||||||
|
const emailPromise = mailGunClient.messages.create(process.env.MAILGUN_SERVER, {
|
||||||
|
from: process.env.MAILGUN_FROM_ADDRESS,
|
||||||
|
to: gymEmail,
|
||||||
|
subject: `Your trainer ${trainerName} has updated their profile`,
|
||||||
|
text: textMessage,
|
||||||
|
html: emailContent
|
||||||
|
});
|
||||||
|
|
||||||
|
emailPromises.push(emailPromise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await Promise.all([...notificationPromises, ...emailPromises]);
|
||||||
|
|
||||||
|
logger.info(`Notifications sent to clients of trainer: ${trainerId}`);
|
||||||
|
response.json({
|
||||||
|
success: true,
|
||||||
|
message: `Notifications sent to ${notificationPromises.length} clients, Emails sent to ${emailPromises.length} clients`
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error sending trainer update notifications:', error);
|
||||||
|
response.status(500).json({ success: false, error: "failed to send notifications" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
2592
package-lock.json
generated
Normal file
2592
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
5
package.json
Normal file
5
package.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"firebase-functions": "^6.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user