Zum Hauptinhalt springen

Events

Alle Broadcast-Events in Postbox. Events werden über Laravel Reverb (WebSockets) an verbundene Clients gesendet. Alle Events (außer TestBroadcast) nutzen private Channels mit Autorisierung.

Location: app/Events/

Event-Übersicht​

EventChannel(s)broadcastAsInterfacePersistenz
WatcherImportProgressworkspace.{id}import.progressShouldBroadcastNur bei Finish
YouTubeVideoSyncCompleteduser.{id}youtube.sync.completedShouldBroadcastNowNur manuell
DailyScrapeCompleteduser.{id}daily.scrape.completedShouldBroadcastNur Toast
RelatedProfilesCalculatedprofile.{id} + user.{id}related.profiles.calculatedShouldBroadcastNur manuell
ProfileReactivateduser.{id} (alle betroffenen)profile.reactivatedShouldBroadcastImmer
ProfileSanitizeduser.{id} (alle betroffenen)profile.sanitizedShouldBroadcastImmer
ProfileUnsanitizeduser.{id} (alle betroffenen)profile.unsanitizedShouldBroadcastImmer
UserRegisteredadminuser.registeredShouldBroadcastImmer (Admin)
TestBroadcasttest-channel (public)toast.showShouldBroadcastNowNein
ServerMetricsUpdatedadminserver.metrics.updatedShouldBroadcastNowNein
ServerAlertTriggeredadminserver.alert.triggeredShouldBroadcastNowNein
ContactLinkProfileImportedworkspace.{id}contact.link.importedShouldBroadcastJa (7 Tage)
YouTubeResearchBatchProgressadminyoutube.research.batch.progressShouldBroadcastNowNein

YouTubeResearchBatchProgress​

Broadcast fuer Echtzeit-Fortschritts-Updates waehrend einer YouTube Research Batch-Suche. Wird nach jedem verarbeiteten Keyword gesendet.

Location: app/Events/YouTubeResearchBatchProgress.php

Properties​

public string $batchId,
public int $processedCount,
public int $totalCount,
public ?string $currentKeyword,
public int $currentResultCount,
public int $filteredCount,
public string $status, // 'processing', 'completed', 'failed'
public ?string $errorMessage,

Broadcast​

  • Channel: PrivateChannel('admin')
  • broadcastAs: youtube.research.batch.progress
  • Interface: ShouldBroadcastNow (synchron, kein Queue-Delay)
  • Trait: SuppressesBroadcastFailures
  • Payload: Inkl. berechneter progress Prozent (0-100)

Trigger​

Dispatched von ProcessYouTubeResearchBatch nach jedem verarbeiteten Keyword und bei Batch-Completion.


ContactLinkProfileImported​

Broadcast wenn ein Kontaktlink-Import erfolgreich abgeschlossen wurde (Profil wurde gefunden/erstellt und verknüpft).

Location: app/Events/ContactLinkProfileImported.php

Properties​

public int $sourceProfileId,
public int $importedProfileId,
public int $workspaceId,
public int $triggeredBy,

Broadcast​

  • Channel: PrivateChannel("workspace.{workspaceId}")
  • broadcastAs: contact.link.imported
  • Payload: sourceHandle, importedHandle, platform, watcherId, message
  • Trait: SuppressesBroadcastFailures

Trigger​

Dispatched von ImportContactLinkProfile nach erfolgreichem Import.


WatcherImportProgress​

Broadcast von Fortschritts-Updates während eines Watcher-Imports. Wird nach jeder verarbeiteten URL gesendet.

Location: app/Events/WatcherImportProgress.php

Properties​

public int $importRunId,
public int $workspaceId,
public int $processedCount,
public int $totalCount,
public int $addedCount,
public int $failedCount,
public int $alreadyInWorkspaceCount,
public string $status,
public ?string $lastProcessedUrl = null,
public ?string $lastResult = null,

Broadcast​

  • Channel: PrivateChannel("workspace.{workspaceId}")
  • broadcastAs: import.progress
  • broadcastQueue: imports-youtube
  • Payload: Inkl. berechneter progress Prozent (0-100)
  • Trait: SuppressesBroadcastFailures

Trigger​

Dispatched von ImportWatcherFromUrl nach jeder verarbeiteten URL und bei Run-Completion.


YouTubeVideoSyncCompleted​

