diff --git a/server/managers/BackupManager.js b/server/managers/BackupManager.js index c26f27045..b2f5ed04d 100644 --- a/server/managers/BackupManager.js +++ b/server/managers/BackupManager.js @@ -19,6 +19,7 @@ class BackupManager { constructor(notificationManager) { this.ItemsMetadataPath = Path.join(global.MetadataPath, 'items') this.AuthorsMetadataPath = Path.join(global.MetadataPath, 'authors') + /** @type {import('./NotificationManager')} */ this.notificationManager = notificationManager this.scheduleTask = null @@ -293,7 +294,8 @@ class BackupManager { // Create backup sqlite file const sqliteBackupPath = await this.backupSqliteDb(newBackup).catch((error) => { Logger.error(`[BackupManager] Failed to backup sqlite db`, error) - this.notificationManager.onBackupFailed(error) + const errorMsg = error?.message || error || 'Unknown Error' + this.notificationManager.onBackupFailed(errorMsg) return false }) @@ -304,7 +306,8 @@ class BackupManager { // Zip sqlite file, /metadata/items, and /metadata/authors folders const zipResult = await this.zipBackup(sqliteBackupPath, newBackup).catch((error) => { Logger.error(`[BackupManager] Backup Failed ${error}`) - this.notificationManager.onBackupFailed(error) + const errorMsg = error?.message || error || 'Unknown Error' + this.notificationManager.onBackupFailed(errorMsg) return false }) @@ -354,7 +357,6 @@ class BackupManager { /** * @see https://github.com/TryGhost/node-sqlite3/pull/1116 * @param {Backup} backup - * @promise */ backupSqliteDb(backup) { const db = new sqlite3.Database(Database.dbPath) diff --git a/server/managers/NotificationManager.js b/server/managers/NotificationManager.js index 2800ac967..f8bd75114 100644 --- a/server/managers/NotificationManager.js +++ b/server/managers/NotificationManager.js @@ -17,6 +17,11 @@ class NotificationManager { async onPodcastEpisodeDownloaded(libraryItem, episode) { if (!Database.notificationSettings.isUseable) return + if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onPodcastEpisodeDownloaded')) { + Logger.debug(`[NotificationManager] onPodcastEpisodeDownloaded: No active notifications`) + return + } + Logger.debug(`[NotificationManager] onPodcastEpisodeDownloaded: Episode "${episode.title}" for podcast ${libraryItem.media.metadata.title}`) const library = await Database.libraryModel.getOldById(libraryItem.libraryId) const eventData = { @@ -36,9 +41,20 @@ class NotificationManager { this.triggerNotification('onPodcastEpisodeDownloaded', eventData) } + /** + * + * @param {import('../objects/Backup')} backup + * @param {number} totalBackupCount + * @param {boolean} removedOldest - If oldest backup was removed + */ async onBackupCompleted(backup, totalBackupCount, removedOldest) { if (!Database.notificationSettings.isUseable) return + if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onBackupCompleted')) { + Logger.debug(`[NotificationManager] onBackupCompleted: No active notifications`) + return + } + Logger.debug(`[NotificationManager] onBackupCompleted: Backup completed`) const eventData = { completionTime: backup.createdAt, @@ -50,10 +66,19 @@ class NotificationManager { this.triggerNotification('onBackupCompleted', eventData) } + /** + * + * @param {string} errorMsg + */ async onBackupFailed(errorMsg) { if (!Database.notificationSettings.isUseable) return - Logger.debug(`[NotificationManager] onBackupFailed: Backup failed`) + if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onBackupFailed')) { + Logger.debug(`[NotificationManager] onBackupFailed: No active notifications`) + return + } + + Logger.debug(`[NotificationManager] onBackupFailed: Backup failed (${errorMsg})`) const eventData = { errorMsg: errorMsg || 'Backup failed' } @@ -64,6 +89,12 @@ class NotificationManager { this.triggerNotification('onTest') } + /** + * + * @param {string} eventName + * @param {any} eventData + * @param {boolean} [intentionallyFail=false] - If true, will intentionally fail the notification + */ async triggerNotification(eventName, eventData, intentionallyFail = false) { if (!Database.notificationSettings.isUseable) return @@ -93,7 +124,12 @@ class NotificationManager { this.notificationFinished() } - // Return TRUE if notification should be triggered now + /** + * + * @param {string} eventName + * @param {any} eventData + * @returns {boolean} - TRUE if notification should be triggered now + */ checkTriggerNotification(eventName, eventData) { if (this.sendingNotification) { if (this.notificationQueue.length >= Database.notificationSettings.maxNotificationQueue) { diff --git a/server/objects/settings/NotificationSettings.js b/server/objects/settings/NotificationSettings.js index 04907f3c8..aab8f3e4a 100644 --- a/server/objects/settings/NotificationSettings.js +++ b/server/objects/settings/NotificationSettings.js @@ -20,7 +20,7 @@ class NotificationSettings { construct(settings) { this.appriseType = settings.appriseType this.appriseApiUrl = settings.appriseApiUrl || null - this.notifications = (settings.notifications || []).map(n => new Notification(n)) + this.notifications = (settings.notifications || []).map((n) => new Notification(n)) this.maxFailedAttempts = settings.maxFailedAttempts || 5 this.maxNotificationQueue = settings.maxNotificationQueue || 20 this.notificationDelay = settings.notificationDelay || 1000 @@ -31,7 +31,7 @@ class NotificationSettings { id: this.id, appriseType: this.appriseType, appriseApiUrl: this.appriseApiUrl, - notifications: this.notifications.map(n => n.toJSON()), + notifications: this.notifications.map((n) => n.toJSON()), maxFailedAttempts: this.maxFailedAttempts, maxNotificationQueue: this.maxNotificationQueue, notificationDelay: this.notificationDelay @@ -42,17 +42,29 @@ class NotificationSettings { return !!this.appriseApiUrl } + /** + * @param {string} eventName + * @returns {boolean} - TRUE if there are active notifications for the event + */ + getHasActiveNotificationsForEvent(eventName) { + return this.notifications.some((n) => n.eventName === eventName && n.enabled) + } + + /** + * @param {string} eventName + * @returns {Notification[]} + */ getActiveNotificationsForEvent(eventName) { - return this.notifications.filter(n => n.eventName === eventName && n.enabled) + return this.notifications.filter((n) => n.eventName === eventName && n.enabled) } getNotification(id) { - return this.notifications.find(n => n.id === id) + return this.notifications.find((n) => n.id === id) } removeNotification(id) { - if (this.notifications.some(n => n.id === id)) { - this.notifications = this.notifications.filter(n => n.id !== id) + if (this.notifications.some((n) => n.id === id)) { + this.notifications = this.notifications.filter((n) => n.id !== id) return true } return false @@ -94,7 +106,7 @@ class NotificationSettings { updateNotification(payload) { if (!payload) return false - const notification = this.notifications.find(n => n.id === payload.id) + const notification = this.notifications.find((n) => n.id === payload.id) if (!notification) { Logger.error(`[NotificationSettings] updateNotification: Notification not found ${payload.id}`) return false @@ -103,4 +115,4 @@ class NotificationSettings { return notification.update(payload) } } -module.exports = NotificationSettings \ No newline at end of file +module.exports = NotificationSettings diff --git a/unicode.dll b/unicode.dll new file mode 100644 index 000000000..7ad46dd55 Binary files /dev/null and b/unicode.dll differ