Skip to content

Instantly share code, notes, and snippets.

@santuman
Created June 1, 2025 06:32
Show Gist options
  • Save santuman/e6d66aac6c21934c15ceb09d75206683 to your computer and use it in GitHub Desktop.
Save santuman/e6d66aac6c21934c15ceb09d75206683 to your computer and use it in GitHub Desktop.
import { appLogger } from 'loaders/logger';
import userContext from 'loaders/userContext';
import mongoose from 'mongoose';
const CHANGE_LOG_COLLECTION_NAME = 'changelog';
interface ChangeLogDocument extends mongoose.Document {
operation: string;
}
const createChangeLogModel = () => {
if (mongoose.models[CHANGE_LOG_COLLECTION_NAME]) {
return mongoose.models[CHANGE_LOG_COLLECTION_NAME];
}
const changelogSchema = new mongoose.Schema(
{
operation: {
type: String,
enum: ['create', 'update', 'delete'],
required: true,
},
timestamp: {
type: Date,
required: true,
},
data: {
type: Object,
},
query: {
type: Object,
},
entity: {
type: String,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
},
{ timestamps: true },
);
changelogSchema.index({
timestamp: -1,
entity: 1,
operation: 1,
user: 1,
'data._id': 1,
'query._id': 1,
});
return mongoose.model(CHANGE_LOG_COLLECTION_NAME, changelogSchema, CHANGE_LOG_COLLECTION_NAME);
};
const saveChangeLog = async (changeLog: ChangeLogDocument) => {
try {
await changeLog.save();
} catch (error) {
appLogger.error({
message: `Error in changelog ${changeLog.operation}`,
error: error.stack,
});
}
};
const changeLogPlugin = (
schema: mongoose.Schema<any>,
options: {
collectionName: string;
},
) => {
const ChangeLog = createChangeLogModel();
schema.post('save', async function () {
appLogger.info({ message: 'executing post changelog save' });
const doc = this as any;
const changelog = new ChangeLog({
operation: 'create',
timestamp: new Date(),
data: doc,
user: userContext.getUserId(),
entity: options.collectionName,
});
await saveChangeLog(changelog);
});
schema.post('updateOne', async function (this) {
appLogger.info({ message: 'executing post changelog updateOne' });
const doc = this as any;
const query = this.getQuery();
const data = doc._update.$set || doc._update;
const changelog = new ChangeLog({
operation: 'update',
timestamp: new Date(),
query,
data,
user: userContext.getUserId(),
entity: options.collectionName,
});
await saveChangeLog(changelog);
});
schema.post('findOneAndUpdate', async function (this): Promise<void> {
appLogger.info({ message: 'executing post changelog findOneAndUpdate' });
const doc = this as any;
const query = this.getQuery();
const data = doc._update.$set || doc._update;
const changelog = new ChangeLog({
operation: 'update',
timestamp: new Date(),
query,
data,
user: userContext.getUserId(),
entity: options.collectionName,
});
await saveChangeLog(changelog);
});
schema.post('deleteOne', async function (this) {
appLogger.info({ message: 'executing post changelog deleteOne' });
const doc = this as any;
const query = this.getQuery();
const changelog = new ChangeLog({
operation: 'delete',
timestamp: new Date(),
query,
data: doc,
user: userContext.getUserId(),
entity: options.collectionName,
});
await saveChangeLog(changelog);
});
};
export default changeLogPlugin;
@santuman
Copy link
Author

santuman commented Jun 1, 2025

Example usage:

User.plugin(changeLogPlugin, {
	collectionName: 'user',
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment