diff --git a/CHANGES b/CHANGES index 3d6b8e5a2..fa327a793 100644 --- a/CHANGES +++ b/CHANGES @@ -2878,4 +2878,5 @@ * remove global lock * hetzner: add helsinki object storage location * backups: implement app archive +* notifications: per user email notification config diff --git a/dashboard/public/translation/en.json b/dashboard/public/translation/en.json index 0388eca5d..eafb84815 100644 --- a/dashboard/public/translation/en.json +++ b/dashboard/public/translation/en.json @@ -1143,7 +1143,7 @@ "certificateRenewalFailed": "Certificate renewal failed", "appOutOfMemory": "App ran out of memory", "appUp": "App is online", - "appDown": "App went down" + "appDown": "App is down" }, "settingsDialog": { "description": "Manage your personal notification preferences here. Cloudron will send an email for the selected events to your primary email address." diff --git a/src/mail_templates/app_down.ejs b/src/mail_templates/app_down-text.ejs similarity index 75% rename from src/mail_templates/app_down.ejs rename to src/mail_templates/app_down-text.ejs index 8e26935e8..fe51ba213 100644 --- a/src/mail_templates/app_down.ejs +++ b/src/mail_templates/app_down-text.ejs @@ -1,5 +1,3 @@ -<%if (format === 'text') { %> - Dear Cloudron Admin, The application '<%= title %>' installed at <%= appFqdn %> is not responding. @@ -8,8 +6,9 @@ This is most likely a problem in the application. To resolve this, you can try the following: +* Check the app logs - https://docs.cloudron.io/apps/#log-viewer * Restart the app by opening the app's web terminal - https://docs.cloudron.io/apps/#web-terminal -* Restore the app to the latest backup - https://docs.cloudron.io/backups/#restoring-an-app +* Check the troubleshooting guidelines - https://docs.cloudron.io/troubleshooting/#unresponsive-app * Contact us in our Forum at https://forum.cloudron.io Powered by https://cloudron.io @@ -18,6 +17,3 @@ Don't want such mails? Change your notification preferences at <%= notifications Sent at: <%= new Date().toUTCString() %> -<% } else { %> - -<% } %> diff --git a/src/mail_templates/app_up-text.ejs b/src/mail_templates/app_up-text.ejs new file mode 100644 index 000000000..4afbb7a19 --- /dev/null +++ b/src/mail_templates/app_up-text.ejs @@ -0,0 +1,11 @@ +Dear Cloudron Admin, + +The application '<%= title %>' installed at <%= appFqdn %> is back online +and responding to health checks. + +Powered by https://cloudron.io + +Don't want such mails? Change your notification preferences at <%= notificationsUrl %> + +Sent at: <%= new Date().toUTCString() %> + diff --git a/src/mail_templates/backup_failed.ejs b/src/mail_templates/backup_failed-text.ejs similarity index 87% rename from src/mail_templates/backup_failed.ejs rename to src/mail_templates/backup_failed-text.ejs index f698998b4..1da647de5 100644 --- a/src/mail_templates/backup_failed.ejs +++ b/src/mail_templates/backup_failed-text.ejs @@ -1,5 +1,3 @@ -<%if (format === 'text') { %> - Dear <%= cloudronName %> Admin, Cloudron failed to create a complete backup. Please see the logs at <%= logUrl %> for more information. @@ -17,7 +15,3 @@ Don't want such mails? Change your notification preferences at <%= notifications Sent at: <%= new Date().toUTCString() %> - -<% } else { %> - -<% } %> diff --git a/src/mail_templates/certificate_renewal_error.ejs b/src/mail_templates/certificate_renewal_error-text.ejs similarity index 92% rename from src/mail_templates/certificate_renewal_error.ejs rename to src/mail_templates/certificate_renewal_error-text.ejs index 2a8b3cb0f..2839cf4e1 100644 --- a/src/mail_templates/certificate_renewal_error.ejs +++ b/src/mail_templates/certificate_renewal_error-text.ejs @@ -1,5 +1,3 @@ -<%if (format === 'text') { %> - Dear Cloudron Admin, The certificate for <%= domain %> could not be renewed. @@ -27,6 +25,3 @@ Don't want such mails? Change your notification preferences at <%= notifications Sent at: <%= new Date().toUTCString() %> -<% } else { %> - -<% } %> diff --git a/src/mail_templates/oom_event.ejs b/src/mail_templates/oom_event-text.ejs similarity index 93% rename from src/mail_templates/oom_event.ejs rename to src/mail_templates/oom_event-text.ejs index 9d99d395e..73ba1fde7 100644 --- a/src/mail_templates/oom_event.ejs +++ b/src/mail_templates/oom_event-text.ejs @@ -1,5 +1,3 @@ -<%if (format === 'text') { %> - Dear <%= cloudronName %> Admin, <%if (app) { %> @@ -24,6 +22,3 @@ Don't want such mails? Change your notification preferences at <%= notifications Sent at: <%= new Date().toUTCString() %> -<% } else { %> - -<% } %> diff --git a/src/mail_templates/test.ejs b/src/mail_templates/test-text.ejs similarity index 100% rename from src/mail_templates/test.ejs rename to src/mail_templates/test-text.ejs diff --git a/src/mailer.js b/src/mailer.js index 25fdc642a..dceb05080 100644 --- a/src/mailer.js +++ b/src/mailer.js @@ -195,7 +195,7 @@ async function appDown(mailTo, app) { from: mailConfig.notificationFrom, to: mailTo, subject: `[${mailConfig.cloudronName}] App ${app.fqdn} is down`, - text: render('app_down.ejs', { title: app.manifest.title, appFqdn: app.fqdn, notificationsUrl: mailConfig.notificationsUrl }) + text: render('app_down-text.ejs', { title: app.manifest.title, appFqdn: app.fqdn, notificationsUrl: mailConfig.notificationsUrl }) }; await sendMail(mailOptions); @@ -211,17 +211,17 @@ async function appUp(mailTo, app) { from: mailConfig.notificationFrom, to: mailTo, subject: `[${mailConfig.cloudronName}] App ${app.fqdn} is back online`, - text: render('app_up.ejs', { title: app.manifest.title, appFqdn: app.fqdn, notificationsUrl: mailConfig.notificationsUrl }) + text: render('app_up-text.ejs', { title: app.manifest.title, appFqdn: app.fqdn, notificationsUrl: mailConfig.notificationsUrl }) }; await sendMail(mailOptions); } -async function oomEvent(mailTo, app, addon, containerId, event) { +async function oomEvent(mailTo, containerId, app, addon, event) { assert.strictEqual(typeof mailTo, 'string'); + assert.strictEqual(typeof containerId, 'string'); assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof addon, 'object'); - assert.strictEqual(typeof containerId, 'string'); assert.strictEqual(typeof event, 'object'); const mailConfig = await getMailConfig(); @@ -239,7 +239,7 @@ async function oomEvent(mailTo, app, addon, containerId, event) { from: mailConfig.notificationFrom, to: mailTo, subject: `[${mailConfig.cloudronName}] ${app ? app.fqdn : addon.name} was restarted (OOM)`, - text: render('oom_event.ejs', templateData) + text: render('oom_event-text.ejs', templateData) }; await sendMail(mailOptions); @@ -256,7 +256,7 @@ async function backupFailed(mailTo, errorMessage, logUrl) { from: mailConfig.notificationFrom, to: mailTo, subject: `[${mailConfig.cloudronName}] Failed to backup`, - text: render('backup_failed.ejs', { cloudronName: mailConfig.cloudronName, message: errorMessage, logUrl, notificationsUrl: mailConfig.notificationsUrl }) + text: render('backup_failed-text.ejs', { cloudronName: mailConfig.cloudronName, message: errorMessage, logUrl, notificationsUrl: mailConfig.notificationsUrl }) }; await sendMail(mailOptions); @@ -273,7 +273,7 @@ async function certificateRenewalError(mailTo, domain, message) { from: mailConfig.notificationFrom, to: mailTo, subject: `[${mailConfig.cloudronName}] Certificate renewal error`, - text: render('certificate_renewal_error.ejs', { domain, message, notificationsUrl: mailConfig.notificationsUrl }) + text: render('certificate_renewal_error-text.ejs', { domain, message, notificationsUrl: mailConfig.notificationsUrl }) }; await sendMail(mailOptions); @@ -290,7 +290,7 @@ async function sendTestMail(domain, email) { from: `"${mailConfig.cloudronName}" `, to: email, subject: `[${mailConfig.cloudronName}] Test Email`, - text: render('test.ejs', { cloudronName: mailConfig.cloudronName}), + text: render('test-text.ejs', { cloudronName: mailConfig.cloudronName}), html: render('test-html.ejs', { cloudronName: mailConfig.cloudronName }) }; diff --git a/src/notifications.js b/src/notifications.js index 59d477c3a..6cc08d997 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -63,6 +63,8 @@ async function add(type, title, message, data) { assert.strictEqual(typeof message, 'string'); assert.strictEqual(typeof data, 'object'); + debug(`add: ${type} ${title}`); + const query = 'INSERT INTO notifications (type, title, message, acknowledged, eventId, context) VALUES (?, ?, ?, ?, ?, ?)'; const args = [ type, title, message, false, data?.eventId || null, data.context || '' ]; @@ -137,7 +139,7 @@ async function list(filters, page, perPage) { return results; } -async function oomEvent(eventId, containerId, app, addonName /*, event*/) { +async function oomEvent(eventId, containerId, app, addonName, event) { assert.strictEqual(typeof eventId, 'string'); assert.strictEqual(typeof containerId, 'string'); assert.strictEqual(typeof app, 'object'); @@ -164,7 +166,7 @@ async function oomEvent(eventId, containerId, app, addonName /*, event*/) { const admins = await users.getAdmins(); for (const admin of admins) { if (admin.notificationConfig.includes(exports.TYPE_APP_OOM)) { - await mailer.oomEvent(admin.email, app, addonName); + await mailer.oomEvent(admin.email, containerId, app, addonName, event); } } } @@ -176,7 +178,7 @@ async function appUp(eventId, app) { const admins = await users.getAdmins(); for (const admin of admins) { if (admin.notificationConfig.includes(exports.TYPE_APP_UP)) { - await mailer.appDown(admin.email, app); + await mailer.appUp(admin.email, app); } } }