Skip to content

Commit

Permalink
feature: database transaction logging (PalisadoesFoundation#1716)
Browse files Browse the repository at this point in the history
* Database transaction logging middleware

* Review changes

* comment change

* fixing failed tests

* updated .gitignore

* write tests for dbLogger

* Update logs destination for test logs
  • Loading branch information
lakshz authored Jan 26, 2024
1 parent f5d762c commit 3a8a11a
Show file tree
Hide file tree
Showing 11 changed files with 416 additions and 16 deletions.
6 changes: 6 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ SMTP_USERNAME=
SMTP_PORT=
SMTP_SSL_TLS=

# Enable or disable the storage of logs
LOG=false


# Path of file that will store logs
LOG_PATH=./logs/transaction.log

# Email for the first user who will be super admin
# The user with the email address set with this parameter will automatically be elevated to Super Admin status on registration.
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ images/**
video/**
videos/**

# Ignore log files
logs/*
!logs/README.md
*.log

# Don't ignore any .gitignore files in any location
!.gitignore

Expand Down
17 changes: 17 additions & 0 deletions logs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Logs Directory

## Overview

This directory is the default location for the logs generated by the audit trailing mechanism of our application. The logging system is designed to provide a comprehensive and chronological record of events, actions, and operations performed within the application, offering transparency and accountability.

## Setup Instructions

### Initial Setup

To initialize the logging system and ensure that all necessary configurations are correctly established, please run the following command from the root of the project:

```bash

npm run setup

```
39 changes: 39 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"@types/express-rate-limit": "^6.0.0",
"@types/graphql-depth-limit": "^1.1.6",
"@types/i18n": "^0.13.10",
"@types/inquirer": "^9.0.7",
"@types/jsonwebtoken": "^9.0.5",
"@types/lodash": "^4.14.202",
"@types/mongoose-paginate-v2": "^1.6.5",
Expand Down
121 changes: 108 additions & 13 deletions setup.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const dotenv = require("dotenv");
const fs = require("fs");
const cryptolib = require("crypto");
const inquirer = require("inquirer");
const mongodb = require("mongodb");
const redis = require("redis");
const { exec } = require("child_process");
const nodemailer = require("nodemailer");
import dotenv from "dotenv";
import fs from "fs";
import path from "path";
import * as cryptolib from "crypto";
import inquirer from "inquirer";
import mongodb from "mongodb";
import * as redis from "redis";
import { exec } from "child_process";
import nodemailer from "nodemailer";
import type { ExecException } from "child_process";

dotenv.config();

Expand Down Expand Up @@ -114,6 +116,70 @@ async function accessAndRefreshTokens(
}
}

function transactionLogPath(logPath: string | null): void {
const config = dotenv.parse(fs.readFileSync(".env"));
config.LOG = "true";
if (!logPath) {
// Check if the logs/transaction.log file exists, if not, create it
const defaultLogPath = path.resolve(__dirname, "logs");
const defaultLogFile = path.join(defaultLogPath, "transaction.log");
if (!fs.existsSync(defaultLogPath)) {
console.log("Creating logs/transaction.log file...");
fs.mkdirSync(defaultLogPath);
}

config.LOG_PATH = defaultLogFile;
} else {
// Remove the logs files, if exists
const logsDirPath = path.resolve(__dirname, "logs");
if (fs.existsSync(logsDirPath)) {
fs.readdirSync(logsDirPath).forEach((file: string) => {
if (file !== "README.md") {
const curPath = path.join(logsDirPath, file);
fs.unlinkSync(curPath);
}
});
}
config.LOG_PATH = logPath;
}
fs.writeFileSync(".env", "");
for (const key in config) {
fs.appendFileSync(".env", `${key}=${config[key]}\n`);
}
}

async function askForTransactionLogPath(): Promise<string> {
let logPath: string | null;
// Keep asking for path, until user gives a valid path
// eslint-disable-next-line no-constant-condition
while (true) {
const response = await inquirer.prompt([
{
type: "input",
name: "logPath",
message: "Enter absolute path of log file:",
default: null,
},
]);
logPath = response.logPath;
if (logPath && fs.existsSync(logPath)) {
try {
fs.accessSync(logPath, fs.constants.R_OK | fs.constants.W_OK);
break;
} catch {
console.error(
"The file is not readable/writable. Please enter a valid file path."
);
}
} else {
console.error(
"Invalid path or file does not exist. Please enter a valid file path."
);
}
}
return logPath;
}

// Check connection to Redis with the specified URL.
/**
* The function `checkRedisConnection` checks if a connection to Redis can be established using the
Expand Down Expand Up @@ -253,7 +319,7 @@ async function redisConfiguration(): Promise<void> {
// Update the .env file
const config = dotenv.parse(fs.readFileSync(".env"));
config.REDIS_HOST = host;
config.REDIS_PORT = port;
config.REDIS_PORT = port.toString();
config.REDIS_PASSWORD = password;
updateEnvVariable(config);
} catch (err) {
Expand Down Expand Up @@ -640,7 +706,7 @@ async function importData(): Promise<void> {
console.log("Importing sample data...");
await exec(
"npm run import:sample-data",
(error: { message: string }, stdout: string, stderr: string) => {
(error: ExecException | null, stdout: string, stderr: string) => {
if (error) {
console.error(`Error: ${error.message}`);
abort();
Expand All @@ -657,7 +723,7 @@ async function importData(): Promise<void> {

type VerifySmtpConnectionReturnType = {
success: boolean;
error: any;
error: unknown;
};

/**
Expand Down Expand Up @@ -687,7 +753,7 @@ async function verifySmtpConnection(
await transporter.verify();
console.log("SMTP connection verified successfully.");
return { success: true, error: null };
} catch (error: any) {
} catch (error: unknown) {
console.error("SMTP connection verification failed:");
return { success: false, error };
} finally {
Expand Down Expand Up @@ -749,7 +815,9 @@ async function configureSmtp(): Promise<void> {
console.error(
"SMTP configuration verification failed. Please check your SMTP settings."
);
console.log(error.message);
if (error instanceof Error) {
console.log(error.message);
}
return;
}

Expand Down Expand Up @@ -815,6 +883,33 @@ async function main(): Promise<void> {

accessAndRefreshTokens(accessToken, refreshToken);

const { shouldLog } = await inquirer.prompt({
type: "confirm",
name: "shouldLog",
message: "Would you like to enable logging for the database transactions?",
default: true,
});

if (shouldLog) {
if (process.env.LOG_PATH) {
console.log(
`\n Log path already exists with the value:\n${process.env.LOG_PATH}`
);
}
let logPath: string | null = null;
const { shouldUseCustomLogPath } = await inquirer.prompt({
type: "confirm",
name: "shouldUseCustomLogPath",
message: "Would you like to provide a custom path for storing logs?",
default: false,
});

if (shouldUseCustomLogPath) {
logPath = await askForTransactionLogPath();
}
transactionLogPath(logPath);
}

const { isDockerInstallation } = await inquirer.prompt({
type: "confirm",
name: "isDockerInstallation",
Expand Down
12 changes: 11 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,5 +474,15 @@ export const REDIS_HOST = process.env.REDIS_HOST || "";
export const REDIS_PORT = Number(process.env.REDIS_PORT);
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD;

export const key = process.env.ENCRYPTION_KEY as string;
export const key = ENV.ENCRYPTION_KEY as string;
export const iv = crypto.randomBytes(16).toString("hex");

export const LOG = ENV.LOG === "true";

export const LOG_PATH = ENV.LOG_PATH;

export enum TransactionLogTypes {
CREATE = "CREATE",
UPDATE = "UPDATE",
DELETE = "DELETE",
}
Loading

0 comments on commit 3a8a11a

Please sign in to comment.