Add platform admin hosting API for Ladill account provisioning.
Deploy Ladill Hosting / deploy (push) Successful in 1m29s

Expose authenticated service endpoints for assigning, listing, renewing, and managing hosting accounts from the platform, with tests and deploy docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-03 22:54:00 +00:00
co-authored by Cursor
parent b0e1cfab4e
commit 2c9877a87c
14 changed files with 806 additions and 66 deletions
@@ -909,16 +909,7 @@ NGINX;
$domain = $site->domain;
$docRoot = $site->document_root ?: "/home/{$username}/public_html/{$domain}";
// Create document root
$quotedDocRoot = escapeshellarg($docRoot);
$this->ensureAdministrativeResultSucceeded(
$this->runLocalAdminOperationOrRemote($ssh, 'mkdir', [$docRoot], "mkdir -p {$quotedDocRoot}"),
"Failed to create document root for {$domain}"
);
$this->ensureAdministrativeResultSucceeded(
$this->runLocalAdminOperationOrRemote($ssh, 'chown', ["{$username}:{$username}", $docRoot], "chown {$username}:{$username} {$quotedDocRoot}"),
"Failed to assign document root ownership for {$domain}"
);
$this->ensureSiteDocumentRoot($ssh, $username, $docRoot);
$this->hardenAccountFilesystem($account, [$docRoot]);
$this->applyManagedSiteConfig($ssh, $site, $docRoot, $username, [
@@ -931,6 +922,24 @@ NGINX;
];
}
private function ensureSiteDocumentRoot(mixed $ssh, string $username, string $docRoot): void
{
$quotedDocRoot = escapeshellarg($docRoot);
$this->ensureAdministrativeResultSucceeded(
$this->runLocalAdminOperationOrRemote($ssh, 'mkdir', [$docRoot], "mkdir -p {$quotedDocRoot}"),
"Failed to create document root at {$docRoot}"
);
$this->ensureAdministrativeResultSucceeded(
$this->runLocalAdminOperationOrRemote(
$ssh,
'run-cmd',
["chown {$username}:" . self::WEB_SERVER_GROUP . " {$quotedDocRoot} && chmod 2750 {$quotedDocRoot}"],
"chown {$username}:" . self::WEB_SERVER_GROUP . " {$quotedDocRoot} && chmod 2750 {$quotedDocRoot}"
),
"Failed to set document root ownership at {$docRoot}"
);
}
public function hardenAccountFilesystem(HostingAccount $account, array $additionalDocumentRoots = []): void
{
$node = $account->node;
@@ -1002,6 +1011,7 @@ NGINX;
$domain = $site->domain;
$email = $account->user?->email ?: 'admin@' . $domain;
$docRoot = $site->document_root ?: "/home/{$account->username}/public_html/{$domain}";
$this->ensureSiteDocumentRoot($ssh, $account->username, $docRoot);
$hadCertificate = $this->siteHasCertificate($ssh, $domain);
Log::info("SSL: Starting certificate request", [
@@ -1677,7 +1687,7 @@ LIMITS;
'runtime_port' => $port,
'runtime_type' => $appType,
]),
]);
])->save();
try {
$this->applyManagedSiteConfig($ssh, $site, $site->document_root, $username, [
@@ -2451,14 +2461,7 @@ SERVICE;
NGINX;
if ($proxyPort !== null) {
$httpBody = <<<NGINX
access_log /home/{$username}/logs/{$domain}-access.log;
error_log /home/{$username}/logs/{$domain}-error.log;
{$acmeLocation}
location / {
proxy_pass http://127.0.0.1:{$proxyPort};
$proxyHeaders = <<<NGINX
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
@@ -2469,6 +2472,27 @@ NGINX;
proxy_cache_bypass \$http_upgrade;
proxy_read_timeout 300;
proxy_connect_timeout 300;
NGINX;
$apiPort = data_get($site->app_config, 'api_runtime_port');
$apiLocation = is_numeric($apiPort)
? <<<NGINX
location /api/ {
proxy_pass http://127.0.0.1:{$apiPort};
{$proxyHeaders}
}
NGINX
: '';
$httpBody = <<<NGINX
access_log /home/{$username}/logs/{$domain}-access.log;
error_log /home/{$username}/logs/{$domain}-error.log;
{$acmeLocation}
{$apiLocation}
location / {
proxy_pass http://127.0.0.1:{$proxyPort};
{$proxyHeaders}
}
client_max_body_size 64M;