Domain Create Modal anpassen Fehler auf Null

main
boban 2025-10-18 22:50:22 +02:00
parent 5d15a757b3
commit a78767809d
5 changed files with 32 additions and 44 deletions

View File

@ -8,8 +8,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
class MailAlias extends Model
{
protected $fillable = ['domain_id','local','type','group_name','is_active','notes'];
protected $casts = ['is_active' => 'bool'];
protected $fillable = ['domain_id','local','type','group_name','is_active','is_system','notes'];
protected $casts = ['is_active' => 'bool', 'is_system' => 'bool'];
public function domain(): BelongsTo
{
@ -18,7 +18,7 @@ class MailAlias extends Model
public function recipients(): HasMany
{
return $this->hasMany(MailAliasRecipient::class, 'alias_id');
return $this->hasMany(MailAliasRecipient::class, 'alias_id')->orderBy('position');
}
public function getAddressAttribute(): string

View File

@ -24,6 +24,7 @@ return new class extends Migration
$table->boolean('is_system')->default(false)->index(); // oft nach Systemkonten filtern
$table->boolean('is_active')->default(true)->index();
$table->boolean('can_login')->default(true)->index();
$table->unsignedInteger('quota_mb')->default(0); // 0 = unlimited
$table->unsignedInteger('rate_limit_per_hour')->nullable();

View File

@ -19,6 +19,7 @@ return new class extends Migration
$table->enum('type', ['single','group'])->default('single');
$table->string('group_name', 80)->nullable();
$table->boolean('is_active')->default(true)->index();
$table->boolean('is_system')->default(false)->after('is_active')->index();
$table->text('notes')->nullable();
$table->timestamps();

View File

@ -13,22 +13,12 @@ return new class extends Migration
{
Schema::create('mail_alias_recipients', function (Blueprint $table) {
$table->id();
$table->foreignId('alias_id')
->constrained('mail_aliases')
->cascadeOnDelete();
// interner Empfänger (MailUser) ODER externer Empfänger (E-Mail)
$table->foreignId('mail_user_id')
->nullable()
->constrained('mail_users') // <-- richtige Tabelle!
->nullOnDelete();
$table->foreignId('alias_id')->constrained('mail_aliases')->cascadeOnDelete();
$table->foreignId('mail_user_id')->nullable()->constrained('mail_users')->nullOnDelete();
$table->string('email', 320)->nullable(); // externer Empfänger
$table->unsignedSmallInteger('position')->default(0);
$table->timestamps();
// Duplikate vermeiden:
$table->unique(['alias_id','mail_user_id']);
$table->unique(['alias_id','email']);
});

View File

@ -4,11 +4,15 @@ 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
{
@ -137,7 +141,7 @@ class SystemDomainSeeder extends Seeder
}
// System-Absender (no-reply) ohne Passwort (kein Login)
MailUser::firstOrCreate(
$noReply = MailUser::firstOrCreate(
['email' => "no-reply@{$systemFqdn}"],
[
'domain_id' => $systemDomain->id,
@ -149,35 +153,27 @@ class SystemDomainSeeder extends Seeder
]
);
// 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
$seedGroup = function(string $local, array $emails) use ($systemDomain, $noReply) {
$alias = MailAlias::updateOrCreate(
['domain_id' => $systemDomain->id, 'local' => $local],
['type' => 'group', 'is_active' => true, 'is_system' => true]
);
$alias->recipients()->delete();
$pos=0;
foreach ($emails as $addr) {
MailAliasRecipient::create([
'alias_id' => $alias->id,
'email' => $addr,
'position' => $pos++,
]);
}
};
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' => ...
]
);
// alle vier erst einmal nur ans no-reply Postfach
$seedGroup('system', [$noReply->email]);
$seedGroup('bounces', [$noReply->email]);
$seedGroup('postmaster', [$noReply->email]);
$seedGroup('abuse', [$noReply->email]);
$this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
$this->printDnsHints($systemDomain);