124 lines
4.1 KiB
PHP
124 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Mail;
|
|
|
|
use App\Mail\CloudResumedMail;
|
|
use App\Mail\CloudSuspendedMail;
|
|
use App\Mail\ContactRequestMail;
|
|
use App\Mail\DormantAccountWarningMail;
|
|
use App\Mail\DunningNoticeMail;
|
|
use App\Mail\InvoiceMail;
|
|
use App\Mail\MaintenanceAnnouncementMail;
|
|
use App\Mail\MaintenanceCancelledMail;
|
|
use App\Mail\NewDeviceSignInMail;
|
|
use App\Mail\OperatorInvitationMail;
|
|
use App\Mail\OperatorMessageMail;
|
|
use App\Mail\OrderConfirmationMail;
|
|
use App\Mail\ResetPasswordMail;
|
|
use App\Mail\SecurityBlockMail;
|
|
use App\Mail\VerifyEmailMail;
|
|
use App\Support\Settings;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* In welcher Spur eine Mail fährt.
|
|
*
|
|
* Drei Spuren, und die Trennung läuft nicht zwischen Massenversand und
|
|
* Einzelmail, sondern zwischen dringend und nicht dringend. Eine
|
|
* Ausfallmeldung geht an alle UND eilt — sie bekommt deshalb ein höheres
|
|
* Kontingent statt gar keines.
|
|
*
|
|
* Die Vorgaben stehen hier, die Änderung des Betreibers darüber in den
|
|
* Einstellungen. Eine unbekannte Klasse fällt in die ruhige Spur: eine zu
|
|
* langsam verschickte Mail ist ein kleinerer Fehler als ein ungedrosselter
|
|
* Schub, den niemand vorhergesehen hat.
|
|
*/
|
|
final class MailLane
|
|
{
|
|
public const DIRECT = 'mail-direkt';
|
|
|
|
public const URGENT = 'mail-wichtig';
|
|
|
|
public const CALM = 'mail-ruhig';
|
|
|
|
/**
|
|
* Die acht, auf die gerade jemand wartet.
|
|
*
|
|
* Sie entstehen einzeln, weil ein einzelner Mensch geklickt hat, und
|
|
* können gar keinen Schub bilden. Drosseln nützt dort nichts und kostet
|
|
* einen Supportfall je verzögertem Kennwort — deshalb sind sie gesperrt
|
|
* und nicht nur voreingestellt.
|
|
*/
|
|
private const LOCKED = [
|
|
ResetPasswordMail::class,
|
|
VerifyEmailMail::class,
|
|
NewDeviceSignInMail::class,
|
|
SecurityBlockMail::class,
|
|
ContactRequestMail::class,
|
|
OrderConfirmationMail::class,
|
|
OperatorMessageMail::class,
|
|
// Die Einladung in die Konsole. Der Inhaber hat gerade geklickt und
|
|
// sagt dem Eingeladenen im selben Moment „schau in dein Postfach" —
|
|
// eine Einladung, die eine Viertelstunde in der ruhigen Spur liegt,
|
|
// ist ein Anruf.
|
|
OperatorInvitationMail::class,
|
|
];
|
|
|
|
/** @var array<class-string, string> */
|
|
private const DEFAULTS = [
|
|
MaintenanceAnnouncementMail::class => self::URGENT,
|
|
MaintenanceCancelledMail::class => self::URGENT,
|
|
CloudSuspendedMail::class => self::URGENT,
|
|
CloudResumedMail::class => self::URGENT,
|
|
InvoiceMail::class => self::CALM,
|
|
DunningNoticeMail::class => self::CALM,
|
|
DormantAccountWarningMail::class => self::CALM,
|
|
];
|
|
|
|
/** Die Schlange, in die diese Mailklasse gehört. */
|
|
public static function for(string $mailableClass): string
|
|
{
|
|
if (self::isLocked($mailableClass)) {
|
|
return self::DIRECT;
|
|
}
|
|
|
|
$stored = (array) Settings::get('mail.lanes', []);
|
|
$lane = $stored[$mailableClass] ?? self::DEFAULTS[$mailableClass] ?? self::CALM;
|
|
|
|
return in_array($lane, [self::DIRECT, self::URGENT, self::CALM], true) ? $lane : self::CALM;
|
|
}
|
|
|
|
public static function isLocked(string $mailableClass): bool
|
|
{
|
|
return in_array($mailableClass, self::LOCKED, true);
|
|
}
|
|
|
|
/** Verschiebt eine Mailklasse. Wirft, wenn sie gesperrt ist. */
|
|
public static function assign(string $mailableClass, string $lane): void
|
|
{
|
|
if (self::isLocked($mailableClass)) {
|
|
throw new RuntimeException(
|
|
"{$mailableClass} fährt immer direkt: darauf wartet gerade jemand."
|
|
);
|
|
}
|
|
|
|
$stored = (array) Settings::get('mail.lanes', []);
|
|
$stored[$mailableClass] = $lane;
|
|
Settings::set('mail.lanes', $stored);
|
|
}
|
|
|
|
/**
|
|
* Jede bekannte Mailklasse mit ihrer Spur, für die Konsole.
|
|
*
|
|
* @return array<class-string, string>
|
|
*/
|
|
public static function all(): array
|
|
{
|
|
$classes = array_merge(self::LOCKED, array_keys(self::DEFAULTS));
|
|
|
|
return collect($classes)
|
|
->mapWithKeys(fn (string $class) => [$class => self::for($class)])
|
|
->all();
|
|
}
|
|
}
|