user(); $payload = $request->validate([ 'product' => ['required'], 'description' => ['nullable', 'string', 'max:1000'], 'name' => ['nullable', 'string', 'max:80', 'regex:/^[A-Za-z0-9._-]+$/'], ], [ 'name.regex' => 'Tên snapshot chỉ gồm chữ không dấu, số và các ký tự . _ -', 'name.max' => 'Tên snapshot tối đa 80 ký tự.', ]); $vm = $this->resolveVmById($payload['product'], $request); if ($vm instanceof JsonResponse) { return $vm; } $snapshotName = $payload['name'] ?? $this->suggestName($vm); if (ModuleSnapshot::where('module_vmware_id', $vm->id)->where('name', $snapshotName)->exists()) { return response()->json(['error' => true, 'message' => 'Tên snapshot đã tồn tại, vui lòng chọn tên khác.'], 422); } $snapshot = ModuleSnapshot::create([ 'name' => $snapshotName, 'description' => $payload['description'] ?? null, 'customer_id' => $customer->id, 'module_vmware_id' => $vm->id, 'status' => 'pending', 'snapshot_ref' => null, ]); CreateSnapshot::dispatch($snapshot->id); $this->activityLogger->log( event: 'created', logName: 'Snapshot', description: "Customer {$customer->email} tạo snapshot: {$snapshot->name} cho VM #{$vm->id}", subject: $snapshot, newValues: ['name' => $snapshot->name, 'vm_id' => $vm->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', ], ]); } private function suggestName(ModuleVmware $vm): string { $prefix = sprintf('%s-%s-', $vm->name, now()->format('d-m-Y')); $sequence = ModuleSnapshot::where('module_vmware_id', $vm->id)->whereDate('created_at', today())->count() + 1; while (ModuleSnapshot::where('module_vmware_id', $vm->id)->where('name', $prefix . $sequence)->exists()) { $sequence++; } return $prefix . $sequence; } public function index(Request $request, vCenter $serviceVCenter): JsonResponse { $productParam = $request->query('product'); if (!$productParam) { return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); } $vm = $this->resolveVmById($productParam, $request, requireActiveService: false); if ($vm instanceof JsonResponse) { return $vm; } $remote = $serviceVCenter->getSnapShot($vm); 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 = ModuleSnapshot::query() ->where('module_vmware_id', $vm->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' => $request->user()->id, 'module_vmware_id' => $vm->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) { if ($snapshot->status === 'deleting') { unset($payload['status']); } $snapshot->update($payload); } else { ModuleSnapshot::create($payload); } } $dbSnapshots = ModuleSnapshot::query() ->where('module_vmware_id', $vm->id) ->where('customer_id', $request->user()->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, 'suggested_name' => $this->suggestName($vm), ]); } public function revert(Request $request): JsonResponse { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required'], 'snapshot_id' => ['required', 'numeric'], ]); $vm = $this->resolveVmById($payload['product'], $request); if ($vm instanceof JsonResponse) { return $vm; } $snapshot = ModuleSnapshot::where('id', $payload['snapshot_id']) ->where('module_vmware_id', $vm->id) ->where('customer_id', $customer->id) ->first(); if (!$snapshot) { return response()->json(['error' => true, 'message' => 'Không tìm thấy snapshot'], 404); } RevertSnapshot::dispatch($vm->id, $snapshot->id); $this->activityLogger->log( event: 'revert', logName: 'Snapshot', description: "Customer {$customer->email} revert snapshot: {$snapshot->name} (ID: {$snapshot->id}) cho VM #{$vm->id}", subject: $snapshot, ); return response()->json([ 'error' => false, 'message' => 'Đã gửi yêu cầu revert snapshot vào hàng đợi', ]); } public function destroy(Request $request): JsonResponse { $customer = $request->user(); $payload = $request->validate([ 'product' => ['required'], 'snapshot_id' => ['required', 'numeric'], ]); $vm = $this->resolveVmById($payload['product'], $request); if ($vm instanceof JsonResponse) { return $vm; } $snapshot = ModuleSnapshot::where('id', $payload['snapshot_id']) ->where('module_vmware_id', $vm->id) ->where('customer_id', $customer->id) ->first(); if (!$snapshot) { return response()->json(['error' => true, 'message' => 'Không tìm thấy snapshot'], 404); } if ($snapshot->status === 'deleting') { return response()->json(['error' => true, 'message' => 'Snapshot đang được xóa.'], 422); } $deletedNow = $snapshot->status === 'failed'; if ($deletedNow) { $snapshot->delete(); } else { $snapshot->update(['status' => 'deleting']); RemoveSnapshot::dispatch($vm->id, $snapshot->id); } $this->activityLogger->log( event: 'deleted', logName: 'Snapshot', description: "Customer {$customer->email} xóa snapshot: {$snapshot->name} (ID: {$snapshot->id}) của VM #{$vm->id}", subject: $snapshot, ); return response()->json([ 'error' => false, 'message' => $deletedNow ? 'Đã xóa snapshot' : 'Đã gửi yêu cầu xóa snapshot vào hàng đợi', ]); } }