errsole-mongodb
Advanced tools
Comparing version 2.0.0 to 2.1.0
141
lib/index.js
@@ -40,2 +40,12 @@ /** | ||
/** | ||
* @typedef {Object} Notification | ||
* @property {number} [id] | ||
* @property {number} [errsole_id] | ||
* @property {string} hostname | ||
* @property {string} hashed_message | ||
* @property {Date} [created_at] | ||
* @property {Date} [updated_at] | ||
*/ | ||
const bcrypt = require('bcryptjs'); | ||
@@ -66,2 +76,3 @@ const { EventEmitter } = require('events'); | ||
this.logsCollectionName = 'errsole_logs'; | ||
this.notificationsCollectionName = 'errsole_notifications'; | ||
this.usersCollectionName = 'errsole_users'; | ||
@@ -112,2 +123,8 @@ this.configCollectionName = 'errsole_config'; | ||
await this.db.collection(this.configCollectionName).createIndex({ key: 1 }, { unique: true }); | ||
if (!collectionNames.includes(this.notificationsCollectionName)) { | ||
await this.db.createCollection(this.notificationsCollectionName); | ||
} | ||
await this.db.collection(this.notificationsCollectionName).createIndex({ hostname: 1, hashed_message: 1, created_at: 1 }); | ||
} | ||
@@ -146,3 +163,2 @@ | ||
async setConfig (key, value) { | ||
// Attempt to update or insert the configuration item | ||
const result = await this.db.collection(this.configCollectionName).updateOne( | ||
@@ -154,3 +170,2 @@ { key }, | ||
// Check if the operation was successful | ||
if (result.matchedCount === 0 && result.upsertedCount === 0) { | ||
@@ -160,3 +175,2 @@ throw new Error('Failed to update or insert configuration.'); | ||
// Retrieve the configuration item | ||
const savedItem = await this.db.collection(this.configCollectionName).findOne({ key }); | ||
@@ -213,3 +227,3 @@ | ||
if (logsToPost.length === 0) { | ||
return {}; // No logs to post | ||
return {}; | ||
} | ||
@@ -240,3 +254,2 @@ try { | ||
// Filter out null and empty hostnames, and then sort them | ||
const filteredHostnames = hostnames.filter(Boolean).sort(); | ||
@@ -259,7 +272,5 @@ return { items: filteredHostnames }; | ||
async getLogs (filters = {}) { | ||
// Set default limit if not provided | ||
const defaultLimit = 100; | ||
filters.limit = filters.limit || defaultLimit; | ||
// Construct the query object based on filters | ||
const query = {}; | ||
@@ -311,3 +322,3 @@ | ||
let sortOrder = { _id: -1 }; // Default sort order | ||
let sortOrder = { _id: -1 }; | ||
let shouldReverse = true; | ||
@@ -366,7 +377,5 @@ | ||
async searchLogs (searchTerms, filters = {}) { | ||
// Set default limit if not provided | ||
const defaultLimit = 100; | ||
filters.limit = filters.limit || defaultLimit; | ||
// Constructing the query for text search | ||
const quotedTerms = searchTerms.map(term => `"${term}"`); | ||
@@ -419,3 +428,3 @@ const query = { $text: { $search: quotedTerms.join(' ') } }; | ||
let sortOrder = { timestamp: -1 }; // Default sort order | ||
let sortOrder = { timestamp: -1 }; | ||
let shouldReverse = true; | ||
@@ -498,2 +507,3 @@ | ||
await this.updateLogsCollectionTTL(result.item.value); | ||
await this.updateNotificationsCollectionTTL(result.item.value); | ||
} catch (err) { | ||
@@ -528,2 +538,95 @@ console.error(err); | ||
/** | ||
* Inserts a notification, counts today's notifications, and retrieves the previous notification. | ||
* @param {Notification} notification - The notification to be inserted. | ||
* @returns {Promise<Object>} - Returns today's notification count and the previous notification. | ||
*/ | ||
async insertNotificationItem (notification = {}) { | ||
const errsoleId = notification.errsole_id; | ||
const hostname = notification.hostname; | ||
const hashedMessage = notification.hashed_message; | ||
const session = this.db.client.startSession(); | ||
try { | ||
const transactionOptions = { | ||
readPreference: 'primary', | ||
readConcern: { level: 'snapshot' }, | ||
writeConcern: { w: 'majority' } | ||
}; | ||
let result; | ||
await session.withTransaction(async () => { | ||
const notificationsCollection = this.db.collection(this.notificationsCollectionName); | ||
const previousNotificationItem = await notificationsCollection.findOne( | ||
{ hostname, hashed_message: hashedMessage }, | ||
{ sort: { created_at: -1 }, session } | ||
); | ||
const timestamp = new Date(); | ||
await notificationsCollection.insertOne( | ||
{ | ||
errsole_id: errsoleId, | ||
hostname, | ||
hashed_message: hashedMessage, | ||
created_at: timestamp, | ||
updated_at: timestamp | ||
}, | ||
{ session } | ||
); | ||
const startOfDayUTC = new Date(); | ||
startOfDayUTC.setUTCHours(0, 0, 0, 0); | ||
const endOfDayUTC = new Date(); | ||
endOfDayUTC.setUTCHours(23, 59, 59, 999); | ||
const todayNotificationCount = await notificationsCollection.countDocuments( | ||
{ | ||
hostname, | ||
hashed_message: hashedMessage, | ||
created_at: { $gte: startOfDayUTC, $lte: endOfDayUTC } | ||
}, | ||
{ session } | ||
); | ||
let formattedPreviousNotification; | ||
if (previousNotificationItem) { | ||
const { _id, ...rest } = previousNotificationItem; | ||
formattedPreviousNotification = { id: _id.toString(), ...rest }; | ||
} | ||
result = { | ||
previousNotificationItem: formattedPreviousNotification, | ||
todayNotificationCount | ||
}; | ||
}, transactionOptions); | ||
return result; | ||
} finally { | ||
await session.endSession(); | ||
} | ||
} | ||
/** | ||
* Updates the TTL index for the notifications collection in the database. | ||
* | ||
* @async | ||
* @function updateNotificationsCollectionTTL | ||
* @param {number} notificationsTTL - The TTL value (in milliseconds) to set for notifications expiration. | ||
* @returns {Promise<void>} - A promise that resolves with an empty object when the notifications collection TTL is successfully updated. | ||
* @throws {Error} - Throws an error if updating the TTL index fails. | ||
*/ | ||
async updateNotificationsCollectionTTL (notificationsTTL) { | ||
const ttlInSeconds = parseInt(notificationsTTL) / 1000; | ||
const indexes = await this.db.collection(this.notificationsCollectionName).indexes(); | ||
const ttlIndex = indexes.find(index => index.expireAfterSeconds && Object.keys(index.key).includes('created_at')); | ||
if (!ttlIndex || ttlIndex.expireAfterSeconds !== ttlInSeconds) { | ||
if (ttlIndex) { | ||
await this.db.collection(this.notificationsCollectionName).dropIndex(ttlIndex.name); | ||
} | ||
await this.db.collection(this.notificationsCollectionName).createIndex({ created_at: 1 }, { expireAfterSeconds: ttlInSeconds }); | ||
} | ||
return {}; | ||
} | ||
/** | ||
* Creates a new user record in the database. | ||
@@ -591,3 +694,2 @@ * | ||
// Prepare the user object to be returned | ||
const returnUser = { ...user, id: user._id.toString() }; | ||
@@ -661,13 +763,7 @@ delete returnUser._id; | ||
async updateUserByEmail (email, updates) { | ||
// Remove the hashed password from the updates if it exists | ||
delete updates.hashed_password; | ||
// Apply the updates to the user identified by email | ||
const result = await this.db.collection(this.usersCollectionName).updateOne({ email }, { $set: updates }); | ||
// Check if the update had any effect | ||
if (result.modifiedCount === 0) { | ||
throw new Error('No updates applied. User record not found or provided updates are identical to existing data.'); | ||
} | ||
// Retrieve the updated user data, excluding the hashed password | ||
const updatedUser = await this.db.collection(this.usersCollectionName).findOne({ email }, { projection: { hashed_password: 0 } }); | ||
@@ -691,3 +787,2 @@ | ||
async updatePassword (email, currentPassword, newPassword) { | ||
// Retrieve user from the database by email | ||
const user = await this.db.collection(this.usersCollectionName).findOne({ email }); | ||
@@ -697,4 +792,2 @@ if (!user) { | ||
} | ||
// Verify the current password | ||
const isMatch = await bcrypt.compare(currentPassword, user.hashed_password); | ||
@@ -704,7 +797,3 @@ if (!isMatch) { | ||
} | ||
// Hash the new password | ||
const hashedNewPassword = await bcrypt.hash(newPassword, saltRounds); | ||
// Update the user's password in the database | ||
const result = await this.db.collection(this.usersCollectionName).updateOne( | ||
@@ -717,4 +806,2 @@ { email }, | ||
} | ||
// Prepare the user object to be returned | ||
const returnUser = { ...user, id: user._id.toString() }; | ||
@@ -721,0 +808,0 @@ delete returnUser._id; |
{ | ||
"name": "errsole-mongodb", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "MongoDB storage plugin for Errsole", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
# errsole-mongodb | ||
<a href="https://coveralls.io/github/errsole/errsole-mongodb"><img src="https://coveralls.io/repos/github/errsole/errsole-mongodb/badge.svg" alt="Coverage Status" /></a> | ||
@@ -3,0 +4,0 @@ MongoDB storage plugin for Errsole. |
@@ -44,2 +44,11 @@ declare module 'errsole-mongodb' { | ||
interface Notification { | ||
id?: string; | ||
errsole_id: number; | ||
hostname: string; | ||
hashed_message: string; | ||
created_at?: Date; | ||
updated_at?: Date; | ||
} | ||
class ErrsoleMongoDB { | ||
@@ -64,2 +73,6 @@ constructor(uri: string, dbNameOrOptions: string | MongoClientOptions, options?: MongoClientOptions); | ||
deleteUser(id: string): Promise<{}>; | ||
insertNotificationItem(notification: Notification): Promise<{ | ||
previousNotificationItem: Notification | null; | ||
todayNotificationCount: number; | ||
}>; | ||
} | ||
@@ -66,0 +79,0 @@ |
31703
788
15