feat(audit-logging): Implement Edge-compatible audit logging utility and safe logging module

- Added `auditLogEdge.js` for Edge Runtime compatible audit logging, including console logging and API fallback.
- Introduced `auditLogSafe.js` for safe audit logging without direct database imports, ensuring compatibility across runtimes.
- Enhanced `auth.js` to integrate safe audit logging for login actions, including success and failure cases.
- Created middleware `auditLog.js` to facilitate audit logging for API routes with predefined configurations.
- Updated `middleware.js` to allow API route access without authentication checks.
- Added tests for audit logging functionality and Edge compatibility in `test-audit-logging.mjs` and `test-edge-compatibility.mjs`.
- Implemented safe audit logging tests in `test-safe-audit-logging.mjs` to verify functionality across environments.
This commit is contained in:
Chop
2025-07-09 23:08:16 +02:00
parent 90875db28b
commit b1a78bf7a8
20 changed files with 2943 additions and 130 deletions

129
src/lib/auditLogEdge.js Normal file
View File

@@ -0,0 +1,129 @@
/**
* Edge-compatible audit logging utility
* This version avoids direct database imports and can be used in Edge Runtime
*/
import { AUDIT_ACTIONS, RESOURCE_TYPES } from "./auditLog.js";
/**
* Log an audit event in Edge Runtime compatible way
* @param {Object} params - Audit log parameters
*/
export async function logAuditEventAsync({
action,
userId = null,
resourceType = null,
resourceId = null,
ipAddress = null,
userAgent = null,
details = null,
timestamp = null,
}) {
try {
// In Edge Runtime or when database is not available, log to console
if (
typeof EdgeRuntime !== "undefined" ||
process.env.NEXT_RUNTIME === "edge"
) {
console.log(
`[Audit Log - Edge] ${action} by user ${
userId || "anonymous"
} on ${resourceType}:${resourceId}`,
{
details,
ipAddress,
userAgent,
timestamp: timestamp || new Date().toISOString(),
}
);
return;
}
// Try to make an API call to log the event
try {
const response = await fetch("/api/audit-logs/log", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
action,
userId,
resourceType,
resourceId,
ipAddress,
userAgent,
details,
timestamp,
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
} catch (fetchError) {
// Fallback to console logging if API call fails
console.log(
`[Audit Log - Fallback] ${action} by user ${
userId || "anonymous"
} on ${resourceType}:${resourceId}`,
{
details,
ipAddress,
userAgent,
timestamp: timestamp || new Date().toISOString(),
error: fetchError.message,
}
);
}
} catch (error) {
console.error("Failed to log audit event:", error);
}
}
/**
* Helper function to extract client information from request (Edge compatible)
* @param {Request} req - The request object
* @returns {Object} Object containing IP address and user agent
*/
export function getClientInfoEdgeCompatible(req) {
const ipAddress =
req.headers.get("x-forwarded-for") ||
req.headers.get("x-real-ip") ||
req.headers.get("cf-connecting-ip") ||
"unknown";
const userAgent = req.headers.get("user-agent") || "unknown";
return { ipAddress, userAgent };
}
/**
* Middleware helper to log API actions (Edge compatible)
*/
export async function logApiActionAsync(
req,
action,
resourceType,
resourceId,
session,
additionalDetails = {}
) {
const { ipAddress, userAgent } = getClientInfoEdgeCompatible(req);
await logAuditEventAsync({
action,
userId: session?.user?.id || null,
resourceType,
resourceId,
ipAddress,
userAgent,
details: {
method: req.method,
url: req.url,
...additionalDetails,
},
});
}
export { AUDIT_ACTIONS, RESOURCE_TYPES };