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