option('out') ?: storage_path('app/mail-previews')); if (! is_dir($directory) && ! mkdir($directory, 0o755, true) && ! is_dir($directory)) { $this->components->error("Could not create output directory: {$directory}"); return self::FAILURE; } $user = $this->fakeUser(); $invitation = $this->fakeInvitation(); [$activeIncident, $resolvedIncident] = $this->fakeIncidents(); /** @var array $mailables */ $mailables = [ 'welcome' => fn (): Mailable => new WelcomeEmail($user), 'forgotten_password' => fn (): Mailable => new ForgottenPassword('https://example.test/reset/abc123'), 'password_reset' => fn (): Mailable => new PasswordReset($user, 'TempPass!2026'), 'new_account_created' => fn (): Mailable => new NewAccountCreatedEmail($user), 'account_change' => fn (): Mailable => new AccountChange($user), 'account_will_expire' => fn (): Mailable => new AccountWillExpire($user, 5), 'account_expired' => fn (): Mailable => new AccountExpired($user), 'account_deleted' => fn (): Mailable => new AccountDeleted($user), 'contact_us' => fn (): Mailable => new ContactUs( 'admin@example.test', 'visitor@example.test', "Hello!\nQuestion inside.", ), 'invitation' => fn (): Mailable => new InvitationMail($invitation), 'incident_detected' => fn (): Mailable => new IncidentDetected($activeIncident, false), 'incident_resolved' => fn (): Mailable => new IncidentDetected($resolvedIncident, true), ]; foreach ($mailables as $name => $factory) { $html = $factory()->render(); file_put_contents("{$directory}/{$name}.html", $html); $this->components->info("Wrote {$name}.html (".number_format(strlen($html)).' bytes)'); } $this->writeVerifyPreview($directory); $this->newLine(); $this->components->info("All previews written to: {$directory}"); return self::SUCCESS; } private function writeVerifyPreview(string $directory): void { $notifiable = new class { public int $id = 7; public string $email = 'tester@example.test'; public string $username = 'tester'; public function getKey(): int { return $this->id; } public function getEmailForVerification(): string { return $this->email; } }; $message = (new VerifyEmailBranded)->toMail($notifiable); $markdown = app(Markdown::class); $html = (string) $markdown->render($message->markdown, $message->data()); file_put_contents("{$directory}/verify_email.html", $html); $this->components->info('Wrote verify_email.html ('.number_format(strlen($html)).' bytes)'); } private function fakeUser(): User { $user = new User; $user->id = 99; $user->username = 'tester'; $user->email = 'tester@example.test'; $user->setRelation('role', new class { public string $name = 'Silver'; }); return $user; } private function fakeInvitation(): Invitation { $inviter = new User; $inviter->id = 1; $inviter->username = 'mod_alice'; $invitation = new Invitation; $invitation->token = 'preview-token-abc123'; $invitation->email = 'newuser@example.test'; $invitation->expires_at = Carbon::now()->addDays(7); $invitation->setRelation('invitedBy', $inviter); return $invitation; } /** * @return array{0: ServiceIncident, 1: ServiceIncident} */ private function fakeIncidents(): array { $services = collect([ (object) ['name' => 'NZB Search'], (object) ['name' => 'API'], ]); $active = new ServiceIncident; $active->title = 'API latency spike'; $active->description = "p99 latency exceeded thresholds.\nFollow status page for updates."; $active->status = IncidentStatusEnum::Identified; $active->impact = IncidentImpactEnum::Critical; $active->started_at = Carbon::now()->subMinutes(15); $active->setRelation('services', $services); $resolved = clone $active; $resolved->status = IncidentStatusEnum::Resolved; $resolved->resolved_at = Carbon::now(); return [$active, $resolved]; } }