eventlog: async'ify

This commit is contained in:
Girish Ramakrishnan
2021-06-01 09:35:20 -07:00
parent bdbda9b80e
commit 7aac4455a9
11 changed files with 309 additions and 657 deletions
+78 -54
View File
@@ -2,11 +2,11 @@
exports = module.exports = {
add,
upsert,
upsertLoginEvent,
get,
getAllPaged,
getByCreationTime,
cleanup,
_clear: clear,
// keep in sync with webadmin index.js filter
ACTION_ACTIVATE: 'cloudron.activate',
@@ -77,94 +77,118 @@ exports = module.exports = {
};
const assert = require('assert'),
database = require('./database.js'),
debug = require('debug')('box:eventlog'),
eventlogdb = require('./eventlogdb.js'),
mysql = require('mysql'),
notifications = require('./notifications.js'),
safe = require('safetydance'),
util = require('util'),
uuid = require('uuid');
const NOOP_CALLBACK = function (error) { if (error) debug(error); };
const EVENTLOG_FIELDS = [ 'id', 'action', 'source', 'data', 'creationTime' ].join(',');
function add(action, source, data, callback) {
function postProcess(record) {
// usually we have sourceJson and dataJson, however since this used to be the JSON data type, we don't
record.source = safe.JSON.parse(record.source);
record.data = safe.JSON.parse(record.data);
return record;
}
// never throws, only logs because previously code did not take a callback
async function add(action, source, data) {
assert.strictEqual(typeof action, 'string');
assert.strictEqual(typeof source, 'object');
assert.strictEqual(typeof data, 'object');
assert(!callback || typeof callback === 'function');
callback = callback || NOOP_CALLBACK;
eventlogdb.add(uuid.v4(), action, source, data, async function (error, id) {
if (error) return callback(error);
callback(null, { id: id });
await safe(notifications.onEvent(id, action, source, data));
});
const id = uuid.v4();
try {
await database.query('INSERT INTO eventlog (id, action, source, data) VALUES (?, ?, ?, ?)', [ id, action, JSON.stringify(source), JSON.stringify(data) ]);
await notifications.onEvent(id, action, source, data);
return id;
} catch (error) {
debug('add: error adding event', error);
return null;
}
}
function upsert(action, source, data, callback) {
// never throws, only logs because previously code did not take a callback
async function upsertLoginEvent(action, source, data) {
assert.strictEqual(typeof action, 'string');
assert.strictEqual(typeof source, 'object');
assert.strictEqual(typeof data, 'object');
assert(!callback || typeof callback === 'function');
callback = callback || NOOP_CALLBACK;
// can't do a real sql upsert, for frequent eventlog entries we only have to do 2 queries once a day
const queries = [{
query: 'UPDATE eventlog SET creationTime=NOW(), data=? WHERE action = ? AND source LIKE ? AND DATE(creationTime)=CURDATE()',
args: [ JSON.stringify(data), action, JSON.stringify(source) ]
}, {
query: 'SELECT ' + EVENTLOG_FIELDS + ' FROM eventlog WHERE action = ? AND source LIKE ? AND DATE(creationTime)=CURDATE()',
args: [ action, JSON.stringify(source) ]
}];
eventlogdb.upsert(uuid.v4(), action, source, data, async function (error, id) {
if (error) return callback(error);
try {
const result = await database.transaction(queries);
if (result[0].affectedRows >= 1) return result[1][0].id;
callback(null, { id: id });
await safe(notifications.onEvent(id, action, source, data));
});
// no existing eventlog found, create one
return await add(action, source, data);
} catch (error) {
debug('add: error adding event', error);
return null;
}
}
function get(id, callback) {
async function get(id) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
eventlogdb.get(id, function (error, result) {
if (error) return callback(error);
const result = await database.query('SELECT ' + EVENTLOG_FIELDS + ' FROM eventlog WHERE id = ?', [ id ]);
if (result.length === 0) return null;
callback(null, result);
});
return postProcess(result[0]);
}
function getAllPaged(actions, search, page, perPage, callback) {
async function getAllPaged(actions, search, page, perPage) {
assert(Array.isArray(actions));
assert(typeof search === 'string' || search === null);
assert.strictEqual(typeof page, 'number');
assert.strictEqual(typeof perPage, 'number');
assert.strictEqual(typeof callback, 'function');
eventlogdb.getAllPaged(actions, search, page, perPage, function (error, events) {
if (error) return callback(error);
let data = [];
let query = `SELECT ${EVENTLOG_FIELDS} FROM eventlog`;
callback(null, events);
if (actions.length || search) query += ' WHERE';
if (search) query += ' (source LIKE ' + mysql.escape('%' + search + '%') + ' OR data LIKE ' + mysql.escape('%' + search + '%') + ')';
if (actions.length && search) query += ' AND ( ';
actions.forEach(function (action, i) {
query += ' (action LIKE ' + mysql.escape(`%${action}%`) + ') ';
if (i < actions.length-1) query += ' OR ';
});
if (actions.length && search) query += ' ) ';
query += ' ORDER BY creationTime DESC LIMIT ?,?';
data.push((page-1)*perPage);
data.push(perPage);
const results = await database.query(query, data);
results.forEach(postProcess);
return results;
}
function getByCreationTime(creationTime, callback) {
assert(util.types.isDate(creationTime));
assert.strictEqual(typeof callback, 'function');
async function cleanup(options) {
assert.strictEqual(typeof options, 'object');
eventlogdb.getByCreationTime(creationTime, function (error, events) {
if (error) return callback(error);
const creationTime = options.creationTime || new Date(Date.now() - 60 * 60 * 24 * 10 * 1000); // 10 days ago
callback(null, events);
});
const results = await database.query('SELECT * FROM eventlog WHERE creationTime <= ?', [ creationTime ]);
for (const result of results) {
await database.query('DELETE FROM notifications WHERE eventId=?', [ result.id ]); // remove notifications that reference the events as well
await database.query('DELETE FROM eventlog WHERE id=?', [ result.id ]);
}
}
function cleanup(callback) {
callback = callback || NOOP_CALLBACK;
var d = new Date();
d.setDate(d.getDate() - 10); // 10 days ago
eventlogdb.delByCreationTime(d, function (error) {
if (error) return callback(error);
callback(null);
});
async function clear() {
await database.query('DELETE FROM eventlog');
}