refactor: Amélioration des performances et migration des Cloud Functions
Cette mise à jour majeure vise à améliorer significativement les performances de l'application, en particulier au démarrage, et à standardiser l'infrastructure backend. Les principaux changements incluent la migration de toutes les Cloud Functions vers une région européenne (`europe-west9`), l'optimisation du chargement des données, et l'introduction d'un moniteur de performance pour le débogage.
**Changements Backend (Cloud Functions) :**
- **Migration de la Région :**
- Toutes les Cloud Functions ont été déplacées de `us-central1` à `europe-west9` (Paris) pour réduire la latence pour les utilisateurs européens. Cela concerne les appels depuis le frontend (ex: `api_config.dart`, `email_service.dart`) et les définitions des fonctions elles-mêmes (`index.js`, etc.).
- **Standardisation des Fonctions :**
- La plupart des fonctions `onCall` (v1) ont été migrées vers le format `onRequest` (v2) avec une gestion d'authentification et de CORS unifiée, améliorant la robustesse et la cohérence.
- Les triggers Firestore (`onDocumentCreated`, `onDocumentUpdated`) et les tâches planifiées (`onSchedule`) ont été mis à jour pour spécifier explicitement la région `europe-west9`.
- **Mise à jour des Index Firestore :**
- Les index `firestore.indexes.json` ont été mis à jour pour supporter les nouvelles requêtes de l'application et optimiser les performances de filtrage.
**Améliorations des Performances Frontend :**
- **Chargement Asynchrone et Mis en Cache :**
- Le chargement des données utilisateur (`LocalUserProvider`) et des événements (`EventProvider`) a été optimisé pour utiliser un cache local à court terme (5 minutes pour l'utilisateur, 30 secondes pour les événements).
- Les données ne sont rechargées que si le cache a expiré ou si un rechargement est forcé, évitant des appels réseau redondants et accélérant la navigation.
- **Démarrage de l'Application Optimisé :**
- Le processus de connexion automatique (`main.dart`) a été revu. L'application navigue désormais immédiatement vers la page demandée sans attendre la fin du chargement des données utilisateur, qui s'effectue en arrière-plan.
- Un écran de chargement plus esthétique avec le logo de l'entreprise a été ajouté, remplaçant l'indicateur de chargement simple.
- **Chargement de la Page Calendrier :**
- Le chargement et la sélection de l'événement par défaut sur la page `CalendarPage` sont maintenant entièrement asynchrones, rendant l'affichage de la page quasi instantané.
**Nouveaux Outils et Améliorations UX :**
- **Moniteur de Performance :**
- Ajout d'un nouvel outil `PerformanceMonitor` (`lib/utils/performance_monitor.dart`) pour mesurer précisément le temps d'exécution des opérations critiques (appels API, parsing, etc.) en mode débogage. Il aide à identifier les goulots d'étranglement.
- **Amélioration du Formulaire de Connexion :**
- Les champs "Email" et "Mot de passe" sur la page de connexion (`LoginPage`) supportent désormais l'autocomplétion du navigateur (`AutofillGroup`).
- Appuyer sur "Entrée" dans l'un des champs déclenche désormais la connexion, améliorant l'ergonomie.
**Mise à jour de la version :**
- La version de l'application a été incrémentée à `1.0.9`.
This commit is contained in:
@@ -28,6 +28,7 @@ const db = admin.firestore();
|
||||
const httpOptions = {
|
||||
cors: false,
|
||||
invoker: 'public', // Permet les invocations non authentifiées (l'auth est gérée par notre token Firebase)
|
||||
region: 'europe-west9', // Région européenne (Paris)
|
||||
// Version: 2.0 - Ajout de l'invoker public pour résoudre les problèmes CORS
|
||||
};
|
||||
|
||||
@@ -2049,19 +2050,20 @@ exports.getUsers = onRequest(httpOptions, withCors(async (req, res) => {
|
||||
* Récupère un utilisateur spécifique par son ID
|
||||
* Tout utilisateur authentifié peut accéder aux données publiques
|
||||
*/
|
||||
exports.getUser = onCall(async (request) => {
|
||||
exports.getUser = onRequest(httpOptions, withCors(async (req, res) => {
|
||||
try {
|
||||
await authenticateUser(request);
|
||||
const db = getFirestore();
|
||||
const decodedToken = await auth.authenticateUser(req);
|
||||
|
||||
const { userId } = request.data;
|
||||
const { userId } = req.body.data || req.body || {};
|
||||
if (!userId) {
|
||||
throw new Error("userId is required");
|
||||
res.status(400).json({ error: 'userId is required' });
|
||||
return;
|
||||
}
|
||||
|
||||
const userDoc = await db.collection("users").doc(userId).get();
|
||||
const userDoc = await db.collection('users').doc(userId).get();
|
||||
if (!userDoc.exists) {
|
||||
throw new Error("User not found");
|
||||
res.status(404).json({ error: 'User not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const user = userDoc.data();
|
||||
@@ -2070,11 +2072,11 @@ exports.getUser = onCall(async (request) => {
|
||||
const userData = {
|
||||
id: userDoc.id,
|
||||
uid: user.uid || userDoc.id,
|
||||
email: user.email || "",
|
||||
firstName: user.firstName || "",
|
||||
lastName: user.lastName || "",
|
||||
phoneNumber: user.phoneNumber || "",
|
||||
profilePhotoUrl: user.profilePhotoUrl || "",
|
||||
email: user.email || '',
|
||||
firstName: user.firstName || '',
|
||||
lastName: user.lastName || '',
|
||||
phoneNumber: user.phoneNumber || '',
|
||||
profilePhotoUrl: user.profilePhotoUrl || '',
|
||||
};
|
||||
|
||||
// Inclure le rôle si disponible
|
||||
@@ -2088,12 +2090,12 @@ exports.getUser = onCall(async (request) => {
|
||||
}
|
||||
}
|
||||
|
||||
return { user: userData };
|
||||
res.status(200).json({ user: userData });
|
||||
} catch (error) {
|
||||
logger.error("Error fetching user:", error);
|
||||
throw new Error(error.message || "Failed to fetch user");
|
||||
logger.error('Error fetching user:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
// ============================================================================
|
||||
@@ -3488,6 +3490,7 @@ const {sendDailyDigest} = require('./sendDailyDigest');
|
||||
exports.sendDailyDigest = onSchedule({
|
||||
schedule: '0 8 * * *',
|
||||
timeZone: 'Europe/Paris',
|
||||
region: 'europe-west9',
|
||||
retryCount: 2,
|
||||
memory: '512MiB'
|
||||
}, async (context) => {
|
||||
@@ -3507,7 +3510,10 @@ exports.sendDailyDigest = onSchedule({
|
||||
* Trigger : Nouvel événement créé
|
||||
* Envoie une notification à tous les membres de la workforce
|
||||
*/
|
||||
exports.onEventCreated = onDocumentCreated('events/{eventId}', async (event) => {
|
||||
exports.onEventCreated = onDocumentCreated({
|
||||
document: 'events/{eventId}',
|
||||
region: 'europe-west9'
|
||||
}, async (event) => {
|
||||
logger.info(`[onEventCreated] Événement créé: ${event.params.eventId}`);
|
||||
|
||||
try {
|
||||
@@ -3547,7 +3553,10 @@ exports.onEventCreated = onDocumentCreated('events/{eventId}', async (event) =>
|
||||
* Trigger : Événement modifié (workforce changée)
|
||||
* Envoie une notification aux nouveaux membres ajoutés à la workforce
|
||||
*/
|
||||
exports.onEventUpdated = onDocumentUpdated('events/{eventId}', async (event) => {
|
||||
exports.onEventUpdated = onDocumentUpdated({
|
||||
document: 'events/{eventId}',
|
||||
region: 'europe-west9'
|
||||
}, async (event) => {
|
||||
const before = event.data.before.data();
|
||||
const after = event.data.after.data();
|
||||
const eventId = event.params.eventId;
|
||||
@@ -3598,7 +3607,10 @@ exports.onEventUpdated = onDocumentUpdated('events/{eventId}', async (event) =>
|
||||
* Trigger : Nouvelle alerte créée
|
||||
* Envoie un email immédiat si l'alerte est critique
|
||||
*/
|
||||
exports.onAlertCreated = onDocumentCreated('alerts/{alertId}', async (event) => {
|
||||
exports.onAlertCreated = onDocumentCreated({
|
||||
document: 'alerts/{alertId}',
|
||||
region: 'europe-west9'
|
||||
}, async (event) => {
|
||||
const alertId = event.params.alertId;
|
||||
const alertData = event.data.data();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user