Skip to content

Broadcast/Notifikationen WIP#580

Merged
dspangenberg merged 1 commit into
mainfrom
develop
Jul 10, 2026
Merged

Broadcast/Notifikationen WIP#580
dspangenberg merged 1 commit into
mainfrom
develop

Conversation

@dspangenberg

@dspangenberg dspangenberg commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Neue Funktionen

    • Echtzeit-Benachrichtigungen für Nutzer hinzugefügt.
    • Benachrichtigungen werden tenant- und nutzerspezifisch über private Kanäle übertragen.
    • Tenant-Daten enthalten nun eine eindeutige ID.
  • Verbesserungen

    • Die Broadcast-Authentifizierung und Nutzerkanäle wurden für eine zuverlässigere Zugriffskontrolle angepasst.
    • Ein Health-Check-Endpunkt unter /up ist verfügbar.
  • Technische Aktualisierung

    • Die Frontend-Typdefinitionen wurden an die erweiterten Tenant-Daten angepasst.

@dspangenberg

Copy link
Copy Markdown
Owner Author

@coderabbitai Kannst Du einen Review machen?

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@dspangenberg Ja, gerne! Ich starte jetzt den Review der Änderungen in diesem PR.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0b3c4b74-562e-4b57-b425-45dbdf36a821

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Die Änderungen führen Echtzeit-Benachrichtigungen für archivierte E-Mails ein. Laravel broadcastet Nachrichten über private Kanäle, tenantbezogene Authentifizierungsrouten werden ergänzt und AppLayout empfängt die Events per Echo.

Changes

Echtzeit-Benachrichtigungen

Layer / File(s) Summary
Benachrichtigungs-Event und Auslösung
app/Events/GeneralNotificationEvent.php, app/Http/Controllers/App/EmailController.php
Ein sofortiges Broadcast-Event wird definiert und nach dem Archivieren mit einer Nachricht ausgelöst.
Broadcast-Kanal und Authentifizierung
app/Broadcasting/GeneralChannel.php, routes/channels.php, routes/tenant.php
Private Kanäle, Join-Logik und eine tenantbezogene /broadcasting/auth-Route werden eingerichtet.
Tenant-Daten und Client-Abonnement
app/Data/TenantData.php, resources/js/Types/generated.d.ts, resources/typescript-transformer-manifest.json, resources/js/Layouts/AppLayout.tsx
Die Tenant-ID wird in den Daten- und TypeScript-Verträgen ergänzt; AppLayout lauscht auf general.notification.
Routing- und Testanpassungen
bootstrap/app.php, tests/Feature/Auth/AuthenticationTest.php
Die Bootstrap-Routingkonfiguration erhält /up; der Authentifizierungstest dokumentiert eine mögliche Tenant-Ausnahme.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Poem

Ich hopp’ durch Kanäle, flink und fein,
Eine Nachricht hüpft ins Layout hinein.
Der Tenant trägt die ID ganz stolz,
Echo lauscht im Datenholz.
Archiviert, gebroadcastet — hurra,
Mein Hasenherz ruft: „Voilà!“

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive Der Titel ist thematisch passend, aber zu allgemein und beschreibt die konkreten Änderungen nicht klar. Bitte die Hauptänderung präzise benennen, z. B. neue Broadcast-Events/Channels und die Notification-Anbindung.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
app/Http/Controllers/App/EmailController.php (1)

191-192: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test-Nachricht ist fest codiert und Auth-Fassade inkonsistent.

Die Nachricht 'Neue Test-Nachricht' ist ein Platzhalter — für WIP akzeptabel, aber vor Merge durch eine sinnvolle Meldung ersetzen. Zudem verwendet der restliche Controller auth()->id() bzw. auth()->user()->id, während hier Auth::user() eingesetzt wird. Für Konsistenz auth()->user() verwenden.

