some progress

This commit is contained in:
Doar Mihai
2026-01-24 12:16:10 +02:00
parent 3665a1d5a3
commit 612cc097f8
51 changed files with 8089 additions and 470 deletions
+646
View File
@@ -0,0 +1,646 @@
# Multi-Channel Notifications Implementation Guide
This guide provides a complete implementation for multi-channel monitoring alerts in your Laravel application.
## Overview
The notification system supports the following channels:
- **Email** (built-in Laravel)
- **Database** (built-in Laravel)
- **Slack** (via webhook)
- **Telegram** (via Bot API)
- **Discord** (via webhook)
- **Microsoft Teams** (via webhook)
- **Google Chat** (via webhook)
- **Custom Webhook** (generic HTTP POST)
- **SMS** (via Twilio/Vonage - requires additional setup)
- **Opsgenie** (via API)
- **PagerDuty** (via Events API)
## Installation Complete
The following packages have been installed:
```bash
composer require laravel/slack-notification-channel
composer require laravel-notification-channels/telegram
composer require laravel-notification-channels/discord
```
## Database Schema
Migration created: `2026_01_18_123539_add_notification_channels_to_users_table.php`
Fields added to `users` table:
- `notification_channels` (JSON) - Stores enabled channels per alert type
- `slack_webhook_url` - Slack incoming webhook URL
- `telegram_chat_id` - Telegram bot chat ID
- `discord_webhook_url` - Discord webhook URL
- `teams_webhook_url` - Microsoft Teams webhook URL
- `google_chat_webhook_url` - Google Chat webhook URL
- `webhook_url` - Custom webhook endpoint
- `sms_phone_number` - Phone number for SMS alerts
- `opsgenie_api_key` - Opsgenie API key
- `pagerduty_integration_key` - PagerDuty integration key
## Alert Types
The system supports these alert types:
- `uptime` - Site down/up alerts
- `performance` - Performance threshold alerts
- `ssl` - SSL certificate expiration
- `dns` - DNS record changes
- `broken_links` - Broken link detection
- `domain_expiry` - Domain expiration warnings
- `sitemap` - Sitemap validation issues
- `health_check` - Health endpoint failures
- `tcp_port` - TCP port monitoring
## Base Notification Class
Create `app/Notifications/MonitorAlert.php`:
```php
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\Discord\DiscordChannel;
use NotificationChannels\Discord\DiscordMessage;
use NotificationChannels\Telegram\TelegramChannel;
use NotificationChannels\Telegram\TelegramMessage;
abstract class MonitorAlert extends Notification implements ShouldQueue
{
use Queueable;
public string $alertType;
public string $siteName;
public string $message;
public array $data;
public string $severity; // info, warning, critical
public function __construct(string $alertType, string $siteName, string $message, array $data = [], string $severity = 'warning')
{
$this->alertType = $alertType;
$this->siteName = $siteName;
$this->message = $message;
$this->data = $data;
$this->severity = $severity;
}
/**
* Get the notification's delivery channels.
*/
public function via($notifiable): array
{
$enabledChannels = $notifiable->getNotificationChannelsFor($this->alertType);
$channelMap = [
'email' => 'mail',
'database' => 'database',
'slack' => 'slack',
'telegram' => TelegramChannel::class,
'discord' => DiscordChannel::class,
];
$channels = [];
foreach ($enabledChannels as $channel) {
if (isset($channelMap[$channel])) {
$channels[] = $channelMap[$channel];
}
}
return $channels ?: ['database'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail($notifiable): MailMessage
{
$color = $this->getColorForSeverity();
return (new MailMessage)
->subject("[{$this->getSeverityLabel()}] {$this->siteName} - {$this->getAlertTypeLabel()}")
->greeting("Alert: {$this->siteName}")
->line($this->message)
->when(!empty($this->data), function ($mail) {
foreach ($this->data as $key => $value) {
$mail->line("**{$key}:** {$value}");
}
})
->action('View Dashboard', url('/dashboard'))
->line('Timestamp: ' . now()->format('Y-m-d H:i:s T'));
}
/**
* Get the Slack representation of the notification.
*/
public function toSlack($notifiable): SlackMessage
{
$color = $this->getColorForSeverity();
$message = (new SlackMessage)
->from('Site Monitor', ':robot_face:')
->to('#monitoring')
->attachment(function ($attachment) use ($color) {
$attachment->title($this->siteName, url('/dashboard'))
->content($this->message)
->color($color)
->timestamp(now());
foreach ($this->data as $key => $value) {
$attachment->field($key, $value, true);
}
});
return $message;
}
/**
* Get the Telegram representation of the notification.
*/
public function toTelegram($notifiable): TelegramMessage
{
$emoji = $this->getEmojiForSeverity();
$text = "{$emoji} **{$this->getSeverityLabel()} Alert**\n\n";
$text .= "**Site:** {$this->siteName}\n";
$text .= "**Type:** {$this->getAlertTypeLabel()}\n\n";
$text .= "{$this->message}\n\n";
if (!empty($this->data)) {
$text .= "**Details:**\n";
foreach ($this->data as $key => $value) {
$text .= "• {$key}: {$value}\n";
}
}
$text .= "\n_" . now()->format('Y-m-d H:i:s T') . "_";
return TelegramMessage::create()
->to($notifiable->telegram_chat_id)
->content($text)
->button('View Dashboard', url('/dashboard'));
}
/**
* Get the Discord representation of the notification.
*/
public function toDiscord($notifiable): DiscordMessage
{
$color = $this->getDiscordColorForSeverity();
return DiscordMessage::create()
->embed(function ($embed) use ($color) {
$embed->title("{$this->siteName} - {$this->getAlertTypeLabel()}")
->description($this->message)
->color($color)
->timestamp(now());
foreach ($this->data as $key => $value) {
$embed->field($key, $value, true);
}
$embed->footer('Site Monitoring Alert');
});
}
/**
* Get the array representation of the notification (database).
*/
public function toArray($notifiable): array
{
return [
'alert_type' => $this->alertType,
'site_name' => $this->siteName,
'message' => $this->message,
'severity' => $this->severity,
'data' => $this->data,
'timestamp' => now()->toDateTimeString(),
];
}
/**
* Get color based on severity
*/
protected function getColorForSeverity(): string
{
return match ($this->severity) {
'critical' => 'danger',
'warning' => 'warning',
default => 'info',
};
}
/**
* Get Discord color based on severity
*/
protected function getDiscordColorForSeverity(): int
{
return match ($this->severity) {
'critical' => 0xFF0000, // Red
'warning' => 0xFFA500, // Orange
default => 0x0099FF, // Blue
};
}
/**
* Get emoji based on severity
*/
protected function getEmojiForSeverity(): string
{
return match ($this->severity) {
'critical' => '🚨',
'warning' => '⚠️',
default => 'ℹ️',
};
}
/**
* Get severity label
*/
protected function getSeverityLabel(): string
{
return strtoupper($this->severity);
}
/**
* Get alert type label
*/
protected function getAlertTypeLabel(): string
{
return str_replace('_', ' ', ucwords($this->alertType));
}
}
```
## Specific Notification Classes
### 1. UptimeAlert
Create `app/Notifications/UptimeAlert.php`:
```php
<?php
namespace App\Notifications;
class UptimeAlert extends MonitorAlert
{
public function __construct(string $siteName, bool $isDown, int $statusCode, string $url, float $responseTime = null)
{
$message = $isDown
? "Your site is DOWN (HTTP {$statusCode})"
: "Your site is back ONLINE";
$data = [
'Status Code' => $statusCode,
'URL' => $url,
];
if ($responseTime) {
$data['Response Time'] = round($responseTime, 2) . 'ms';
}
parent::__construct(
'uptime',
$siteName,
$message,
$data,
$isDown ? 'critical' : 'info'
);
}
}
```
### 2. SslExpiryAlert
Create `app/Notifications/SslExpiryAlert.php`:
```php
<?php
namespace App\Notifications;
use Carbon\Carbon;
class SslExpiryAlert extends MonitorAlert
{
public function __construct(string $siteName, Carbon $expiresAt, int $daysRemaining)
{
$message = $daysRemaining <= 0
? "SSL certificate has EXPIRED"
: "SSL certificate expires in {$daysRemaining} days";
parent::__construct(
'ssl',
$siteName,
$message,
[
'Expires On' => $expiresAt->format('Y-m-d H:i:s'),
'Days Remaining' => $daysRemaining,
],
$daysRemaining <= 7 ? 'critical' : 'warning'
);
}
}
```
### 3. DomainExpiryAlert
Create `app/Notifications/DomainExpiryAlert.php`:
```php
<?php
namespace App\Notifications;
use Carbon\Carbon;
class DomainExpiryAlert extends MonitorAlert
{
public function __construct(string $siteName, string $domain, Carbon $expiresAt, int $daysRemaining)
{
$message = $daysRemaining <= 0
? "Domain '{$domain}' has EXPIRED"
: "Domain '{$domain}' expires in {$daysRemaining} days";
parent::__construct(
'domain_expiry',
$siteName,
$message,
[
'Domain' => $domain,
'Expires On' => $expiresAt->format('Y-m-d'),
'Days Remaining' => $daysRemaining,
],
$daysRemaining <= 7 ? 'critical' : 'warning'
);
}
}
```
### 4. SitemapAlert
Create `app/Notifications/SitemapAlert.php`:
```php
<?php
namespace App\Notifications;
class SitemapAlert extends MonitorAlert
{
public function __construct(string $siteName, string $sitemapUrl, string $error)
{
parent::__construct(
'sitemap',
$siteName,
'Sitemap validation failed',
[
'Sitemap URL' => $sitemapUrl,
'Error' => $error,
],
'warning'
);
}
}
```
### 5. PerformanceAlert
Create `app/Notifications/PerformanceAlert.php`:
```php
<?php
namespace App\Notifications;
class PerformanceAlert extends MonitorAlert
{
public function __construct(string $siteName, float $responseTime, float $threshold)
{
parent::__construct(
'performance',
$siteName,
"Response time exceeded threshold",
[
'Response Time' => round($responseTime, 2) . 'ms',
'Threshold' => round($threshold, 2) . 'ms',
'Difference' => '+' . round($responseTime - $threshold, 2) . 'ms',
],
'warning'
);
}
}
```
## Integration with Existing Jobs
Update your monitoring jobs to send notifications. Example for `CheckSitemap`:
```php
// In app/Jobs/CheckSitemap.php
use App\Notifications\SitemapAlert;
protected function sendAlert(string $message): void
{
// Send notification using the new system
$this->sitemap->site->user->notify(
new SitemapAlert(
$this->sitemap->site->name,
$this->sitemap->url,
$this->sitemap->last_error
)
);
// Keep existing Notification model creation for backwards compatibility
Notification::create([
'user_id' => $this->sitemap->site->user_id,
'type' => 'sitemap_invalid',
'message' => "{$message}: {$this->sitemap->url}",
'data' => json_encode([
'sitemap_id' => $this->sitemap->id,
'site_id' => $this->sitemap->site_id,
'url' => $this->sitemap->url,
'error' => $this->sitemap->last_error,
]),
'read_at' => null,
]);
}
```
## Configuration Files
### Services Configuration
Add to `config/services.php`:
```php
'telegram-bot-api' => [
'token' => env('TELEGRAM_BOT_TOKEN'),
],
'twilio' => [
'sid' => env('TWILIO_SID'),
'token' => env('TWILIO_TOKEN'),
'from' => env('TWILIO_FROM'),
],
'opsgenie' => [
'api_key' => env('OPSGENIE_API_KEY'),
'api_url' => 'https://api.opsgenie.com',
],
'pagerduty' => [
'api_url' => 'https://events.pagerduty.com/v2/enqueue',
],
```
### Environment Variables
Add to `.env`:
```env
# Telegram Bot
TELEGRAM_BOT_TOKEN=your_bot_token_here
# Twilio SMS
TWILIO_SID=your_account_sid
TWILIO_TOKEN=your_auth_token
TWILIO_FROM=+1234567890
# Opsgenie
OPSGENIE_API_KEY=your_api_key
# Global defaults (optional)
DEFAULT_SLACK_CHANNEL=#monitoring
DEFAULT_NOTIFICATION_QUEUE=notifications
```
## Notification Settings Controller
The controller and views for managing notification settings will be created separately to allow users to:
- Configure which channels to use for each alert type
- Enter webhook URLs and API keys
- Test notification delivery
- View notification history
## Channel Setup Guides
### Slack
1. Create an Incoming Webhook in your Slack workspace
2. Copy the webhook URL
3. Paste in notification settings
### Telegram
1. Create a bot via @BotFather
2. Get your bot token
3. Start a chat with your bot
4. Get your chat ID (use @userinfobot)
5. Enter chat ID in settings
### Discord
1. Edit channel settings
2. Go to Integrations > Webhooks
3. Create webhook
4. Copy webhook URL
### Microsoft Teams
1. Add "Incoming Webhook" connector to channel
2. Configure and create
3. Copy webhook URL
### Google Chat
1. Create a webhook in Google Chat space
2. Copy webhook URL
## Testing Notifications
Create a test command:
```bash
php artisan make:command TestNotifications
```
```php
<?php
namespace App\Console\Commands;
use App\Notifications\UptimeAlert;
use App\Models\User;
use Illuminate\Console\Command;
class TestNotifications extends Command
{
protected $signature = 'notifications:test {user}';
protected $description = 'Test notification delivery for a user';
public function handle()
{
$user = User::findOrFail($this->argument('user'));
$user->notify(new UptimeAlert(
'Test Site',
false, // Not down
200,
'https://example.com',
150.5
));
$this->info('Test notification sent!');
}
}
```
## Best Practices
1. **Queue Notifications**: All notifications implement `ShouldQueue`
2. **Rate Limiting**: Implement rate limiting to prevent notification spam
3. **Retry Logic**: Configure queue retry attempts
4. **Monitoring**: Log notification failures
5. **Testing**: Test each channel before production use
6. **Security**: Encrypt sensitive webhook URLs and API keys
7. **User Preferences**: Respect user notification preferences
8. **Batching**: Consider batching similar alerts
## Next Steps
1. Run migration: `php artisan migrate`
2. Create notification settings UI
3. Integrate with existing monitoring jobs
4. Test each notification channel
5. Configure queue worker for notifications
6. Set up monitoring for the notification system itself
## Troubleshooting
### Notifications not sending
- Check queue worker is running
- Verify webhook URLs are correct
- Check logs: `storage/logs/laravel.log`
- Test with `notifications:test` command
### Channel-specific issues
- **Slack**: Verify webhook URL format
- **Telegram**: Ensure bot token is valid and chat ID is correct
- **Discord**: Check webhook hasn't been deleted
- **Email**: Verify mail configuration
## Future Enhancements
- Notification grouping (digest emails)
- Quiet hours configuration
- Escalation policies
- Notification templates
- Webhook retry logic
- Notification analytics
- Mobile push notifications
- In-app notification center