Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

errsole-sqlite

Package Overview
Dependencies
Maintainers
0
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

errsole-sqlite - npm Package Compare versions

Comparing version 0.0.0 to 1.0.0

416

lib/index.js

@@ -7,36 +7,55 @@ const sqlite3 = require('sqlite3');

class ErrsoleSQLite extends EventEmitter {
constructor (options = {}) {
constructor (filename) {
super();
if (!options.logging) options.logging = false;
this.name = require('../package.json').name;
this.version = require('../package.json').version || '0.0.0';
this.isConnectionInProgress = true;
this.db = new sqlite3.Database(filename, err => {
if (err) throw err;
this.initialize();
});
}
this.db = new sqlite3.Database(options.database || ':memory:', (err) => {
if (err) {
console.error('Failed to connect to SQLite database:', err);
} else {
this.initialize();
}
async initialize () {
await this.setCacheSize();
await this.createTables();
await this.ensureLogsTTL();
this.emit('ready');
cron.schedule('0 * * * *', () => {
this.deleteExpiredLogs();
});
}
async initialize () {
try {
await this.defineTables();
this.ensureLogsTTL();
this.setCacheSize();
this.emit('ready');
cron.schedule('0 * * * *', () => {
this.deleteExpiredLogs();
async setCacheSize () {
const desiredSize = 8 * 1024; // Desired cache size in pages, where each page is approximately 1.5 KB (12 MB total)
const currentSize = await this.getCacheSize();
if (currentSize >= desiredSize) {
return Promise.resolve(); // No need to update cache size
}
const query = `PRAGMA cache_size = ${desiredSize}`;
return new Promise((resolve, reject) => {
this.db.run(query, err => {
if (err) return reject(err);
resolve();
});
} catch (err) {
console.error('Failed to initialize:', err);
}
});
}
defineTables () {
async getCacheSize () {
const query = 'PRAGMA cache_size';
return new Promise((resolve, reject) => {
this.db.get(query, (err, row) => {
if (err) return reject(err);
resolve(row.cache_size);
});
});
}
async createTables () {
const queries = [
`CREATE TABLE IF NOT EXISTS errsole_logs (
`CREATE TABLE IF NOT EXISTS errsole_logs_v1 (
id INTEGER PRIMARY KEY AUTOINCREMENT,

@@ -65,27 +84,49 @@ hostname TEXT,

return new Promise((resolve, reject) => {
let remainingQueries = queries.length;
queries.forEach((query) => {
this.db.run(query, (err) => {
if (err) {
console.error('Failed to execute query:', query, err);
return reject(err);
}
remainingQueries--;
if (remainingQueries === 0) {
this.isConnectionInProgress = false;
resolve();
}
await Promise.all(queries.map(query => {
return new Promise((resolve, reject) => {
this.db.run(query, err => {
if (err) return reject(err);
resolve();
});
});
});
}));
this.isConnectionInProgress = false;
}
/**
* Ensures that the Time To Live (TTL) configuration for logs is set.
*
* @async
* @function ensureLogsTTL
* @returns {Promise<{}>} - A promise that resolves with an empty object once the TTL configuration is confirmed or updated.
*/
async ensureLogsTTL () {
const defaultTTL = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
try {
const configResult = await this.getConfig('logsTTL');
if (!configResult.item) {
await this.setConfig('logsTTL', defaultTTL.toString());
}
} catch (err) {
console.error(err);
}
return {};
}
/**
* Retrieves a configuration entry from the database.
*
* @async
* @function getConfig
* @param {string} key - The key of the configuration entry to retrieve.
* @returns {Promise<{item: Config}>} - A promise that resolves with an object containing the configuration item.
* @throws {Error} - Throws an error if the operation fails.
*/
async getConfig (key) {
const query = 'SELECT * FROM errsole_config WHERE `key` = ?';
return new Promise((resolve, reject) => {
this.db.get(query, [key], (err, result) => {
this.db.get(query, [key], (err, row) => {
if (err) return reject(err);
resolve({ item: result });
resolve({ item: row });
});

@@ -95,6 +136,16 @@ });

/**
* Updates or adds a configuration entry in the database.
*
* @async
* @function setConfig
* @param {string} key - The key of the configuration entry.
* @param {string} value - The value to be stored for the configuration entry.
* @returns {Promise<{item: Config}>} - A promise that resolves with an object containing the updated or added configuration item.
* @throws {Error} - Throws an error if the operation fails.
*/
async setConfig (key, value) {
const query = 'INSERT INTO errsole_config (`key`, `value`) VALUES (?, ?) ON CONFLICT(`key`) DO UPDATE SET `value` = excluded.value';
return new Promise((resolve, reject) => {
this.db.run(query, [key, value], (err) => {
this.db.run(query, [key, value], err => {
if (err) return reject(err);

@@ -106,8 +157,17 @@ this.getConfig(key).then(resolve).catch(reject);

/**
* Deletes a configuration entry from the database.
*
* @async
* @function deleteConfig
* @param {string} key - The key of the configuration entry to be deleted.
* @returns {Promise<{}>} - A Promise that resolves with an empty object upon successful deletion of the configuration.
* @throws {Error} - Throws an error if the operation fails.
*/
async deleteConfig (key) {
const query = 'DELETE FROM errsole_config WHERE `key` = ?';
return new Promise((resolve, reject) => {
this.db.run(query, [key], (err, result) => {
this.db.run(query, [key], function (err) { // You must use an old-school function () { ... } style callback rather than a lambda function, otherwise this.lastID and this.changes will be undefined.
if (err) return reject(err);
if (result.changes === 0) return reject(new Error('Configuration not found.'));
if (this.changes === 0) return reject(new Error('Configuration not found.'));
resolve({});

@@ -118,2 +178,11 @@ });

/**
* Adds log entries to the database.
*
* @async
* @function postLogs
* @param {Log[]} logEntries - An array of log entries to be added to the database.
* @returns {Promise<{}>} - A Promise that resolves with an empty object.
* @throws {Error} - Throws an error if the operation fails.
*/
async postLogs (logEntries) {

@@ -135,3 +204,3 @@ while (this.isConnectionInProgress) {

const query = `INSERT INTO errsole_logs (hostname, pid, source, level, message, meta) VALUES ${placeholders}`;
const query = `INSERT INTO errsole_logs_v1 (hostname, pid, source, level, message, meta) VALUES ${placeholders}`;

@@ -141,5 +210,4 @@ const flattenedValues = values.flat();

return new Promise((resolve, reject) => {
this.db.run(query, flattenedValues, (err) => {
if (err) return reject(err);
resolve();
this.db.run(query, flattenedValues, () => {
resolve({});
});

@@ -149,2 +217,11 @@ });

/**
* Retrieves log entries from the database based on specified filters.
*
* @async
* @function getLogs
* @param {LogFilter} [filters] - Filters to apply for log retrieval.
* @returns {Promise<{items: Log[]}>} - A Promise that resolves with an object containing log items.
* @throws {Error} - Throws an error if the operation fails.
*/
async getLogs (filters = {}) {

@@ -208,10 +285,10 @@ const whereClauses = [];

const whereClause = whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : '';
const query = `SELECT id, hostname, pid, source, timestamp, level, message FROM errsole_logs ${whereClause} ORDER BY id ${sortOrder} LIMIT ?`;
const query = `SELECT id, hostname, pid, source, timestamp, level, message FROM errsole_logs_v1 ${whereClause} ORDER BY id ${sortOrder} LIMIT ?`;
values.push(filters.limit);
return new Promise((resolve, reject) => {
this.db.all(query, values, (err, results) => {
this.db.all(query, values, (err, rows) => {
if (err) return reject(err);
if (shouldReverse) results.reverse();
resolve({ items: results });
if (shouldReverse) rows.reverse();
resolve({ items: rows });
});

@@ -221,2 +298,12 @@ });

/**
* Retrieves log entries from the database based on specified search terms and filters.
*
* @async
* @function searchLogs
* @param {string[]} searchTerms - An array of search terms.
* @param {LogFilter} [filters] - Filters to refine the search.
* @returns {Promise<{items: Log[]}>} - A promise that resolves with an object containing an array of log items.
* @throws {Error} - Throws an error if the operation fails.
*/
async searchLogs (searchTerms, filters = {}) {

@@ -267,9 +354,9 @@ const whereClauses = searchTerms.map(() => 'message LIKE ?');

const whereClause = whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : '';
const query = `SELECT id, hostname, pid, source, timestamp, level, message FROM errsole_logs ${whereClause} ORDER BY id DESC LIMIT ?`;
const query = `SELECT id, hostname, pid, source, timestamp, level, message FROM errsole_logs_v1 ${whereClause} ORDER BY id DESC LIMIT ?`;
values.push(filters.limit);
return new Promise((resolve, reject) => {
this.db.all(query, values, (err, results) => {
this.db.all(query, values, (err, rows) => {
if (err) return reject(err);
resolve({ items: results });
resolve({ items: rows });
});

@@ -279,9 +366,18 @@ });

/**
* Retrieves the meta data of a log entry.
*
* @async
* @function getMeta
* @param {number} id - The unique ID of the log entry.
* @returns {Promise<{item: id, meta}>} - A Promise that resolves with an object containing the log ID and its associated metadata.
* @throws {Error} - Throws an error if the log entry is not found or the operation fails.
*/
async getMeta (id) {
const query = 'SELECT id, meta FROM errsole_logs WHERE id = ?';
const query = 'SELECT id, meta FROM errsole_logs_v1 WHERE id = ?';
return new Promise((resolve, reject) => {
this.db.get(query, [id], (err, result) => {
this.db.get(query, [id], (err, row) => {
if (err) return reject(err);
if (!result) return reject(new Error('Log entry not found.'));
resolve({ item: result });
if (!row) return reject(new Error('Log entry not found.'));
resolve({ item: row });
});

@@ -291,12 +387,2 @@ });

async ensureLogsTTL () {
const DEFAULT_TTL = 2592000000;
try {
const configResult = await this.getConfig('logsTTL');
if (!configResult.item) {
await this.setConfig('logsTTL', DEFAULT_TTL.toString());
}
} catch {}
}
/**

@@ -308,3 +394,2 @@ * Deletes expired logs based on TTL configuration.

*/
async deleteExpiredLogs () {

@@ -315,22 +400,21 @@ if (this.deleteExpiredLogsRunning) return;

const logsTTLDefault = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
const defaultLogsTTL = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
try {
let logsTTL = logsTTLDefault;
let logsTTL = defaultLogsTTL;
const configResult = await this.getConfig('logsTTL');
if (configResult && configResult.item && configResult.item.value) {
logsTTL = parseInt(configResult.item.value, 10);
if (configResult.item) {
const parsedTTL = parseInt(configResult.item.value, 10);
logsTTL = isNaN(parsedTTL) ? defaultLogsTTL : parsedTTL;
}
const expirationTime = new Date(Date.now() - logsTTL);
let expirationTime = new Date(Date.now() - logsTTL);
expirationTime = new Date(expirationTime).toISOString();
let deletedRowCount;
do {
const result = await new Promise((resolve, reject) => {
deletedRowCount = await new Promise((resolve, reject) => {
this.db.run(
'DELETE FROM errsole_logs WHERE timestamp < ?',
'DELETE FROM errsole_logs_v1 WHERE id IN (SELECT id FROM errsole_logs_v1 WHERE timestamp < ? LIMIT 1000)',
[expirationTime],
function (err) {
if (err) {
return reject(err);
}
function (err) { // You must use an old-school function () { ... } style callback rather than a lambda function, otherwise this.lastID and this.changes will be undefined.
if (err) return reject(err);
resolve(this.changes);

@@ -340,3 +424,2 @@ }

});
deletedRowCount = result;
await new Promise(resolve => setTimeout(resolve, 10000));

@@ -351,6 +434,15 @@ } while (deletedRowCount > 0);

async delay (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Creates a new user record in the database.
*
* @async
* @function createUser
* @param {Object} user - The user data.
* @param {string} user.name - The name of the user.
* @param {string} user.email - The email address of the user.
* @param {string} user.password - The password of the user.
* @param {string} user.role - The role of the user.
* @returns {Promise<{item: User}>} - A promise that resolves with an object containing the new user item.
* @throws {Error} - Throws an error if the user creation fails due to duplicate email or other database issues.
*/
async createUser (user) {

@@ -361,5 +453,7 @@ const SALT_ROUNDS = 10;

return new Promise((resolve, reject) => {
this.db.run(query, [user.name, user.email, hashedPassword, user.role], function (err) {
this.db.run(query, [user.name, user.email, hashedPassword, user.role], function (err) { // You must use an old-school function () { ... } style callback rather than a lambda function, otherwise this.lastID and this.changes will be undefined.
if (err) {
if (err.code === 'SQLITE_CONSTRAINT') return reject(new Error('A user with the provided email already exists.'));
if (err.code === 'SQLITE_CONSTRAINT') {
return reject(new Error('A user with the provided email already exists.'));
}
return reject(err);

@@ -372,16 +466,28 @@ }

/**
* Verifies a user's credentials against stored records.
*
* @async
* @function verifyUser
* @param {string} email - The email address of the user.
* @param {string} password - The password of the user
* @returns {Promise<{item: User}>} - A promise that resolves with an object containing the user item upon successful verification.
* @throws {Error} - Throws an error if the operation fails.
*/
async verifyUser (email, password) {
if (!email || !password) throw new Error('Both email and password are required for verification.');
if (!email || !password) {
throw new Error('Both email and password are required for verification.');
}
const query = 'SELECT * FROM errsole_users WHERE email = ?';
return new Promise((resolve, reject) => {
this.db.get(query, [email], async (err, user) => {
this.db.get(query, [email], async (err, row) => {
if (err) return reject(err);
if (!user) return reject(new Error('User not found.'));
if (!row) return reject(new Error('User not found.'));
const isPasswordCorrect = await bcrypt.compare(password, user.hashed_password);
const isPasswordCorrect = await bcrypt.compare(password, row.hashed_password);
if (!isPasswordCorrect) return reject(new Error('Incorrect password.'));
delete user.hashed_password;
resolve({ item: user });
delete row.hashed_password;
resolve({ item: row });
});

@@ -391,8 +497,16 @@ });

/**
* Retrieves the total count of users from the database.
*
* @async
* @function getUserCount
* @returns {Promise<{count: number}>} - A promise that resolves with an object containing the count of users.
* @throws {Error} - Throws an error if the operation fails.
*/
async getUserCount () {
const query = 'SELECT COUNT(*) as count FROM errsole_users';
return new Promise((resolve, reject) => {
this.db.get(query, (err, result) => {
this.db.get(query, (err, row) => {
if (err) return reject(err);
resolve({ count: result.count });
resolve({ count: row.count });
});

@@ -402,8 +516,16 @@ });

/**
* Retrieves all user records from the database.
*
* @async
* @function getAllUsers
* @returns {Promise<{items: User[]}>} - A promise that resolves with an object containing an array of user items.
* @throws {Error} - Throws an error if the operation fails.
*/
async getAllUsers () {
const query = 'SELECT id, name, email, role FROM errsole_users';
return new Promise((resolve, reject) => {
this.db.all(query, (err, results) => {
this.db.all(query, (err, rows) => {
if (err) return reject(err);
resolve({ items: results });
resolve({ items: rows });
});

@@ -413,2 +535,11 @@ });

/**
* Retrieves a user record from the database based on the provided email.
*
* @async
* @function getUserByEmail
* @param {string} email - The email address of the user.
* @returns {Promise<{item: User}>} - A Promise that resolves with an object containing the user item.
* @throws {Error} - Throws an error if no user matches the email address.
*/
async getUserByEmail (email) {

@@ -419,6 +550,6 @@ if (!email) throw new Error('Email is required.');

return new Promise((resolve, reject) => {
this.db.get(query, [email], (err, result) => {
this.db.get(query, [email], (err, row) => {
if (err) return reject(err);
if (!result) return reject(new Error('User not found.'));
resolve({ item: result });
if (!row) return reject(new Error('User not found.'));
resolve({ item: row });
});

@@ -428,2 +559,12 @@ });

/**
* Updates a user's record in the database based on the provided email.
*
* @async
* @function updateUserByEmail
* @param {string} email - The email address of the user to be updated.
* @param {Object} updates - The updates to be applied to the user record.
* @returns {Promise<{item: User}>} - A Promise that resolves with an object containing the updated user item.
* @throws {Error} - Throws an error if no updates could be applied or the user is not found.
*/
async updateUserByEmail (email, updates) {

@@ -441,3 +582,3 @@ if (!email) throw new Error('Email is required.');

return new Promise((resolve, reject) => {
this.db.run(query, values, async (err) => {
this.db.run(query, values, err => {
if (err) return reject(err);

@@ -449,12 +590,25 @@ this.getUserByEmail(email).then(resolve).catch(reject);

/**
* Updates a user's password in the database.
*
* @async
* @function updatePassword
* @param {string} email - The email address of the user whose password is to be updated.
* @param {string} currentPassword - The current password of the user for verification.
* @param {string} newPassword - The new password to replace the current one.
* @returns {Promise<{item: User}>} - A Promise that resolves with an object containing the updated user item (excluding sensitive information).
* @throws {Error} - If the user is not found, if the current password is incorrect, or if the password update fails.
*/
async updatePassword (email, currentPassword, newPassword) {
if (!email || !currentPassword || !newPassword) throw new Error('Email, current password, and new password are required.');
if (!email || !currentPassword || !newPassword) {
throw new Error('Email, current password, and new password are required.');
}
const query = 'SELECT * FROM errsole_users WHERE email = ?';
return new Promise((resolve, reject) => {
this.db.get(query, [email], async (err, user) => {
this.db.get(query, [email], async (err, row) => {
if (err) return reject(err);
if (!user) return reject(new Error('User not found.'));
if (!row) return reject(new Error('User not found.'));
const isPasswordCorrect = await bcrypt.compare(currentPassword, user.hashed_password);
const isPasswordCorrect = await bcrypt.compare(currentPassword, row.hashed_password);
if (!isPasswordCorrect) return reject(new Error('Current password is incorrect.'));

@@ -464,6 +618,6 @@

const updateQuery = 'UPDATE errsole_users SET hashed_password = ? WHERE email = ?';
this.db.run(updateQuery, [hashedPassword, email], function (err) {
this.db.run(updateQuery, [hashedPassword, email], err => {
if (err) return reject(err);
delete user.hashed_password;
resolve({ item: user });
delete row.hashed_password;
resolve({ item: row });
});

@@ -474,8 +628,17 @@ });

async deleteUser (userId) {
if (!userId) throw new Error('User ID is required.');
/**
* Deletes a user record from the database.
*
* @async
* @function deleteUser
* @param {number} id - The unique ID of the user to be deleted.
* @returns {Promise<{}>} - A Promise that resolves with an empty object upon successful deletion of the user.
* @throws {Error} - Throws an error if no user is found with the given ID or if the database operation fails.
*/
async deleteUser (id) {
if (!id) throw new Error('User ID is required.');
const query = 'DELETE FROM errsole_users WHERE id = ?';
return new Promise((resolve, reject) => {
this.db.run(query, [userId], function (err) {
this.db.run(query, [id], function (err) { // You must use an old-school function () { ... } style callback rather than a lambda function, otherwise this.lastID and this.changes will be undefined.
if (err) return reject(err);

@@ -487,37 +650,4 @@ if (this.changes === 0) return reject(new Error('User not found.'));

}
async getCacheSize () {
const query = 'PRAGMA cache_size';
return new Promise((resolve, reject) => {
this.db.get(query, (err, result) => {
if (err) {
return reject(err);
}
resolve(result.cache_size);
});
});
}
async setCacheSize () {
const desiredSize = 8192;
const currentSize = await this.getCacheSize();
if (currentSize < desiredSize) {
const query = `PRAGMA cache_size = ${desiredSize}`;
return new Promise((resolve, reject) => {
this.db.run(query, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
} else {
return Promise.resolve();
}
}
}
module.exports = ErrsoleSQLite;
{
"name": "errsole-sqlite",
"version": "0.0.0",
"version": "1.0.0",
"description": "SQLite storage plugin for Errsole",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -1,1 +0,13 @@

# errsole-sqlite
# errsole-sqlite
SQLite storage plugin for Errsole.
## What is Errsole?
Errsole is a Node.js logger with a built-in web dashboard. In this dashboard, you can easily view, filter, and search your app logs.
If your application uses SQLite as its database or if you prefer storing your application logs in SQLite, you should install both the "errsole" and "errsole-sqlite" modules. This setup allows you to store your application logs directly in your SQLite database.
## Full Documentation
[https://github.com/errsole/errsole.js](https://github.com/errsole/errsole.js)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc