From 93d6bd91279895badab4831e96ed80feadd16df9 Mon Sep 17 00:00:00 2001 From: Clemens Creutzburg Date: Sun, 23 Aug 2026 20:39:22 +0200 Subject: [PATCH] Pushover-Benachrichtigung bei neuer Registrierung Neuer Helfer app/pushover.php meldet Selbstregistrierungen an den Betreiber. Der Aufruf sitzt in register.php statt in saas_register_tenant_owner(), damit die check-*-Skripte keine Pushes ausloesen, und wertet das Ergebnis nicht aus: ein fehlgeschlagener Push darf die Registrierung nicht abbrechen. Ohne PUSHOVER_TOKEN/PUSHOVER_USER ist die Funktion still deaktiviert. Co-Authored-By: Claude Opus 5 --- app/pushover.php | 95 +++++++++++++++++++++++++++++++++++++++++++ docs/deployment.md | 15 +++++++ env.local.example.php | 7 ++++ register.php | 12 ++++++ 4 files changed, 129 insertions(+) create mode 100644 app/pushover.php diff --git a/app/pushover.php b/app/pushover.php new file mode 100644 index 0000000..3976714 --- /dev/null +++ b/app/pushover.php @@ -0,0 +1,95 @@ + false, 'error' => 'Pushover ist nicht konfiguriert.']; + } + + $params = [ + 'token' => (string)app_env('PUSHOVER_TOKEN', ''), + 'user' => (string)app_env('PUSHOVER_USER', ''), + // Pushover kappt laengere Werte serverseitig; wir kuerzen selbst, + // damit die Meldung nicht mitten im Wort abbricht. + 'title' => mb_substr($title, 0, 250), + 'message' => mb_substr($message, 0, 1024), + ]; + if ($url !== null && $url !== '') { + $params['url'] = $url; + $params['url_title'] = $urlTitle ?? $url; + } + + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => 'Content-Type: application/x-www-form-urlencoded', + 'content' => http_build_query($params), + 'timeout' => 10, + 'ignore_errors' => true, + ], + ]); + + $responseBody = @file_get_contents('https://api.pushover.net/1/messages.json', false, $context); + $status = 0; + foreach ($http_response_header ?? [] as $header) { + if (preg_match('~^HTTP/\S+\s+(\d{3})~', $header, $m) === 1) { + $status = (int)$m[1]; + } + } + + if ($responseBody === false) { + error_log('Pushover: keine Antwort von api.pushover.net'); + return ['ok' => false, 'error' => 'Keine Antwort von Pushover.']; + } + + $data = json_decode($responseBody, true); + if (!is_array($data) || $status >= 400 || (int)($data['status'] ?? 0) !== 1) { + $errors = is_array($data['errors'] ?? null) ? implode('; ', $data['errors']) : 'HTTP ' . $status; + error_log('Pushover fehlgeschlagen: ' . $errors); + return ['ok' => false, 'error' => $errors]; + } + + return ['ok' => true, 'error' => null]; +} + +/** + * Meldung ueber eine abgeschlossene Selbstregistrierung. Wird nur aus + * register.php aufgerufen, nicht aus saas_register_tenant_owner() selbst - + * sonst wuerden die check-*-Skripte in scripts/ bei jedem Testlauf Pushes + * ausloesen. + */ +function pushover_notify_new_tenant(string $tenantName, string $tenantSlug, string $email, string $customerType): array +{ + $type = $customerType === 'b2c' ? 'Privatkunde' : 'Geschäftskunde'; + $baseUrl = rtrim((string)app_env('APP_BASE_URL', ''), '/'); + + return pushover_notify( + 'Neue Registrierung: ' . $tenantName, + sprintf("%s (%s)\nKürzel: %s\nE-Mail: %s", $tenantName, $type, $tenantSlug, $email), + $baseUrl !== '' ? $baseUrl . '/backoffice.php' : null, + 'Back-Office öffnen' + ); +} diff --git a/docs/deployment.md b/docs/deployment.md index c2234b8..1763820 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -266,6 +266,21 @@ Stripe-Flow zur Aktualisierung der Zahlungsart. Im Stripe-Dashboard dürfen der zusätzlich freigeschaltet werden; Bestellungen und Kündigungen sollen nur über die dokumentierten Abläufe der Kaffeeliste erfolgen. +## Betreiber-Benachrichtigung bei neuer Registrierung + +`register.php` schickt nach einer erfolgreichen Selbstregistrierung einen +Pushover-Push an den Betreiber (`app/pushover.php`). Dafür in +`env.local.php` `PUSHOVER_TOKEN` (App-Token aus dem Pushover-Dashboard) und +`PUSHOVER_USER` (eigener User- oder Group-Key) setzen. Fehlt einer der +beiden Werte, ist die Benachrichtigung still deaktiviert — auf Staging also +einfach leer lassen. + +Der Versand ist bewusst folgenlos: Schlägt er fehl, landet die Ursache im +PHP-Error-Log, die Registrierung läuft trotzdem durch. Der Aufruf sitzt +deshalb in `register.php` und nicht in `saas_register_tenant_owner()` — die +`check-*`-Skripte in `scripts/` registrieren Testmandanten und würden sonst +bei jedem Lauf Pushes auslösen. + ## PayPal-Postfach Für die automatische Verbuchung weitergeleiteter PayPal-Zahlungsmails diff --git a/env.local.example.php b/env.local.example.php index b0677c5..2f03730 100644 --- a/env.local.example.php +++ b/env.local.example.php @@ -81,3 +81,10 @@ putenv('PAYPAL_IMAP_MAILBOX=INBOX'); // die Anmeldung laeuft immer ueber login.php, auch lokal. Die Variable wird nur // noch von scripts/init-mysql-dev.php gelesen, um beim Aufsetzen einer // Entwicklungsdatenbank den ersten Benutzer anzulegen. + +// Betreiber-Push bei neuer Kundenregistrierung (optional). Beide Werte +// stehen im Pushover-Dashboard: PUSHOVER_TOKEN ist das App-Token einer dort +// angelegten Anwendung, PUSHOVER_USER der eigene User- oder Group-Key. +// Fehlt einer der beiden, ist die Benachrichtigung still deaktiviert. +putenv('PUSHOVER_TOKEN='); +putenv('PUSHOVER_USER='); diff --git a/register.php b/register.php index 39b811a..518d09f 100644 --- a/register.php +++ b/register.php @@ -4,6 +4,7 @@ require_once __DIR__ . '/functions.php'; require_once __DIR__ . '/app/saas-mail.php'; require_once __DIR__ . '/app/rate-limit.php'; require_once __DIR__ . '/app/legal.php'; +require_once __DIR__ . '/app/pushover.php'; $pdo = app_db_pdo(); $errors = []; @@ -45,6 +46,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!empty($result['email_verification_token'])) { $verificationMail = saas_send_email_verification_mail($values['email'], (string)$result['email_verification_token']); } + // Betreiber-Push ueber die Neuanmeldung. Bewusst hier statt in + // saas_register_tenant_owner(), damit die check-*-Skripte in + // scripts/ keine Pushes ausloesen. Das Ergebnis wird nicht + // ausgewertet: ein fehlgeschlagener Push darf die Registrierung + // nie stoppen, er landet nur im PHP-Error-Log. + pushover_notify_new_tenant( + $values['tenant_name'], + (string)$result['identity']['tenant_slug'], + $values['email'], + $values['customer_type'] + ); saas_session_login($result['identity']); if (empty($contractMail['ok'])) { $_SESSION['saas_contract_mail_failed'] = true;