Files
jabali-panel/app/Services/AdminNotificationService.php
2026-01-24 19:36:46 +02:00

186 lines
6.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\DnsSetting;
use App\Models\NotificationLog;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class AdminNotificationService
{
public static function send(string $type, string $subject, string $message, array $context = []): bool
{
$settingKey = "notify_{$type}";
if (!DnsSetting::get($settingKey, true)) {
// Log as skipped (notification type disabled)
self::logNotification($type, $subject, $message, [], 'skipped', $context, 'Notification type disabled');
return false;
}
$recipients = DnsSetting::get('admin_email_recipients', '');
if (empty($recipients)) {
Log::warning("AdminNotification: No recipients configured for {$type}");
self::logNotification($type, $subject, $message, [], 'skipped', $context, 'No recipients configured');
return false;
}
$recipientList = array_filter(array_map('trim', explode(',', $recipients)));
if (empty($recipientList)) {
self::logNotification($type, $subject, $message, [], 'skipped', $context, 'No valid recipients');
return false;
}
try {
$hostname = gethostname();
$fullMessage = $message . "\n\n---\n" .
"Server: {$hostname}\n" .
"Time: " . now()->format('Y-m-d H:i:s') . "\n";
if (!empty($context)) {
$fullMessage .= "\nDetails:\n";
foreach ($context as $key => $value) {
$fullMessage .= "- {$key}: {$value}\n";
}
}
$sender = "webmaster@{$hostname}";
Mail::raw($fullMessage, function ($mail) use ($recipientList, $sender, $subject, $hostname) {
$mail->from($sender, "Jabali Panel ({$hostname})");
$mail->to($recipientList);
$mail->subject("[Jabali] {$subject}");
});
Log::info("AdminNotification sent: {$type} - {$subject}");
self::logNotification($type, $subject, $message, $recipientList, 'sent', $context);
return true;
} catch (\Exception $e) {
Log::error("AdminNotification failed: {$e->getMessage()}");
self::logNotification($type, $subject, $message, $recipientList, 'failed', $context, $e->getMessage());
return false;
}
}
/**
* Log a notification to the database.
*/
protected static function logNotification(
string $type,
string $subject,
string $message,
array $recipients,
string $status,
?array $context = null,
?string $error = null
): void {
try {
NotificationLog::log($type, $subject, $message, $recipients, $status, $context, $error);
} catch (\Exception $e) {
// Don't let logging failures break the notification system
Log::error("Failed to log notification: {$e->getMessage()}");
}
}
public static function sslError(string $domain, string $error): bool
{
return self::send(
'ssl_errors',
"SSL Certificate Error: {$domain}",
"An SSL certificate error occurred for domain: {$domain}",
['Domain' => $domain, 'Error' => $error]
);
}
public static function sslExpiring(string $domain, int $daysUntilExpiry): bool
{
return self::send(
'ssl_errors',
"SSL Certificate Expiring: {$domain}",
"The SSL certificate for {$domain} will expire in {$daysUntilExpiry} days.",
['Domain' => $domain, 'Days Until Expiry' => $daysUntilExpiry]
);
}
public static function backupFailure(string $backupName, string $error): bool
{
return self::send(
'backup_failures',
"Backup Failed: {$backupName}",
"A scheduled backup has failed.",
['Backup Name' => $backupName, 'Error' => $error]
);
}
public static function backupSuccess(string $backupName, int $sizeBytes, ?string $destination = null): bool
{
$size = self::formatBytes($sizeBytes);
$context = [
'Backup Name' => $backupName,
'Size' => $size,
];
if ($destination) {
$context['Destination'] = $destination;
}
return self::send(
'backup_success',
"Backup Completed: {$backupName}",
"A backup has completed successfully.",
$context
);
}
protected static function formatBytes(int $bytes, int $precision = 2): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
public static function diskQuotaWarning(string $username, int $usagePercent): bool
{
return self::send(
'disk_quota',
"Disk Quota Warning: {$username}",
"User {$username} has reached {$usagePercent}% of their disk quota.",
['Username' => $username, 'Usage' => "{$usagePercent}%"]
);
}
public static function loginFailure(string $ip, string $service, int $attempts): bool
{
return self::send(
'login_failures',
"Login Failure Alert: {$ip}",
"Multiple failed login attempts detected.",
['IP Address' => $ip, 'Service' => $service, 'Attempts' => $attempts]
);
}
public static function systemUpdatesAvailable(int $updateCount): bool
{
return self::send(
'system_updates',
"System Updates Available",
"{$updateCount} system update(s) are available for your Jabali Panel.",
['Available Updates' => $updateCount]
);
}
public static function sshLogin(string $username, string $ip, string $method = 'password'): bool
{
return self::send(
'ssh_logins',
"SSH Login: {$username}",
"Successful SSH login detected.",
['Username' => $username, 'IP Address' => $ip, 'Auth Method' => $method]
);
}
}