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>
182 lines
7.3 KiB
PHP
182 lines
7.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Hosting;
|
|
|
|
use App\Models\HostingProduct;
|
|
use Illuminate\Support\Str;
|
|
|
|
class ServerPanelCapabilityService
|
|
{
|
|
private const FUTURE_HOSTING_PANEL_CAPABILITIES = [
|
|
'Files',
|
|
'Databases',
|
|
'Domains',
|
|
'PHP',
|
|
'SSL',
|
|
'Cron',
|
|
'Logs',
|
|
];
|
|
|
|
/**
|
|
* @param array<string, mixed> $selection
|
|
* @param array<string, mixed> $catalog
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function describeSelection(HostingProduct $product, array $selection, array $catalog): array
|
|
{
|
|
$license = $this->findOption((array) ($catalog['licenses'] ?? []), (string) ($selection['license'] ?? ''));
|
|
$image = $this->findOption((array) ($catalog['images'] ?? []), (string) ($selection['image'] ?? ''));
|
|
|
|
$licenseLabel = (string) ($license['label'] ?? 'Remote Login Only');
|
|
$panel = (string) ($license['panel'] ?? '');
|
|
$vendorLicense = (string) ($license['license'] ?? '');
|
|
$osFamily = strtolower((string) ($image['os_family'] ?? 'linux'));
|
|
$installLater = (bool) ($image['is_install_later'] ?? false);
|
|
$supportedManagedImage = $this->supportedManagedImage($image);
|
|
$linuxManagedCandidate = $panel === 'ladill'
|
|
&& ! $installLater
|
|
&& ! str_starts_with($osFamily, 'win')
|
|
&& $supportedManagedImage !== null;
|
|
$supportedManagedImageLabels = array_values(array_filter(array_map(
|
|
static fn (array $supported): string => (string) ($supported['label'] ?? ''),
|
|
(array) config('hosting.server_order.managed_stack_supported_images', [])
|
|
)));
|
|
|
|
if ($panel === 'ladill') {
|
|
return [
|
|
'mode' => 'server_manager',
|
|
'label' => $licenseLabel,
|
|
'runtime' => 'provider_api',
|
|
'is_ladill' => true,
|
|
'managed_stack_candidate' => $linuxManagedCandidate,
|
|
'managed_stack_supported_image' => $supportedManagedImage,
|
|
'managed_stack_supported_image_labels' => $supportedManagedImageLabels,
|
|
'hosting_panel_ready' => $linuxManagedCandidate,
|
|
'future_hosting_panel_status' => $linuxManagedCandidate ? 'managed_stack_supported' : 'not_available',
|
|
'current_capabilities' => [
|
|
'Power controls',
|
|
'Status sync',
|
|
'Instance details',
|
|
'SSH access',
|
|
],
|
|
'future_capabilities' => $linuxManagedCandidate ? self::FUTURE_HOSTING_PANEL_CAPABILITIES : [],
|
|
'primary_message' => $linuxManagedCandidate
|
|
? 'Ladill Server Manager will provision this server with Ladill-managed controls on the selected supported Linux image.'
|
|
: 'Ladill Server Manager is not available for the selected image.',
|
|
'secondary_message' => $linuxManagedCandidate
|
|
? 'After provisioning finishes, Server Manager can handle power, status, files, databases, domains, PHP, SSL, cron, and logs.'
|
|
: 'Choose a plain supported Linux image without cPanel or Plesk. Supported Ladill-managed images: '.implode(', ', $supportedManagedImageLabels).'.',
|
|
'manager_cta_label' => 'Open Server Manager',
|
|
'hosting_panel_target_label' => 'Ladill Hosting Panel',
|
|
'product_type' => $product->type,
|
|
];
|
|
}
|
|
|
|
if ($vendorLicense !== '') {
|
|
return [
|
|
'mode' => 'vendor_panel',
|
|
'label' => $licenseLabel,
|
|
'runtime' => 'in_server_panel',
|
|
'is_ladill' => false,
|
|
'managed_stack_candidate' => false,
|
|
'hosting_panel_ready' => false,
|
|
'future_hosting_panel_status' => 'external_panel',
|
|
'current_capabilities' => [
|
|
'Provider provisioning',
|
|
'Server login',
|
|
'Installed panel management',
|
|
],
|
|
'future_capabilities' => [],
|
|
'primary_message' => "{$licenseLabel} will be the panel running inside this server. Ladill will not replace it with the shared hosting panel.",
|
|
'secondary_message' => 'Use Ladill for order tracking and server lifecycle, then manage sites and services from the installed panel itself.',
|
|
'manager_cta_label' => 'Open Panel',
|
|
'hosting_panel_target_label' => 'Ladill Hosting Panel',
|
|
'product_type' => $product->type,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'mode' => 'remote_login_only',
|
|
'label' => $licenseLabel,
|
|
'runtime' => 'ssh_only',
|
|
'is_ladill' => false,
|
|
'managed_stack_candidate' => false,
|
|
'hosting_panel_ready' => false,
|
|
'future_hosting_panel_status' => 'not_enabled',
|
|
'current_capabilities' => [
|
|
'Server login',
|
|
],
|
|
'future_capabilities' => [],
|
|
'primary_message' => 'This server will be provisioned without a Ladill-managed or commercial control panel.',
|
|
'secondary_message' => 'You will manage the machine directly over SSH or RDP until a panel or managed stack is installed separately.',
|
|
'manager_cta_label' => 'Open Access Details',
|
|
'hosting_panel_target_label' => 'Ladill Hosting Panel',
|
|
'product_type' => $product->type,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array<string, mixed>> $options
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
private function findOption(array $options, string $value): ?array
|
|
{
|
|
foreach ($options as $option) {
|
|
if ((string) ($option['value'] ?? '') === $value) {
|
|
return $option;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed>|null $image
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
private function supportedManagedImage(?array $image): ?array
|
|
{
|
|
if ($image === null) {
|
|
return null;
|
|
}
|
|
|
|
$value = Str::lower((string) ($image['value'] ?? ''));
|
|
$label = Str::lower((string) ($image['label'] ?? ''));
|
|
$osFamily = Str::lower((string) ($image['os_family'] ?? ''));
|
|
|
|
if ($this->imageIncludesVendorPanel($label)) {
|
|
return null;
|
|
}
|
|
|
|
foreach ((array) config('hosting.server_order.managed_stack_supported_images', []) as $supported) {
|
|
if (($supported['os_family'] ?? 'linux') !== '' && Str::lower((string) ($supported['os_family'] ?? 'linux')) !== $osFamily) {
|
|
continue;
|
|
}
|
|
|
|
$supportedValue = Str::lower((string) ($supported['value'] ?? ''));
|
|
if ($supportedValue !== '' && $supportedValue === $value) {
|
|
return $supported;
|
|
}
|
|
|
|
foreach ((array) ($supported['match'] ?? []) as $needle) {
|
|
if ($needle !== '' && Str::contains($label, Str::lower((string) $needle))) {
|
|
return $supported;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function imageIncludesVendorPanel(string $label): bool
|
|
{
|
|
foreach (['cpanel', 'plesk', 'whm'] as $needle) {
|
|
if ($needle !== '' && Str::contains($label, $needle)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|