Files
ladill-hosting/app/Services/Hosting/ServerPanelCapabilityService.php
isaaccladandCursor e251a4cf60
Deploy Ladill Hosting / deploy (push) Failing after 17s
Initial Ladill Hosting app with Gitea deploy pipeline.
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>
2026-06-06 16:24:20 +00:00

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;
}
}