NotificationLatch/src/notification/repository.js

211 lines
5.4 KiB
JavaScript

const fs = require('fs').promises;
const crypto = require('crypto');
class NotificationRepository
{
static create = container => new this(container.Logger, container.DateTimeProvider, container.AsyncFs, container.Config);
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: now
};
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.title = title;
notification.latched = true;
notification.latchTime = now;
notification.reminders = true;
notification.remindTime = 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;
await this._flush();
}
getLatchedNotifications(token)
{
this._checkInitialized();
if (!this.notifications.hasOwnProperty(token))
return null;
return Object.keys(this.notifications)
.filter(notificationToken => this.notifications[notificationToken].latched)
.map(notificationToken => {
const notification = this.notifications[notificationToken];
return {
token: notificationToken,
id: notification.id,
title: notification.title,
latched: notification.latched,
latchTime: notification.latchTime,
resetTime: notification.resetTime,
reminders: notification.reminders,
remindTime: notification.remindTime
};
});
}
async processReminderNotifications(interval, callback)
{
this._checkInitialized();
const now = this.dateTimeProvider.unixTimestamp();
let shouldFlush = false;
Object.keys(this.notifications).forEach(token =>
{
const notification = this.notifications[token];
if (!notification.latched || !notification.reminders || (notification.remindTime !== null && (now - notification.remindTime) < interval))
return;
this.logger.info(`Sending reminder for notification with id '${notification.id}' and token '${token}'`)
callback(token, notification);
notification.remindTime = now;
shouldFlush = true;
});
if (shouldFlush)
await this._flush();
}
async setReminders(token, enabled)
{
this._checkInitialized();
if (!this.notifications.hasOwnProperty(token))
{
this.logger.info(`Notification token '${token}' does not exist, unable to change reminders setting`);
return;
}
const notification = this.notifications[token];
if (notification.reminders == enabled)
{
this.logger.info(`Notification with id '${notification.id}' and token '${token}' reminders is already ${enabled}`);
return;
}
this.logger.info(`Setting reminders for notification with id '${notification.id}' and token '${token}' to ${enabled}`);
notification.reminders = enabled;
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;