VPS and dedicated server ordering, managed panels, SSO, billing, and launcher integration — forked from hosting infrastructure with server-focused routes and dashboard. Co-authored-by: Cursor <cursoragent@cursor.com>
177 lines
7.5 KiB
PHP
177 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Infrastructure;
|
|
|
|
use App\Models\ContaboInfrastructureProvision;
|
|
use App\Models\EmailServer;
|
|
use App\Services\Hosting\Providers\ContaboProvider;
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
class ContaboInfrastructureCloudInitBuilder
|
|
{
|
|
public function __construct(
|
|
private ContaboProvider $contaboProvider,
|
|
) {}
|
|
|
|
public function build(ContaboInfrastructureProvision $provision): string
|
|
{
|
|
return match ($provision->purpose) {
|
|
ContaboInfrastructureProvision::PURPOSE_HOSTING_NODE => $this->buildHostingNode($provision),
|
|
ContaboInfrastructureProvision::PURPOSE_EMAIL_PRIMARY => $this->buildMailPrimary($provision),
|
|
ContaboInfrastructureProvision::PURPOSE_EMAIL_EXTENSION => $this->buildMailExtension($provision),
|
|
default => throw new \InvalidArgumentException("Unknown provision purpose: {$provision->purpose}"),
|
|
};
|
|
}
|
|
|
|
private function buildHostingNode(ContaboInfrastructureProvision $provision): string
|
|
{
|
|
$helperScript = File::get(base_path('deploy/bin/ladill-hosting-admin'));
|
|
$sudoers = "www-data ALL=(root) NOPASSWD: /usr/local/bin/ladill-hosting-admin *\n";
|
|
$completionUrl = $provision->completionUrl();
|
|
|
|
return $this->contaboProvider->generateCloudInit([
|
|
'packages' => ['curl', 'wget', 'git', 'unzip', 'nginx', 'php-fpm', 'mysql-client', 'quota'],
|
|
'ssh_keys' => array_filter([$provision->ssh_public_key]),
|
|
'write_files' => [
|
|
[
|
|
'path' => '/opt/ladill/ladill-hosting-admin',
|
|
'permissions' => '0755',
|
|
'owner' => 'root:root',
|
|
'content' => $helperScript,
|
|
],
|
|
[
|
|
'path' => '/opt/ladill/ladill-hosting.sudoers',
|
|
'permissions' => '0440',
|
|
'owner' => 'root:root',
|
|
'content' => $sudoers,
|
|
],
|
|
],
|
|
'run_commands' => [
|
|
'install -Dm755 /opt/ladill/ladill-hosting-admin /usr/local/bin/ladill-hosting-admin',
|
|
'install -Dm440 /opt/ladill/ladill-hosting.sudoers /etc/sudoers.d/ladill-hosting',
|
|
'visudo -cf /etc/sudoers.d/ladill-hosting',
|
|
$this->completionCurlCommand($completionUrl, ['bootstrap' => 'hosting']),
|
|
],
|
|
]);
|
|
}
|
|
|
|
private function buildMailPrimary(ContaboInfrastructureProvision $provision): string
|
|
{
|
|
$payload = (array) ($provision->payload ?? []);
|
|
$mailHost = (string) ($payload['mail_host'] ?? config('mailinfra.mail_host', 'mail.ladill.com'));
|
|
$dbName = config('infrastructure.mail_stack.db_database', 'mail');
|
|
$dbUser = config('infrastructure.mail_stack.db_username', 'mailuser');
|
|
$dbPassword = (string) ($provision->generated_mail_db_password ?? '');
|
|
$vmailRoot = config('infrastructure.mail_stack.vmail_root', '/var/vmail');
|
|
$vmailUid = (string) config('infrastructure.mail_stack.vmail_uid', 5000);
|
|
$vmailGid = (string) config('infrastructure.mail_stack.vmail_gid', 5000);
|
|
|
|
$exportClients = (string) ($payload['export_client_ips'] ?? '');
|
|
|
|
$bootstrapScript = str_replace(
|
|
[
|
|
'__MAIL_HOST__', '__DB_PASSWORD__', '__DB_NAME__', '__DB_USER__', '__COMPLETION_URL__',
|
|
'__VMAIL_ROOT__', '__VMAIL_UID__', '__VMAIL_GID__', '__EXPORT_CLIENTS__',
|
|
],
|
|
[
|
|
$mailHost, $dbPassword, $dbName, $dbUser, $provision->completionUrl(),
|
|
$vmailRoot, $vmailUid, $vmailGid, $exportClients,
|
|
],
|
|
File::get(resource_path('infrastructure/scripts/mail-primary-bootstrap.sh'))
|
|
);
|
|
|
|
$writeFiles = [
|
|
$this->scriptFile('/opt/ladill/nfs-primary-setup.sh', 'infrastructure/scripts/nfs-primary-setup.sh'),
|
|
$this->scriptFile('/opt/ladill/vmail-usage-scan.sh', 'infrastructure/scripts/vmail-usage-scan.sh'),
|
|
[
|
|
'path' => '/opt/ladill/mail-primary-bootstrap.sh',
|
|
'permissions' => '0755',
|
|
'owner' => 'root:root',
|
|
'encoding' => 'b64',
|
|
'content' => base64_encode($bootstrapScript),
|
|
],
|
|
];
|
|
|
|
return $this->contaboProvider->generateCloudInit([
|
|
'packages' => ['curl', 'wget', 'jq', 'ca-certificates', 'sudo'],
|
|
'ssh_keys' => array_filter([$provision->ssh_public_key]),
|
|
'write_files' => $writeFiles,
|
|
'run_commands' => [
|
|
'bash /opt/ladill/mail-primary-bootstrap.sh',
|
|
],
|
|
]);
|
|
}
|
|
|
|
private function buildMailExtension(ContaboInfrastructureProvision $provision): string
|
|
{
|
|
$payload = (array) ($provision->payload ?? []);
|
|
$mailHost = (string) ($payload['mail_host'] ?? config('mailinfra.mail_host', 'mail.ladill.com'));
|
|
$primaryIp = (string) ($payload['primary_ip'] ?? '');
|
|
$vmailRoot = config('infrastructure.mail_stack.vmail_root', '/var/vmail');
|
|
$mountOpts = config('infrastructure.mail_stack.nfs_mount_options', 'nfsvers=4.1,rw,hard,timeo=600,retrans=2,_netdev');
|
|
|
|
if ($primaryIp === '' && ! empty($payload['primary_email_server_id'])) {
|
|
$primary = EmailServer::find($payload['primary_email_server_id']);
|
|
$primaryIp = $primary?->ip_address ?? '';
|
|
$vmailRoot = $primary?->pool_vmail_root ?: $vmailRoot;
|
|
}
|
|
|
|
$bootstrapScript = str_replace(
|
|
[
|
|
'__MAIL_HOST__', '__PRIMARY_IP__', '__COMPLETION_URL__',
|
|
'__VMAIL_ROOT__', '__MOUNT_OPTS__',
|
|
],
|
|
[
|
|
$mailHost, $primaryIp, $provision->completionUrl(),
|
|
$vmailRoot, $mountOpts,
|
|
],
|
|
File::get(resource_path('infrastructure/scripts/mail-extension-bootstrap.sh'))
|
|
);
|
|
|
|
$writeFiles = [
|
|
$this->scriptFile('/opt/ladill/nfs-extension-setup.sh', 'infrastructure/scripts/nfs-extension-setup.sh'),
|
|
$this->scriptFile('/opt/ladill/vmail-usage-scan.sh', 'infrastructure/scripts/vmail-usage-scan.sh'),
|
|
[
|
|
'path' => '/opt/ladill/mail-extension-bootstrap.sh',
|
|
'permissions' => '0755',
|
|
'owner' => 'root:root',
|
|
'encoding' => 'b64',
|
|
'content' => base64_encode($bootstrapScript),
|
|
],
|
|
];
|
|
|
|
return $this->contaboProvider->generateCloudInit([
|
|
'packages' => ['curl', 'wget', 'jq', 'ca-certificates', 'sudo'],
|
|
'ssh_keys' => array_filter([$provision->ssh_public_key]),
|
|
'write_files' => $writeFiles,
|
|
'run_commands' => [
|
|
'bash /opt/ladill/mail-extension-bootstrap.sh',
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function scriptFile(string $remotePath, string $localRelativePath): array
|
|
{
|
|
return [
|
|
'path' => $remotePath,
|
|
'permissions' => '0755',
|
|
'owner' => 'root:root',
|
|
'encoding' => 'b64',
|
|
'content' => base64_encode(File::get(resource_path($localRelativePath))),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $extra
|
|
*/
|
|
private function completionCurlCommand(string $url, array $extra = []): string
|
|
{
|
|
$payload = json_encode(array_merge(['status' => 'ok'], $extra), JSON_UNESCAPED_SLASHES);
|
|
|
|
return "curl -fsS -X POST ".escapeshellarg($url)." -H 'Content-Type: application/json' -d ".escapeshellarg($payload)." || true";
|
|
}
|
|
}
|