const map = require('lodash/map'); const retry = require('async-retry'); const generate = require('nanoid/generate'); const markdown = require('markdown').markdown; const async = require('async'); const ExpirationUnits = require('../expirationunits'); class Code { constructor(values) { var self = this; self.id = values.id || values._id || null; self.userId = values.userId || null; self.created = values.created || new Date(); self.expiration = values.expiration || null; self.expirationDate = values.expirationDate || null; self.description = values.description || null; self.message = values.message || null; self.messageHTML = values.messageHTML || null; self.history = values.history || [{ userId: self.userId, date: self.created }]; } } class CodeRepository { constructor(store) { var self = this; self.store = store; } init() { var self = this; return new Promise((resolve, reject) => { resolve(); }); } async insert(code) { var self = this; return await retry(async bail => { var codeId = generate(config.code.alphabet, config.code.length); if ((await self.get(codeId)) !== null) throw new Error('Code ' + codeId + ' already exists'); let now = new Date(); self.store.insert({ _id: codeId, userId: code.userId, created: code.created || now, expiration: code.expiration, expirationDate: ExpirationUnits.apply(code.expiration, code.created), description: code.description, message: code.message, messageHTML: self.getMessageHTML(code.message), history: [ { userId: code.userId, date: code.created || now } ] }) return codeId; }, { retries: 100, minTimeout: 0, maxTimeout: 0 }); } update(code) { var self = this; return new Promise((resolve, reject) => { self.store.update({ _id: code.id }, { $set: { expiration: code.expiration, expirationDate: ExpirationUnits.apply(code.expiration, code.created), description: code.description, message: code.message, messageHTML: self.getMessageHTML(code.message) }}, (err, numAffected) => { if (err) { reject(err); return; } if (numAffected == 0) { reject(); } resolve(); }); }); } move(codeId, userId) { var self = this; return new Promise((resolve, reject) => { self.store.update({ _id: codeId }, { $set: { userId: userId }, $push: { history: { userId: userId, date: new Date() } } }, (err, numAffected) => { if (err) { reject(err); return; } if (numAffected == 0) { reject(); } resolve(); }); }); } list(userId) { var self = this; return new Promise((resolve, reject) => { self.store.find(userId != null ? { userId: userId } : {}, (err, docs) => { if (err) { reject(err); return; } resolve(map(docs, (doc) => new Code(doc))); }); }); } getDescriptions() { var self = this; return new Promise((resolve, reject) => { self.store.find({}, (err, docs) => { if (err) { reject(err); return; } var descriptions = {}; docs.forEach((dbCode) => { if (dbCode.description) descriptions[dbCode._id] = dbCode.description; }); resolve(descriptions); }); }); } get(codeId) { var self = this; return new Promise((resolve, reject) => { self.store.findOne({ _id: codeId }, (err, doc) => { if (err) { reject(err); return; } resolve(doc !== null ? new Code(doc) : null); }); }); } getMessageHTML(message) { return message !== null ? markdown.toHTML(message) : null; } delete(codeId) { var self = this; return new Promise((resolve, reject) => { self.store.remove({ _id: codeId }, (err, numRemoved) => { if (err) { reject(err); return; } resolve(); }); }); } deleteExpired() { var self = this; return new Promise((resolve, reject) => { let now = new Date(); self.store.find({ $where: function() { return this.expirationDate !== null && this.expirationDate < now }}, (err, docs) => { if (err) { reject(err); return; } async.eachOfSeries(docs, async (doc) => { console.log('Expired code: ' + doc._id); await self.delete(doc._id); }, (err) => { if (err) reject(err); else resolve(); }); }); }); } } module.exports = { Code, CodeRepository }