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 class MailAlias extends Model
{ {
protected $fillable = ['domain_id','local','type','group_name','is_active','notes']; protected $fillable = ['domain_id','local','type','group_name','is_active','is_system','notes'];
protected $casts = ['is_active' => 'bool']; protected $casts = ['is_active' => 'bool', 'is_system' => 'bool'];
public function domain(): BelongsTo public function domain(): BelongsTo
{ {
@ -18,7 +18,7 @@ class MailAlias extends Model
public function recipients(): HasMany public function recipients(): HasMany
{ {
return $this->hasMany(MailAliasRecipient::class, 'alias_id'); return $this->hasMany(MailAliasRecipient::class, 'alias_id')->orderBy('position');
} }
public function getAddressAttribute(): string 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_system')->default(false)->index(); // oft nach Systemkonten filtern
$table->boolean('is_active')->default(true)->index(); $table->boolean('is_active')->default(true)->index();
$table->boolean('can_login')->default(true)->index();
$table->unsignedInteger('quota_mb')->default(0); // 0 = unlimited $table->unsignedInteger('quota_mb')->default(0); // 0 = unlimited
$table->unsignedInteger('rate_limit_per_hour')->nullable(); $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->enum('type', ['single','group'])->default('single');
$table->string('group_name', 80)->nullable(); $table->string('group_name', 80)->nullable();
$table->boolean('is_active')->default(true)->index(); $table->boolean('is_active')->default(true)->index();
$table->boolean('is_system')->default(false)->after('is_active')->index();
$table->text('notes')->nullable(); $table->text('notes')->nullable();
$table->timestamps(); $table->timestamps();

View File

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

View File

@ -4,11 +4,15 @@ namespace Database\Seeders;
use App\Models\DkimKey; use App\Models\DkimKey;
use App\Models\Domain; use App\Models\Domain;
use App\Models\MailAlias;
use App\Models\MailAliasRecipient;
use App\Models\MailUser; use App\Models\MailUser;
use App\Models\TlsaRecord; use App\Models\TlsaRecord;
use App\Services\DnsRecordService; use App\Services\DnsRecordService;
use App\Services\TlsaService; use App\Services\TlsaService;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Str;
class SystemDomainSeeder extends Seeder class SystemDomainSeeder extends Seeder
{ {
@ -137,7 +141,7 @@ class SystemDomainSeeder extends Seeder
} }
// System-Absender (no-reply) ohne Passwort (kein Login) // System-Absender (no-reply) ohne Passwort (kein Login)
MailUser::firstOrCreate( $noReply = MailUser::firstOrCreate(
['email' => "no-reply@{$systemFqdn}"], ['email' => "no-reply@{$systemFqdn}"],
[ [
'domain_id' => $systemDomain->id, 'domain_id' => $systemDomain->id,
@ -149,35 +153,27 @@ class SystemDomainSeeder extends Seeder
] ]
); );
// DKIM Key erzeugen, falls keiner aktiv existiert $seedGroup = function(string $local, array $emails) use ($systemDomain, $noReply) {
if (!$systemDomain->dkimKeys()->where('is_active', true)->exists()) { $alias = MailAlias::updateOrCreate(
[$privPem, $pubTxt] = $this->generateDkimKeyPair(); ['domain_id' => $systemDomain->id, 'local' => $local],
$selector = 'mwl1'; // frei wählbar, später rotieren ['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([ // alle vier erst einmal nur ans no-reply Postfach
'domain_id' => $systemDomain->id, $seedGroup('system', [$noReply->email]);
'selector' => $selector, $seedGroup('bounces', [$noReply->email]);
'private_key_pem' => $privPem, $seedGroup('postmaster', [$noReply->email]);
'public_key_txt' => $pubTxt, $seedGroup('abuse', [$noReply->email]);
'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->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
$this->printDnsHints($systemDomain); $this->printDnsHints($systemDomain);