as('api.')->group(function () { // Auth Route::prefix('client')->as('client.') ->group(function () { Route::controller(ClientController::class)->group(function () { Route::post('login', 'login')->name('login'); Route::post('register', 'register')->name('register'); Route::post('refresh', 'refresh')->name('refresh'); Route::group(['middleware' => ['api.auth']], function () { Route::get('me', 'me')->name('me'); Route::post('logout', 'logout')->name('logout'); Route::put('update', 'update')->name('update'); }); }); Route::middleware(['api.auth'])->controller(OrderController::class)->group(function () { Route::get('orders', 'getClientOrders')->name('orders.list'); Route::get('orders/{id}', 'getClientOrderDetail')->name('orders.detail'); Route::get('invoices', 'getClientInvoices')->name('invoices.list'); Route::get('invoices/{id}', 'getClientInvoiceDetail')->name('invoices.detail'); Route::get('invoices/order/{orderNum}', 'getClientInvoiceDetailByOrderNum')->name('invoices.detailByOrderNum'); Route::get('dich-vu', 'getClientServices')->name('services.list'); Route::patch('dich-vu/{id}/auto-renew', 'updateClientServiceAutoRenew')->name('services.autoRenew.update'); Route::post('dich-vu/{id}/renew', 'renewService')->name('services.renew'); }); Route::middleware(['api.auth'])->prefix('client-service')->controller(ServiceClientController::class)->as('client-service.')->group(function () { Route::get('/', 'getService')->name('list'); Route::get('{uuid}', 'getDetail')->name('detail'); Route::get('{uuid}/console-ticket', 'getConsoleTicket')->name('console-ticket'); }); Route::middleware(['api.auth'])->prefix('service')->controller(ServiceController::class)->as('service.')->group(function () { Route::get('/', 'getService')->name('list'); Route::get('{uuid}', 'getDetailServiceCustomer')->name('detail'); }); Route::middleware(['api.auth'])->controller(TransactionController::class)->group(function () { Route::get('payment-history', 'getClientPaymentHistory')->name('payment-history.list'); }); Route::middleware(['api.auth'])->get('get-ticket-console', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) { $orderNum = (string)$request->query('order_num', ''); if ($orderNum === '') { return response()->json(['error' => true, 'message' => 'Thiếu order_num'], 422); } $customer = $request->user(); $order = \App\Models\Order::with(['vmWare.vCenter']) ->where('customer_id', $customer->id) ->where('order_num', $orderNum) ->first(); if (!$order) { return response()->json(['error' => true, 'message' => 'Không tìm thấy đơn hàng'], 404); } $vm = $order->vmWare->first(); if (!$vm) { return response()->json(['error' => true, 'message' => 'Đơn hàng chưa có VM'], 404); } $vcenter = $vm->vCenter; if (!$vcenter) { return response()->json(['error' => true, 'message' => 'Không tìm thấy thông tin vCenter'], 404); } if (!$serviceVCenter->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Không thể đăng nhập vào vCenter']); } $consoleTicket = $serviceVCenter->GetConsoleTickets($vm->vmware_id); if ($consoleTicket === 'Login_Again') { if ($serviceVCenter->loginServer($vcenter)) { $consoleTicket = $serviceVCenter->GetConsoleTickets($vm->vmware_id); } else { return response()->json(['error' => true, 'message' => 'Không thể đăng nhập lại vào vCenter']); } } if (is_string($consoleTicket)) { return response()->json(['error' => true, 'message' => "Failed to get console ticket: $consoleTicket"]); } if ($consoleTicket['error']) { return response()->json(['error' => true, 'message' => $consoleTicket['data']]); } return response()->json([ 'error' => false, 'data' => $consoleTicket['data'], 'order_num' => $order->order_num, 'vm_id' => $vm->id, ]); })->name('client.console-ticket'); }); // Services (Products) - Public & Protected Routes Route::prefix('services')->as('services.')->group(function () { Route::controller(ServiceController::class)->group(function () { Route::get('get-list', 'getService')->name('getList'); Route::get('{product:slug}', 'getDetailService')->name('detail'); }); }); Route::middleware(['api.auth'])->prefix('orders')->as('orders.')->group(function () { Route::controller(OrderController::class)->group(function () { Route::get('/', 'getList')->name('list'); Route::post('/', 'storeOrder')->name('store'); Route::post('{id}/pay', 'payOrder')->name('pay'); }); }); Route::prefix('setting')->as('setting.')->group(function () { Route::controller(SettingController::class)->group(function () { Route::get('/', 'getSetting')->name('get'); }); }); // Product Categories - Public Routes (no auth required) Route::prefix('group')->as('product-categories.')->group(function () { Route::controller(ProductCategoryController::class)->group(function () { Route::get('get-list', 'getCategories')->name('getList'); Route::get('{product_categories:slug}', 'getDetailCategory')->name('detail'); Route::get('{parentSlug}/{childSlug}', 'getChildCategoryWithParent')->name('childWithParent'); }); }); // Addons - Get available addons for a product Route::prefix('products')->as('products.')->group(function () { Route::get('{productId}/addons', [ServiceController::class, 'getProductAddons'])->name('addons'); }); Route::middleware(['api.auth'])->prefix('transaction')->as('transaction.')->group(function () { Route::controller(TransactionController::class)->group(function () { Route::get('get-list', 'getTransactions')->name('getList'); Route::post('pay', 'handle')->name('pay'); Route::get('verify', 'verifyPayment')->name('verify'); Route::get('detail-by-code', 'getByPublicCode')->name('detailByCode'); }); }); }); Route::get('v1/payment/vnpay/return', [TransactionController::class, 'returnCallback']); Route::prefix('v1/system')->middleware(['api.auth'])->group(function () { // VM power & network operations Route::controller(VmController::class)->group(function () { Route::post('vm-power-on', 'powerOn') ->name('vm.power-on'); Route::post('vm-power-off', 'powerOff') ->name('vm.power-off'); Route::post('vm-change-ip', 'changeIp') ->name('vm.change-ip'); Route::post('vm-change-port','changePort') ->name('vm.change-port'); }); Route::get('get-ticket-console', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) { $product_id = $request->get('product'); if (!$product_id) { return response()->json(['error' => true, 'message' => "Please choose product"]); } $product = \App\Models\ModuleVmware::with('vCenter')->find($product_id); if (!$product) { return response()->json(['error' => true, 'message' => "Can't find product"]); } $vcenter = $product->vCenter; if (!$vcenter) { return response()->json(['error' => true, 'message' => "Can't find vCenter information"]); } if (!$serviceVCenter->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Không thể đăng nhập vào vCenter']); } $ConsoleTicket = $serviceVCenter->GetConsoleTickets($product->vmware_id); if ($ConsoleTicket === 'Login_Again') { if ($serviceVCenter->loginServer($vcenter)) { $ConsoleTicket = $serviceVCenter->GetConsoleTickets($product->vmware_id); } else { return response()->json(['error' => true, 'message' => 'Không thể đăng nhập lại vào vCenter']); } } if (is_string($ConsoleTicket)) { return response()->json(['error' => true, 'message' => "Failed to get console ticket: $ConsoleTicket"]); } if ($ConsoleTicket['error']) { return response()->json(['error' => true, 'message' => $ConsoleTicket['data']]); } return response()->json([ 'error' => false, 'data' => $ConsoleTicket['data'], // wss://10.10.10.120:443/ticket/... ]); }); Route::post('vm-restart', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) { $productId = $request->input('product'); $customer = $request->user(); if (!$productId) { return response()->json([ 'error' => true, 'message' => 'Thiếu product id', ], 422); } // Kiểm tra nếu là UUID (Service UUID) $isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $productId); $product = null; if ($isUuid) { // Tìm qua Service UUID $service = \App\Models\Service::with('vmware.ipAddress.host.datacenter.vCenter', 'vmware.vCenter') ->where('uuid', $productId) ->where('customer_id', $customer->id) ->first(); if ($service && $service->vmware) { $product = $service->vmware; } } elseif (is_numeric($productId)) { $product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find((int)$productId); } else { // Tìm theo vmware_id (ví dụ: vm-3238) $product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter') ->where('vmware_id', $productId) ->where('customer_id', $customer->id) ->first(); } if (!$product) { return response()->json([ 'error' => true, 'message' => 'Không tìm thấy VM', ], 404); } if ((int)$product->customer_id !== (int)$customer->id) { return response()->json([ 'error' => true, 'message' => 'Bạn không có quyền thao tác VM này', ], 403); } $result = $serviceVCenter->restartVM($product, $product->vmware_id); if (is_string($result) || ($result['error'] ?? true)) { return response()->json([ 'error' => true, 'message' => is_string($result) ? $result : ($result['data'] ?? 'Không thể restart VM'), ], 400); } return response()->json([ 'error' => false, 'message' => 'Đã gửi lệnh restart VM', 'data' => $result['data'] ?? 'success', ]); }); Route::post('vm-create-snapshot', function (\Illuminate\Http\Request $request) { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required'], 'description' => ['nullable', 'string', 'max:1000'], ]); // Kiểm tra nếu là UUID (Service UUID) $isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $payload['product']); if ($isUuid) { // Tìm qua Service UUID $service = \App\Models\Service::with('vmware.ipAddress.host.datacenter.vCenter', 'vmware.vCenter') ->where('uuid', $payload['product']) ->where('customer_id', $customer->id) ->first(); if (!$service || !$service->vmware) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } $product = $service->vmware; } elseif (is_numeric($payload['product'])) { $product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find((int)$payload['product']); } else { $product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter') ->where('vmware_id', $payload['product']) ->where('customer_id', $customer->id) ->first(); } if (!$product) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$product->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } // Auto-generate snapshot name: {vm_name}-{date}-{count} $today = now()->format('d-m-Y'); $existingCount = \App\Models\ModuleSnapshot::where('module_vmware_id', $product->id) ->whereDate('created_at', today()) ->count(); $snapshotName = sprintf('%s-%s-%d', $product->name, $today, $existingCount + 1); // Create snapshot record with pending status $snapshot = \App\Models\ModuleSnapshot::create([ 'name' => $snapshotName, 'description' => $payload['description'] ?? null, 'customer_id' => $customer->id, 'module_vmware_id' => $product->id, 'status' => 'pending', 'snapshot_ref' => null, ]); // Dispatch job to create snapshot \App\Jobs\VirtualMachine\Snapshot\CreateSnapshot::dispatch($snapshot->id); return response()->json([ 'error' => false, 'message' => 'Đã gửi yêu cầu tạo snapshot vào hàng đợi', 'data' => [ 'id' => $snapshot->id, 'name' => $snapshot->name, 'description' => $snapshot->description, 'status' => 'pending', ], ]); }); Route::get('vm-snapshots', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) { $customer = $request->user(); $productParam = $request->query('product'); if (!$productParam) { return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); } // Kiểm tra nếu là UUID (Service UUID) $isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $productParam); if ($isUuid) { // Tìm qua Service UUID $service = \App\Models\Service::with('vmware.ipAddress.host.datacenter.vCenter', 'vmware.vCenter') ->where('uuid', $productParam) ->where('customer_id', $customer->id) ->first(); if (!$service || !$service->vmware) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } $product = $service->vmware; } elseif (is_numeric($productParam)) { $product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find((int)$productParam); } else { $product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter') ->where('vmware_id', $productParam) ->where('customer_id', $customer->id) ->first(); } if (!$product) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$product->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } $remote = $serviceVCenter->getSnapShot($product); if (is_string($remote) || ($remote['error'] ?? true)) { return response()->json([ 'error' => true, 'message' => is_string($remote) ? $remote : ($remote['message'] ?? 'Không thể lấy danh sách snapshot'), ], 400); } $remoteItems = $remote['data'] ?? []; foreach ($remoteItems as $item) { $ref = (string)($item['id'] ?? $item['name'] ?? ''); $name = (string)($item['name'] ?? $ref); if ($name === '') { continue; } $snapshot = \App\Models\ModuleSnapshot::query() ->where('module_vmware_id', $product->id) ->where(function ($q) use ($ref, $name) { $q->where('snapshot_ref', $ref)->orWhere('name', $name); }) ->first(); $payload = [ 'name' => $name, 'description' => $item['description'] ?? null, 'customer_id' => $customer->id, 'module_vmware_id' => $product->id, 'status' => 'success', 'snapshot_ref' => $ref ?: $name, 'size_gb' => isset($item['size_gb']) ? (float)$item['size_gb'] : null, 'size_last_synced_at' => now(), ]; if ($snapshot) { $snapshot->update($payload); } else { \App\Models\ModuleSnapshot::create($payload); } } $dbSnapshots = \App\Models\ModuleSnapshot::query() ->where('module_vmware_id', $product->id) ->where('customer_id', $customer->id) ->orderByDesc('created_at') ->get() ->map(function ($snap) { return [ 'id' => $snap->id, 'name' => $snap->name, 'description' => $snap->description, 'snapshot_ref' => $snap->snapshot_ref, 'status' => $snap->status, 'size_gb' => $snap->size_gb, 'created_at' => $snap->created_at?->toISOString(), ]; }); return response()->json([ 'error' => false, 'data' => $dbSnapshots, ]); }); Route::post('vm-revert-snapshot', function (\Illuminate\Http\Request $request) { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required'], 'snapshot_id' => ['required', 'numeric'], ]); // Kiểm tra nếu là UUID (Service UUID) $isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $payload['product']); if ($isUuid) { // Tìm qua Service UUID $service = \App\Models\Service::with('vmware') ->where('uuid', $payload['product']) ->where('customer_id', $customer->id) ->first(); if (!$service || !$service->vmware) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } $product = $service->vmware; } elseif (is_numeric($payload['product'])) { $product = \App\Models\ModuleVmware::find((int)$payload['product']); } else { $product = \App\Models\ModuleVmware::where('vmware_id', $payload['product']) ->where('customer_id', $customer->id) ->first(); } if (!$product) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$product->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } // Find snapshot $snapshot = \App\Models\ModuleSnapshot::where('id', $payload['snapshot_id']) ->where('module_vmware_id', $product->id) ->where('customer_id', $customer->id) ->first(); if (!$snapshot) { return response()->json(['error' => true, 'message' => 'Không tìm thấy snapshot'], 404); } // Dispatch job \App\Jobs\VirtualMachine\Snapshot\RevertSnapshot::dispatch($product->id, $snapshot->id); return response()->json([ 'error' => false, 'message' => 'Đã gửi yêu cầu revert snapshot vào hàng đợi', ]); }); Route::post('vm-delete-snapshot', function (\Illuminate\Http\Request $request) { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required'], 'snapshot_id' => ['required', 'numeric'], ]); // Kiểm tra nếu là UUID (Service UUID) $isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $payload['product']); if ($isUuid) { // Tìm qua Service UUID $service = \App\Models\Service::with('vmware') ->where('uuid', $payload['product']) ->where('customer_id', $customer->id) ->first(); if (!$service || !$service->vmware) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } $product = $service->vmware; } elseif (is_numeric($payload['product'])) { $product = \App\Models\ModuleVmware::find((int)$payload['product']); } else { $product = \App\Models\ModuleVmware::where('vmware_id', $payload['product']) ->where('customer_id', $customer->id) ->first(); } if (!$product) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$product->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } // Find snapshot $snapshot = \App\Models\ModuleSnapshot::where('id', $payload['snapshot_id']) ->where('module_vmware_id', $product->id) ->where('customer_id', $customer->id) ->first(); if (!$snapshot) { return response()->json(['error' => true, 'message' => 'Không tìm thấy snapshot'], 404); } // Dispatch job \App\Jobs\VirtualMachine\Snapshot\RemoveSnapshot::dispatch($product->id, $snapshot->id); return response()->json([ 'error' => false, 'message' => 'Đã gửi yêu cầu xóa snapshot vào hàng đợi', ]); }); Route::get('vm-rebuild-options', function (\Illuminate\Http\Request $request) { $customer = $request->user(); $productId = (int)$request->query('product'); if (!$productId) { return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); } $vm = \App\Models\ModuleVmware::with(['order.orderItems.product.osVersions.osGroup']) ->find($productId); if (!$vm) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$vm->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } $product = optional($vm->order?->orderItems?->first())->product; if (!$product) { return response()->json(['error' => true, 'message' => 'Không tìm thấy product của dịch vụ hiện tại'], 404); } $osOptions = $product->osVersions ->where('status', 1) ->map(fn($os) => [ 'id' => $os->id, 'name' => $os->name, 'version' => $os->version, 'group' => $os->osGroup?->name, ]) ->values(); return response()->json([ 'error' => false, 'data' => [ 'product_id' => $product->id, 'product_uuid' => $product->uuid, 'product_name' => $product->name, 'os_options' => $osOptions, ], ]); }); Route::post('vm-rebuild', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required', 'integer'], 'os_id' => ['required', 'integer', 'exists:os,id'], ]); $vm = \App\Models\ModuleVmware::with(['order.orderItems.product', 'vCenter', 'ipAddress.host.datacenter.vCenter']) ->find($payload['product']); if (!$vm) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$vm->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } $product = optional($vm->order?->orderItems?->first())->product; if (!$product) { return response()->json(['error' => true, 'message' => 'Không tìm thấy product của dịch vụ hiện tại'], 404); } $allowedOs = $product->osVersions()->where('status', 1)->pluck('id')->toArray(); if (!in_array((int)$payload['os_id'], array_map('intval', $allowedOs), true)) { return response()->json(['error' => true, 'message' => 'OS không hợp lệ cho product hiện tại'], 422); } \Illuminate\Support\Facades\DB::table('reinstall_logs')->insert([ 'product_id' => $vm->id, 'product_type' => 4, 'custome_id' => $customer->id, 'os_id' => $payload['os_id'], 'status' => 'pending', 'initiator' => 'client', 'note' => 'Client request rebuild from portal-client', 'created_at' => now(), 'updated_at' => now(), ]); $result = $serviceVCenter->reBuildVPS((string)$vm->name); if (is_string($result) || ($result['error'] ?? false)) { return response()->json([ 'error' => true, 'message' => is_string($result) ? $result : ($result['message'] ?? 'Không thể rebuild VM'), ], 400); } return response()->json([ 'error' => false, 'message' => 'Đã gửi yêu cầu rebuild VM', 'data' => $result, ]); }); Route::get('vm-addon-options', function (\Illuminate\Http\Request $request) { $customer = $request->user(); $productId = (int)$request->query('product'); if (!$productId) { return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); } $vm = \App\Models\ModuleVmware::with(['order.orderItems.product.addons']) ->find($productId); if (!$vm) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$vm->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } $product = optional($vm->order?->orderItems?->first())->product; if (!$product) { return response()->json(['error' => true, 'message' => 'Không tìm thấy product của dịch vụ hiện tại'], 404); } $addons = $product->addons()->where('status', true)->get()->map(function ($addon) { return [ 'id' => $addon->id, 'code' => strtolower((string)$addon->code), 'name' => $addon->name, 'price' => (float)$addon->price, 'min' => (int)($addon->min ?? 0), 'max' => (int)($addon->max ?? 0), 'step' => (int)($addon->step ?? 1), ]; }); return response()->json([ 'error' => false, 'data' => [ 'product_id' => $product->id, 'product_name' => $product->name, 'base_specs' => [ 'cpu' => (int)($product->cpu ?? 0), 'ram' => (int)($product->ram ?? 0), 'disk' => (int)($product->disk ?? 0), 'bandwidth' => (int)($product->bandwidth ?? 0), 'gpu' => $product->gpu, ], 'addons' => $addons->values(), ], ]); }); Route::post('vm-upgrade-addons', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required', 'integer'], 'cpu' => ['nullable', 'integer', 'min:0'], 'ram' => ['nullable', 'integer', 'min:0'], 'disk' => ['nullable', 'integer', 'min:0'], 'bandwidth' => ['nullable', 'integer', 'min:0'], 'gpu' => ['nullable', 'string', 'max:255'], ]); $vm = \App\Models\ModuleVmware::with(['vCenter', 'ipAddress.host.datacenter.vCenter']) ->find($payload['product']); if (!$vm) { return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404); } if ((int)$vm->customer_id !== (int)$customer->id) { return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403); } $vcenter = $vm->vCenter ?? $vm->ipAddress?->host?->datacenter?->vCenter; if (!$vcenter || !$serviceVCenter->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Không thể đăng nhập vCenter'], 400); } $applied = []; if (isset($payload['cpu']) && $payload['cpu'] > 0) { $res = $serviceVCenter->UpgradeCPUVirtualMachine($vm->vmware_id, (int)$payload['cpu']); if (is_array($res) && !($res['error'] ?? true)) $applied[] = 'cpu'; } if (isset($payload['ram']) && $payload['ram'] > 0) { $res = $serviceVCenter->UpgradeMemoryVirtualMachine($vm->vmware_id, (int)$payload['ram']); if (is_array($res) && !($res['error'] ?? true)) $applied[] = 'ram'; } if (isset($payload['disk']) && $payload['disk'] > 0) { $res = $serviceVCenter->UpgradeDiskVirtualMachine($vm, (int)$payload['disk']); if (is_array($res) && !($res['error'] ?? true)) $applied[] = 'disk'; } return response()->json([ 'error' => false, 'message' => 'Đã gửi yêu cầu nâng cấp addons', 'data' => [ 'applied' => $applied, 'note' => 'bandwidth/gpu hiện được ghi nhận yêu cầu, chưa có luồng tự động áp vào VM', ], ]); }); }); Route::any('{any}', function () { return response()->json([ 'error' => true, 'message' => 'API Route not found' ], 404); })->where('any', '.*');