Files
HexaHost-Panel/app/Http/Controllers/Api/WhmcsServiceController.php
2026-05-17 13:26:14 +02:00

163 lines
6.1 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Enums\UserRole;
use App\Http\Controllers\Controller;
use App\Http\Requests\Whmcs\ProvisionServiceRequest;
use App\Jobs\ProvisionCustomerJob;
use App\Models\Customer;
use App\Models\HostingPlan;
use App\Models\User;
use App\Models\WhmcsService;
use App\Services\Hosting\Provisioning\DeprovisionService;
use App\Services\Hosting\Provisioning\VmidReservationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class WhmcsServiceController extends Controller
{
public function provision(ProvisionServiceRequest $request): JsonResponse
{
$plan = HostingPlan::query()->where('slug', $request->validated('plan_slug'))->firstOrFail();
$domain = $request->domain();
if ($domain && Customer::query()->where('domain', $domain)->exists()) {
return response()->json(['error' => 'Domain already taken.'], 422);
}
$result = DB::transaction(function () use ($request, $plan, $domain) {
$user = User::query()->updateOrCreate(
['whmcs_client_id' => $request->integer('whmcs_client_id')],
[
'name' => $request->validated('client_name'),
'email' => $request->validated('client_email'),
'password' => Hash::make(Str::random(32)),
'role' => UserRole::Customer,
'is_active' => true,
],
);
$customer = Customer::query()->create([
'user_id' => $user->id,
'hosting_plan_id' => $plan->id,
'name' => $request->validated('hostname'),
'domain' => $domain ?? 'direct-'.Str::slug($request->validated('hostname')).'-'.Str::lower(Str::random(6)).'.internal',
'behind_traefik' => $request->boolean('behind_traefik', true),
'provision_mode' => $request->validated('provision_mode'),
'attached_iso' => $request->validated('iso_volid'),
'cpu' => $plan->cpu,
'ram' => $plan->ram,
'disk' => $plan->disk,
'status' => 'pending',
'provisioning_step' => 'queued',
]);
$whmcsService = WhmcsService::query()->create([
'whmcs_service_id' => $request->integer('whmcs_service_id'),
'whmcs_client_id' => $request->integer('whmcs_client_id'),
'whmcs_order_id' => $request->input('whmcs_order_id'),
'customer_id' => $customer->id,
'user_id' => $user->id,
'hosting_plan_id' => $plan->id,
'status' => 'provisioning',
'config' => $request->only([
'provision_mode', 'template_slug', 'iso_volid', 'behind_traefik', 'subdomain',
]),
]);
$customer->update(['whmcs_service_id' => $whmcsService->id]);
$vmid = app(VmidReservationService::class)->reserveForCustomer($customer);
$customer->update(['vmid' => $vmid]);
ProvisionCustomerJob::dispatch($customer->id);
return compact('customer', 'user', 'whmcsService', 'vmid');
});
return response()->json([
'message' => 'Provisioning queued.',
'customer_id' => $result['customer']->id,
'vmid' => $result['vmid'],
'panel_url' => config('hosting.panel.url'),
], 202);
}
public function suspend(Request $request, int $whmcsServiceId): JsonResponse
{
$service = $this->resolveService($whmcsServiceId);
$customer = $service->customer;
if ($customer?->vmid) {
app(\App\Services\Hosting\Proxmox\ProxmoxClient::class)->stopVM((int) $customer->vmid);
}
$service->update(['status' => 'suspended']);
$customer?->update(['status' => 'failed', 'provisioning_step' => 'suspended']);
return response()->json(['message' => 'Service suspended.']);
}
public function unsuspend(Request $request, int $whmcsServiceId): JsonResponse
{
$service = $this->resolveService($whmcsServiceId);
$customer = $service->customer;
if ($customer?->vmid) {
app(\App\Services\Hosting\Proxmox\ProxmoxClient::class)->startVM((int) $customer->vmid);
$customer->update(['status' => 'active', 'provisioning_step' => 'completed']);
}
$service->update(['status' => 'active']);
return response()->json(['message' => 'Service unsuspended.']);
}
public function terminate(Request $request, int $whmcsServiceId, DeprovisionService $deprovision): JsonResponse
{
$service = $this->resolveService($whmcsServiceId);
$customer = $service->customer;
if ($customer) {
$deprovision->terminatePermanently($customer);
}
$service->update(['status' => 'terminated', 'customer_id' => null]);
return response()->json([
'message' => 'Service terminated. VMID will be released after configured retention.',
]);
}
public function status(int $whmcsServiceId): JsonResponse
{
$service = $this->resolveService($whmcsServiceId);
$service->load('customer');
return response()->json([
'whmcs_service_id' => $service->whmcs_service_id,
'status' => $service->status,
'customer' => $service->customer ? [
'id' => $service->customer->id,
'name' => $service->customer->name,
'vmid' => $service->customer->vmid,
'status' => $service->customer->status,
'provisioning_step' => $service->customer->provisioning_step,
'ip_address' => $service->customer->ip_address,
'domain' => $service->customer->domain,
] : null,
]);
}
private function resolveService(int $whmcsServiceId): WhmcsService
{
return WhmcsService::query()
->where('whmcs_service_id', $whmcsServiceId)
->firstOrFail();
}
}