diff --git a/README.md b/README.md index 41b9ee9..e69ca77 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,34 @@ All application and email settings are now managed through the **Settings** page 4. Copy the webhook URL 5. Add it in the notification group settings +#### 🌐 Webhook (Custom) + +Send JSON payloads to any HTTP endpoint (e.g., n8n, Zapier, Make, your own API): + +1. Go to Notification Groups → Edit → Add Channel +2. Choose "Webhook (Custom)" +3. Paste your endpoint URL (HTTPS recommended) +4. Click "Test Channel" to verify + +Payload example sent on domain alerts: + +```json +{ + "event": "domain_expiration_alert", + "message": "⚠️ WARNING: Domain 'example.com' expires in 7 days (January 30, 2026)!\n\nRegistrar: Example Registrar\nPlease renew soon.", + "data": { + "domain": "example.com", + "domain_id": 123, + "days_left": 7, + "expiration_date": "2026-01-30", + "registrar": "Example Registrar" + }, + "sent_at": "2025-10-17T12:34:56Z" +} +``` + +Use this with n8n's "Webhook" trigger to start flows. + ## 📅 Setting Up Cron Jobs The application requires a cron job to check domains periodically. diff --git a/app/Controllers/NotificationGroupController.php b/app/Controllers/NotificationGroupController.php index db5eefd..d4fd843 100644 --- a/app/Controllers/NotificationGroupController.php +++ b/app/Controllers/NotificationGroupController.php @@ -214,6 +214,7 @@ class NotificationGroupController extends Controller break; case 'discord': case 'slack': + case 'webhook': $missingField = 'webhook URL'; break; } @@ -427,6 +428,17 @@ class NotificationGroupController extends Controller } return ['webhook_url' => $webhookUrl]; + case 'webhook': + $webhookUrl = trim($data['webhook_url'] ?? ''); + if (empty($webhookUrl) || !filter_var($webhookUrl, FILTER_VALIDATE_URL)) { + return null; + } + // Optional: Allow any HTTPS URL; prefer HTTPS for security + if (!str_starts_with($webhookUrl, 'https://') && !str_starts_with($webhookUrl, 'http://')) { + return null; + } + return ['webhook_url' => $webhookUrl]; + default: return null; } diff --git a/app/Services/Channels/WebhookChannel.php b/app/Services/Channels/WebhookChannel.php new file mode 100644 index 0000000..7900f7a --- /dev/null +++ b/app/Services/Channels/WebhookChannel.php @@ -0,0 +1,48 @@ +httpClient = new Client(['timeout' => 10]); + } + + public function send(array $config, string $message, array $data = []): bool + { + $url = trim($config['webhook_url'] ?? ''); + if (empty($url)) { + return false; + } + + // Build a sane, generic JSON payload for automation tools (n8n, Zapier, etc.) + $payload = [ + 'event' => 'domain_expiration_alert', + 'message' => $message, + 'data' => $data, + 'sent_at' => date('c') + ]; + + try { + $response = $this->httpClient->post($url, [ + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'json' => $payload + ]); + + $status = $response->getStatusCode(); + return $status >= 200 && $status < 300; + } catch (\Exception $e) { + error_log('Webhook send failed: ' . $e->getMessage()); + return false; + } + } +} + + diff --git a/app/Services/NotificationService.php b/app/Services/NotificationService.php index 7badf6a..cef98db 100644 --- a/app/Services/NotificationService.php +++ b/app/Services/NotificationService.php @@ -6,6 +6,7 @@ use App\Services\Channels\EmailChannel; use App\Services\Channels\TelegramChannel; use App\Services\Channels\DiscordChannel; use App\Services\Channels\SlackChannel; +use App\Services\Channels\WebhookChannel; class NotificationService { @@ -18,6 +19,7 @@ class NotificationService 'telegram' => new TelegramChannel(), 'discord' => new DiscordChannel(), 'slack' => new SlackChannel(), + 'webhook' => new WebhookChannel(), ]; } diff --git a/app/Views/groups/edit.php b/app/Views/groups/edit.php index fbec48b..424e2d3 100644 --- a/app/Views/groups/edit.php +++ b/app/Views/groups/edit.php @@ -78,9 +78,9 @@ ob_start();
'fa-envelope', 'telegram' => 'fa-telegram', 'discord' => 'fa-discord', 'slack' => 'fa-slack']; - $iconClasses = ['email' => 'fas', 'telegram' => 'fab', 'discord' => 'fab', 'slack' => 'fab']; - $colors = ['email' => 'blue', 'telegram' => 'blue', 'discord' => 'indigo', 'slack' => 'teal']; + $icons = ['email' => 'fa-envelope', 'telegram' => 'fa-telegram', 'discord' => 'fa-discord', 'slack' => 'fa-slack', 'webhook' => 'fa-link']; + $iconClasses = ['email' => 'fas', 'telegram' => 'fab', 'discord' => 'fab', 'slack' => 'fab', 'webhook' => 'fas']; + $colors = ['email' => 'blue', 'telegram' => 'blue', 'discord' => 'indigo', 'slack' => 'teal', 'webhook' => 'purple']; $icon = $icons[$channel['channel_type']] ?? 'fa-bell'; $iconClass = $iconClasses[$channel['channel_type']] ?? 'fas'; $color = $colors[$channel['channel_type']] ?? 'gray'; @@ -152,6 +152,7 @@ ob_start(); +
@@ -236,6 +237,24 @@ ob_start(); + + +
- + +
+
+

Two-Factor Authentication

+

Configure 2FA policy and security settings

+
- -
-
-

Two-Factor Authentication

-

Configure 2FA policy and security settings

+
+ +
+
+ + +

+ + Users must have verified email addresses to enable 2FA +

+
+ +
+
+ + +

Maximum failed attempts per IP address

+
+ +
+ + +

How long email backup codes remain valid

+
+
+ + +
+

+ + Two-Factor Authentication Features +

+
    +
  • TOTP Authenticator Apps: Google Authenticator, Authy, Microsoft Authenticator
  • +
  • Email Backup Codes: One-time codes sent to verified email addresses
  • +
  • Backup Recovery Codes: 8 single-use codes generated during setup
  • +
  • Rate Limiting: Prevents brute force attacks on verification codes
  • +
+
+
+ +
+ +
+
- -
- -
-
- - -

- - Users must have verified email addresses to enable 2FA -

-
- -
-
- - -

Maximum failed attempts per IP address

-
- -
- - -

How long email backup codes remain valid

-
-
- - -
-

- - Two-Factor Authentication Features -

-
    -
  • TOTP Authenticator Apps: Google Authenticator, Authy, Microsoft Authenticator
  • -
  • Email Backup Codes: One-time codes sent to verified email addresses
  • -
  • Backup Recovery Codes: 8 single-use codes generated during setup
  • -
  • Rate Limiting: Prevents brute force attacks on verification codes
  • -
-
-
- -
- -
-
-