♻️ Proposed refactor: auth() helper für Konsistenz
-        GeneralNotificationEvent::dispatch(Auth::user(), 'Neue Test-Nachricht');
+        GeneralNotificationEvent::dispatch(auth()->user(), 'E-Mail wurde archiviert.');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Http/Controllers/App/EmailController.php` around lines 191 - 192, Ersetze
in der Dispatch-Zeile von GeneralNotificationEvent::dispatch die fest codierte
Platzhalternachricht durch eine sinnvolle, produktive Meldung und verwende statt
Auth::user() konsistent auth()->user(), entsprechend der übrigen
Controller-Implementierung.
app/Events/GeneralNotificationEvent.php (1)

18-18: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

ShouldBroadcastNow für Produktion überdenken.

ShouldBroadcastNow broadcastet synchron im Request-Zyklus. Bei langsamen Broadcasting-Treibern blockiert das den Response. Für Produktion ist ShouldBroadcast (mit Queue) vorzuziehen, da dann auch Tenant-Kontext-Wiederherstellung im Queue-Worker sichergestellt werden muss.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Events/GeneralNotificationEvent.php` at line 18, Ersetze in der Klasse
GeneralNotificationEvent das synchrone ShouldBroadcastNow durch ShouldBroadcast,
damit Benachrichtigungen über die Queue verarbeitet werden; prüfe anschließend
die Queue-Verarbeitung auf korrekte Wiederherstellung des Tenant-Kontexts im
Worker.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Broadcasting/GeneralChannel.php`:
- Around line 1-37: GeneralChannel ist nicht registriert und join() liefert
implizit null. Wenn die Klasse benötigt wird, registriere sie in channels.php,
implementiere join(User $user) mit einer expliziten Autorisierungsentscheidung
und verschiebe broadcastOn() sowie broadcastWith() in das zugehörige Event;
andernfalls entferne GeneralChannel und behalte die bestehende
Closure-Autorisierung bei.

In `@app/Events/GeneralNotificationEvent.php`:
- Around line 27-32: Füge in GeneralNotificationEvent::broadcastOn() den
Tenant-Präfix vor dem Benutzerkanal ein, sodass der PrivateChannel den Namen
tenant('id').'.user.'.$this->user->id verwendet. Orientiere dich dabei an der
bereits korrekten Kanalbildung in GeneralChannel.

In `@resources/js/Layouts/AppLayout.tsx`:
- Around line 80-82: Ersetze den Platzhalter console.log(e) im Fehler-Callback
durch eine toast()-Benachrichtigung. Verwende die bereits in AppLayout
verfügbare toast-Funktion und übergib eine verständliche Fehlermeldung, optional
ergänzt um die Details aus e.

---

Nitpick comments:
In `@app/Events/GeneralNotificationEvent.php`:
- Line 18: Ersetze in der Klasse GeneralNotificationEvent das synchrone
ShouldBroadcastNow durch ShouldBroadcast, damit Benachrichtigungen über die
Queue verarbeitet werden; prüfe anschließend die Queue-Verarbeitung auf korrekte
Wiederherstellung des Tenant-Kontexts im Worker.

In `@app/Http/Controllers/App/EmailController.php`:
- Around line 191-192: Ersetze in der Dispatch-Zeile von
GeneralNotificationEvent::dispatch die fest codierte Platzhalternachricht durch
eine sinnvolle, produktive Meldung und verwende statt Auth::user() konsistent
auth()->user(), entsprechend der übrigen Controller-Implementierung.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9a3e493c-43b7-409b-978b-3c00f2d8b606

📥 Commits

Reviewing files that changed from the base of the PR and between d504671 and 2a5c616.

📒 Files selected for processing (11)
  • app/Broadcasting/GeneralChannel.php
  • app/Data/TenantData.php
  • app/Events/GeneralNotificationEvent.php
  • app/Http/Controllers/App/EmailController.php
  • bootstrap/app.php
  • resources/js/Layouts/AppLayout.tsx
  • resources/js/Types/generated.d.ts
  • resources/typescript-transformer-manifest.json
  • routes/channels.php
  • routes/tenant.php
  • tests/Feature/Auth/AuthenticationTest.php
💤 Files with no reviewable changes (1)
  • bootstrap/app.php

Comment on lines +1 to +37
<?php

namespace App\Broadcasting;

use App\Models\User;

class GeneralChannel
{
/**
* Create a new channel instance.
*/
public function __construct(
public User $user,
public string $message
) {}

public function broadcastOn(): array
{

return [
new PrivateChannel(tenant('id').'.user.'.$this->user->id),
];
}

public function broadcastWith(): array
{
return ['message' => $this->message];
}

/**
* Authenticate the user's access to the channel.
*/
public function join(User $user): array|bool
{
//
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

GeneralChannel ist nicht registriert und join() gibt null zurück.

Die Klasse wird in routes/channels.php nicht referenziert — dort wird eine Closure verwendet. join() hat einen leeren Body und gibt null zurück, was alle Subscriptions ablehnen würde. Zudem enthalten broadcastOn() und broadcastWith() Event-Logik, die in einer Channel-Authorisierungsklasse nicht vorgesehen ist. Entweder GeneralChannel in channels.php registrieren und join() implementieren, oder die Klasse entfernen, wenn sie nicht verwendet wird.

♻️ Proposed fix: join() implementieren oder Klasse entfernen

Option A — join() implementieren (falls die Klasse genutzt werden soll):

 public function join(User $user): array|bool
 {
-    //
+    return (int) $user->id === (int) $this->user->id;
 }

Option B — Klasse entfernen und Closure in channels.php beibehalten:

- use App\Broadcasting\GeneralChannel;

Die Closure in routes/channels.php übernimmt die Autorisierung bereits korrekt.

🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 33-33: Avoid unused parameters such as '$user'. (undefined)

(UnusedFormalParameter)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Broadcasting/GeneralChannel.php` around lines 1 - 37, GeneralChannel ist
nicht registriert und join() liefert implizit null. Wenn die Klasse benötigt
wird, registriere sie in channels.php, implementiere join(User $user) mit einer
expliziten Autorisierungsentscheidung und verschiebe broadcastOn() sowie
broadcastWith() in das zugehörige Event; andernfalls entferne GeneralChannel und
behalte die bestehende Closure-Autorisierung bei.

