**Nouvelles fonctionnalités** : 1. **Page d'historique des rappels** (`/admin/rappels/historique`) - Tableau complet de tous les rappels envoyés - Filtres avancés : date début/fin, séquence, statut (succès/échec), email - Recherche par email avec mise en surbrillance - Pagination (50 résultats par page) - Affichage du nombre de tentatives pour chaque rappel - Code couleur : vert pour succès, rouge pour échec 2. **Tableau de bord statistiques** (`/admin/rappels/statistiques`) - 4 cartes de statistiques globales : total envoyés, succès, échecs, taux de succès - Graphique d'évolution : nombre de rappels envoyés par jour (succès vs échecs) - Graphique du taux de succès par jour (barres) - Top 10 des emails problématiques avec nombre d'échecs - Visualisation interactive avec Recharts 3. **Système de réessai automatique** - Détection automatique des rappels échoués - Délai exponentiel : 1h après 1er échec, 4h après 2ème, 24h après 3ème - Limite de 3 tentatives maximum - Notification automatique à l'admin en cas d'abandon (3 échecs) - Mise à jour automatique du statut dans les logs - Vérification toutes les heures **Nouveaux champs dans logsRappels** : - `nbTentatives` : nombre de tentatives d'envoi (1 à 3) - `prochainEssai` : date du prochain essai en cas d'échec **Nouveaux fichiers** : - `client/src/pages/admin/AdminRappelsHistorique.tsx` : Page d'historique - `client/src/pages/admin/AdminRappelsStats.tsx` : Page de statistiques - `server/rappelRetry.ts` : Système de réessai automatique - `server/rappelDb.ts` : Fonctions étendues (filtres, stats, évolution) **Fichiers modifiés** : - `drizzle/schema.ts` : Ajout de nbTentatives et prochainEssai - `server/routers.ts` : Nouvelles procédures tRPC (historique, statistiques, évolution) - `client/src/App.tsx` : Routes pour historique et statistiques - `client/src/components/DashboardLayout.tsx` : Sous-menu Rappels - `server/_core/index.ts` : Initialisation du scheduler de retry **Bénéfices** : - ✅ Traçabilité complète de tous les rappels - ✅ Analyse des performances avec graphiques - ✅ Identification rapide des emails problématiques - ✅ Réessai automatique des échecs sans intervention manuelle - ✅ Notification en cas d'échec persistant - ✅ Optimisation de la délivrabilité des emails
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
import "dotenv/config";
|
|
import express from "express";
|
|
import { createServer } from "http";
|
|
import net from "net";
|
|
import { createExpressMiddleware } from "@trpc/server/adapters/express";
|
|
import { registerOAuthRoutes } from "./oauth";
|
|
import { appRouter } from "../routers";
|
|
import { createContext } from "./context";
|
|
import localAuthRouter from "./localAuth";
|
|
import { serveStatic, setupVite } from "./vite";
|
|
import { initRappelScheduler } from "../rappelScheduler";
|
|
import { initRappelRetryScheduler } from "../rappelRetry";
|
|
|
|
function isPortAvailable(port: number): Promise<boolean> {
|
|
return new Promise(resolve => {
|
|
const server = net.createServer();
|
|
server.listen(port, () => {
|
|
server.close(() => resolve(true));
|
|
});
|
|
server.on("error", () => resolve(false));
|
|
});
|
|
}
|
|
|
|
async function findAvailablePort(startPort: number = 3000): Promise<number> {
|
|
for (let port = startPort; port < startPort + 20; port++) {
|
|
if (await isPortAvailable(port)) {
|
|
return port;
|
|
}
|
|
}
|
|
throw new Error(`No available port found starting from ${startPort}`);
|
|
}
|
|
|
|
async function startServer() {
|
|
const app = express();
|
|
const server = createServer(app);
|
|
// Configure body parser with larger size limit for file uploads
|
|
app.use(express.json({ limit: "50mb" }));
|
|
app.use(express.urlencoded({ limit: "50mb", extended: true }));
|
|
// OAuth callback under /api/oauth/callback
|
|
registerOAuthRoutes(app);
|
|
// Local authentication under /api/auth/local
|
|
app.use("/api/auth/local", localAuthRouter);
|
|
// tRPC API
|
|
app.use(
|
|
"/api/trpc",
|
|
createExpressMiddleware({
|
|
router: appRouter,
|
|
createContext,
|
|
})
|
|
);
|
|
// development mode uses Vite, production mode uses static files
|
|
if (process.env.NODE_ENV === "development") {
|
|
await setupVite(app, server);
|
|
} else {
|
|
serveStatic(app);
|
|
}
|
|
|
|
const preferredPort = parseInt(process.env.PORT || "3000");
|
|
const port = await findAvailablePort(preferredPort);
|
|
|
|
if (port !== preferredPort) {
|
|
console.log(`Port ${preferredPort} is busy, using port ${port} instead`);
|
|
}
|
|
|
|
server.listen(port, () => {
|
|
console.log(`Server running on http://localhost:${port}/`);
|
|
|
|
// Initialiser le scheduler de rappels automatiques
|
|
initRappelScheduler();
|
|
|
|
// Initialiser le scheduler de réessai automatique
|
|
initRappelRetryScheduler();
|
|
});
|
|
}
|
|
|
|
startServer().catch(console.error);
|