feat: implement comprehensive Firebase Functions backend for equipment management and migrate core repository services
This commit is contained in:
@@ -1,34 +1,34 @@
|
||||
const {onRequest} = require('firebase-functions/v2/https');
|
||||
const admin = require('firebase-admin');
|
||||
const nodemailer = require('nodemailer');
|
||||
const logger = require('firebase-functions/logger');
|
||||
const {getSmtpConfig, EMAIL_CONFIG} = require('./utils/emailConfig');
|
||||
const {renderTemplate, getEmailSubject, getAlertTitle, prepareTemplateData, checkAlertPreference} = require('./utils/emailTemplates');
|
||||
const auth = require('./utils/auth');
|
||||
const {onRequest} = require("firebase-functions/v2/https");
|
||||
const admin = require("firebase-admin");
|
||||
const nodemailer = require("nodemailer");
|
||||
const logger = require("firebase-functions/logger");
|
||||
const {getSmtpConfig, EMAIL_CONFIG} = require("./utils/emailConfig");
|
||||
const {renderTemplate, getEmailSubject, getAlertTitle, prepareTemplateData, checkAlertPreference} = require("./utils/emailTemplates");
|
||||
const auth = require("./utils/auth");
|
||||
|
||||
// Configuration CORS
|
||||
const setCorsHeaders = (res, req) => {
|
||||
// Utiliser l'origin de la requête pour permettre les credentials
|
||||
const origin = req.headers.origin || '*';
|
||||
const origin = req.headers.origin || "*";
|
||||
|
||||
res.set('Access-Control-Allow-Origin', origin);
|
||||
res.set("Access-Control-Allow-Origin", origin);
|
||||
|
||||
// N'autoriser les credentials que si on a un origin spécifique (pas '*')
|
||||
if (origin !== '*') {
|
||||
res.set('Access-Control-Allow-Credentials', 'true');
|
||||
if (origin !== "*") {
|
||||
res.set("Access-Control-Allow-Credentials", "true");
|
||||
}
|
||||
|
||||
res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
res.set('Access-Control-Allow-Headers', 'Authorization, Content-Type, Accept, Origin, X-Requested-With');
|
||||
res.set('Access-Control-Max-Age', '3600');
|
||||
res.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
res.set("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Origin, X-Requested-With");
|
||||
res.set("Access-Control-Max-Age", "3600");
|
||||
};
|
||||
|
||||
const withCors = (handler) => {
|
||||
return async (req, res) => {
|
||||
setCorsHeaders(res, req);
|
||||
// Gérer les requêtes preflight OPTIONS immédiatement
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.status(204).send('');
|
||||
if (req.method === "OPTIONS") {
|
||||
res.status(204).send("");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -48,8 +48,8 @@ const withCors = (handler) => {
|
||||
*/
|
||||
exports.createAlert = onRequest({
|
||||
cors: false,
|
||||
invoker: 'public',
|
||||
region: 'europe-west9'
|
||||
invoker: "public",
|
||||
region: "europe-west9",
|
||||
}, withCors(async (req, res) => {
|
||||
try {
|
||||
// Vérifier l'authentification
|
||||
@@ -70,7 +70,7 @@ exports.createAlert = onRequest({
|
||||
|
||||
// Validation des données
|
||||
if (!type || !severity || !message) {
|
||||
res.status(400).json({error: 'type, severity et message sont requis'});
|
||||
res.status(400).json({error: "type, severity et message sont requis"});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,12 +78,12 @@ exports.createAlert = onRequest({
|
||||
const userIds = await determineTargetUsers(type, severity, eventId);
|
||||
|
||||
if (userIds.length === 0) {
|
||||
res.status(400).json({error: 'Aucun utilisateur à notifier'});
|
||||
res.status(400).json({error: "Aucun utilisateur à notifier"});
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Créer l'alerte dans Firestore
|
||||
const alertRef = admin.firestore().collection('alerts').doc();
|
||||
const alertRef = admin.firestore().collection("alerts").doc();
|
||||
const alertData = {
|
||||
id: alertRef.id,
|
||||
type,
|
||||
@@ -99,14 +99,14 @@ exports.createAlert = onRequest({
|
||||
createdBy: decodedToken.uid,
|
||||
isRead: false,
|
||||
emailSent: false,
|
||||
status: 'ACTIVE',
|
||||
status: "ACTIVE",
|
||||
};
|
||||
|
||||
await alertRef.set(alertData);
|
||||
|
||||
// 3. Envoyer les emails si alerte critique
|
||||
let emailResults = {};
|
||||
if (severity === 'CRITICAL') {
|
||||
if (severity === "CRITICAL") {
|
||||
emailResults = await sendAlertEmails(alertRef.id, alertData, userIds);
|
||||
|
||||
// Mettre à jour le statut d'envoi
|
||||
@@ -124,7 +124,7 @@ exports.createAlert = onRequest({
|
||||
emailsSent: Object.values(emailResults).filter((v) => v).length,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[createAlert] Erreur:', error);
|
||||
logger.error("[createAlert] Erreur:", error);
|
||||
res.status(500).json({error: `Erreur lors de la création de l'alerte: ${error.message}`});
|
||||
}
|
||||
}));
|
||||
@@ -137,23 +137,23 @@ async function determineTargetUsers(alertType, severity, eventId) {
|
||||
const targetUserIds = new Set();
|
||||
|
||||
// 1. Récupérer TOUS les utilisateurs pour déterminer lesquels sont admins
|
||||
const allUsersSnapshot = await db.collection('users').get();
|
||||
const allUsersSnapshot = await db.collection("users").get();
|
||||
|
||||
allUsersSnapshot.forEach((doc) => {
|
||||
const user = doc.data();
|
||||
if (user.role) {
|
||||
// Le rôle peut être une référence Firestore ou une string
|
||||
let rolePath = '';
|
||||
if (typeof user.role === 'string') {
|
||||
let rolePath = "";
|
||||
if (typeof user.role === "string") {
|
||||
rolePath = user.role;
|
||||
} else if (user.role.path) {
|
||||
rolePath = user.role.path;
|
||||
} else if (user.role._path && user.role._path.segments) {
|
||||
rolePath = user.role._path.segments.join('/');
|
||||
rolePath = user.role._path.segments.join("/");
|
||||
}
|
||||
|
||||
// Vérifier si c'est un admin (path = "roles/ADMIN")
|
||||
if (rolePath === 'roles/ADMIN' || rolePath === 'ADMIN') {
|
||||
if (rolePath === "roles/ADMIN" || rolePath === "ADMIN") {
|
||||
targetUserIds.add(doc.id);
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ async function determineTargetUsers(alertType, severity, eventId) {
|
||||
// 2. Si un événement est lié, ajouter tous les membres de la workforce
|
||||
if (eventId) {
|
||||
try {
|
||||
const eventDoc = await db.collection('events').doc(eventId).get();
|
||||
const eventDoc = await db.collection("events").doc(eventId).get();
|
||||
|
||||
if (eventDoc.exists) {
|
||||
const event = eventDoc.data();
|
||||
@@ -177,7 +177,7 @@ async function determineTargetUsers(alertType, severity, eventId) {
|
||||
logger.warn(`[determineTargetUsers] Événement ${eventId} introuvable`);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[determineTargetUsers] Erreur récupération événement:', error);
|
||||
logger.error("[determineTargetUsers] Erreur récupération événement:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ async function sendSingleEmail(transporter, alertId, alertData, userId) {
|
||||
const db = admin.firestore();
|
||||
|
||||
// Récupérer l'utilisateur
|
||||
const userDoc = await db.collection('users').doc(userId).get();
|
||||
const userDoc = await db.collection("users").doc(userId).get();
|
||||
|
||||
if (!userDoc.exists) {
|
||||
return false;
|
||||
@@ -250,7 +250,7 @@ async function sendSingleEmail(transporter, alertId, alertData, userId) {
|
||||
const templateData = await prepareTemplateData(alertData, user);
|
||||
|
||||
// Rendre le template
|
||||
const html = await renderTemplate('alert-individual', templateData);
|
||||
const html = await renderTemplate("alert-individual", templateData);
|
||||
|
||||
// Envoyer l'email
|
||||
await transporter.sendMail({
|
||||
|
||||
Reference in New Issue
Block a user