const fs = require('fs').promises; const crypto = require('crypto'); class NotificationRepository { constructor(logger, dateTimeProvider, asyncFs, config) { this.logger = logger; this.dateTimeProvider = dateTimeProvider; this.asyncFs = asyncFs; this.filename = config.dataFilename; this.salt = config.salt; this.notifications = null; } async init() { if (this.notifications !== null) throw new Error('Repository is already initialized'); this.notifications = {}; if (!(await this.asyncFs.exists(this.filename))) return; const contents = await fs.readFile(this.filename, 'utf8'); this.notifications = JSON.parse(contents.toString()); } // Returns the notification token if it should be sent out or null if the notification is latched async storeNotification(id, title) { this._checkInitialized(); const now = this.dateTimeProvider.unixTimestamp(); const token = this._getNotificationToken(id); if (!this.notifications.hasOwnProperty(token)) { this.logger.info(`New notification with id '${id}' and token '${token}', send permitted`); this.notifications[token] = { id: id, title: title, latched: true, latchTime: now, resetTime: null, reminders: true, remindTime: null }; await this._flush(); return token; } const notification = this.notifications[token]; if (notification.latched) { this.logger.info(`Notification with id ${id} and token '${token}' is already latched, send blocked`); return null; } this.logger.info(`Latching notification with id ${id} and token '${token}', send permitted`); notification.latched = true; notification.latchTime = now; await this._flush(); return token; } async resetNotification(token) { this._checkInitialized(); const now = this.dateTimeProvider.unixTimestamp(); if (!this.notifications.hasOwnProperty(token)) { this.logger.info(`Notification token '${token}' does not exist, reset unneccesary`); return; } const notification = this.notifications[token]; if (!notification.latched) { this.logger.info(`Notification with id '${notification.id}' and token '${token}' is not latched, reset unneccesary`); return; } this.logger.info(`Resetting notification with id '${notification.id}' and token '${token}'`); notification.latched = false; notification.resetTime = now; notification.reminders = true; notification.remindTime = null; await this._flush(); } _getNotificationToken(id) { const hasher = crypto.createHmac("sha256", this.salt); return hasher.update(id).digest("hex"); } async _flush() { const contents = JSON.stringify(this.notifications, null, 2); await fs.writeFile(this.filename, contents, 'utf8'); } _checkInitialized() { if (this.notifications === null) throw new Error('Repository not initialized, call init() first'); } } module.exports = NotificationRepository;