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 @@
| {{ __('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') }} |
|---|---|---|---|---|---|---|---|---|---|---|
| - | - | + |
@@ -61,8 +60,11 @@
:disabled="snap.is_current"
:class="snap.is_current ? 'opacity-50 cursor-not-allowed' : ''"
title="{{ __('Xóa snapshot này') }}">
- |