diff --git a/app/Livewire/Setup/Wizard.php b/app/Livewire/Setup/Wizard.php
index 5e1e34a..29fb65a 100644
--- a/app/Livewire/Setup/Wizard.php
+++ b/app/Livewire/Setup/Wizard.php
@@ -112,7 +112,7 @@ class Wizard extends Component
$user->name = $this->form_admin_name;
$user->is_admin = true;
$user->password = Hash::make($this->form_admin_password);
- $user->must_change_pw = true;
+ $user->required_change_password = true;
$user->save();
// 3) Zertifikat jetzt ausstellen (optional)
diff --git a/app/Livewire/Ui/Mail/Modal/MailboxCreateModal.php b/app/Livewire/Ui/Mail/Modal/MailboxCreateModal.php
index 0bf167c..36e2f0a 100644
--- a/app/Livewire/Ui/Mail/Modal/MailboxCreateModal.php
+++ b/app/Livewire/Ui/Mail/Modal/MailboxCreateModal.php
@@ -31,7 +31,6 @@ class MailboxCreateModal extends ModalComponent
public int $quota_mb = 0;
public ?int $rate_limit_per_hour = null;
public bool $is_active = true;
- public bool $must_change_pw = true;
// Limits / Status
public ?int $limit_max_mailboxes = null;
@@ -85,7 +84,6 @@ class MailboxCreateModal extends ModalComponent
'quota_mb' => ['required', 'integer', 'min:0', 'max:' . $cap],
'rate_limit_per_hour' => ['nullable', 'integer', 'min:1'],
'is_active' => ['boolean'],
- 'must_change_pw' => ['boolean'],
];
}
@@ -256,7 +254,6 @@ class MailboxCreateModal extends ModalComponent
$u->password_hash = $this->password ? Hash::make($this->password) : null;
$u->is_system = false;
$u->is_active = (bool)$data['is_active'];
- $u->must_change_pw = (bool)$data['must_change_pw'];
$u->quota_mb = (int)$data['quota_mb'];
$u->rate_limit_per_hour = $data['rate_limit_per_hour'];
$u->save();
diff --git a/app/Livewire/Ui/Mail/Modal/MailboxEditModal.php b/app/Livewire/Ui/Mail/Modal/MailboxEditModal.php
index 6c4f257..cb73e2a 100644
--- a/app/Livewire/Ui/Mail/Modal/MailboxEditModal.php
+++ b/app/Livewire/Ui/Mail/Modal/MailboxEditModal.php
@@ -20,7 +20,6 @@ class MailboxEditModal extends ModalComponent
public int $quota_mb = 0;
public ?int $rate_limit_per_hour = null;
public bool $is_active = true;
- public bool $must_change_pw = true;
// UI
public string $email_readonly = '';
@@ -42,7 +41,6 @@ class MailboxEditModal extends ModalComponent
$this->quota_mb = (int)$this->mailbox->quota_mb;
$this->rate_limit_per_hour = $this->mailbox->rate_limit_per_hour;
$this->is_active = (bool)$this->mailbox->is_active;
- $this->must_change_pw = (bool)$this->mailbox->must_change_pw;
$dom = $this->mailbox->domain;
$this->email_readonly = $this->mailbox->localpart . '@' . $dom->domain;
@@ -105,7 +103,6 @@ class MailboxEditModal extends ModalComponent
'quota_mb' => ['required', 'integer', 'min:0', 'max:' . $cap],
'rate_limit_per_hour' => ['nullable', 'integer', 'min:1'],
'is_active' => ['boolean'],
- 'must_change_pw' => ['boolean'],
];
}
@@ -121,12 +118,10 @@ class MailboxEditModal extends ModalComponent
$u->display_name = $this->display_name ?: null;
if (!empty($this->password)) {
$u->password_hash = Hash::make($this->password);
- $u->must_change_pw = true;
}
$u->quota_mb = (int)$this->quota_mb;
$u->rate_limit_per_hour = $this->rate_limit_readonly ? $d->rate_limit_per_hour : $this->rate_limit_per_hour;
$u->is_active = (bool)$this->is_active;
- $u->must_change_pw = (bool)$this->must_change_pw;
$u->save();
$mailbox = $u->localpart . '@' . $d->domain;
diff --git a/app/Models/MailUser.php b/app/Models/MailUser.php
index 524b941..14b711f 100644
--- a/app/Models/MailUser.php
+++ b/app/Models/MailUser.php
@@ -11,14 +11,13 @@ class MailUser extends Model
protected $fillable = [
'domain_id','localpart','email','display_name','password_hash',
- 'is_active','must_change_pw','quota_mb','is_system'
+ 'is_active','quota_mb','is_system'
];
protected $hidden = ['password_hash'];
protected $casts = [
'is_active'=>'bool',
'is_system' => 'boolean',
- 'must_change_pw'=>'bool',
'quota_mb'=>'int',
'last_login_at'=>'datetime',
];
diff --git a/app/Models/User.php b/app/Models/User.php
index 3e2eee2..62cd759 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -17,9 +17,10 @@ class User extends Authenticatable
'name',
'username',
'email',
+ 'admin_email',
'password',
'is_active',
- 'must_change_pw',
+ 'required_change_password',
'role',
];
@@ -29,10 +30,10 @@ class User extends Authenticatable
];
protected $casts = [
- 'email_verified_at' => 'datetime',
- 'is_active' => 'boolean',
- 'must_change_pw' => 'boolean',
- 'role' => Role::class,
+ 'email_verified_at' => 'datetime',
+ 'is_active' => 'boolean',
+ 'required_change_password' => 'boolean',
+ 'role' => Role::class,
];
/** Quick helper: check if user is admin */
@@ -41,6 +42,19 @@ class User extends Authenticatable
return $this->role === Role::Admin;
}
+ // Fallback: admin_email || email
+ public function getSystemNotifyEmailAttribute(): string
+ {
+ $admin = trim((string)$this->admin_email);
+ return filter_var($admin, FILTER_VALIDATE_EMAIL) ? $admin : $this->email;
+ }
+
+ // Wenn du Laravel Notifications nutzt:
+ public function routeNotificationForMail($notification): string
+ {
+ return $this->system_notify_email;
+ }
+
public function twoFactorMethods()
{
return $this->hasMany(\App\Models\TwoFactorMethod::class);
diff --git a/app/Observers/DomainObserver.php b/app/Observers/DomainObserver.php
index e951de8..c29af51 100644
--- a/app/Observers/DomainObserver.php
+++ b/app/Observers/DomainObserver.php
@@ -16,6 +16,10 @@ class DomainObserver
*/
public function created(Domain $domain): void
{
+ if ($domain->is_server) {
+ return;
+ }
+
$selector = (string) config('mailpool.defaults.dkim_selector', 'mwl1');
$bits = (int) config('mailpool.defaults.dkim_bits', 2048);
@@ -51,6 +55,8 @@ class DomainObserver
]
);
}
+
+
// public function created(Domain $domain): void
// {
// // Standardwerte aus Config oder .env
diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php
index ecfa1b3..d940b17 100644
--- a/database/migrations/0001_01_01_000000_create_users_table.php
+++ b/database/migrations/0001_01_01_000000_create_users_table.php
@@ -16,10 +16,11 @@ return new class extends Migration
$table->string('name')->unique();
$table->string('username')->nullable()->unique();
$table->string('email')->unique();
+ $table->string('admin_email', 255)->nullable()->index();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('is_active')->default(true)->index();
- $table->boolean('must_change_pw')->default(true)->index();
+ $table->boolean('required_change_password')->default(false)->index();
$table->boolean('two_factor_enabled')->default(false);
$table->boolean('two_factor_email_enabled')->default(false);
$table->string('totp_secret')->nullable();
diff --git a/database/migrations/2025_09_27_153311_create_mail_users_table.php b/database/migrations/2025_09_27_153311_create_mail_users_table.php
index 759c0c7..d9d0e02 100644
--- a/database/migrations/2025_09_27_153311_create_mail_users_table.php
+++ b/database/migrations/2025_09_27_153311_create_mail_users_table.php
@@ -24,7 +24,6 @@ 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('must_change_pw')->default(true)->index();
$table->unsignedInteger('quota_mb')->default(0); // 0 = unlimited
$table->unsignedInteger('rate_limit_per_hour')->nullable();
diff --git a/database/seeders/AdminUserSeeder.php b/database/seeders/AdminUserSeeder.php
deleted file mode 100644
index 7b50d24..0000000
--- a/database/seeders/AdminUserSeeder.php
+++ /dev/null
@@ -1,46 +0,0 @@
- $email],
- [
- 'name' => $name,
- 'username' => $name,
- 'password' => Hash::make($pass),
- 'is_active' => true,
- 'must_change_pw' => true,
- 'role' => 'admin',
- 'email_verified_at' => now(),
- 'remember_token' => Str::random(10),
- ]
- );
-
- // falls es den User schon gab, Flags sicher setzen
- if (!$user->wasRecentlyCreated) {
- $user->forceFill([
- 'is_active' => true,
- 'must_change_pw' => true,
- 'role' => 'admin',
- ])->save();
- }
-
- }
-}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
deleted file mode 100644
index d01a0ef..0000000
--- a/database/seeders/DatabaseSeeder.php
+++ /dev/null
@@ -1,23 +0,0 @@
-create();
-
- User::factory()->create([
- 'name' => 'Test User',
- 'email' => 'test@example.com',
- ]);
- }
-}
diff --git a/database/seeders/SystemDomainSeeder.php b/database/seeders/SystemDomainSeeder.php
index aa76af9..156dde3 100644
--- a/database/seeders/SystemDomainSeeder.php
+++ b/database/seeders/SystemDomainSeeder.php
@@ -32,9 +32,39 @@ class SystemDomainSeeder extends Seeder
// =========================================================================
$serverDomain = Domain::firstOrCreate(
['domain' => $serverFqdn],
- ['is_active' => true, 'is_system' => false, 'is_server' => true]
+ [
+ 'is_active' => true,
+ 'is_system' => false,
+ 'is_server' => true,
+
+ // nix anlegen unter mx.
+ '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]);
@@ -71,8 +101,37 @@ class SystemDomainSeeder extends Seeder
// =========================================================================
$systemDomain = Domain::firstOrCreate(
['domain' => $systemFqdn],
- ['is_active' => true, 'is_system' => true]
+ [
+ '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}");
}
@@ -81,13 +140,12 @@ class SystemDomainSeeder extends Seeder
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,
+ 'domain_id' => $systemDomain->id,
+ 'localpart' => 'no-reply',
+ 'password_hash' => null,
+ 'is_active' => true,
+ 'is_system' => true,
+ 'quota_mb' => 0,
]
);
diff --git a/resources/views/livewire/ui/mail/modal/mailbox-create-modal.blade.php b/resources/views/livewire/ui/mail/modal/mailbox-create-modal.blade.php
index dec45bd..353de22 100644
--- a/resources/views/livewire/ui/mail/modal/mailbox-create-modal.blade.php
+++ b/resources/views/livewire/ui/mail/modal/mailbox-create-modal.blade.php
@@ -164,15 +164,15 @@
Postfach aktivieren
-
+{{-- --}}