diff --git a/app/Http/Controllers/frontend/ServiceController.php b/app/Http/Controllers/frontend/ServiceController.php index 41bf0cc..7256cfd 100644 --- a/app/Http/Controllers/frontend/ServiceController.php +++ b/app/Http/Controllers/frontend/ServiceController.php @@ -11,6 +11,7 @@ use App\Repositories\Invoice\InvoiceRepositoryInterface; use App\Http\Services\vCenter; use App\Jobs\VirtualMachine\Action\ChangeRemotePortJob; use App\Models\ModuleSnapshot; +use App\Models\ModuleSnapshotLimit; use App\Models\ModuleVmware; use App\Models\Order; use App\Models\Os; @@ -59,7 +60,6 @@ class ServiceController extends Controller $this->dispatchVMInfoUpdateJob($invoice); } $title = "Chi tiết dịch vụ #" . $invoice->id; - dd($vmInfo); return view('frontend.service.show', [ 'title' => $title, 'invoice' => $invoice, @@ -354,194 +354,253 @@ class ServiceController extends Controller return view('frontend.service.console', $data); } - public function createSnapshots(SnapShotRequest $request, vCenter $serviceVCenter) + public function createSnapshots(SnapShotRequest $request) { try { $user = customer(); if (!$user) { - return redirect()->back()->with('error', 'Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.'); + return redirect()->back()->with('error', 'Chưa đăng nhập.'); } - $validator = $request->validated(); - - $productId = $validator['product_id']; - $userId = $user->id; + $data = $request->validated(); + $productId = $data['product_id']; $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter') ->where('vmware_id', $productId) - ->where('customer_id', $userId) + ->where('customer_id', $user->id) ->first(); if (!$product) { - return redirect()->back()->with('error', "Không tìm thấy VPS #{$productId} hoặc bạn không có quyền truy cập"); - } - - if (!$product->ipAddress) { - return redirect()->back()->with('error', "VPS #{$productId} không có thông tin IP được cấu hình"); - } - - if (!$product->ipAddress->host) { - return redirect()->back()->with('error', "VPS #{$productId} không có thông tin Host được cấu hình"); - } - - if (!$product->ipAddress->host->datacenter) { - return redirect()->back()->with('error', "VPS #{$productId} không có thông tin Datacenter được cấu hình"); - } - - if (!$product->ipAddress->host->datacenter->vCenter) { - return redirect()->back()->with('error', "VPS #{$productId} không có thông tin vCenter được cấu hình"); + return redirect()->back()->with('error', "Không tìm thấy VPS hoặc bạn không có quyền."); } if ($product->status != 1) { - return redirect()->back()->with('error', 'Chỉ có thể tạo snapshot cho máy ảo đang hoạt động'); + return redirect()->back()->with('error', 'Chỉ tạo snapshot cho VM đang hoạt động.'); } - // Kiểm tra giới hạn snapshot - $snapshotLimit = \App\Models\ModuleSnapshotLimit::where('customer_id', $user->id)->first(); - if ($snapshotLimit) { - $currentCount = ModuleSnapshot::where('customer_id', $user->id) + $limit = ModuleSnapshotLimit::where('customer_id', $user->id)->first(); + if ($limit) { + $count = ModuleSnapshot::where('customer_id', $user->id) ->where('module_vmware_id', $product->id) ->count(); - if ($currentCount >= $snapshotLimit->limit) { - return redirect()->back()->with('error', "Đã đạt giới hạn snapshot ({$snapshotLimit->limit}). Vui lòng xóa snapshot cũ."); + if ($count >= $limit->limit) { + return redirect()->back()->with( + 'error', + "Đã đạt giới hạn snapshot ({$limit->limit})." + ); } } - // Dispatch job để tạo snapshot trong background - \App\Jobs\VirtualMachine\Snapshot\CreateSnapshot::dispatch($product->id) - ->delay(now()->addSeconds(5)) - ->onQueue('snapshots'); - - return redirect()->back()->with('success', 'Đã gửi yêu cầu tạo snapshot. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.'); - } catch (\Exception $e) { - Log::error('❌ Lỗi khi tạo snapshot', [ - 'product_id' => $request->product_id ?? 'not_provided', - 'user_id' => $user->id ?? 'guest', - 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine() + $snapshot = ModuleSnapshot::create([ + 'name' => 'snapshot-' . now()->format('Ymd-His'), + 'customer_id' => $user->id, + 'module_vmware_id' => $product->id, + 'status' => 'pending', + 'created_by' => $user->id, ]); - return redirect()->back()->with('error', 'Lỗi hệ thống khi tạo snapshot: ' . $e->getMessage()); + \App\Jobs\VirtualMachine\Snapshot\CreateSnapshot::dispatch( + $snapshot->id + ) + ->onQueue('snapshots') + ->delay(now()->addSeconds(3)); + + return redirect()->back()->with( + 'success', + 'Đã gửi yêu cầu tạo snapshot. Hệ thống sẽ xử lý trong nền.' + ); + } catch (\Throwable $e) { + Log::error('Lỗi khi tạo snapshot', [ + 'product_id' => $request->product_id ?? null, + 'user_id' => $user->id ?? null, + 'error' => $e->getMessage(), + ]); + + return redirect()->back()->with( + 'error', + 'Lỗi hệ thống khi tạo snapshot.' + ); } } + /** * Lấy danh sách snapshot với bảo mật và tối ưu hóa */ - public function getSnapshots(Request $request, vCenter $serviceVCenter) + // public function getSnapshots(Request $request, vCenter $serviceVCenter) + // { + // try { + // // Kiểm tra authentication + // $user = customer(); + // if (!$user) { + // return response()->json([ + // 'error' => true, + // 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.') + // ], 401); + // } + + // // Validation với sanitization + // $validator = Validator::make($request->all(), [ + // 'product_id' => 'required|string|max:255', + // ]); + + // if ($validator->fails()) { + // return response()->json([ + // 'error' => true, + // 'message' => __('Dữ liệu không hợp lệ'), + // 'errors' => $validator->errors() + // ], 422); + // } + + // $productId = $this->sanitizeInput($request->product_id); + // $userId = $user->id; + + // // Cache key cho snapshot data + // $cacheKey = "snapshots_{$userId}_{$productId}"; + // $cacheDuration = 300; // 5 phút + + // // Thử lấy từ cache trước + // if (Cache::has($cacheKey)) { + // $cachedData = Cache::get($cacheKey); + // if ($cachedData && !$this->isCacheExpired($cachedData)) { + // return response()->json([ + // 'error' => false, + // 'message' => __('Lấy danh sách snapshot thành công'), + // 'data' => $cachedData, + // 'cached' => true + // ]); + // } + // } + + // // Tìm VPS với eager loading tối ưu + // $product = $this->findVmwareProduct($productId, $userId); + // if (!$product) { + // return response()->json([ + // 'error' => true, + // 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập') + // ], 404); + // } + + // // Kiểm tra cấu hình vCenter + // $vcenter = $this->validateVcenterConfiguration($product); + // if (!$vcenter) { + // return response()->json([ + // 'error' => true, + // 'message' => __('Cấu hình vCenter không hợp lệ') + // ], 400); + // } + + // // Lấy snapshot từ vCenter + // $snapshots = $this->fetchSnapshotsFromVCenter($serviceVCenter, $product, $vcenter); + // if ($snapshots === false) { + // return response()->json([ + // 'error' => true, + // 'message' => __('Không thể kết nối đến vCenter') + // ], 500); + // } + + // // Lấy thông tin bổ sung từ database + // $dbSnapshots = $this->getDatabaseSnapshots($userId, $product->id); + + // // Merge và sanitize data + // $processedSnapshots = $this->processSnapshotsData($snapshots, $dbSnapshots); + + // // Chuẩn bị response data + // $responseData = [ + // 'snapshots' => $processedSnapshots, + // 'total_count' => count($processedSnapshots), + // 'vm_name' => $this->sanitizeOutput($product->name), + // 'vm_id' => $product->vmware_id, + // 'last_updated' => now()->format('Y-m-d H:i:s') + // ]; + + // // Cache kết quả + // Cache::put($cacheKey, $responseData, $cacheDuration); + + // return response()->json([ + // 'error' => false, + // 'message' => __('Lấy danh sách snapshot thành công'), + // 'data' => $responseData, + // 'cached' => false + // ]); + // } catch (\Exception $e) { + // Log::error('Lỗi khi lấy danh sách snapshot', [ + // 'user_id' => $user->id ?? 'guest', + // 'product_id' => $request->product_id ?? 'not_provided', + // 'error' => $e->getMessage(), + // 'file' => $e->getFile(), + // 'line' => $e->getLine() + // ]); + + // return response()->json([ + // 'error' => true, + // 'message' => __('Có lỗi xảy ra khi lấy danh sách snapshot') + // ], 500); + // } + // } + + public function getSnapshots(Request $request) { - try { - // Kiểm tra authentication - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.') - ], 401); - } - - // Validation với sanitization - $validator = Validator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - ]); - - if ($validator->fails()) { - return response()->json([ - 'error' => true, - 'message' => __('Dữ liệu không hợp lệ'), - 'errors' => $validator->errors() - ], 422); - } - - $productId = $this->sanitizeInput($request->product_id); - $userId = $user->id; - - // Cache key cho snapshot data - $cacheKey = "snapshots_{$userId}_{$productId}"; - $cacheDuration = 300; // 5 phút - - // Thử lấy từ cache trước - if (Cache::has($cacheKey)) { - $cachedData = Cache::get($cacheKey); - if ($cachedData && !$this->isCacheExpired($cachedData)) { - return response()->json([ - 'error' => false, - 'message' => __('Lấy danh sách snapshot thành công'), - 'data' => $cachedData, - 'cached' => true - ]); - } - } - - // Tìm VPS với eager loading tối ưu - $product = $this->findVmwareProduct($productId, $userId); - if (!$product) { - return response()->json([ - 'error' => true, - 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập') - ], 404); - } - - // Kiểm tra cấu hình vCenter - $vcenter = $this->validateVcenterConfiguration($product); - if (!$vcenter) { - return response()->json([ - 'error' => true, - 'message' => __('Cấu hình vCenter không hợp lệ') - ], 400); - } - - // Lấy snapshot từ vCenter - $snapshots = $this->fetchSnapshotsFromVCenter($serviceVCenter, $product, $vcenter); - if ($snapshots === false) { - return response()->json([ - 'error' => true, - 'message' => __('Không thể kết nối đến vCenter') - ], 500); - } - - // Lấy thông tin bổ sung từ database - $dbSnapshots = $this->getDatabaseSnapshots($userId, $product->id); - - // Merge và sanitize data - $processedSnapshots = $this->processSnapshotsData($snapshots, $dbSnapshots); - - // Chuẩn bị response data - $responseData = [ - 'snapshots' => $processedSnapshots, - 'total_count' => count($processedSnapshots), - 'vm_name' => $this->sanitizeOutput($product->name), - 'vm_id' => $product->vmware_id, - 'last_updated' => now()->format('Y-m-d H:i:s') - ]; - - // Cache kết quả - Cache::put($cacheKey, $responseData, $cacheDuration); - - return response()->json([ - 'error' => false, - 'message' => __('Lấy danh sách snapshot thành công'), - 'data' => $responseData, - 'cached' => false - ]); - } catch (\Exception $e) { - Log::error('Lỗi khi lấy danh sách snapshot', [ - 'user_id' => $user->id ?? 'guest', - 'product_id' => $request->product_id ?? 'not_provided', - 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine() - ]); - + $user = customer(); + if (!$user) { return response()->json([ 'error' => true, - 'message' => __('Có lỗi xảy ra khi lấy danh sách snapshot') - ], 500); + 'message' => __('Chưa đăng nhập') + ], 401); } + + $validator = Validator::make($request->all(), [ + 'product_id' => 'required|string|max:255', + ]); + + if ($validator->fails()) { + return response()->json([ + 'error' => true, + 'message' => __('Dữ liệu không hợp lệ'), + 'errors' => $validator->errors() + ], 422); + } + + $product = $this->findVmwareProduct( + $this->sanitizeInput($request->product_id), + $user->id + ); + + if (!$product) { + return response()->json([ + 'error' => true, + 'message' => __('Không tìm thấy VPS') + ], 404); + } + + $snapshots = ModuleSnapshot::query() + ->where('customer_id', $user->id) + ->where('module_vmware_id', $product->id) + ->orderByDesc('created_at') + ->get([ + 'id', + 'name', + 'status', + 'snapshot_ref', + 'error_message', + 'created_at', + ]); + + return response()->json([ + 'error' => false, + 'message' => __('Lấy danh sách snapshot thành công'), + 'data' => [ + 'vm_name' => e($product->name), + 'vm_id' => $product->vmware_id, + 'snapshots' => $snapshots, + 'total_count' => $snapshots->count(), + 'last_updated' => now()->format('Y-m-d H:i:s'), + ] + ]); } + /** * Reset VM password (Windows/Linux) via vCenter helpers */ @@ -956,7 +1015,7 @@ class ServiceController extends Controller ], 401); } - $validator = \Validator::make($request->all(), [ + $validator = Validator::make($request->all(), [ 'product_id' => 'required|string|max:255', ]); if ($validator->fails()) { @@ -983,7 +1042,7 @@ class ServiceController extends Controller $result = $vCenterService->StartVirtualMachine($product, $product->vmware_id); if (is_array($result) && isset($result['error']) && $result['error'] === false) { // Clear VM info cache after starting VM - \Cache::forget('vm_info_' . $product->vmware_id); + Cache::forget('vm_info_' . $product->vmware_id); return response()->json([ 'error' => false, 'message' => __('Yêu cầu khởi động máy ảo đã được gửi thành công.') @@ -996,7 +1055,7 @@ class ServiceController extends Controller ], 500); } } catch (\Exception $e) { - \Log::error('Lỗi khi khởi động máy ảo', [ + Log::error('Lỗi khi khởi động máy ảo', [ 'user_id' => $user->id ?? 'guest', 'product_id' => $request->product_id ?? 'not_provided', 'error' => $e->getMessage(), @@ -1050,7 +1109,6 @@ class ServiceController extends Controller $result = $vCenterService->ShutdownGuestVirtualMachine($product, $product->vmware_id); if (is_array($result) && isset($result['error']) && $result['error'] === false) { - // Clear VM info cache after stopping VM Cache::forget('the_gioi_so_database_the_gioi_so_cache:vm_info_' . $product->vmware_id); return response()->json([ 'error' => false, @@ -1064,7 +1122,7 @@ class ServiceController extends Controller ], 500); } } catch (\Exception $e) { - \Log::error('Lỗi khi tắt máy ảo', [ + Log::error('Lỗi khi tắt máy ảo', [ 'user_id' => $user->id ?? 'guest', 'product_id' => $request->product_id ?? 'not_provided', 'error' => $e->getMessage(), @@ -1092,7 +1150,7 @@ class ServiceController extends Controller ], 401); } - $validator = \Validator::make($request->all(), [ + $validator = Validator::make($request->all(), [ 'product_id' => 'required|string|max:255', ]); if ($validator->fails()) { @@ -1130,7 +1188,7 @@ class ServiceController extends Controller ], 500); } } catch (\Exception $e) { - \Log::error('Lỗi khi reboot VM', [ + Log::error('Lỗi khi reboot VM', [ 'user_id' => $user->id ?? 'guest', 'product_id' => $request->product_id ?? 'not_provided', 'error' => $e->getMessage(), @@ -1148,12 +1206,6 @@ class ServiceController extends Controller public function reinstallVM(Request $request, vCenter $vCenterService) { try { - // Debug: Log request data - Log::info('reinstallVM called', [ - 'request_data' => $request->all(), - 'user_id' => customer()?->id ?? 'not_logged_in' - ]); - $user = customer(); if (!$user) { Log::warning('User not logged in for reinstallVM'); @@ -1178,18 +1230,7 @@ class ServiceController extends Controller ], 422); } - // Debug: Log invoice lookup - Log::info('Looking for invoice', ['invoice_id' => $request->invoice_id]); - $invoice = Invoice::find($request->invoice_id); - - // Debug: Log invoice result - Log::info('Invoice lookup result', [ - 'invoice_id' => $request->invoice_id, - 'found' => $invoice ? 'yes' : 'no', - 'invoice_data' => $invoice ? $invoice->toArray() : null - ]); - if (!$invoice) { return response()->json([ 'error' => true, @@ -1199,27 +1240,11 @@ class ServiceController extends Controller // Debug: Log product lookup $invoiceItem = $invoice->invoiceItem->first(); - Log::info('Invoice item lookup', [ - 'invoice_item_found' => $invoiceItem ? 'yes' : 'no', - 'invoice_item_data' => $invoiceItem ? $invoiceItem->toArray() : null - ]); - $productId = $invoiceItem?->getProductAttribute()?->id; - Log::info('Product lookup', [ - 'product_id' => $productId, - 'product_data' => $invoiceItem?->getProductAttribute()?->toArray() ?? null - ]); - $vmware = $productId; $osId = $request->os_id; $userId = $user->id; - Log::info('VM lookup parameters', [ - 'vmware_id' => $vmware?->vmware_id ?? 'null', - 'os_id' => $osId, - 'user_id' => $userId - ]); - // Tìm product và kiểm tra quyền sở hữu $vm = ModuleVmware::where('vmware_id', $vmware->vmware_id) ->where('customer_id', $userId) @@ -1254,42 +1279,14 @@ class ServiceController extends Controller ], 403); } - // Debug: Log before vCenter call - Log::info('About to call vCenter DeleteVirtualMachine', [ - 'vmware_id' => $vm->vmware_id, - 'os_id' => $osId - ]); - - // Gọi vCenter service để xóa VM $result = $vCenterService->DeleteVirtualMachine($vm->vmware_id); - - // Debug: Log vCenter result - Log::info('vCenter DeleteVirtualMachine result', [ - 'result' => $result, - 'is_array' => is_array($result), - 'has_error_key' => is_array($result) && isset($result['error']), - 'error_value' => is_array($result) ? ($result['error'] ?? 'not_set') : 'not_array' - ]); - if (is_array($result) && isset($result['error']) && $result['error'] === false) { // Cập nhật OS trong database $vm->update([ 'os_id' => $osId ]); - // Clear VM info cache Cache::forget('vm_info_' . $vm->vmware_id); - - // Log hoạt động - Log::info('VM reinstall initiated', [ - 'user_id' => $userId, - 'product_id' => $vm->id, - 'old_os_id' => $vm->os_id, - 'new_os_id' => $osId, - 'os_name' => $os->version - ]); - - Log::info('Returning success response'); return response()->json([ 'error' => false, 'message' => __('Yêu cầu cài lại máy ảo đã được gửi thành công. Quá trình sẽ mất 5-10 phút.') diff --git a/app/Http/Services/vCenter.php b/app/Http/Services/vCenter.php index 7c44186..527adf7 100644 --- a/app/Http/Services/vCenter.php +++ b/app/Http/Services/vCenter.php @@ -898,6 +898,7 @@ PS; ]) ->patch("https://{$this->apiUri}/rest/vcenter/vm/{$vmId}/hardware/cpu", $payload); + if ($response->successful()) { Log::info("✅ API call cập nhật CPU thành công cho VM {$vmId}"); return true; @@ -1627,9 +1628,6 @@ PS; return "Login_Again"; } - /** - * Clear snapshot cache for a specific VM - */ private function clearSnapshotCache($product): void { $vmware_name = (string) $product->name; diff --git a/app/Jobs/VirtualMachine/.DS_Store b/app/Jobs/VirtualMachine/.DS_Store index c12b39a..edf147f 100644 Binary files a/app/Jobs/VirtualMachine/.DS_Store and b/app/Jobs/VirtualMachine/.DS_Store differ diff --git a/app/Jobs/VirtualMachine/Snapshot/CreateSnapshot.php b/app/Jobs/VirtualMachine/Snapshot/CreateSnapshot.php index a07c9a8..85fa1d7 100644 --- a/app/Jobs/VirtualMachine/Snapshot/CreateSnapshot.php +++ b/app/Jobs/VirtualMachine/Snapshot/CreateSnapshot.php @@ -19,153 +19,87 @@ class CreateSnapshot implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $productId; + public function __construct( + public int $snapshotId + ) {} - protected $telegram; - - /** - * Create a new job instance. - * - * @param int $productId - * @return void - */ - public function __construct($productId = 0) - { - $this->productId = $productId; - $this->telegram = new Telegram(); - } - - /** - * Execute the job. - * - * @return void - */ public function handle(ServiceVCenter $serviceVCenter) { + $snapshot = ModuleSnapshot::with('vm.ipAddress.host.datacenter.vCenter') + ->find($this->snapshotId); + + if (!$snapshot) { + return; + } + + $snapshot->update(['status' => 'processing']); + + $product = $snapshot->vm; + $vcenter = $product->ipAddress->host->datacenter->vCenter; + try { - // Load product with necessary relationships - $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')->find($this->productId); - - if (!$product) { - throw new \Exception("Không tìm thấy thông tin VPS với ID: {$this->productId}"); - } - - // Kiểm tra cấu hình vCenter - if (!$product->ipAddress?->host?->datacenter?->vCenter) { - throw new \Exception("VPS không có thông tin vCenter được cấu hình"); - } - - $vcenter = $product->ipAddress->host->datacenter->vCenter; - - // Set vCenter connection info + // Config vCenter $serviceVCenter->token = $vcenter->token; $serviceVCenter->apiUri = $vcenter->host; - Log::info('🔄 Bắt đầu tạo snapshot', [ - 'product_id' => $product->id, - 'vmware_id' => $product->vmware_id, - 'vcenter_host' => $vcenter->host - ]); + // Call vCenter + $result = $serviceVCenter->CreateSnapShot($product); - // Attempt to create snapshot - $snapshot = $serviceVCenter->CreateSnapShot($product); - - // Handle re-login if needed - if ($snapshot === 'Login_Again') { - Log::info('🔑 Cần đăng nhập lại vCenter'); + if ($result === 'Login_Again') { $token = $serviceVCenter->loginServer($vcenter); - if ($token) { - $vcenter->update(['token' => $token]); - $serviceVCenter->token = $token; - $snapshot = $serviceVCenter->CreateSnapShot($product); - } else { - throw new \Exception("Không thể đăng nhập lại vào vCenter"); + if (!$token) { + throw new \Exception('Không thể login lại vCenter'); } + + $vcenter->update(['token' => $token]); + $serviceVCenter->token = $token; + $result = $serviceVCenter->CreateSnapShot($product); } - // Check for errors - if (is_array($snapshot) && isset($snapshot['error']) && $snapshot['error']) { - $errorMessage = 'Lỗi tạo snapshot'; - if (isset($snapshot['message'])) { - $errorMessage .= ': ' . (is_array($snapshot['message']) ? json_encode($snapshot['message']) : $snapshot['message']); - } - throw new \Exception($errorMessage); + if (is_array($result) && ($result['error'] ?? false)) { + throw new \Exception($result['message'] ?? 'Lỗi tạo snapshot'); } - if (is_string($snapshot) && $snapshot !== 'Login_Again') { - throw new \Exception("Lỗi tạo snapshot: {$snapshot}"); - } - - // Get snapshot name from response - $snapshotName = null; - if (is_array($snapshot) && isset($snapshot['data'])) { - $snapshotName = $snapshot['data']; - } else { - $snapshotName = 'snapshot_' . now()->format('Y_m_d_H_i_s'); - } - - // Update snapshot limit counter - ModuleSnapshotLimit::where('customer_id', $product->customer_id)->increment('used', 1); - - // Save snapshot to database - ModuleSnapshot::create([ - 'name' => $snapshotName, - 'customer_id' => $product->customer_id, - 'module_vmware_id' => $product->id + // Update DB SUCCESS + $snapshot->update([ + 'name' => $result['name'] ?? $snapshot->name, + 'status' => 'success', + 'snapshot_ref' => $result['snapshot_id'] ?? null, + 'vcenter_task_id' => $result['task_id'] ?? null, ]); - Log::info('✅ Tạo snapshot thành công', [ - 'product_id' => $product->id, - 'snapshot_name' => $snapshotName + // Increment quota CHỈ khi success + ModuleSnapshotLimit::where('customer_id', $snapshot->customer_id) + ->increment('used'); + + event(new NotificationStatus( + "Snapshot {$snapshot->name} tạo thành công", + true + )); + } catch (\Throwable $e) { + $snapshot->update([ + 'status' => 'failed', + 'error_message' => $e->getMessage(), ]); - // Send success notification - Event(new NotificationStatus("Khởi tạo snapshot thành công cho VPS {$product->name}", true)); - - // Send Telegram notification - $this->telegram->SendMessage( - "🎉 SNAPSHOT CREATED SUCCESSFULLY\n\n" . - "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" . - "🖥️ Server: `{$product->name}`\n" . - "📸 Snapshot: `{$snapshotName}`\n" . - "👤 Customer: `#{$product->customer_id}`\n" . - "🆔 VM ID: `{$product->vmware_id}`\n" . - "⏰ Created: `" . now()->format('d/m/Y H:i:s') . "`\n" . - "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" . - "💡 Status: ✅ Completed\n" . - "🔧 Action: Snapshot Creation\n" . - "📊 Queue: snapshots" - ); - - } catch (\Exception $e) { - Log::error('❌ Lỗi tạo snapshot', [ - 'product_id' => $this->productId, - 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine() - ]); - - throw $e; // Re-throw để kích hoạt failed method + throw $e; } } public function failed(\Throwable $exception) { - Log::error('❌ Job CreateSnapshot failed', [ - 'product_id' => $this->productId, - 'error' => $exception->getMessage(), - 'trace' => $exception->getTraceAsString() - ]); + $snapshot = ModuleSnapshot::find($this->snapshotId); - // Send failure notification - Event(new NotificationStatus("Lỗi tạo snapshot: {$exception->getMessage()}", false)); + if ($snapshot) { + $snapshot->update([ + 'status' => 'failed', + 'error_message' => $exception->getMessage(), + ]); + } - // Send Telegram error notification - $this->telegram->SendErrorMessage([ - 'job_name' => 'CreateSnapshot', - 'product_id' => $this->productId, - 'error' => $exception->getMessage(), - 'trace' => $exception->getTraceAsString() - ]); + event(new NotificationStatus( + "Snapshot thất bại: {$exception->getMessage()}", + false + )); } } diff --git a/app/Models/ModuleSnapshot.php b/app/Models/ModuleSnapshot.php index 67d1107..bb59040 100644 --- a/app/Models/ModuleSnapshot.php +++ b/app/Models/ModuleSnapshot.php @@ -14,6 +14,16 @@ class ModuleSnapshot extends Model protected $fillable = [ 'name', 'customer_id', - 'module_vmware_id' + 'module_vmware_id', + 'status', + 'description', + 'snapshot_ref', + 'vcenter_task_id', + 'error_message', ]; + + public function vm() + { + return $this->belongsTo(ModuleVmware::class, 'module_vmware_id'); + } } diff --git a/database/migrations/2026_01_17_143127_update_column_snapshot.php b/database/migrations/2026_01_17_143127_update_column_snapshot.php new file mode 100644 index 0000000..c6aecfa --- /dev/null +++ b/database/migrations/2026_01_17_143127_update_column_snapshot.php @@ -0,0 +1,49 @@ +text('description')->nullable()->after('name'); + + // Async snapshot status + $table->enum('status', [ + 'pending', + 'processing', + 'success', + 'failed' + ])->default('pending')->index()->after('module_vmware_id'); + + // vCenter mapping + $table->string('snapshot_ref')->nullable()->comment('Snapshot ID / moRef from vCenter'); + $table->string('vcenter_task_id')->nullable()->comment('Task ID from vCenter'); + + // Error handling + $table->text('error_message')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_snapshot', function (Blueprint $table) { + $table->dropColumn([ + 'description', + 'status', + 'snapshot_ref', + 'vcenter_task_id', + 'error_message', + ]); + }); + } +}; diff --git a/database/migrations/2026_01_17_151930_update_column_module_snapshot.php b/database/migrations/2026_01_17_151930_update_column_module_snapshot.php new file mode 100644 index 0000000..2c0e44f --- /dev/null +++ b/database/migrations/2026_01_17_151930_update_column_module_snapshot.php @@ -0,0 +1,39 @@ +unsignedBigInteger('size_bytes') + ->nullable() + ->comment('Snapshot size in bytes'); + + $table->decimal('size_gb', 10, 2) + ->nullable() + ->comment('Snapshot size in GB'); + + $table->timestamp('size_last_synced_at') + ->nullable() + ->comment('Last time snapshot size was synced'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('module_snapshot', function (Blueprint $table) { + // + }); + } +}; diff --git a/resources/views/frontend/service/partials/snapshots-modal.blade.php b/resources/views/frontend/service/partials/snapshots-modal.blade.php index 31055eb..9110f60 100644 --- a/resources/views/frontend/service/partials/snapshots-modal.blade.php +++ b/resources/views/frontend/service/partials/snapshots-modal.blade.php @@ -7,14 +7,7 @@ {{ __('Snapshot của máy ảo') }} {{ __('Xem, tạo mới, khôi phục hoặc xóa snapshot.') }} -
- {{ __('Tải lại') }} -
- @csrf - - {{ __('Tạo snapshot') }} -
-
+
@@ -26,24 +19,28 @@ - - - - - - + + + + + +
{{ __('Không có snapshot nào.') }}
+
+
+ + {{ __('Tải lại') }} +
+ @csrf + + {{ __('Tạo snapshot') }} + +
+ - -
{{ __('Tên') }}{{ __('Mô tả') }}{{ __('Ngày tạo') }}{{ __('Trạng thái') }}{{ __('Dung lượng') }}{{ __('Thao tác') }} + {{ __('Tên') }} + {{ __('Ngày tạo') }} + {{ __('Trạng thái') }} + {{ __('Dung lượng') }} + {{ __('Thao tác') }}