UNPKG

@kingdiablo/auditor

Version:

A lightweight and customizable audit logger for Node.js apps. Tracks database changes, errors, and user actions with support for external loggers like Winston or Pino.

230 lines (225 loc) 10.9 kB
import * as _koa_router from '@koa/router'; import * as express_serve_static_core from 'express-serve-static-core'; import * as koa from 'koa'; import { Context, Next } from 'koa'; import * as http from 'http'; import { FastifyRequest } from 'fastify/types/request'; import * as fastify from 'fastify'; import { FastifyReply, HookHandlerDoneFunction } from 'fastify'; import * as express from 'express'; import { Request, Response, NextFunction } from 'express'; import { Schema } from 'mongoose'; interface ExtendedFastifyRequest extends FastifyRequest { startTime: number; userId: string; } type TActionMap = { auth: "login" | "logout" | "signup" | "password_reset" | "password_update" | "token_refreshed" | "session_expired" | "2fa_requested" | "2fa_verified" | "unauthorized" | "account_locked" | "email_verified" | "email_verification_sent"; billing: "payment_success" | "payment_failed" | "subscription_created" | "subscription_cancelled"; system: "start" | "stop" | "restart"; error: "server_error" | "client_error" | "validation_error"; }; type TMessages = { login: "User logged in successfully."; logout: "User logged out."; signup: "New user signed up."; password_reset: "Password reset requested."; password_update: "User updated their password."; token_refreshed: "Authentication token was refreshed."; session_expired: "User session has expired."; "2fa_requested": "Two-factor authentication requested."; "2fa_verified": "Two-factor authentication verified."; unauthorized: "Unauthorized access attempt detected."; account_locked: "User account has been locked."; email_verified: "Email address successfully verified."; email_verification_sent: "Verification email sent to user."; payment_success: "Payment completed successfully."; payment_failed: "Payment attempt failed."; subscription_created: "New subscription created."; subscription_cancelled: "Subscription has been cancelled."; start: "System has started."; stop: "System has stopped."; restart: "System has been restarted."; server_error: "A server error has occurred."; client_error: "A client-side error has occurred."; validation_error: "Validation failed for the provided data."; }; type TDestinations = "console" | "file" | "remote"; type TKnownEvent = { [K in keyof TActionMap]: TActionMap[K] extends infer Actions ? Actions extends keyof TMessages ? { [A in Actions]: { type: K; action: A; message: TMessages[A]; }; }[Actions] : never : never; }[keyof TActionMap]; type TAuditOptions<F extends Framework> = { destinations?: TDestinations[]; logger?: Logger; dbType?: 'none' | 'mongoose'; framework?: F; useTimeStamp?: boolean; splitFiles?: boolean; captureSystemErrors?: boolean; useUI?: boolean; }; type TCustomEvent = { type: Exclude<string, keyof TActionMap>; action: string; message: string; }; type TEvent = TKnownEvent | TCustomEvent; type TFileConfig = { fileName: string; folderName: string; fullPath: string; }; type ExpressLogger = (req: Request, res: Response, next: NextFunction) => void; type KoaLogger = (ctx: Context, next: Next) => Promise<void>; type FastifyLogger = { onRequest: (request: ExtendedFastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) => void; onResponse: (request: ExtendedFastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) => void; }; type SupportedLoggersRequest = { express: ExpressLogger; koa: KoaLogger; fastify: FastifyLogger; }; type Framework = keyof SupportedLoggersRequest; type Logger = { info: (...args: any[]) => void; error: (...args: any[]) => void; warn: (...args: any[]) => void; }; declare class Audit<F extends Framework = "express"> { private options?; private logFilePath; private defaultFileConfigs; private fileConfig; private isInitialized; private auditOptions; /** * Creates an instance of the Auditor class with the provided options. * * @param options - Optional configuration object for the auditor. * - `logger`: Custom logger to use; defaults to `console` if not provided. * - `dbType`: Type of database to use; defaults to `"none"`. * - `destinations`: Array of destinations for audit logs; defaults to `["console"]`. * - `framework`: The framework being used (e.g., "express"); defaults to `"express"`. * - `useTimeStamp`: Whether to include timestamps in logs; defaults to `true`. * - `splitFiles`: Whether to split logs into multiple files; defaults to `false`. * - `captureSystemErrors`: Whether to capture system errors; defaults to `false`. * * Initializes the `auditOptions` property by merging the provided options with sensible defaults. */ constructor(options?: TAuditOptions<F> | undefined); /** * Initializes the auditor by creating the necessary file location and logging a success message. * * This method sets up the audit configuration by invoking `CreateFileLocation` with the current file configuration, * and logs a confirmation message using the configured logger. * * @remarks * Should be called before performing any audit operations to ensure the environment is properly configured. */ Setup(): Promise<void>; /** * Logs an event to the configured destinations (console and/or file). * * Depending on the configuration, this method will: * - Add a timestamp to the event if `useTimeStamp` is enabled. * - Log the event to the console if "console" is included in `destinations`. * - Log the event to a file if "file" is included in `destinations`. * - If `splitFiles` is enabled, logs to a specific action log file. * - Otherwise, logs to the default file configuration. * - If the file path is not set when logging to a file, logs an error. * * @param event - The event object to be logged. */ Log(event: TEvent): void; /** * Logs an error event to the configured destinations (console and/or file). * @example * LogError(new Error("Unexpected failure")) * Logs error details to the console and file depending on configuration. * @param {any} error - The error object to be logged. * @description * - Extracts the error stack trace to capture informative details. * - Compiles additional user context such as IP and user agent when available. * - Handles uninitialized state by printing a warning message. * - Utilizes timestamp inclusion and log destination logic based on audit options. */ LogError(error: any): void; /** * Audits a given schema model by invoking the `auditModel` function with the current configuration, * a generated timestamp, and the provided schema. * * @template T - The type of the schema being audited. * @param schema - The schema object to be audited. */ AuditModel<T>(schema: Schema<T>): void; /** * Configures the file logging settings for the auditor. * * This method sets up the file configuration used for logging audit events to a file. * It checks if "file" is included in the destinations and if split file logging is disabled. * If these conditions are not met, it logs appropriate warnings and returns early. * Otherwise, it sets up the file configuration with the provided options or defaults. * * @param config - Optional configuration object for the file logger, excluding the `fullPath` property. * - `folderName` (optional): The folder where the log file will be stored. Defaults to `"audit"`. * - `fileName` (optional): The name of the log file. Defaults to `"audit.log"`. */ SetFileConfig(config: Omit<TFileConfig, "fullPath">): void; /** * Logs all incoming requests using the configured logger. * * Depending on the `splitFiles` configuration, this method will either: * - Log requests to a dedicated "request.log" file if `splitFiles` is enabled. * - Log requests to the default log file otherwise. * * @returns The result of the `requestLogger` function, which handles the actual logging process. */ RequestLogger(): SupportedLoggersRequest[F]; /** * Logs all errors using the configured error logger. * * If the `splitFiles` option is enabled in the configuration, errors are logged to a separate * "error.log" file as specified in the default file configurations. Otherwise, errors are logged * to the main file configuration. * * @returns The result of the error logger function, which handles error logging based on the current configuration. */ ErrorLogger(): { express: (err: any, req: express.Request, res: express.Response, next: express.NextFunction) => void; fastify: (error: fastify.FastifyError, request: ExtendedFastifyRequest, reply: fastify.FastifyReply) => fastify.FastifyReply<fastify.RouteGenericInterface, fastify.RawServerDefault, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, unknown, fastify.FastifySchema, fastify.FastifyTypeProviderDefault, unknown>; koa: (ctx: koa.Context, next: koa.Next) => Promise<void>; }[F]; /** * Asynchronously creates and returns the UI component or handler based on the specified framework * in the audit options. * * @returns {Promise<any>} A promise that resolves to the UI component or handler corresponding to the selected framework. */ CreateUI(): { express: () => Promise<((req: any, res: any, next: any) => any) | express_serve_static_core.Router>; fastify: () => Promise<(fastify: any, opts: any) => Promise<void>>; koa: () => Promise<koa.Middleware<koa.DefaultState, koa.DefaultContext & _koa_router.RouterParamContext<koa.DefaultState, koa.DefaultContext>, any>>; }[F]; private CreateFileLocation; private GenerateFile; /** * Sets up system error handling and logging for audit purposes. * @example * handleSystemErrors() * Initializes processes to log exceptions, rejections, signals, and exits. * @description * - Listens for Node.js process events such as uncaught exceptions, unhandled rejections, SIGTERM, SIGINT, and exit. * - Utilizes the `generateAuditContent` utility to format the logs. * - Logs generated content using `handleLog` with the error file configuration. * - Only activates if `captureSystemErrors` is true within audit options. */ private HandleSystemErrors; } export { Audit as Auditor, type TAuditOptions, type TEvent, type TFileConfig };