// src/middleware/index.js import { defineMiddleware } from 'astro:middleware'; import fs from 'fs'; import path from 'path'; const requestLogger = async (context, next) => { const start = Date.now(); const timestamp = new Date(); // Get client info const ip = context.request.headers.get('x-forwarded-for') || context.request.headers.get('x-real-ip') || context.clientAddress || 'unknown'; const method = context.request.method; const url = context.url.pathname + context.url.search; const userAgent = context.request.headers.get('user-agent') || '-'; const referer = context.request.headers.get('referer') || '-'; // Process the request const response = await next(); const duration = Date.now() - start; const status = response.status; const contentLength = response.headers.get('content-length') || '-'; // Format timestamp like nginx: [26/Jun/2025:18:19:28 +0000] const day = timestamp.getDate().toString().padStart(2, '0'); const month = timestamp.toLocaleString('en', { month: 'short' }); const year = timestamp.getFullYear(); const time = timestamp.toTimeString().split(' ')[0]; const formattedTimestamp = `${day}/${month}/${year}:${time} +0000`; // Create log entry in Combined Log Format (like nginx) const logEntry = `${ip} - - [${formattedTimestamp}] "${method} ${url} HTTP/1.1" ${status} ${contentLength} "${referer}" "${userAgent}"\n`; // Skip logging for static assets to reduce noise const isStaticAsset = /\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|otf|webp|map)$/i.test(url); if (!isStaticAsset) { try { const logFile = '/var/log/astro-access.log'; fs.appendFileSync(logFile, logEntry); } catch (error) { console.error('Failed to write access log:', error); } } return response; }; export const onRequest = defineMiddleware(requestLogger);