Broadcast wenn ein YouTube-Video-Stats-Sync abgeschlossen ist (Erfolg oder Fehler).

Location: app/Events/YouTubeVideoSyncCompleted.php

Properties​

public int $userId,
public int $socialProfileId,
public string $profileHandle,
public ?string $profileTitle,
public int $videosSynced,
public bool $success,
public ?string $errorMessage = null,
public bool $triggeredManually = false,
public ?int $watcherId = null,

Broadcast​

  • Channel: PrivateChannel("user.{userId}")
  • broadcastAs: youtube.sync.completed
  • Interface: ShouldBroadcastNow (synchron, kein Queue-Delay)
  • Trait: SuppressesBroadcastFailures
  • Message: "YouTube-Statistiken für @{handle} aktualisiert ({n} Videos)" oder Fehlermeldung

Trigger​

Nur bei manuell angefordertem Sync (requested_by gesetzt). Automatische nightly Syncs senden kein Event.


DailyScrapeCompleted​

Broadcast wenn alle täglichen Scrapes für einen User abgeschlossen sind.

Location: app/Events/DailyScrapeCompleted.php

Properties​

public int $userId,
public string $date,
public int $expectedCount,
public int $completedCount,
public int $failedCount,

Broadcast​

  • Channel: PrivateChannel("user.{userId}")
  • broadcastAs: daily.scrape.completed
  • Trait: SuppressesBroadcastFailures
  • Payload: Inkl. berechneter successRate (Prozent)
  • Message: "Tägliche Aktualisierung abgeschlossen: X/Y Profile aktualisiert"

Rotation-System​

expectedCount berücksichtigt das Rotation-System -- nicht alle Profile werden täglich gescrapt, nur die des aktuellen Rotation-Buckets.


RelatedProfilesCalculated​

Broadcast wenn die Berechnung ähnlicher Profile/Channels abgeschlossen ist.

Location: app/Events/RelatedProfilesCalculated.php

Properties​

public int $socialProfileId,
public string $platform, // 'youtube', 'instagram', 'cross_platform_youtube', ...
public string $profileHandle,
public ?string $profileTitle,
public string $status, // 'completed' oder 'failed'
public int $foundCount,
public ?string $errorMessage = null,
public ?int $triggeredByUserId = null,
public ?int $watcherId = null,

Broadcast​

  • Channels: PrivateChannel("profile.{socialProfileId}") + optional PrivateChannel("user.{triggeredByUserId}")
  • broadcastAs: related.profiles.calculated
  • Trait: SuppressesBroadcastFailures

Der User-Channel wird nur hinzugefügt wenn triggeredByUserId gesetzt ist (manueller Trigger).

Trigger​

Dispatched von FindRelatedYouTubeChannels, FindRelatedInstagramProfiles und FindCrossPlatformRelatedProfiles.


ProfileReactivated​

Broadcast wenn ein deaktiviertes Profil erfolgreich reaktiviert wurde. Gesendet an alle User, die einen Watcher für dieses Profil haben.

Location: app/Events/ProfileReactivated.php

Properties​

public int $socialProfileId,
public string $profileHandle,
public ?string $profileTitle,
public string $platform,
public ?int $triggeredByUserId = null,
public bool $isManualRetry = false,

Broadcast​

  • Channels: PrivateChannel("user.{userId}") für jeden betroffenen User
  • broadcastAs: profile.reactivated
  • Trait: SuppressesBroadcastFailures
  • Message: "Profil @{handle} wurde reaktiviert und wird wieder aktualisiert"

Betroffene User ermitteln​

// Constructor: Join über watcher_sources -> watchers -> workspaces
$this->userIds = DB::table('watcher_sources')
->join('watchers', ...)
->join('workspaces', ...)
->where('social_profile_id', $this->socialProfileId)
->distinct()
->pluck('workspaces.owner_id');

Trigger​

Dispatched von RetryInactiveProfileScrape bei erfolgreichem Retry.


ProfileSanitized​

Broadcast wenn ein Profil durch den Sanitizer automatisch deaktiviert wurde.

Location: app/Events/ProfileSanitized.php

Properties​

public int $socialProfileId,
public string $profileHandle,
public ?string $profileTitle,
public string $platform,
public string $reason,

