the-tip-top-frontend/middleware.ts
soufiane a200fbfc7d feat: add Prometheus metrics endpoint
- Add /api/metrics endpoint for Prometheus scraping
- Add /api/track endpoint for metrics tracking
- Add metrics library with HTTP request counters
- Add middleware for request tracking
- Add instrumentation for Next.js

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 15:57:33 +01:00

76 lines
2.2 KiB
TypeScript

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
// Routes only accessible when not authenticated
const authRoutes = ['/login', '/register'];
// Routes à ne pas tracker
const excludedPaths = ['/api/track', '/api/metrics', '/_next', '/favicon.ico'];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const startTime = Date.now();
// Get token from cookies or headers
const token = request.cookies.get('auth_token')?.value ||
request.headers.get('authorization')?.replace('Bearer ', '');
// Check if route is auth route
const isAuthRoute = authRoutes.some(route => pathname.startsWith(route));
// Préparer la réponse
let response: NextResponse;
// If accessing auth routes with token in cookies, redirect to home
if (isAuthRoute && token) {
response = NextResponse.redirect(new URL('/', request.url));
} else {
response = NextResponse.next();
}
// Tracker les métriques HTTP (fire and forget)
const shouldTrack = !excludedPaths.some(path => pathname.startsWith(path));
if (shouldTrack) {
const durationMs = Date.now() - startTime;
const statusCode = response.status || 200;
// Envoyer les métriques de manière asynchrone (non bloquant)
const trackUrl = new URL('/api/track', request.url);
// Utiliser waitUntil pour ne pas bloquer la réponse
// Note: waitUntil n'est pas disponible dans tous les environnements
try {
fetch(trackUrl.toString(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
method: request.method,
path: pathname,
statusCode,
durationMs,
}),
}).catch(() => {
// Ignorer les erreurs de tracking
});
} catch {
// Ignorer les erreurs de tracking
}
}
return response;
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - public files (public folder)
*/
'/((?!_next/static|_next/image|favicon.ico|.*\\..*|public).*)',
],
};