notifications: send email when manual platform update is required
This commit is contained in:
@@ -3205,3 +3205,7 @@
|
||||
* passkey: implement passwordless login
|
||||
* oidcserver: fix jwks_rsaonly response
|
||||
|
||||
[9.1.6]
|
||||
* apps: fix wrong disabled state for devices config
|
||||
* notifications: send email when manual platform update required
|
||||
|
||||
|
||||
@@ -48,7 +48,9 @@
|
||||
"configure": "Nakonfigurovat",
|
||||
"restart": "Restartovat",
|
||||
"reset": "Zresetovat",
|
||||
"loadMore": "Načíst více"
|
||||
"loadMore": "Načíst více",
|
||||
"setup": "Nastavit",
|
||||
"disable": "Zakázat"
|
||||
},
|
||||
"rebootDialog": {
|
||||
"title": "Restart serveru",
|
||||
@@ -361,8 +363,23 @@
|
||||
},
|
||||
"twoFactorAuth": {
|
||||
"title": "Dvoufaktorová autentizace",
|
||||
"totpEnabled": "Použít časově omezené jednorázové heslo (TOTP)",
|
||||
"passkeyEnabled": "Použít passkey"
|
||||
"totpEnabled": "Povoleno",
|
||||
"passkeyEnabled": "Povoleno",
|
||||
"totpTitle": "TOTP",
|
||||
"passkeyTitle": "Passkey"
|
||||
},
|
||||
"notSet": "nenastaveno",
|
||||
"enablePasskey": {
|
||||
"title": "Povolit passkey"
|
||||
},
|
||||
"enableTotp": {
|
||||
"title": "Povolit TOTP"
|
||||
},
|
||||
"disableTotp": {
|
||||
"title": "Zakázat TOTP"
|
||||
},
|
||||
"disablePasskey": {
|
||||
"title": "Zakázat passkey"
|
||||
}
|
||||
},
|
||||
"backups": {
|
||||
@@ -648,7 +665,10 @@
|
||||
"stopUpdateAction": "Zastavit aktualizaci",
|
||||
"disabled": "Zakázáno",
|
||||
"onLatest": "poslední",
|
||||
"description": "Aktualizace platformy a aplikací se aplikují v nastavený čas podle <a href=\"/#/system-settings\">časové zóny systému</a>."
|
||||
"description": "Aktualizace se aplikují v nastavený čas podle <a href=\"/#/system-settings\">časové zóny systému</a>.",
|
||||
"config": "Automatické aktualizace",
|
||||
"platformAndApps": "Platforma a aplikace",
|
||||
"appsOnly": "Pouze aplikace"
|
||||
},
|
||||
"updateScheduleDialog": {
|
||||
"disableCheckbox": "Zakázat automatické aktualizace",
|
||||
@@ -673,6 +693,14 @@
|
||||
"registryConfig": {
|
||||
"provider": "Poskytovatel docker registrů",
|
||||
"providerOther": "Jiné"
|
||||
},
|
||||
"configureUpdates": {
|
||||
"title": "Konfigurovat automatické aktualizace",
|
||||
"policy": "Politika",
|
||||
"policyDescription": "Vyberte, co se bude automaticky aktualizovat",
|
||||
"days": "Dnů/y",
|
||||
"hours": "Hodin/y",
|
||||
"schedule": "Plány záloh"
|
||||
}
|
||||
},
|
||||
"branding": {
|
||||
@@ -1594,7 +1622,9 @@
|
||||
"errorIncorrect2FAToken": "Token 2FA je neplatný",
|
||||
"errorInternal": "Interní chyba, zkuste akci opakovat později",
|
||||
"loginAction": "Přihlásit se",
|
||||
"usePasskeyAction": "Použít passkey"
|
||||
"usePasskeyAction": "Použít passkey",
|
||||
"passkeyAction": "Přihlásit se přes passkey",
|
||||
"errorPasskeyFailed": "Přihlášení pomocí passkey selhalo"
|
||||
},
|
||||
"passwordReset": {
|
||||
"title": "Reset hesla",
|
||||
|
||||
@@ -912,7 +912,8 @@
|
||||
"rebootRequired": "Server reboot required",
|
||||
"cloudronUpdateFailed": "Cloudron update failed",
|
||||
"diskSpace": "Low disk space",
|
||||
"appAutoUpdateFailed": "App automatic update failed"
|
||||
"appAutoUpdateFailed": "App automatic update failed",
|
||||
"manualUpdateRequired": "Platform or app requires manual update"
|
||||
},
|
||||
"settingsDialog": {
|
||||
"description": "An email will be sent for the selected events to your primary email."
|
||||
|
||||
@@ -44,7 +44,9 @@
|
||||
"restart": "Mulai ulang",
|
||||
"reset": "Atur Ulang",
|
||||
"logs": "Log",
|
||||
"loadMore": "Muat lebih banyak"
|
||||
"loadMore": "Muat lebih banyak",
|
||||
"setup": "Siapkan",
|
||||
"disable": "Nonaktifkan"
|
||||
},
|
||||
"searchPlaceholder": "Cari",
|
||||
"actions": "Tindakan",
|
||||
@@ -361,8 +363,23 @@
|
||||
},
|
||||
"twoFactorAuth": {
|
||||
"title": "Autentikasi dua faktor",
|
||||
"totpEnabled": "Menggunakan kata sandi sekali pakai berbasis waktu (TOTP)",
|
||||
"passkeyEnabled": "Menggunakan passkey"
|
||||
"totpEnabled": "Diaktifkan",
|
||||
"passkeyEnabled": "Diaktifkan",
|
||||
"totpTitle": "TOTP",
|
||||
"passkeyTitle": "Passkey"
|
||||
},
|
||||
"notSet": "Belum diatur",
|
||||
"enablePasskey": {
|
||||
"title": "Aktifkan passkey"
|
||||
},
|
||||
"enableTotp": {
|
||||
"title": "Aktifkan TOTP"
|
||||
},
|
||||
"disableTotp": {
|
||||
"title": "Nonaktifkan TOTP"
|
||||
},
|
||||
"disablePasskey": {
|
||||
"title": "Nonaktifkan Passkey"
|
||||
}
|
||||
},
|
||||
"backups": {
|
||||
@@ -724,8 +741,11 @@
|
||||
"updateAvailableAction": "Pembaruan tersedia",
|
||||
"stopUpdateAction": "Hentikan pembaruan",
|
||||
"disabled": "Dinonaktifkan",
|
||||
"description": "Pembaruan platform dan aplikasi diterapkan sesuai jadwal yang telah dikonfigurasi, menggunakan <a href=\"/#/system-settings\">Zona waktu sistem</a>.",
|
||||
"onLatest": "terbaru"
|
||||
"description": "Pembaruan diterapkan sesuai jadwal yang telah dikonfigurasi, menggunakan <a href=\"/#/system-settings\">Zona waktu sistem</a>.",
|
||||
"onLatest": "terbaru",
|
||||
"config": "Pembaruan otomatis",
|
||||
"appsOnly": "Hanya aplikasi",
|
||||
"platformAndApps": "Platform & aplikasi"
|
||||
},
|
||||
"updateScheduleDialog": {
|
||||
"disableCheckbox": "Nonaktifkan pembaruan otomatis",
|
||||
@@ -750,6 +770,14 @@
|
||||
"registryConfig": {
|
||||
"provider": "Penyedia registri Docker",
|
||||
"providerOther": "Lainnya"
|
||||
},
|
||||
"configureUpdates": {
|
||||
"title": "Konfigurasi Pembaruan Otomatis",
|
||||
"policy": "Kebijakan",
|
||||
"policyDescription": "Pilih apa yang diperbarui secara otomatis",
|
||||
"days": "Hari",
|
||||
"hours": "Jam",
|
||||
"schedule": "Jadwal"
|
||||
}
|
||||
},
|
||||
"support": {
|
||||
@@ -1662,7 +1690,8 @@
|
||||
"appDown": "Aplikasi sedang tidak berfungsi",
|
||||
"rebootRequired": "Diperlukan menyalakan ulang server",
|
||||
"cloudronUpdateFailed": "Pembaruan Cloudron gagal",
|
||||
"diskSpace": "Ruang disk hampir penuh"
|
||||
"diskSpace": "Ruang disk hampir penuh",
|
||||
"appAutoUpdateFailed": "Pembaruan otomatis aplikasi gagal"
|
||||
},
|
||||
"settingsDialog": {
|
||||
"description": "E-mail akan dikirimkan ke e-mail utama Anda untuk acara-acara yang dipilih."
|
||||
@@ -1688,7 +1717,9 @@
|
||||
"errorIncorrect2FAToken": "Token 2FA tidak valid",
|
||||
"errorInternal": "Terjadi kesalahan internal, coba lagi nanti",
|
||||
"loginAction": "Masuk",
|
||||
"usePasskeyAction": "Gunakan passkey"
|
||||
"usePasskeyAction": "Gunakan passkey",
|
||||
"errorPasskeyFailed": "Gagal masuk dengan passkey",
|
||||
"passkeyAction": "Masuk dengan passkey"
|
||||
},
|
||||
"passwordReset": {
|
||||
"title": "Pengaturan ulang kata sandi",
|
||||
|
||||
@@ -47,7 +47,9 @@
|
||||
"configure": "Configureer",
|
||||
"restart": "Herstart",
|
||||
"reset": "Reset",
|
||||
"loadMore": "Laad meer"
|
||||
"loadMore": "Laad meer",
|
||||
"setup": "Instellen",
|
||||
"disable": "Uitschakelen"
|
||||
},
|
||||
"rebootDialog": {
|
||||
"title": "Herstart Server",
|
||||
@@ -361,8 +363,23 @@
|
||||
},
|
||||
"twoFactorAuth": {
|
||||
"title": "Twee-Factor (2FA) authenticatie",
|
||||
"totpEnabled": "Gebruikt tijdgebaseerd eenmalige wachtwoord (TOTP)",
|
||||
"passkeyEnabled": "Gebruikt passkey"
|
||||
"totpEnabled": "Ingeschakeld",
|
||||
"passkeyEnabled": "Ingeschakeld",
|
||||
"totpTitle": "TOTP",
|
||||
"passkeyTitle": "Passkey"
|
||||
},
|
||||
"notSet": "Niet ingesteld",
|
||||
"enablePasskey": {
|
||||
"title": "Passkey activeren"
|
||||
},
|
||||
"enableTotp": {
|
||||
"title": "TOTP activeren"
|
||||
},
|
||||
"disableTotp": {
|
||||
"title": "TOTP Uitschakelen"
|
||||
},
|
||||
"disablePasskey": {
|
||||
"title": "Passkey uitschakelen"
|
||||
}
|
||||
},
|
||||
"backups": {
|
||||
@@ -1141,9 +1158,12 @@
|
||||
"checkForUpdatesAction": "Controleer op updates",
|
||||
"updateAvailableAction": "Update beschikbaar",
|
||||
"stopUpdateAction": "Stop update",
|
||||
"description": "Platform en app updates worden toegepast met de geconfigureerde planning met deze <a href=\"/#/system-locale\">Systeem tijdzone</a>.",
|
||||
"description": "Updates worden toegepast volgens het geconfigureerde schema, met behulp van de <a href=\"/#/system-settings\">System time zone</a>.",
|
||||
"disabled": "Uitgeschakeld",
|
||||
"onLatest": "Laatste"
|
||||
"onLatest": "Laatste",
|
||||
"config": "Automatische updates",
|
||||
"appsOnly": "Alleen Apps",
|
||||
"platformAndApps": "Platform & Apps"
|
||||
},
|
||||
"updateScheduleDialog": {
|
||||
"disableCheckbox": "Automatische updates uitschakelen",
|
||||
@@ -1169,6 +1189,14 @@
|
||||
"registryConfig": {
|
||||
"provider": "Docker registry aanbieder",
|
||||
"providerOther": "Anders"
|
||||
},
|
||||
"configureUpdates": {
|
||||
"title": "Automatische updates configureren",
|
||||
"policy": "Beleid",
|
||||
"policyDescription": "Kies wat er automatisch wordt bijgewerkt",
|
||||
"days": "Dagen",
|
||||
"hours": "Uren",
|
||||
"schedule": "Planning"
|
||||
}
|
||||
},
|
||||
"support": {
|
||||
@@ -1226,7 +1254,8 @@
|
||||
"appDown": "App werkt niet",
|
||||
"rebootRequired": "Server herstart noodzakelijk",
|
||||
"cloudronUpdateFailed": "Cloudron update mislukt",
|
||||
"diskSpace": "Weinig diskruimte"
|
||||
"diskSpace": "Weinig diskruimte",
|
||||
"appAutoUpdateFailed": "Automatische update van de app is mislukt"
|
||||
},
|
||||
"settingsDialog": {
|
||||
"description": "Een e-mail wordt verstuurd voor de geselecteerde gebeurtenissen naar je primaire e-mail."
|
||||
@@ -1513,7 +1542,9 @@
|
||||
"errorIncorrect2FAToken": "2FA token is niet geldig",
|
||||
"errorInternal": "Interne fout, probeer later opnieuw",
|
||||
"loginAction": "Inloggen",
|
||||
"usePasskeyAction": "Gebruik een passkey"
|
||||
"usePasskeyAction": "Gebruik een passkey",
|
||||
"errorPasskeyFailed": "Inloggen met passkey mislukt",
|
||||
"passkeyAction": "Inloggen met een passkey"
|
||||
},
|
||||
"passwordReset": {
|
||||
"title": "Wachtwoord herstellen",
|
||||
|
||||
@@ -17,6 +17,7 @@ const appAutoUpdateFailed = ref(false);
|
||||
const certificateRenewalFailed = ref(false);
|
||||
const diskSpace = ref(false);
|
||||
const cloudronUpdateFailed = ref(false);
|
||||
const manualUpdateRequired = ref(false);
|
||||
const reboot = ref(false);
|
||||
|
||||
async function onSubmit() {
|
||||
@@ -31,6 +32,7 @@ async function onSubmit() {
|
||||
if (certificateRenewalFailed.value) config.push('certificateRenewalFailed');
|
||||
if (diskSpace.value) config.push('diskSpace');
|
||||
if (cloudronUpdateFailed.value) config.push('cloudronUpdateFailed');
|
||||
if (manualUpdateRequired.value) config.push('manualUpdateRequired');
|
||||
if (reboot.value) config.push('reboot');
|
||||
|
||||
const [error] = await profileModel.setNotificationConfig(config);
|
||||
@@ -55,6 +57,7 @@ async function open() {
|
||||
certificateRenewalFailed.value = config.indexOf('certificateRenewalFailed') !== -1;
|
||||
diskSpace.value = config.indexOf('diskSpace') !== -1;
|
||||
cloudronUpdateFailed.value = config.indexOf('cloudronUpdateFailed') !== -1;
|
||||
manualUpdateRequired.value = config.indexOf('manualUpdateRequired') !== -1;
|
||||
reboot.value = config.indexOf('reboot') !== -1;
|
||||
|
||||
dialogItem.value.open();
|
||||
@@ -121,6 +124,11 @@ defineExpose({
|
||||
<Switch v-model="cloudronUpdateFailed" :disabled="busy"/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem>
|
||||
<div>{{ $t('notifications.settings.manualUpdateRequired') }}</div>
|
||||
<Switch v-model="manualUpdateRequired" :disabled="busy"/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem>
|
||||
<div>{{ $t('notifications.settings.rebootRequired') }}</div>
|
||||
<Switch v-model="reboot" :disabled="busy"/>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
Dear <%= cloudronName %> Admin,
|
||||
|
||||
Cloudron v<%= version %> is available.
|
||||
|
||||
Changelog:
|
||||
<%- changelog %>
|
||||
|
||||
Go to the Updates view to update: <%= updateUrl %>
|
||||
|
||||
Powered by https://cloudron.io
|
||||
|
||||
Don't want such mails? Change your notification preferences at <%= notificationsUrl %>
|
||||
|
||||
Sent at: <%= new Date().toUTCString() %>
|
||||
@@ -322,6 +322,23 @@ async function boxUpdateError(mailTo, message) {
|
||||
await sendMail(mailOptions);
|
||||
}
|
||||
|
||||
async function boxManualUpdateRequired(mailTo, version, changelog) {
|
||||
assert.strictEqual(typeof mailTo, 'string');
|
||||
assert.strictEqual(typeof version, 'string');
|
||||
assert.strictEqual(typeof changelog, 'string');
|
||||
|
||||
const mailConfig = await getMailConfig();
|
||||
|
||||
const mailOptions = {
|
||||
from: mailConfig.notificationFrom,
|
||||
to: mailTo,
|
||||
subject: `[${mailConfig.cloudronName}] Cloudron v${version} is available`,
|
||||
text: render('box_manual_update_required-text.ejs', { cloudronName: mailConfig.cloudronName, version, changelog, updateUrl: `https://${mailConfig.dashboardFqdn}/#/system-update`, notificationsUrl: mailConfig.notificationsUrl })
|
||||
};
|
||||
|
||||
await sendMail(mailOptions);
|
||||
}
|
||||
|
||||
async function certificateRenewalError(mailTo, domain, message) {
|
||||
assert.strictEqual(typeof mailTo, 'string');
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
@@ -368,6 +385,7 @@ export default {
|
||||
appUp,
|
||||
oomEvent,
|
||||
rebootRequired,
|
||||
boxManualUpdateRequired,
|
||||
boxUpdateError,
|
||||
lowDiskSpace,
|
||||
sendTestMail,
|
||||
|
||||
+20
-3
@@ -29,6 +29,7 @@ const TYPE_UPDATE_UBUNTU = 'ubuntuUpdate';
|
||||
const TYPE_BOX_UPDATE = 'boxUpdate';
|
||||
const TYPE_MANUAL_APP_UPDATE_NEEDED = 'manualAppUpdate';
|
||||
const TYPE_APP_AUTO_UPDATE_FAILED = 'appAutoUpdateFailed';
|
||||
const TYPE_MANUAL_UPDATE_REQUIRED = 'manualUpdateRequired';
|
||||
|
||||
const NOTIFICATION_FIELDS = [ 'id', 'eventId', 'type', 'title', 'message', 'creationTime', 'acknowledged', 'context' ];
|
||||
|
||||
@@ -298,15 +299,30 @@ async function lowDiskSpace(message) {
|
||||
}
|
||||
}
|
||||
|
||||
async function onPin(type, message) {
|
||||
async function boxManualUpdateRequired(version, changelogText) {
|
||||
assert.strictEqual(typeof version, 'string');
|
||||
assert.strictEqual(typeof changelogText, 'string');
|
||||
|
||||
const admins = await users.getAdmins();
|
||||
for (const admin of admins) {
|
||||
if (admin.notificationConfig.includes(TYPE_MANUAL_UPDATE_REQUIRED)) {
|
||||
await safe(mailer.boxManualUpdateRequired(admin.email, version, changelogText), { debug: log });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onPin(type, message, options) {
|
||||
assert.strictEqual(typeof type, 'string'); // TYPE_
|
||||
assert.strictEqual(typeof message, 'string');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
switch (type) {
|
||||
case TYPE_REBOOT:
|
||||
return await rebootRequired();
|
||||
case TYPE_DISK_SPACE:
|
||||
return await lowDiskSpace(message);
|
||||
case TYPE_BOX_UPDATE:
|
||||
return await boxManualUpdateRequired(options.context, message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -320,7 +336,7 @@ async function pin(type, title, message, options) {
|
||||
|
||||
const result = await getByType(type, options.context || '');
|
||||
if (!result) {
|
||||
await onPin(type, message);
|
||||
await onPin(type, message, options);
|
||||
return await add(type, title, message, { eventId: null, context: options.context || '' });
|
||||
}
|
||||
|
||||
@@ -328,7 +344,7 @@ async function pin(type, title, message, options) {
|
||||
const isUpdateType = type === TYPE_BOX_UPDATE || type === TYPE_MANUAL_APP_UPDATE_NEEDED;
|
||||
const acknowledged = (isUpdateType && result.message === message) ? result.acknowledged : false;
|
||||
|
||||
if (result.acknowledged && !acknowledged) await onPin(type, message);
|
||||
if (result.acknowledged && !acknowledged) await onPin(type, message, options);
|
||||
|
||||
await update(result, { title, message, acknowledged, creationTime: new Date() });
|
||||
return result.id;
|
||||
@@ -412,6 +428,7 @@ export default {
|
||||
TYPE_BOX_UPDATE,
|
||||
TYPE_MANUAL_APP_UPDATE_NEEDED,
|
||||
TYPE_APP_AUTO_UPDATE_FAILED,
|
||||
TYPE_MANUAL_UPDATE_REQUIRED,
|
||||
TYPE_DOMAIN_CONFIG_CHECK_FAILED,
|
||||
pin,
|
||||
unpin,
|
||||
|
||||
Reference in New Issue
Block a user