feat: ntfy push notification support per backup profile
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 10s
Universal: Auto Version Bump / Version Bump (push) Successful in 15s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 10s
Universal: Auto Version Bump / Version Bump (push) Successful in 15s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 10s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Add ntfy (https://ntfy.sh) push notifications alongside email. Each backup profile can configure its own ntfy topic, server, and access token independently. - New profile fields: ntfy_topic, ntfy_server (default ntfy.sh), ntfy_token (optional, for private topics) - NotificationSender sends both email and ntfy in parallel - Uses priority 5 (urgent) for failures, 3 (default) for success - Includes backup status emoji, profile name, type, archive, size - 10-second timeout to prevent blocking backup completion - SQL migration 01.18.00 adds columns to profiles table
This commit is contained in:
@@ -215,6 +215,37 @@
|
||||
<option value="1">JYES</option>
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
<field
|
||||
name="ntfy_spacer"
|
||||
type="note"
|
||||
label=""
|
||||
description="COM_MOKOJOOMBACKUP_FIELD_NTFY_SPACER_DESC"
|
||||
class="alert alert-light border"
|
||||
/>
|
||||
<field
|
||||
name="ntfy_topic"
|
||||
type="text"
|
||||
label="COM_MOKOJOOMBACKUP_FIELD_NTFY_TOPIC"
|
||||
description="COM_MOKOJOOMBACKUP_FIELD_NTFY_TOPIC_DESC"
|
||||
maxlength="255"
|
||||
hint="my-backups"
|
||||
/>
|
||||
<field
|
||||
name="ntfy_server"
|
||||
type="url"
|
||||
label="COM_MOKOJOOMBACKUP_FIELD_NTFY_SERVER"
|
||||
description="COM_MOKOJOOMBACKUP_FIELD_NTFY_SERVER_DESC"
|
||||
maxlength="512"
|
||||
default="https://ntfy.sh"
|
||||
hint="https://ntfy.sh"
|
||||
/>
|
||||
<field
|
||||
name="ntfy_token"
|
||||
type="password"
|
||||
label="COM_MOKOJOOMBACKUP_FIELD_NTFY_TOKEN"
|
||||
description="COM_MOKOJOOMBACKUP_FIELD_NTFY_TOKEN_DESC"
|
||||
maxlength="255"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
<fieldset name="ftp" label="COM_MOKOJOOMBACKUP_FIELDSET_FTP">
|
||||
|
||||
@@ -197,6 +197,13 @@ COM_MOKOJOOMBACKUP_FIELD_NOTIFY_SUCCESS="Notify on Success"
|
||||
COM_MOKOJOOMBACKUP_FIELD_NOTIFY_SUCCESS_DESC="Send an email when a backup completes successfully."
|
||||
COM_MOKOJOOMBACKUP_FIELD_NOTIFY_FAILURE="Notify on Failure"
|
||||
COM_MOKOJOOMBACKUP_FIELD_NOTIFY_FAILURE_DESC="Send an email when a backup fails. Includes log excerpt for debugging."
|
||||
COM_MOKOJOOMBACKUP_FIELD_NTFY_SPACER_DESC="<strong>Push Notifications (ntfy)</strong> — Send instant push notifications to your phone or desktop via <a href='https://ntfy.sh' target='_blank'>ntfy.sh</a> or a self-hosted ntfy server."
|
||||
COM_MOKOJOOMBACKUP_FIELD_NTFY_TOPIC="ntfy Topic"
|
||||
COM_MOKOJOOMBACKUP_FIELD_NTFY_TOPIC_DESC="The ntfy topic to publish notifications to. Leave blank to disable push notifications."
|
||||
COM_MOKOJOOMBACKUP_FIELD_NTFY_SERVER="ntfy Server"
|
||||
COM_MOKOJOOMBACKUP_FIELD_NTFY_SERVER_DESC="URL of the ntfy server. Default is the public ntfy.sh service. Use your own server URL for self-hosted instances."
|
||||
COM_MOKOJOOMBACKUP_FIELD_NTFY_TOKEN="Access Token"
|
||||
COM_MOKOJOOMBACKUP_FIELD_NTFY_TOKEN_DESC="Optional access token for private ntfy topics. Leave blank for public topics."
|
||||
|
||||
; Integrity verification
|
||||
COM_MOKOJOOMBACKUP_TOOLBAR_VERIFY="Verify Integrity"
|
||||
|
||||
@@ -36,6 +36,9 @@ CREATE TABLE IF NOT EXISTS `#__mokosuitebackup_profiles` (
|
||||
`notify_user_groups` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Comma-separated Joomla user group IDs',
|
||||
`notify_on_success` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`notify_on_failure` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`ntfy_topic` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'ntfy topic name',
|
||||
`ntfy_server` VARCHAR(512) NOT NULL DEFAULT 'https://ntfy.sh' COMMENT 'ntfy server URL',
|
||||
`ntfy_token` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'ntfy access token (optional)',
|
||||
`published` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`ordering` INT(11) NOT NULL DEFAULT 0,
|
||||
`created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Add ntfy push notification fields to backup profiles
|
||||
ALTER TABLE `#__mokosuitebackup_profiles`
|
||||
ADD COLUMN `ntfy_topic` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'ntfy topic name' AFTER `notify_on_failure`,
|
||||
ADD COLUMN `ntfy_server` VARCHAR(512) NOT NULL DEFAULT 'https://ntfy.sh' COMMENT 'ntfy server URL' AFTER `ntfy_topic`,
|
||||
ADD COLUMN `ntfy_token` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'ntfy access token (optional, for private topics)' AFTER `ntfy_server`;
|
||||
@@ -32,6 +32,14 @@ class NotificationSender
|
||||
* @return bool True if email was sent
|
||||
*/
|
||||
public static function send(object $profile, object $record, bool $success, string $logText = ''): bool
|
||||
{
|
||||
$emailSent = self::sendEmail($profile, $record, $success, $logText);
|
||||
$ntfySent = self::sendNtfy($profile, $record, $success);
|
||||
|
||||
return $emailSent || $ntfySent;
|
||||
}
|
||||
|
||||
private static function sendEmail(object $profile, object $record, bool $success, string $logText = ''): bool
|
||||
{
|
||||
$notifyEmail = trim($profile->notify_email ?? '');
|
||||
$notifyUserGroups = $profile->notify_user_groups ?? '';
|
||||
@@ -139,6 +147,89 @@ class NotificationSender
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a push notification via ntfy.
|
||||
*/
|
||||
private static function sendNtfy(object $profile, object $record, bool $success): bool
|
||||
{
|
||||
$topic = trim($profile->ntfy_topic ?? '');
|
||||
$server = trim($profile->ntfy_server ?? 'https://ntfy.sh');
|
||||
$token = trim($profile->ntfy_token ?? '');
|
||||
|
||||
if ($topic === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Respect the same success/failure preferences as email
|
||||
if ($success && empty($profile->notify_on_success)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$success && empty($profile->notify_on_failure)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$config = Factory::getApplication()->getConfig();
|
||||
$siteName = $config->get('sitename', 'Joomla Site');
|
||||
|
||||
$statusLabel = $success ? 'SUCCESS' : 'FAILED';
|
||||
$statusEmoji = $success ? "\xE2\x9C\x85" : "\xE2\x9D\x8C";
|
||||
|
||||
$sizeHuman = $record->total_size > 0
|
||||
? number_format($record->total_size / 1048576, 2) . ' MB'
|
||||
: 'N/A';
|
||||
|
||||
$title = "{$statusEmoji} Backup {$statusLabel}: {$siteName}";
|
||||
$body = "Profile: {$profile->title}\n"
|
||||
. "Type: {$record->backup_type}\n"
|
||||
. "Archive: {$record->archivename}\n"
|
||||
. "Size: {$sizeHuman}";
|
||||
|
||||
$url = rtrim($server, '/') . '/' . rawurlencode($topic);
|
||||
|
||||
$headers = [
|
||||
'Title: ' . $title,
|
||||
'Priority: ' . ($success ? '3' : '5'),
|
||||
'Tags: ' . ($success ? 'white_check_mark' : 'rotating_light'),
|
||||
];
|
||||
|
||||
if ($token !== '') {
|
||||
$headers[] = 'Authorization: Bearer ' . $token;
|
||||
}
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
CURLOPT_CONNECTTIMEOUT => 5,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error !== '') {
|
||||
error_log('MokoSuiteBackup: ntfy error: ' . $error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
error_log('MokoSuiteBackup: ntfy returned HTTP ' . $httpCode . ': ' . $response);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
error_log('MokoSuiteBackup: ntfy notification error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve user group IDs to email addresses of group members.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user