163 lines
6.1 KiB
PHP
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();
|
|
}
|
|
}
|