Initial Ladill Hosting app with Gitea deploy pipeline.
Deploy Ladill Hosting / deploy (push) Failing after 17s
Deploy Ladill Hosting / deploy (push) Failing after 17s
Shared web hosting extracted from the platform monolith, with CI deploy to /var/www/ladill-hosting matching Bird/Domains/Email. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Mail;
|
||||
|
||||
use App\Models\Mailbox;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Every mailbox holder gets a Ladill account (users.email = mailbox address).
|
||||
* The Ladill login password matches the mailbox password so holders can sign in
|
||||
* to account.ladill.com with the same credentials they use for webmail/IMAP.
|
||||
*/
|
||||
class MailboxUserProvisioner
|
||||
{
|
||||
public function __construct(private MailboxWebmailVault $vault) {}
|
||||
|
||||
public function ensureForMailbox(Mailbox $mailbox, ?string $password = null): User
|
||||
{
|
||||
$user = $this->ensureForAddress((string) $mailbox->address, (string) $mailbox->display_name, $password);
|
||||
$this->syncHolderPassword($mailbox, $password);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function ensureForAddress(string $address, string $displayName = '', ?string $password = null): User
|
||||
{
|
||||
$email = strtolower(trim($address));
|
||||
if ($email === '' || ! str_contains($email, '@')) {
|
||||
throw new \InvalidArgumentException('Invalid mailbox address.');
|
||||
}
|
||||
|
||||
$attributes = [
|
||||
'name' => ($name = trim($displayName) ?: Str::headline(Str::before($email, '@'))),
|
||||
'first_name' => Str::before($name, ' '),
|
||||
'last_name' => trim(Str::after($name, ' ')) ?: '',
|
||||
'password' => $password ?? Str::random(40),
|
||||
];
|
||||
|
||||
$user = User::firstOrCreate(['email' => $email], $attributes);
|
||||
|
||||
if (! $user->wasRecentlyCreated && $password !== null && $password !== '') {
|
||||
$user->password = $password;
|
||||
$user->save();
|
||||
}
|
||||
|
||||
if ($user->email_verified_at === null) {
|
||||
$user->forceFill(['email_verified_at' => now()])->save();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the Ladill account password aligned with the mailbox password.
|
||||
* Uses the plaintext when available; otherwise copies the stored bcrypt hash.
|
||||
*/
|
||||
public function syncHolderPassword(Mailbox $mailbox, ?string $plaintextPassword = null): void
|
||||
{
|
||||
$email = strtolower(trim((string) $mailbox->address));
|
||||
if ($email === '' || ! str_contains($email, '@')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::query()->where('email', $email)->first();
|
||||
if (! $user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($plaintextPassword !== null && $plaintextPassword !== '') {
|
||||
$user->password = $plaintextPassword;
|
||||
$user->save();
|
||||
$this->vault->store($mailbox, $plaintextPassword);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$hash = (string) $mailbox->password_hash;
|
||||
if ($hash !== '') {
|
||||
$this->assignPasswordHash($user, $hash);
|
||||
}
|
||||
}
|
||||
|
||||
public function provisionSilently(string $address, string $displayName = '', ?string $password = null): ?User
|
||||
{
|
||||
try {
|
||||
return $this->ensureForAddress($address, $displayName, $password);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function provisionMailboxSilently(Mailbox $mailbox, ?string $password = null): ?User
|
||||
{
|
||||
try {
|
||||
return $this->ensureForMailbox($mailbox, $password);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function assignPasswordHash(User $user, string $passwordHash): void
|
||||
{
|
||||
$user->getConnection()
|
||||
->table($user->getTable())
|
||||
->where($user->getKeyName(), $user->getKey())
|
||||
->update(['password' => $passwordHash]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user