Broadcast​

  • Channels: PrivateChannel("user.{userId}") für jeden betroffenen User
  • broadcastAs: profile.sanitized
  • Trait: SuppressesBroadcastFailures
  • Message: "Profil @{handle} wurde automatisch deaktiviert: {reason}"

Trigger​

Dispatched von profiles:sanitize Command.


ProfileUnsanitized​

Broadcast wenn ein zuvor sanitized Profil wieder aktiviert wird (Admin Override).

Location: app/Events/ProfileUnsanitized.php

Properties​

public int $socialProfileId,
public string $profileHandle,
public ?string $profileTitle,
public string $platform,

Broadcast​

  • Channels: PrivateChannel("user.{userId}") für jeden betroffenen User
  • broadcastAs: profile.unsanitized
  • Trait: SuppressesBroadcastFailures
  • Message: "Profil @{handle} wurde reaktiviert und wird wieder aktualisiert"

Trigger​

Dispatched von Admin\SocialProfiles\Index::enableTracking().


UserRegistered​

Broadcast wenn sich ein neuer User registriert. Nur an Admins.

Location: app/Events/UserRegistered.php

Properties​

public int $userId,
public string $userName,
public string $userEmail,
public ?string $userAvatar,

Broadcast​

  • Channel: PrivateChannel('admin')
  • broadcastAs: user.registered
  • Trait: SuppressesBroadcastFailures
  • Message: "Neuer User registriert: {name} ({email})"

Trigger​

Dispatched von UserObserver::created() (indirekt über NotificationCenter).


TestBroadcast​

Test-Event für die Verifikation der Reverb WebSocket-Konnektivität. Einziges Event auf einem public Channel.

Location: app/Events/TestBroadcast.php

Properties​

public string $message,
public string $type = 'info', // 'success', 'info', 'warning', 'danger'

Broadcast​

  • Channel: Channel('test-channel') (public, keine Auth)
  • broadcastAs: toast.show
  • Interface: ShouldBroadcastNow (synchron, kein Queue)
  • Kein SuppressesBroadcastFailures Trait (Test soll Fehler zeigen)

Trigger​

Admin-Seite /admin/reverb-test oder CLI:

php artisan tinker --execute='App\Events\TestBroadcast::dispatch("Test", "success");'

ServerMetricsUpdated​

Broadcast von Server-Metriken (CPU, RAM, Disk, etc.) an das Admin-Dashboard. Wird alle ~15 Sekunden gesendet.

Location: app/Events/ServerMetricsUpdated.php

Properties​

public array $metrics,    // Komplettes Metriken-Array

Broadcast​

  • Channel: PrivateChannel('admin')
  • broadcastAs: server.metrics.updated
  • Interface: ShouldBroadcastNow (Metriken müssen sofort ankommen)
  • Trait: SuppressesBroadcastFailures

ServerAlertTriggered​

Broadcast wenn eine Server-Metrik einen Warn-/Kritisch-Schwellwert überschreitet.

Location: app/Events/ServerAlertTriggered.php

Properties​

public string $metric,       // z.B. 'cpu_percent'
public string $level, // 'warning' oder 'critical'
public float $value, // Aktueller Wert
public float $threshold, // Überschrittener Schwellwert
public string $message, // Menschenlesbare Nachricht

Broadcast​

  • Channel: PrivateChannel('admin')
  • broadcastAs: server.alert.triggered
  • Interface: ShouldBroadcastNow
  • Trait: SuppressesBroadcastFailures

Trigger​

Dispatched von ServerAlertService::check() via server:check-alerts Command (alle 5 min).


Channel-Autorisierung​

Alle privaten Channels werden in routes/channels.php autorisiert:

// User kann nur eigenen Kanal abonnieren
Broadcast::channel('user.{userId}', fn ($user, $userId) =>
(int) $user->id === (int) $userId
);

// User muss Mitglied des Workspace sein
Broadcast::channel('workspace.{workspaceId}', fn ($user, $workspaceId) =>
$user->workspaces()->where('workspaces.id', $workspaceId)->exists()
);

// User muss Watcher mit diesem Profil haben
Broadcast::channel('profile.{profileId}', fn ($user, $profileId) =>
WatcherSource::query()
->where('social_profile_id', $profileId)
->whereHas('watcher', fn ($q) => $q->whereIn('workspace_id', ...))
->exists()
);

// Nur Admins
Broadcast::channel('admin', fn ($user) => $user->isAdmin());

Location: routes/channels.php