mailwolt/database/seeders/SystemDomainSeeder.php

664 lines
26 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace Database\Seeders;
use App\Models\DkimKey;
use App\Models\Domain;
use App\Models\MailAlias;
use App\Models\MailAliasRecipient;
use App\Models\MailUser;
use App\Models\TlsaRecord;
use App\Services\DnsRecordService;
use App\Services\TlsaService;
use Illuminate\Database\Seeder;
use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Str;
class SystemDomainSeeder extends Seeder
{
public function run(): void
{
// ---- Basis aus ENV/Config ------------------------------------------------
$platformBase = strtolower(config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com'))); // z.B. nexlab.at
if (!$platformBase || $platformBase === 'example.com') {
$this->command->warn("BASE_DOMAIN ist 'example.com' Seeder überspringt produktive Einträge.");
return;
}
$mtaSub = strtolower(env('MTA_SUB', 'mx') ?: 'mx'); // z.B. mx
$systemSub = strtolower(config('mailpool.platform_system_zone') ?: 'sysmail'); // z.B. sysmail
$serverFqdn = strtolower("{$mtaSub}.{$platformBase}"); // mx.nexlab.at
$systemFqdn = strtolower("{$systemSub}.{$platformBase}"); // sysmail.nexlab.at
// =========================================================================
// 1) MAILSERVER-DOMAIN zuerst (mx.<BASE_DOMAIN>), is_server = true
// =========================================================================
$serverDomain = Domain::firstOrCreate(
['domain' => $serverFqdn],
[
'is_active' => true,
'is_system' => false,
'is_server' => true,
// nix anlegen unter mx.<base>
'max_mailboxes' => 0,
'max_aliases' => 0,
// Quotas praktisch sperren
'default_quota_mb' => 0,
'max_quota_per_mailbox_mb' => 0,
'total_quota_mb' => 0,
// kein Versand über diese Domain
'rate_limit_per_hour' => 0,
'rate_limit_override' => false,
]
);
$serverDomain->fill([
'is_active' => true,
'is_system' => false,
'is_server' => true,
'max_mailboxes' => 0,
'max_aliases' => 0,
'default_quota_mb' => 0,
'max_quota_per_mailbox_mb' => 0,
'total_quota_mb' => 0,
'rate_limit_per_hour' => 0,
'rate_limit_override' => false,
])->save();
Domain::where('is_server', true)
->where('id', '!=', $serverDomain->id)
->update(['is_server' => false]);
// Falls bereits vorhanden, sicherstellen, dass is_server=true bleibt
if (!$serverDomain->is_server) {
$serverDomain->is_server = true;
$serverDomain->save();
$this->command->info("Server-Host markiert: {$serverDomain->domain}");
}
// --- TLSA (3 1 1) prüfen/erzeugen (nur wenn noch nicht vorhanden) -------
$hasTlsa = TlsaRecord::where('domain_id', $serverDomain->id)
->where('host', $serverFqdn)
->where('service', '_25._tcp')
->exists();
if (!$hasTlsa) {
// TlsaService nutzt denselben Host aus ENV (mx.<base>) passt zu serverFqdn.
// $tlsa = app(TlsaService::class)->refreshForServerDomain($serverDomain);
$tlsa = app(TlsaService::class)->refreshForMx('_25._tcp');
if ($tlsa) {
$this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
} else {
$path = "/etc/letsencrypt/live/{$serverFqdn}/fullchain.pem";
$this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden unter {$path}");
}
} else {
$this->command->line("TLSA bereits vorhanden, übersprungen.");
}
// =========================================================================
// 2) SYSTEM-DOMAIN danach (sysmail.<BASE_DOMAIN>), is_system = true
// =========================================================================
$systemDomain = Domain::firstOrCreate(
['domain' => $systemFqdn],
[
'is_active' => true,
'is_system' => true,
// Limits
'max_aliases' => 20,
'max_mailboxes'=> 1,
// Quota
'default_quota_mb' => 512,
'max_quota_per_mailbox_mb' => 2048,
'total_quota_mb' => 2048,
// Rate limiting
'rate_limit_per_hour' => 600,
'rate_limit_override' => false,
]
);
$systemDomain->fill([
'is_active' => true,
'is_system' => true,
'max_aliases' => 20,
'max_mailboxes' => 1,
'default_quota_mb' => 512,
'max_quota_per_mailbox_mb' => 2048,
'total_quota_mb' => 2048,
'rate_limit_per_hour' => 600,
'rate_limit_override' => false,
])->save();
if ($systemDomain->wasRecentlyCreated) {
$this->command->line("System-Domain angelegt: {$systemDomain->domain}");
}
$noReply = MailUser::firstOrCreate(
['domain_id' => $systemDomain->id, 'localpart' => 'no-reply'],
[
'email' => 'no-reply@' . $systemDomain->domain,
'password_hash' => null,
'is_active' => true,
'is_system' => true,
'quota_mb' => 0,
]
);
$addRecipient = function (MailAlias $alias, MailUser $user) {
// sichere, vollständige Adresse bauen
$user->loadMissing('domain');
$addr = $user->localpart.'@'.$user->domain->domain;
MailAliasRecipient::create([
'alias_id' => $alias->id,
'mail_user_id' => $user->id, // Referenz
'email' => $addr, // denormalisierte, lesbare Adresse
'position' => 0,
]);
};
$seedGroup = function (string $local, MailUser $user) use ($systemDomain, $addRecipient) {
$alias = MailAlias::updateOrCreate(
['domain_id' => $systemDomain->id, 'local' => $local],
['type' => 'group', 'is_active' => true, 'is_system' => true]
);
$alias->recipients()->delete();
$addRecipient($alias, $user);
};
$seedGroup('system', $noReply);
$seedGroup('bounces', $noReply);
$seedGroup('postmaster', $noReply);
$seedGroup('abuse', $noReply);
$this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
$this->printDnsHints($systemDomain);
}
/** @return array{0:string privatePem,1:string publicTxt} */
private function generateDkimKeyPair(): array
{
$res = openssl_pkey_new([
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
openssl_pkey_export($res, $privateKeyPem);
$details = openssl_pkey_get_details($res);
$pubDer = $details['key'];
// Public PEM zu reinem Base64 für DKIM p= normalisieren
$publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
return [$privateKeyPem, $publicTxt];
}
private function printDnsHints(Domain $domain): void
{
$base = $domain->domain;
$dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
if ($dkim) {
$this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
$this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
}
$spf = $domain->spf()->where('is_active', true)->latest()->first();
if ($spf) {
$this->command->line(" • SPF TXT @ {$base}");
$this->command->line(" {$spf->record_txt}");
}
$dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
if ($dmarc) {
$this->command->line(" • DMARC TXT @ _dmarc.{$base}");
$this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
}
}
}
//namespace Database\Seeders;
//
//use App\Models\DkimKey;
//use App\Models\Domain;
//use App\Models\MailUser;
//use App\Models\TlsaRecord;
//
//// ← NEU: fürs exists()
//use App\Services\DnsRecordService;
//use App\Services\TlsaService;
//use Illuminate\Database\Seeder;
//
//class SystemDomainSeeder extends Seeder
//{
// public function run(): void
// {
// // --- Basiswerte aus Config/ENV ---
// $platformBase = config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com')); // z.B. nexlab.at
// if (!$platformBase || $platformBase === 'example.com') {
// $this->command->warn("BASE_DOMAIN ist 'example.com' Seeder überspringt produktive Einträge.");
// return;
// }
//
// $mtaSub = env('MTA_SUB', 'mx') ?: 'mx'; // z.B. mx
// $serverFqdn = "{$mtaSub}.{$platformBase}"; // z.B. mx.nexlab.at
//
// $systemSub = config('mailpool.platform_system_zone') ?: 'sysmail';
// $systemFqdn = "{$systemSub}.{$platformBase}"; // z.B. sysmail.nexlab.at
//
// // =========================================================================
// // 1) MAILSERVER-DOMAIN zuerst (mx.<BASE_DOMAIN>), is_server = true
// // =========================================================================
// $serverDomain = Domain::firstOrCreate(
// ['domain' => $serverFqdn],
// ['is_active' => true, 'is_system' => false, 'is_server' => true]
// );
//
// if (!$serverDomain->is_server) {
// $serverDomain->is_server = true;
// $serverDomain->save();
// $this->command->info("Server-Host markiert: {$serverDomain->domain}");
// }
//
// // --- TLSA (3 1 1) prüfen/erzeugen (nur wenn LE-Zert schon da ist) -----
// $hasTlsa = TlsaRecord::where('domain_id', $serverDomain->id)
// ->where('host', $serverFqdn)
// ->where('service', '_25._tcp')
// ->exists();
//
// if (!$hasTlsa) {
// $tlsa = app(TlsaService::class)->refreshForServerDomain($serverDomain);
// if ($tlsa) {
// $this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
// } else {
// $path = "/etc/letsencrypt/live/{$serverDomain->domain}/fullchain.pem";
// $this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden unter {$path}");
// }
// }
//
// // =========================================================================
// // 2) SYSTEM-DOMAIN danach (sysmail.<BASE_DOMAIN>), is_system = true
// // =========================================================================
// $systemDomain = Domain::firstOrCreate(
// ['domain' => $systemFqdn],
// ['is_active' => true, 'is_system' => true]
// );
//
// // System-Absender (no-reply) ohne Passwort (kein Login)
// MailUser::firstOrCreate(
// ['email' => "no-reply@{$systemFqdn}"],
// [
// 'domain_id' => $systemDomain->id,
// 'localpart' => 'no-reply',
// 'password_hash' => null,
// 'is_active' => true,
// 'is_system' => true,
// 'must_change_pw' => false,
// 'quota_mb' => 0,
// ]
// );
//
// // DKIM Key erzeugen, falls keiner aktiv existiert
// if (!$systemDomain->dkimKeys()->where('is_active', true)->exists()) {
// [$privPem, $pubTxt] = $this->generateDkimKeyPair();
// $selector = 'mwl1'; // frei wählbar, später rotieren
//
// DkimKey::create([
// 'domain_id' => $systemDomain->id,
// 'selector' => $selector,
// 'private_key_pem' => $privPem,
// 'public_key_txt' => $pubTxt,
// 'is_active' => true,
// ]);
//
// $this->command->info("DKIM angelegt: Host = {$selector}._domainkey.{$systemFqdn}");
// }
//
// $dk = $systemDomain->dkimKeys()->where('is_active', true)->latest()->first();
// $dkimTxt = $dk ? "v=DKIM1; k=rsa; p={$dk->public_key_txt}" : null;
//
// app(DnsRecordService::class)->provision(
// $systemDomain,
// dkimSelector: $dk?->selector,
// dkimTxt: $dkimTxt,
// opts: [
// 'dmarc_policy' => 'none',
// 'spf_tail' => '-all',
// // optional: 'ipv4' => $serverIp, 'ipv6' => ...
// ]
// );
//
// $this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
// $this->printDnsHints($systemDomain);
// }
//
// /** @return array{0:string privatePem,1:string publicTxt} */
// private function generateDkimKeyPair(): array
// {
// $res = openssl_pkey_new([
// 'private_key_bits' => 2048,
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
// ]);
// openssl_pkey_export($res, $privateKeyPem);
// $details = openssl_pkey_get_details($res);
// $pubDer = $details['key'];
// $publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
// return [$privateKeyPem, $publicTxt];
// }
//
// private function printDnsHints(Domain $domain): void
// {
// $base = $domain->domain;
//
// $dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
// if ($dkim) {
// $this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
// $this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
// }
//
// $spf = $domain->spf()->where('is_active', true)->latest()->first();
// if ($spf) {
// $this->command->line(" • SPF TXT @ {$base}");
// $this->command->line(" {$spf->record_txt}");
// }
//
// $dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
// if ($dmarc) {
// $this->command->line(" • DMARC TXT @ _dmarc.{$base}");
// $this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
// }
// }
//}
//namespace Database\Seeders;
//
//use App\Models\DkimKey;
//use App\Models\Domain;
//use App\Models\MailUser;
//use App\Services\DnsRecordService;
//use App\Services\TlsaService;
//use Illuminate\Database\Seeder;
//
//class SystemDomainSeeder extends Seeder
//{
// public function run(): void
// {
// // --- Basiswerte aus Config/ENV ---
// $platformBase = config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com')); // z.B. nexlab.at
// if (!$platformBase || $platformBase === 'example.com') {
// $this->command->warn("BASE_DOMAIN ist 'example.com' Seeder überspringt produktive Einträge.");
// return;
// }
//
// $mtaSub = env('MTA_SUB', 'mx') ?: 'mx'; // z.B. mx
// $serverFqdn = "{$mtaSub}.{$platformBase}"; // z.B. mx.nexlab.at
//
// $systemSub = config('mailpool.platform_system_zone') ?: 'sysmail'; // z.B. sysmail
// $systemFqdn = "{$systemSub}.{$platformBase}"; // z.B. sysmail.nexlab.at
//
// // =========================================================================
// // 1) MAILSERVER-DOMAIN zuerst (mx.<BASE_DOMAIN>), is_server = true
// // =========================================================================
// $serverDomain = Domain::firstOrCreate(
// ['domain' => $serverFqdn],
// ['is_active' => true, 'is_system' => false, 'is_server' => true]
// );
//
// if (!$serverDomain->is_server) {
// $serverDomain->is_server = true;
// $serverDomain->save();
// $this->command->info("Server-Host markiert: {$serverDomain->domain}");
// }
//
// // TLSA (3 1 1) nur in Laravel erzeugen/aktualisieren (falls LE-Zert bereits da ist)
// if (! $serverDomain->tlsa()->exists()) {
// $tlsa = app(\App\Services\TlsaService::class)->refreshForServerDomain($serverDomain);
// if ($tlsa) {
// $this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
// } else {
// $this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden später erneut seeden.");
// }
// } else {
// $this->command->line("TLSA bereits vorhanden, übersprungen.");
// }
//
// // =========================================================================
// // 2) SYSTEM-DOMAIN danach (sysmail.<BASE_DOMAIN>), is_system = true
// // =========================================================================
// $systemDomain = Domain::firstOrCreate(
// ['domain' => $systemFqdn],
// ['is_active' => true, 'is_system' => true]
// );
//
// // System-Absender (no-reply) ohne Passwort (kein Login)
// MailUser::firstOrCreate(
// ['email' => "no-reply@{$systemFqdn}"],
// [
// 'domain_id' => $systemDomain->id,
// 'localpart' => 'no-reply',
// 'password_hash' => null,
// 'is_active' => true,
// 'is_system' => true,
// 'must_change_pw' => false,
// 'quota_mb' => 0,
// ]
// );
//
// // DKIM Key erzeugen, falls keiner aktiv existiert
// if (!$systemDomain->dkimKeys()->where('is_active', true)->exists()) {
// [$privPem, $pubTxt] = $this->generateDkimKeyPair();
// $selector = 'mwl1'; // frei wählbar, später rotieren
//
// DkimKey::create([
// 'domain_id' => $systemDomain->id,
// 'selector' => $selector,
// 'private_key_pem' => $privPem,
// 'public_key_txt' => $pubTxt,
// 'is_active' => true,
// ]);
//
// $this->command->info("DKIM angelegt: Host = {$selector}._domainkey.{$systemFqdn}");
// }
//
// $dk = $systemDomain->dkimKeys()->where('is_active', true)->latest()->first();
// $dkimTxt = $dk ? "v=DKIM1; k=rsa; p={$dk->public_key_txt}" : null;
//
// app(DnsRecordService::class)->provision(
// $systemDomain,
// dkimSelector: $dk?->selector,
// dkimTxt: $dkimTxt,
// opts: [
// 'dmarc_policy' => 'none',
// 'spf_tail' => '-all',
// // optional: 'ipv4' => $serverIp, 'ipv6' => ...
// ]
// );
//
// $this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
// $this->printDnsHints($systemDomain);
// }
//
// /** @return array{0:string privatePem,1:string publicTxt} */
// private function generateDkimKeyPair(): array
// {
// $res = openssl_pkey_new([
// 'private_key_bits' => 2048,
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
// ]);
// openssl_pkey_export($res, $privateKeyPem);
// $details = openssl_pkey_get_details($res);
// $pubDer = $details['key'];
// // Public PEM zu "p=" (reines Base64) normalisieren
// $publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
// return [$privateKeyPem, $publicTxt];
// }
//
// private function printDnsHints(Domain $domain): void
// {
// $base = $domain->domain;
//
// $dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
// if ($dkim) {
// $this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
// $this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
// }
//
// $spf = $domain->spf()->where('is_active', true)->latest()->first();
// if ($spf) {
// $this->command->line(" • SPF TXT @ {$base}");
// $this->command->line(" {$spf->record_txt}");
// }
//
// $dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
// if ($dmarc) {
// $this->command->line(" • DMARC TXT @ _dmarc.{$base}");
// $this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
// }
// }
//}
//
//----------------------------------------------------------------
//
//namespace Database\Seeders;
//
//use App\Models\DkimKey;
//use App\Models\Domain;
//use App\Models\MailUser;
//use App\Services\DnsRecordService;
//use Illuminate\Database\Seeder;
//
//class SystemDomainSeeder extends Seeder
//{
// public function run(): void
// {
// $base = config('mailpool.platform_zone', 'example.com');
// if (!$base || $base === 'example.com') {
// $this->command->warn("BASE_DOMAIN ist 'example.com' Seeder überspringt produktive Einträge.");
// return;
// }
//
// $systemSub = config('mailpool.platform_system_zone');
// $base = "{$systemSub}.{$base}";
//
// // Domain anlegen/holen
// $domain = Domain::firstOrCreate(
// ['domain' => $base],
// ['is_active' => true, 'is_system' => true]
// );
//
// // System Absender (no-reply) ohne Passwort (kein Login)
// MailUser::firstOrCreate(
// ['email' => "no-reply@{$base}"],
// [
// 'domain_id' => $domain->id,
// 'localpart' => 'no-reply',
// 'password_hash' => null,
// 'is_active' => true,
// 'is_system' => true,
// 'must_change_pw' => false,
// 'quota_mb' => 0,
// ]
// );
//
// // DKIM Key erzeugen, falls keiner aktiv existiert
// if (! $domain->dkimKeys()->where('is_active', true)->exists()) {
// [$privPem, $pubTxt] = $this->generateDkimKeyPair();
// $selector = 'mwl1'; // frei wählbar, z. B. rotierend später
//
// DkimKey::create([
// 'domain_id' => $domain->id,
// 'selector' => $selector,
// 'private_key_pem'=> $privPem,
// 'public_key_txt' => $pubTxt,
// 'is_active' => true,
// ]);
//
// $this->command->info("DKIM angelegt: Host = {$selector}._domainkey.{$base}");
// }
//
// $dk = $domain->dkimKeys()->where('is_active', true)->latest()->first();
// $dkimTxt = $dk ? "v=DKIM1; k=rsa; p={$dk->public_key_txt}" : null;
//
// app(DnsRecordService::class)->provision(
// $domain,
// dkimSelector: $dk?->selector,
// dkimTxt: $dkimTxt,
// opts: [
// 'dmarc_policy' => 'none',
// 'spf_tail' => '-all',
// // optional: 'ipv4' => $serverIp, 'ipv6' => ...
// ]
// );
//
// $base = config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com')); // z.B. nexlab.at
// $mta = env('MTA_SUB', 'mx') ?: 'mx';
// $serverHostFqdn = "{$mta}.{$base}";
//
// $serverHostDomain = \App\Models\Domain::firstOrCreate(
// ['domain' => $serverHostFqdn],
// ['is_active' => true, 'is_system' => false, 'is_server' => true]
// );
//
// if (!$serverHostDomain->is_server) {
// $serverHostDomain->is_server = true;
// $serverHostDomain->save();
// $this->command->info("Server-Host markiert: {$serverHostDomain->domain}");
// }
//
// $tlsa = app(\App\Services\TlsaService::class)->refreshForServerDomain($serverHostDomain);
// if ($tlsa) {
// $this->command->info("TLSA gespeichert: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
// } else {
// $this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden später erneut seeden.");
// }
//
// $this->command->info("System-Domain '{$base}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
// $this->printDnsHints($domain);
// }
//
// /** @return array{0:string privatePem,1:string publicTxt} */
// private function generateDkimKeyPair(): array
// {
// $res = openssl_pkey_new([
// 'private_key_bits' => 2048,
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
// ]);
// openssl_pkey_export($res, $privateKeyPem);
// $details = openssl_pkey_get_details($res);
// // $details['key'] ist PEM, wir brauchen Base64 ohne Header/Footers
// $pubDer = $details['key'];
// // Public PEM zu "p=" Wert (reines Base64) normalisieren
// $pubTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
//
// return [$privateKeyPem, $pubTxt];
// }
//
// private function printDnsHints(Domain $domain): void
// {
// $base = $domain->domain;
// $dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
// if ($dkim) {
// $this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
// $this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
// }
//
// $spf = $domain->spf()->where('is_active', true)->latest()->first();
// if ($spf) {
// $this->command->line(" • SPF TXT @ {$base}");
// $this->command->line(" {$spf->record_txt}");
// }
//
// $dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
// if ($dmarc) {
// $this->command->line(" • DMARC TXT @ _dmarc.{$base}");
// $this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
// }
// }
//}