query('product'); if (!$productId) { return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); } $vm = $this->resolveVmById($productId, $request); if ($vm instanceof JsonResponse) { return $vm; } $vm->loadMissing(['order.orderItems.product.osVersions.osGroup']); $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, ], ]); } public function rebuild(Request $request, VmRebuildService $rebuildService): JsonResponse { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required'], 'os_id' => ['required', 'integer', 'exists:os,id'], 'password' => ['nullable', 'string', 'min:8', 'max:255'], ]); $vm = $this->resolveVmById($payload['product'], $request); if ($vm instanceof JsonResponse) { return $vm; } $vm->loadMissing(['order.orderItems.product', 'vCenter', 'ipAddress.host.datacenter.vCenter']); $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); } try { $log = $rebuildService->dispatchRebuild( moduleVmwareId: (int) $vm->id, newOsId: (int) $payload['os_id'], initiatorCustomerId: (int) $customer->id, customPassword: !empty($payload['password']) ? (string) $payload['password'] : null, initiator: 'client', note: 'Client request rebuild from portal-client', ); } catch (Throwable $e) { Log::error('VmRebuildController: dispatchRebuild failed', [ 'module_vmware_id' => $vm->id, 'new_os_id' => $payload['os_id'], 'error' => $e->getMessage(), ]); return response()->json([ 'error' => true, 'message' => 'Không thể gửi yêu cầu rebuild: ' . $e->getMessage(), ], 400); } $this->activityLogger->log( event: 'rebuild', logName: 'VM', description: "Customer {$customer->email} rebuild VM: {$vm->name} với OS ID: {$payload['os_id']}", subject: $vm, newValues: ['os_id' => $payload['os_id']], ); return response()->json([ 'error' => false, 'message' => 'Đã gửi yêu cầu rebuild VM', 'data' => [ 'module_vmware_id' => $vm->id, 'new_os_id' => (int) $payload['os_id'], 'reinstall_log_id' => $log->id, 'status' => $log->status, ], ]); } public function addonOptions(Request $request): JsonResponse { $productId = $request->query('product'); if (!$productId) { return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); } $vm = $this->resolveVmById($productId, $request); if ($vm instanceof JsonResponse) { return $vm; } $vm->loadMissing(['order.orderItems.product.addons']); $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, 'type' => $addon->type instanceof \BackedEnum ? $addon->type->value : (string) $addon->type, '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), 'prefix' => $addon->prefix, 'input_type' => $addon->input_type ?? 'number', 'sort_order' => (int) ($addon->sort_order ?? 0), ]; }); return response()->json([ 'error' => false, 'data' => $addons->values(), 'meta' => [ '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, ], ], ]); } public function upgradeAddons(Request $request, vCenter $serviceVCenter): JsonResponse { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required'], 'cpu' => ['nullable', 'integer', 'min:0'], 'ram' => ['nullable', 'integer', 'min:0'], 'disk' => ['nullable', 'integer', 'min:0'], 'bandwidth' => ['nullable', 'integer', 'min:0'], 'gpu' => ['nullable'], ]); $vm = $this->resolveVmById($payload['product'], $request); if ($vm instanceof JsonResponse) { return $vm; } $vm->loadMissing(['vCenter', 'ipAddress.host.datacenter.vCenter']); $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'; } } $this->activityLogger->log( event: 'upgrade_addons', logName: 'VM', description: "Customer {$customer->email} nâng cấp addons VM: {$vm->name} - áp dụng: " . implode(', ', $applied ?: ['none']), subject: $vm, newValues: array_filter(['cpu' => $payload['cpu'] ?? null, 'ram' => $payload['ram'] ?? null, 'disk' => $payload['disk'] ?? null]), ); 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', ], ]); } }