Comment on lines +27 to +32
public function broadcastOn(): array
{
return [
new PrivateChannel('user.'.$this->user->id),
];
}

@coderabbitai coderabbitai Bot Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Kanalname enthält keinen Tenant-Präfix — Benachrichtigung erreicht den Client nicht.

broadcastOn() broadcastet auf user.{id}, aber AppLayout.tsx abonniert ${tenantId}.user.${userId}. Da tenant_channel() in routes/channels.php den Kanalnamen mit der Tenant-ID präfixiert (siehe Kommentar Zeile 6), landet das Event auf einem Kanal, auf dem kein Client hört. GeneralChannel (Zeile 21) verwendet bereits korrekt tenant('id').'.user.'.$this->user->id — diese Logik muss hier ebenfalls angewendet werden.

🐛 Proposed fix: Tenant-Präfix im Kanalnamen ergänzen
 public function broadcastOn(): array
 {
     return [
-        new PrivateChannel('user.'.$this->user->id),
+        new PrivateChannel(tenant('id').'.user.'.$this->user->id),
     ];
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function broadcastOn(): array
{
return [
new PrivateChannel('user.'.$this->user->id),
];
}
public function broadcastOn(): array
{
return [
new PrivateChannel(tenant('id').'.user.'.$this->user->id),
];
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Events/GeneralNotificationEvent.php` around lines 27 - 32, Füge in
GeneralNotificationEvent::broadcastOn() den Tenant-Präfix vor dem Benutzerkanal
ein, sodass der PrivateChannel den Namen tenant('id').'.user.'.$this->user->id
verwendet. Orientiere dich dabei an der bereits korrekten Kanalbildung in
GeneralChannel.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nein, wenn der Tenant hier gesetzt wird, funktioniert das Broadcasting nicht mehr. Der Tenant wird über routes/channels.php gestzt.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat -n routes/channels.php

Length of output: 464


@dspangenberg Danke für die Klarstellung — das ergibt Sinn. Da tenant_channel() in routes/channels.php das automatische Tenant-Prefixing von stancl/tenancy für Broadcasting-Kanäle nutzt, würde ein manuell gesetzter tenant('id')-Präfix im Event zu einer doppelten Präfixierung führen und den Abgleich mit dem Client-seitigen Kanalnamen brechen. Mein ursprünglicher Vorschlag war hier falsch, ich nehme ihn zurück.

Als kleine Randnotiz (kein Blocker für diesen Kommentar): GeneralChannel.php verwendet aktuell tenant('id').'.user.'.$this->user->id — falls dort dasselbe Auto-Prefixing via tenant_channel()/Broadcasting-Bootstrapper greift, könnte das denselben Doppel-Präfix-Effekt haben. Lohnt sich vielleicht, kurz zu prüfen, ob dieser Kanal tatsächlich korrekt funktioniert.

ʘ‿ʘ


✏️ Learnings added
Learnt from: dspangenberg
Repo: dspangenberg/opsc PR: 580
File: app/Events/GeneralNotificationEvent.php:27-32
Timestamp: 2026-07-10T14:42:17.171Z
Learning: Repository dspangenberg/opsc (Laravel, using stancl/tenancy): Broadcasting channels registered via `tenant_channel()` in `routes/channels.php` (e.g. `tenant_channel('user.{id}', ...)`) are automatically prefixed with the tenant ID by the stancl/tenancy package's broadcasting integration. Do NOT manually prepend `tenant('id')` to `PrivateChannel` names in event classes (e.g. `App\Events\GeneralNotificationEvent`), since that causes double-prefixing and breaks broadcasting/subscription matching with the frontend. Use plain channel names like `'user.'.$this->user->id` in `broadcastOn()`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +80 to +82
e => {
console.log(e)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

console.log als Platzhalter durch tatsächliche Benachrichtigung ersetzen

Der console.log(e) ist Platzhalter-Code. Die toast()-Funktion ist in derselben Komponente bereits verfügbar (Zeilen 90–97). Vor Merge nach main sollte hier eine echte Benachrichtigungsbehandlung stehen.

💡 Vorschlag: toast-Benachrichtigung
   e => {
-    console.log(e)
+    toast(e.message ?? 'Neue Benachrichtigung', 'info')
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
e => {
console.log(e)
}
e => {
toast(e.message ?? 'Neue Benachrichtigung', 'info')
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/js/Layouts/AppLayout.tsx` around lines 80 - 82, Ersetze den
Platzhalter console.log(e) im Fehler-Callback durch eine
toast()-Benachrichtigung. Verwende die bereits in AppLayout verfügbare
toast-Funktion und übergib eine verständliche Fehlermeldung, optional ergänzt um
die Details aus e.

@dspangenberg
dspangenberg merged commit 53c3e00 into main Jul 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant