@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
text/typescript
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 };