notifications: add low disk space notification
This commit is contained in:
@@ -2940,4 +2940,5 @@
|
||||
* sendmail: requiresValidCertificate option for using mail server domain
|
||||
* mail: update haraka to 3.1.0
|
||||
* sshfs: implement rm via ssh
|
||||
* notification: low disk notification
|
||||
|
||||
|
||||
@@ -1159,7 +1159,8 @@
|
||||
"appUp": "App is back online",
|
||||
"appDown": "App is down",
|
||||
"rebootRequired": "Server reboot required",
|
||||
"cloudronUpdateFailed": "Cloudron Update Failed"
|
||||
"cloudronUpdateFailed": "Cloudron Update Failed",
|
||||
"diskSpace": "Low Disk Space"
|
||||
},
|
||||
"settingsDialog": {
|
||||
"description": "Manage your personal notification preferences here. Cloudron will send an email for the selected events to your primary email address."
|
||||
|
||||
@@ -1201,7 +1201,9 @@
|
||||
"title": "Herstel {{ app }}",
|
||||
"restoreAction": "Herstel",
|
||||
"warning": "Alle gegevens tussen nu en de laatst bekende backup zullen onherstelbaar verloren gaan. Het is aanbevolen om eerst handmatig een backup te maken van de gegevens vóór het herstellen.",
|
||||
"description": "Hierdoor zal deze app worden hersteld met de gegevens van {{ creationTime }}."
|
||||
"description": "Hierdoor zal deze app worden hersteld met de gegevens van {{ creationTime }}.",
|
||||
"cloneAction": "Kloon",
|
||||
"cloneActionOverwrite": "Kloon en overschrijf DNS"
|
||||
},
|
||||
"cloneDialog": {
|
||||
"title": "Kloon {{ app }}",
|
||||
|
||||
@@ -368,7 +368,7 @@
|
||||
"body": "Письмо отправлено на {{ email }}"
|
||||
},
|
||||
"exposedLdap": {
|
||||
"title": "Сервер LDAP",
|
||||
"title": "Поставщик LDAP",
|
||||
"ipRestriction": {
|
||||
"description": "Ограничьте доступ к серверу каталогов только для определённого круга IP-адресов и диапазонов. Строки, начинающиеся с <code>#</code>, будут считаться комментарием.",
|
||||
"placeholder": "IP-адреса или подсети, разделённые строками",
|
||||
@@ -545,7 +545,7 @@
|
||||
"updates": {
|
||||
"info": {
|
||||
"customAppUpdateInfo": "Для сторонних приложений автообновления недоступны.",
|
||||
"updateAvailableAction": "Доступно обновление",
|
||||
"updateAvailableAction": "Доступно Обновление",
|
||||
"title": "Данные приложения",
|
||||
"description": "Название и версия приложения",
|
||||
"appId": "ID приложения",
|
||||
@@ -707,7 +707,7 @@
|
||||
"running": "Запущено",
|
||||
"stopped": "Остановлено",
|
||||
"notResponding": "Не отвечает",
|
||||
"updateAvailable": "Доступно обновление"
|
||||
"updateAvailable": "Доступно Обновление"
|
||||
},
|
||||
"display": {
|
||||
"tags": "Метки",
|
||||
@@ -839,7 +839,9 @@
|
||||
"title": "Восстановить {{ app }}",
|
||||
"restoreAction": "Восстановить",
|
||||
"description": "Данное действие восстановит данные приложения от {{ creationTime }}.",
|
||||
"warning": "Любые данные, созданные между настоящим моментом и последней известной резервной копией будут безвозвратно утеряны. Рекомендуем создать резервную копию текущих данных перед восстановлением."
|
||||
"warning": "Любые данные, созданные между настоящим моментом и последней известной резервной копией будут безвозвратно утеряны. Рекомендуем создать резервную копию текущих данных перед восстановлением.",
|
||||
"cloneAction": "Клонировать",
|
||||
"cloneActionOverwrite": "Клонировать и перезаписать DNS"
|
||||
},
|
||||
"cloneDialog": {
|
||||
"title": "Клонировать {{ app }}",
|
||||
@@ -890,7 +892,8 @@
|
||||
"archiveDialog": {
|
||||
"title": "Архивирование {{app}}",
|
||||
"description": "Это действие удалит приложение и поместит его последнюю резервную копию от {{date}} в Архив."
|
||||
}
|
||||
},
|
||||
"updateAvailableTooltip": "Доступно обновление"
|
||||
},
|
||||
"backups": {
|
||||
"location": {
|
||||
@@ -1049,7 +1052,7 @@
|
||||
"title": "Подвал",
|
||||
"description": "Используйте разметку Markdown для стилизации подвала.",
|
||||
"subscriptionRequired": "Настройка подвала доступна только в платной подписке.",
|
||||
"setupSubscriptionNow": "Настроить подписку"
|
||||
"setupSubscriptionNow": "Оформить подписку"
|
||||
},
|
||||
"changeLogo": {
|
||||
"title": "Выбрать изображение Cloudron"
|
||||
@@ -1273,7 +1276,7 @@
|
||||
"showLogsAction": "Показать логи",
|
||||
"changeScheduleAction": "Изменить расписание",
|
||||
"checkForUpdatesAction": "Проверить обновления",
|
||||
"updateAvailableAction": "Обновление доступно",
|
||||
"updateAvailableAction": "Доступно Обновление",
|
||||
"version": "Версия платформы",
|
||||
"stopUpdateAction": "Остановить обновление",
|
||||
"description": "Обновления платформы и приложений применяются автоматически, на основании Расписания в <a href=\"/#/settings\">Системной часовой зоне</a>.",
|
||||
@@ -1284,7 +1287,7 @@
|
||||
"title": "Частный реестр Docker",
|
||||
"description": "Cloudron может извлекать образ и устанавливать <a href=\"{{ customAppsLink }}\" target=\"_blank\">сторонние приложения</a> из частного реестра Docker.",
|
||||
"subscriptionRequired": "Данная функция доступна только в платной подписке.",
|
||||
"setupSubscriptionAction": "Настроить подписку",
|
||||
"setupSubscriptionAction": "Оформить подписку",
|
||||
"server": "Адрес сервера",
|
||||
"username": "Имя пользователя",
|
||||
"configureAction": "Настроить реестр",
|
||||
@@ -2020,7 +2023,7 @@
|
||||
"newClient": "Новый Клиент",
|
||||
"empty": "Клиенты не найдены"
|
||||
},
|
||||
"title": "Поставщик OpenID Сonnect",
|
||||
"title": "Поставщик OpenID",
|
||||
"description": "Cloudron может выступать в качестве поставщика OpenID connect для внутренних приложений и внешних сервисов.",
|
||||
"editClientDialog": {
|
||||
"title": "Редактировать клиента {{ client }}"
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
<input type="checkbox" ng-model="settings.config.certificateRenewalFailed"> {{ 'notifications.settings.certificateRenewalFailed' | tr }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="settings.config.diskSpace"> {{ 'notifications.settings.diskSpace' | tr }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="settings.config.cloudronUpdateFailed"> {{ 'notifications.settings.cloudronUpdateFailed' | tr }}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
Dear Cloudron Admin,
|
||||
|
||||
<%- message %>
|
||||
|
||||
Powered by https://cloudron.io
|
||||
|
||||
Don't want such mails? Change your notification preferences at <%= notificationsUrl %>
|
||||
|
||||
Sent at: <%= new Date().toUTCString() %>
|
||||
@@ -13,6 +13,7 @@ exports = module.exports = {
|
||||
oomEvent,
|
||||
rebootRequired,
|
||||
boxUpdateError,
|
||||
lowDiskSpace,
|
||||
|
||||
sendTestMail,
|
||||
|
||||
@@ -279,6 +280,22 @@ async function rebootRequired(mailTo) {
|
||||
await sendMail(mailOptions);
|
||||
}
|
||||
|
||||
async function lowDiskSpace(mailTo, message) {
|
||||
assert.strictEqual(typeof mailTo, 'string');
|
||||
assert.strictEqual(typeof message, 'string');
|
||||
|
||||
const mailConfig = await getMailConfig();
|
||||
|
||||
const mailOptions = {
|
||||
from: mailConfig.notificationFrom,
|
||||
to: mailTo,
|
||||
subject: `[${mailConfig.cloudronName}] Server is running low on disk space`,
|
||||
text: render('low_disk_space-text.ejs', { message, notificationsUrl: mailConfig.notificationsUrl })
|
||||
};
|
||||
|
||||
await sendMail(mailOptions);
|
||||
}
|
||||
|
||||
async function boxUpdateError(mailTo, message) {
|
||||
assert.strictEqual(typeof mailTo, 'string');
|
||||
assert.strictEqual(typeof message, 'string');
|
||||
|
||||
+17
-3
@@ -287,12 +287,26 @@ async function rebootRequired() {
|
||||
}
|
||||
}
|
||||
|
||||
async function onPin(type) {
|
||||
async function lowDiskSpace(message) {
|
||||
assert.strictEqual(typeof message, 'string');
|
||||
|
||||
const admins = await users.getAdmins();
|
||||
for (const admin of admins) {
|
||||
if (admin.notificationConfig.includes(exports.TYPE_DISK_SPACE)) {
|
||||
await mailer.lowDiskSpace(admin.email, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onPin(type, message) {
|
||||
assert.strictEqual(typeof type, 'string'); // TYPE_
|
||||
assert.strictEqual(typeof message, 'string');
|
||||
|
||||
switch (type) {
|
||||
case exports.TYPE_REBOOT:
|
||||
return await rebootRequired();
|
||||
case exports.TYPE_DISK_SPACE:
|
||||
return await lowDiskSpace(message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -306,7 +320,7 @@ async function pin(type, title, message, options) {
|
||||
|
||||
const result = await getByType(type, options.context || '');
|
||||
if (!result) {
|
||||
await onPin(type);
|
||||
await onPin(type, message);
|
||||
return await add(type, title, message, { eventId: null, context: options.context || '' });
|
||||
}
|
||||
|
||||
@@ -314,7 +328,7 @@ async function pin(type, title, message, options) {
|
||||
const isUpdateType = type === exports.TYPE_BOX_UPDATE || type === exports.TYPE_MANUAL_APP_UPDATE_NEEDED;
|
||||
const acknowledged = (isUpdateType && result.message === message) ? result.acknowledged : false;
|
||||
|
||||
if (result.acknowledged && !acknowledged) await onPin(type);
|
||||
if (result.acknowledged && !acknowledged) await onPin(type, message);
|
||||
|
||||
await update(result, { title, message, acknowledged, creationTime: new Date() });
|
||||
return result.id;
|
||||
|
||||
+7
-4
@@ -189,15 +189,18 @@ async function checkDiskSpace() {
|
||||
const filesystem = filesystems[fsPath];
|
||||
if (filesystem.contents.length === 0) continue; // ignore if nothing interesting here
|
||||
|
||||
if (filesystem.available <= (1.25 * 1024 * 1024 * 1024)) { // 1.5G
|
||||
markdownMessage += `* ${filesystem.filesystem} is at ${filesystem.capacity*100}% capacity.\n`;
|
||||
if (filesystem.capacity >= 0.90) { // > 90%
|
||||
const prettyUsed = df.prettyBytes(filesystem.used),
|
||||
prettyAvailable = df.prettyBytes(filesystem.available),
|
||||
prettySize = df.prettySize(filesystem.size);
|
||||
markdownMessage += `* ${filesystem.filesystem}(${filesystem.type}) is at ${filesystem.capacity*100}% capacity. Used: ${prettyUsed} Available: ${prettyAvailable} Size: ${prettySize}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
debug(`checkDiskSpace: disk space checked. out of space: ${markdownMessage || 'no'}`);
|
||||
debug(`checkDiskSpace: disk space checked. low disk space: ${markdownMessage || 'no'}`);
|
||||
|
||||
if (markdownMessage) {
|
||||
const finalMessage = `One or more file systems are running out of space. Please increase the disk size at the earliest.\n\n${markdownMessage}`;
|
||||
const finalMessage = `One or more file systems are running low on space. Please increase the disk size at the earliest.\n\n${markdownMessage}`;
|
||||
await notifications.pin(notifications.TYPE_DISK_SPACE, 'Server is running out of disk space', finalMessage, {});
|
||||
} else {
|
||||
await notifications.unpin(notifications.TYPE_DISK_SPACE, {});
|
||||
|
||||
Reference in New Issue
Block a user