1457 lines
55 KiB
PHP
1457 lines
55 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\frontend;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Vm\SnapShotRequest;
|
|
use App\Jobs\VirtualMachine\Action\ResetVmPasswordJob;
|
|
use App\Models\Invoice;
|
|
use App\Models\InvoiceItem;
|
|
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;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\Validation\Validator as ValidatorInstance;
|
|
use Illuminate\Support\Facades\Response;
|
|
use Illuminate\Support\Facades\Validator as FacadesValidator;
|
|
|
|
class ServiceController extends Controller
|
|
{
|
|
public function __construct(
|
|
public InvoiceRepositoryInterface $invoiceRepository,
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* Hiển thị chi tiết service dựa trên invoice_id - FURTHER OPTIMIZED
|
|
*/
|
|
public function show($invoice_ordernum, vCenter $vCenterService)
|
|
{
|
|
try {
|
|
// Tìm invoice với các relationship cần thiết - OPTIMIZED
|
|
$invoice = $this->invoiceRepository->findWithRelationships($invoice_ordernum, [
|
|
'order',
|
|
'invoiceItem.addons',
|
|
'invoiceItem.os.osGroup',
|
|
'customer'
|
|
]);
|
|
|
|
if (!$invoice) {
|
|
return redirect()->route('service.index')->with('error', 'Không tìm thấy dịch vụ.');
|
|
}
|
|
|
|
if ($invoice->customer_id !== customer()->id) {
|
|
return redirect()->route('service.index')->with('error', 'Bạn không có quyền truy cập dịch vụ này.');
|
|
}
|
|
|
|
$vmInfo = null;
|
|
if ($invoice->vm_id) {
|
|
$vmInfo = $this->getVMInfoFromVCenter($invoice, $vCenterService);
|
|
$this->dispatchVMInfoUpdateJob($invoice);
|
|
}
|
|
$title = "Chi tiết dịch vụ #" . $invoice->id;
|
|
return view('frontend.service.show', [
|
|
'title' => $title,
|
|
'invoice' => $invoice,
|
|
'vmInfo' => $vmInfo,
|
|
'breadcrumbs' => $this->crums->add(__('Dashboard'))->add('Dịch vụ #' . $invoice->id),
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Error in ServiceController@show: ' . $e->getMessage());
|
|
return redirect()->route('service.index')->with('error', 'Có lỗi xảy ra khi tải thông tin dịch vụ.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lấy thông tin VM từ vCenter API - OPTIMIZED WITH CACHING
|
|
*/
|
|
private function getVMInfoFromVCenter($invoice, $vCenterService)
|
|
{
|
|
$cacheKey = "vm_info_{$invoice->vm_id}";
|
|
$cacheDuration = 300; // 5 minutes
|
|
|
|
if (Cache::has($cacheKey)) {
|
|
return Cache::get($cacheKey);
|
|
}
|
|
|
|
try {
|
|
$orderItem = $invoice->invoiceItem->first();
|
|
if (!$orderItem) {
|
|
Log::warning('Không tìm thấy order item cho invoice', ['invoice_id' => $invoice->id]);
|
|
$defaultInfo = $this->getDefaultVMInfo($invoice);
|
|
Cache::put($cacheKey, $defaultInfo, $cacheDuration);
|
|
return $defaultInfo;
|
|
}
|
|
|
|
$product = $orderItem->getProductAttribute();
|
|
if (!$product) {
|
|
Log::warning('Không tìm thấy product cho order item', ['order_item_id' => $orderItem->id]);
|
|
$defaultInfo = $this->getDefaultVMInfo($invoice);
|
|
Cache::put($cacheKey, $defaultInfo, $cacheDuration);
|
|
return $defaultInfo;
|
|
}
|
|
|
|
$host = $product->host;
|
|
$datacenter = $host?->datacenter;
|
|
$vcenter = $datacenter?->vcenter;
|
|
|
|
if (!$vcenter) {
|
|
$defaultInfo = $this->getDefaultVMInfo($invoice);
|
|
Cache::put($cacheKey, $defaultInfo, $cacheDuration);
|
|
return $defaultInfo;
|
|
}
|
|
|
|
$loginSuccess = false;
|
|
try {
|
|
$loginSuccess = $vCenterService->loginServer($vcenter);
|
|
} catch (\Exception $e) {
|
|
Log::warning('vCenter login failed', ['vm_id' => $invoice->vm_id, 'error' => $e->getMessage()]);
|
|
}
|
|
|
|
if (!$loginSuccess) {
|
|
$defaultInfo = $this->getDefaultVMInfo($invoice);
|
|
Cache::put($cacheKey, $defaultInfo, $cacheDuration);
|
|
return $defaultInfo;
|
|
}
|
|
|
|
$vmDetail = null;
|
|
$ipInfo = null;
|
|
|
|
try {
|
|
// Set timeout for API calls
|
|
$vmDetail = $vCenterService->GetDetailVirtualMachine($invoice->vm_id);
|
|
$ipInfo = $vCenterService->GetIPAddressVirtualMachine($invoice->vm_id);
|
|
} catch (\Exception $e) {
|
|
Log::warning('vCenter API calls failed', ['vm_id' => $invoice->vm_id, 'error' => $e->getMessage()]);
|
|
$defaultInfo = $this->getDefaultVMInfo($invoice);
|
|
Cache::put($cacheKey, $defaultInfo, $cacheDuration);
|
|
return $defaultInfo;
|
|
}
|
|
|
|
if ($vmDetail['error'] === false) {
|
|
$vmData = $vmDetail['data'];
|
|
|
|
// Lấy thông tin IP
|
|
$ipAddress = 'N/A';
|
|
if ($ipInfo['error'] === false && !empty($ipInfo['data']['ip_address'])) {
|
|
$ipAddress = $ipInfo['data']['ip_address'] ?? 'N/A';
|
|
} elseif ($ipInfo['error'] === false && isset($ipInfo['data']['value']['ip_address'])) {
|
|
// Fallback for nested structure
|
|
$ipAddress = $ipInfo['data']['value']['ip_address'] ?? 'N/A';
|
|
}
|
|
|
|
// Tính toán thông tin phần cứng
|
|
$cpuCount = $vmData['cpu']['count'] ?? 1;
|
|
$memoryMB = $vmData['memory']['size_MiB'] ?? 1024;
|
|
$memoryGB = round($memoryMB / 1024, 1);
|
|
|
|
$diskSizeGB = 0;
|
|
if (isset($vmData['disks']) && is_array($vmData['disks'])) {
|
|
foreach ($vmData['disks'] as $disk) {
|
|
$diskSizeGB += round(($disk['value']['capacity'] ?? 0) / (1024 * 1024 * 1024), 1);
|
|
}
|
|
}
|
|
|
|
$vmInfo = [
|
|
'vm_id' => $invoice->vm_id,
|
|
'status' => $vmData['power_state'],
|
|
'ip_address' => $ipAddress,
|
|
'hostname' => $orderItem->server_name ?? 'Unknown',
|
|
'os' => $orderItem->os->osGroup->name ?? 'Unknown',
|
|
'cpu' => $cpuCount . ' cores',
|
|
'ram' => $memoryGB . ' GB',
|
|
'disk' => $diskSizeGB . ' GB',
|
|
'uptime' => 'N/A',
|
|
'last_updated' => now()->format('Y-m-d H:i:s'),
|
|
'vm_name' => $vmData['name'] ?? 'Unknown',
|
|
'vm_folder' => $vmData['folder'] ?? 'Unknown',
|
|
'vm_resource_pool' => $vmData['resource_pool'] ?? 'Unknown',
|
|
'vm_datastore' => $vmData['datastore'] ?? 'Unknown',
|
|
'vm_network' => $vmData['nics'][0]['backing']['network'] ?? 'Unknown',
|
|
'vm_mac' => $vmData['nics'][0]['mac_address'] ?? 'N/A',
|
|
'vm_guest_id' => $vmData['guest_ID'] ?? 'Unknown',
|
|
'vm_version' => $vmData['version'] ?? 'Unknown',
|
|
'vm_hardware_version' => $vmData['hardware']['version'] ?? 'Unknown',
|
|
];
|
|
|
|
// Cache the successful result
|
|
Cache::put($cacheKey, $vmInfo, $cacheDuration);
|
|
return $vmInfo;
|
|
} else {
|
|
$defaultInfo = $this->getDefaultVMInfo($invoice);
|
|
Cache::put($cacheKey, $defaultInfo, $cacheDuration);
|
|
return $defaultInfo;
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::error('Error in getVMInfoFromVCenter', ['vm_id' => $invoice->vm_id, 'error' => $e->getMessage()]);
|
|
$defaultInfo = $this->getDefaultVMInfo($invoice);
|
|
Cache::put($cacheKey, $defaultInfo, $cacheDuration);
|
|
return $defaultInfo;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lấy thông tin VM mặc định khi không thể kết nối vCenter
|
|
*/
|
|
private function getDefaultVMInfo($invoice)
|
|
{
|
|
$orderItem = $invoice->invoiceItem->first();
|
|
return [
|
|
'vm_id' => $invoice->vm_id,
|
|
'status' => 'unknown',
|
|
'ip_address' => 'N/A',
|
|
'hostname' => $orderItem->server_name ?? 'Unknown',
|
|
'os' => $orderItem->os->osGroup->name ?? 'Unknown',
|
|
'cpu' => 'N/A',
|
|
'ram' => 'N/A',
|
|
'disk' => 'N/A',
|
|
'uptime' => 'N/A',
|
|
'last_updated' => now()->format('Y-m-d H:i:s'),
|
|
'vm_name' => 'Unknown',
|
|
'vm_folder' => 'Unknown',
|
|
'vm_resource_pool' => 'Unknown',
|
|
'vm_datastore' => 'Unknown',
|
|
'vm_network' => 'Unknown',
|
|
'vm_mac' => 'N/A',
|
|
'vm_guest_id' => 'Unknown',
|
|
'vm_version' => 'Unknown',
|
|
'vm_hardware_version' => 'Unknown',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Action để bật/tắt VM (placeholder - chưa implement)
|
|
*/
|
|
public function toggleVM(Request $request, $invoice_ordernum)
|
|
{
|
|
try {
|
|
$invoice = Invoice::where('order_num', $invoice_ordernum)->first();
|
|
|
|
// Kiểm tra quyền truy cập
|
|
if ($invoice->customer_id !== customer()->id) {
|
|
return response()->json(['error' => true, 'message' => 'Bạn không có quyền thực hiện thao tác này.']);
|
|
}
|
|
|
|
$action = $request->input('action');
|
|
|
|
$response = [
|
|
'success' => true,
|
|
'message' => "Đã thực hiện thao tác: {$action}",
|
|
'vm_id' => $invoice->vm_id,
|
|
'action' => $action
|
|
];
|
|
|
|
// Clear cache when VM state changes
|
|
if ($invoice->vm_id) {
|
|
Cache::forget("vm_info_{$invoice->vm_id}");
|
|
}
|
|
|
|
return response()->json($response);
|
|
} catch (\Exception $e) {
|
|
// Log::error('Error in ServiceController@toggleVM: ' . $e->getMessage());
|
|
return response()->json(['error' => true, 'message' => 'Có lỗi xảy ra khi thực hiện thao tác. ' . $e->getMessage()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lấy thông tin VM từ vCenter (placeholder - chưa implement)
|
|
*/
|
|
public function getVMInfo($invoice_id)
|
|
{
|
|
try {
|
|
$invoice = Invoice::findOrFail($invoice_id);
|
|
|
|
// Kiểm tra quyền truy cập
|
|
if ($invoice->customer_id !== customer()->id) {
|
|
return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập thông tin này.']);
|
|
}
|
|
|
|
if (!$invoice->vm_id) {
|
|
return response()->json(['error' => true, 'message' => 'Dịch vụ này chưa có VM ID.']);
|
|
}
|
|
|
|
// TODO: Implement vCenter API calls here
|
|
// Hiện tại chỉ trả về data mẫu
|
|
$vmInfo = [
|
|
'vm_id' => $invoice->vm_id,
|
|
'status' => 'running',
|
|
'ip_address' => '192.168.1.100',
|
|
'hostname' => $invoice->invoiceItem->first()->server_name ?? 'Unknown',
|
|
'os' => $invoice->invoiceItem->first()->os->osGroup->name ?? 'Unknown',
|
|
'cpu' => '2 cores',
|
|
'ram' => '4 GB',
|
|
'disk' => '50 GB',
|
|
'uptime' => '2 days, 5 hours',
|
|
'last_updated' => now()->format('Y-m-d H:i:s')
|
|
];
|
|
|
|
return redirect()->back()->with('success', 'Lấy thông tin VM thành công');
|
|
} catch (\Exception $e) {
|
|
// Log::error('Error in ServiceController@getVMInfo: ' . $e->getMessage());
|
|
return response()->json(['error' => true, 'message' => 'Có lỗi xảy ra khi lấy thông tin VM.']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dispatch background job to update VM info
|
|
*/
|
|
private function dispatchVMInfoUpdateJob($invoice): void
|
|
{
|
|
try {
|
|
// Check if cache is stale (older than 2 minutes)
|
|
$cacheKey = "vm_info_{$invoice->vm_id}";
|
|
$cachedData = Cache::get($cacheKey);
|
|
|
|
if (
|
|
!$cachedData ||
|
|
(isset($cachedData['last_updated']) &&
|
|
now()->diffInMinutes($cachedData['last_updated']) > 2)
|
|
) {
|
|
|
|
// Dispatch background job to update VM info
|
|
\App\Jobs\VirtualMachine\Sync\UpdateVMInfoJob::dispatch($invoice->id, $invoice->vm_id)
|
|
->delay(now()->addSeconds(5))
|
|
->onQueue('vm-updates');
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::warning('Failed to dispatch VM info update job', [
|
|
'vm_id' => $invoice->vm_id,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
|
|
public function vmConsole(Request $request, $invoice_ordernum)
|
|
{
|
|
$cacheKey = 'vm_console_invoice_' . $invoice_ordernum;
|
|
$data = cache()->remember($cacheKey, 300, function () use ($invoice_ordernum) {
|
|
$invoice = Invoice::where('order_num', $invoice_ordernum)->first();
|
|
if (!$invoice)
|
|
return null;
|
|
$order = Order::find($invoice->order_id);
|
|
if (!$order)
|
|
return null;
|
|
$product = ModuleVmware::where('order_id', $order->id)->first();
|
|
if (!$product)
|
|
return null;
|
|
$title = "VM Console - " . $product->name;
|
|
return compact('product', 'title', 'invoice');
|
|
});
|
|
if (!$data) {
|
|
abort(404, 'Không tìm thấy thông tin VM Console');
|
|
}
|
|
return view('frontend.service.console', $data);
|
|
}
|
|
|
|
public function createSnapshots(SnapShotRequest $request)
|
|
{
|
|
try {
|
|
$user = customer();
|
|
if (!$user) {
|
|
return redirect()->back()->with('error', 'Chưa đăng nhập.');
|
|
}
|
|
|
|
$data = $request->validated();
|
|
$productId = $data['product_id'];
|
|
|
|
$product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')
|
|
->where('vmware_id', $productId)
|
|
->where('customer_id', $user->id)
|
|
->first();
|
|
|
|
if (!$product) {
|
|
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ỉ tạo snapshot cho VM đang hoạt động.');
|
|
}
|
|
|
|
$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 ($count >= $limit->limit) {
|
|
return redirect()->back()->with(
|
|
'error',
|
|
"Đã đạt giới hạn snapshot ({$limit->limit})."
|
|
);
|
|
}
|
|
}
|
|
|
|
$snapshot = ModuleSnapshot::create([
|
|
'name' => 'snapshot-' . now()->format('Ymd-His'),
|
|
'customer_id' => $user->id,
|
|
'module_vmware_id' => $product->id,
|
|
'status' => 'pending',
|
|
'created_by' => $user->id,
|
|
]);
|
|
|
|
\App\Jobs\VirtualMachine\Snapshot\CreateSnapshot::dispatch(
|
|
$snapshot->id
|
|
)->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)
|
|
// {
|
|
// 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)
|
|
{
|
|
$user = customer();
|
|
if (!$user) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'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
|
|
*/
|
|
public function resetVMPassword(Request $request)
|
|
{
|
|
$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);
|
|
}
|
|
|
|
$validated = Validator::make($request->all(), [
|
|
'product_id' => 'required|string|max:255',
|
|
'new_password' => 'required|string|min:8|max:128',
|
|
'old_password' => 'required|string|min:8|max:128',
|
|
]);
|
|
if ($validated->fails()) {
|
|
return response()->json(['error' => true, 'message' => __('Dữ liệu không hợp lệ'), 'errors' => $validated->errors()], 422);
|
|
}
|
|
|
|
$productId = $this->sanitizeInput($request->product_id);
|
|
$newPassword = (string) $request->new_password;
|
|
$oldPassword = (string) $request->old_password;
|
|
|
|
// load & ownership check nhanh (không cần toàn bộ nặng nề vì Job sẽ retry)
|
|
$product = ModuleVmware::where('vmware_id', $productId)
|
|
->where('customer_id', $user->id)
|
|
->first();
|
|
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);
|
|
}
|
|
|
|
// Dispatch job với delay 5s
|
|
ResetVmPasswordJob::dispatch(
|
|
productId: $productId,
|
|
newPassword: $newPassword,
|
|
oldPassword: $oldPassword,
|
|
userId: $user->id,
|
|
email: $user->email
|
|
)->delay(now()->addSeconds(5));
|
|
|
|
// trả accepted để UI biết đang xử lý nền
|
|
return redirect()->back()->with('success', 'Đã gửi yêu cầu tạo đặt lại mật khẩu. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.');
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Change Remote Desktop port (Windows only)
|
|
*/
|
|
public function changeRemotePort(Request $request)
|
|
{
|
|
$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);
|
|
}
|
|
|
|
$validated = Validator::make($request->all(), [
|
|
'product_id' => 'required|string|max:255',
|
|
'new_port' => 'required|string|min:4|max:128',
|
|
'password' => 'required|string|min:4|max:128',
|
|
]);
|
|
if ($validated->fails()) {
|
|
return response()->json(['error' => true, 'message' => __('Dữ liệu không hợp lệ'), 'errors' => $validated->errors()], 422);
|
|
}
|
|
|
|
$productId = $this->sanitizeInput($request->product_id);
|
|
$newPort = (string) $request->new_port;
|
|
|
|
// load & ownership check nhanh (không cần toàn bộ nặng nề vì Job sẽ retry)
|
|
$product = ModuleVmware::where('vmware_id', $productId)
|
|
->where('customer_id', $user->id)
|
|
->first();
|
|
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);
|
|
}
|
|
|
|
// Dispatch job với delay 5s
|
|
ChangeRemotePortJob::dispatch(
|
|
productId: $productId,
|
|
newPort: $newPort,
|
|
password: (string) $request->password,
|
|
userId: $user->id,
|
|
email: $user->email
|
|
)->delay(now()->addSeconds(5));
|
|
|
|
// trả accepted để UI biết đang xử lý nền
|
|
return redirect()->back()->with('success', 'Đã gửi yêu cầu thay đổi cổng Remote Desktop. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.');
|
|
}
|
|
|
|
/**
|
|
* Change public IP for VM (request-based), logs request.
|
|
*/
|
|
public function changeIP(Request $request)
|
|
{
|
|
try {
|
|
$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);
|
|
}
|
|
|
|
$validated = FacadesValidator::make($request->all(), [
|
|
'product_id' => 'required|string|max:255',
|
|
'ip' => ['required', 'string', 'regex:/^(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})$/'],
|
|
]);
|
|
|
|
if ($validated->fails()) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Dữ liệu không hợp lệ'),
|
|
'errors' => $validated->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$productId = $this->sanitizeInput($request->product_id);
|
|
$desiredIp = $this->sanitizeInput($request->ip);
|
|
|
|
// Persist an audit log entry for ops to handle allocation if needed
|
|
Log::info('Khách yêu cầu đổi IP', [
|
|
'user_id' => $user->id,
|
|
'product_vmware_id' => $productId,
|
|
'desired_ip' => $desiredIp,
|
|
]);
|
|
|
|
// Here you could enqueue a Job to validate/assign IP from pool
|
|
// For now, return accepted message
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => __('Yêu cầu đổi IP đã được ghi nhận, chúng tôi sẽ xử lý trong thời gian sớm nhất.'),
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Lỗi changeIP', [
|
|
'user_id' => customer()?->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Có lỗi xảy ra khi đổi IP'),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renew service for selected months
|
|
*/
|
|
public function renew(Request $request)
|
|
{
|
|
try {
|
|
$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);
|
|
}
|
|
|
|
$validated = FacadesValidator::make($request->all(), [
|
|
'product_id' => 'required|string|max:255',
|
|
'plan' => 'required|string|in:3,6,12,24,other',
|
|
'months' => 'nullable|integer|min:1|max:60',
|
|
]);
|
|
|
|
if ($validated->fails()) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Dữ liệu không hợp lệ'),
|
|
'errors' => $validated->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$months = $request->plan === 'other' ? (int) ($request->months ?? 1) : (int) $request->plan;
|
|
$productId = $this->sanitizeInput($request->product_id);
|
|
|
|
// Fetch product price based on invoice item link
|
|
$invoiceItem = InvoiceItem::whereHas('invoice', function ($q) use ($user) {
|
|
$q->where('customer_id', $user->id);
|
|
})
|
|
->where('product_type', 4) // VPS
|
|
->whereHas('invoice', fn($q) => $q->where('status', '!=', 0))
|
|
->first();
|
|
|
|
$product = $invoiceItem?->getProductAttribute();
|
|
$unitPrice = (int) ($product?->price_sale ?? $product?->price ?? 0);
|
|
$total = $unitPrice * max($months, 1);
|
|
|
|
Log::info('Khách yêu cầu gia hạn dịch vụ', [
|
|
'user_id' => $user->id,
|
|
'product_vmware_id' => $productId,
|
|
'months' => $months,
|
|
'unit_price' => $unitPrice,
|
|
'total' => $total,
|
|
]);
|
|
|
|
// TODO: implement actual renewal billing/workflow
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => __('Yêu cầu gia hạn đã được ghi nhận'),
|
|
'data' => [
|
|
'unit_price' => $unitPrice,
|
|
'months' => $months,
|
|
'total' => $total,
|
|
],
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('Lỗi renew', [
|
|
'user_id' => customer()?->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Có lỗi xảy ra khi gia hạn'),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Secure remove snapshot using session authentication - NO BEARER TOKEN
|
|
*/
|
|
public function removeSnapshot(Request $request)
|
|
{
|
|
try {
|
|
$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);
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'product_id' => 'required|string',
|
|
'snapshot_name' => 'required|string',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Dữ liệu không hợp lệ',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$productId = $request->product_id;
|
|
$snapshotName = $request->snapshot_name;
|
|
$userId = $user->id;
|
|
|
|
// Tìm VPS
|
|
$product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')
|
|
->where('vmware_id', $productId)
|
|
->where('customer_id', $userId)
|
|
->first();
|
|
|
|
if (!$product) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Không tìm thấy VPS #{$productId} hoặc bạn không có quyền truy cập"
|
|
], 404);
|
|
}
|
|
|
|
// Tìm snapshot trong database
|
|
$snapshot = ModuleSnapshot::where('customer_id', $userId)
|
|
->where('module_vmware_id', $product->id)
|
|
->where('name', $snapshotName)
|
|
->first();
|
|
|
|
if (!$snapshot) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Không tìm thấy snapshot '{$snapshotName}' hoặc bạn không có quyền truy cập"
|
|
], 404);
|
|
}
|
|
|
|
// Kiểm tra trạng thái VPS
|
|
if ($product->status != 1) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Chỉ có thể xóa snapshot cho máy ảo đang hoạt động'
|
|
], 400);
|
|
}
|
|
|
|
// Dispatch job để xóa snapshot trong background
|
|
\App\Jobs\VirtualMachine\Snapshot\RemoveSnapshot::dispatch($product->id, $snapshot->id)
|
|
->delay(now()->addSeconds(5))
|
|
->onQueue('snapshots');
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Đã gửi yêu cầu xóa snapshot. Quá trình sẽ mất 1-2 phút, vui lòng chờ đợi.'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('❌ Lỗi khi xóa snapshot', [
|
|
'product_id' => $request->product_id ?? 'not_provided',
|
|
'snapshot_name' => $request->snapshot_name ?? 'not_provided',
|
|
'user_id' => $user->id ?? 'guest',
|
|
'error' => $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Lỗi hệ thống khi xóa snapshot: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Secure revert snapshot using session authentication - NO BEARER TOKEN
|
|
*/
|
|
public function revertSnapshot(Request $request)
|
|
{
|
|
try {
|
|
$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);
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'product_id' => 'required|string',
|
|
'snapshot_name' => 'required|string',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Dữ liệu không hợp lệ',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$productId = $request->product_id;
|
|
$snapshotName = $request->snapshot_name;
|
|
$userId = $user->id;
|
|
|
|
// Tìm VPS
|
|
$product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')
|
|
->where('vmware_id', $productId)
|
|
->where('customer_id', $userId)
|
|
->first();
|
|
|
|
if (!$product) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Không tìm thấy VPS #{$productId} hoặc bạn không có quyền truy cập"
|
|
], 404);
|
|
}
|
|
|
|
// Tìm snapshot trong database
|
|
$snapshot = ModuleSnapshot::where('customer_id', $userId)
|
|
->where('module_vmware_id', $product->id)
|
|
->where('name', $snapshotName)
|
|
->first();
|
|
|
|
if (!$snapshot) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => "Không tìm thấy snapshot '{$snapshotName}' hoặc bạn không có quyền truy cập"
|
|
], 404);
|
|
}
|
|
|
|
// Kiểm tra trạng thái VPS
|
|
if ($product->status != 1) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Chỉ có thể khôi phục snapshot cho máy ảo đang hoạt động'
|
|
], 400);
|
|
}
|
|
|
|
// Dispatch job để khôi phục snapshot trong background
|
|
\App\Jobs\VirtualMachine\Snapshot\RevertSnapshot::dispatch($product->id, $snapshot->id)
|
|
->delay(now()->addSeconds(5))
|
|
->onQueue('snapshots');
|
|
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => 'Đã gửi yêu cầu khôi phục snapshot. Quá trình sẽ mất 2-3 phút, vui lòng chờ đợi.'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Log::error('❌ Lỗi khi khôi phục snapshot', [
|
|
'product_id' => $request->product_id ?? 'not_provided',
|
|
'snapshot_name' => $request->snapshot_name ?? 'not_provided',
|
|
'user_id' => $user->id ?? 'guest',
|
|
'error' => $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => 'Lỗi hệ thống khi khôi phục snapshot: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Secure start VM using session authentication - NO BEARER TOKEN
|
|
*/
|
|
public function startVM(Request $request, vCenter $vCenterService)
|
|
{
|
|
try {
|
|
$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);
|
|
}
|
|
|
|
$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 = $request->product_id;
|
|
$userId = $user->id;
|
|
|
|
$product = ModuleVmware::where('vmware_id', $productId)
|
|
->where('customer_id', $userId)
|
|
->first();
|
|
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);
|
|
}
|
|
|
|
$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);
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => __('Yêu cầu khởi động máy ảo đã được gửi thành công.')
|
|
]);
|
|
} else {
|
|
$msg = is_array($result) && isset($result['data']) ? $result['data'] : __('Không thể khởi động máy ảo');
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $msg
|
|
], 500);
|
|
}
|
|
} catch (\Exception $e) {
|
|
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(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Có lỗi xảy ra khi khởi động máy ảo')
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Secure stop VM using session authentication - NO BEARER TOKEN
|
|
*/
|
|
public function stopVM(Request $request, vCenter $vCenterService)
|
|
{
|
|
try {
|
|
$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);
|
|
}
|
|
|
|
$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 = htmlspecialchars(strip_tags(trim($request->product_id)), ENT_QUOTES, 'UTF-8');
|
|
$userId = $user->id;
|
|
|
|
$product = ModuleVmware::where('vmware_id', $productId)
|
|
->where('customer_id', $userId)
|
|
->first();
|
|
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);
|
|
}
|
|
|
|
$result = $vCenterService->ShutdownGuestVirtualMachine($product, $product->vmware_id);
|
|
if (is_array($result) && isset($result['error']) && $result['error'] === false) {
|
|
Cache::forget('the_gioi_so_database_the_gioi_so_cache:vm_info_' . $product->vmware_id);
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => __('Yêu cầu tắt máy ảo đã được gửi thành công.')
|
|
]);
|
|
} else {
|
|
$msg = is_array($result) && isset($result['data']) ? $result['data'] : __('Không thể tắt máy ảo');
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $msg
|
|
], 500);
|
|
}
|
|
} catch (\Exception $e) {
|
|
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(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Có lỗi xảy ra khi tắt máy ảo')
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Secure reboot VM using session authentication - NO BEARER TOKEN
|
|
*/
|
|
public function rebootVM(Request $request, vCenter $vCenterService)
|
|
{
|
|
try {
|
|
$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);
|
|
}
|
|
|
|
$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 = htmlspecialchars(strip_tags(trim($request->product_id)), ENT_QUOTES, 'UTF-8');
|
|
$userId = $user->id;
|
|
|
|
$product = ModuleVmware::where('vmware_id', $productId)
|
|
->where('customer_id', $userId)
|
|
->first();
|
|
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);
|
|
}
|
|
|
|
$result = $vCenterService->RebootVirtualMachine($product, $product->vmware_id);
|
|
if (is_array($result) && isset($result['error']) && $result['error'] === false) {
|
|
return response()->json([
|
|
'error' => false,
|
|
'message' => __('Yêu cầu khởi động lại máy ảo đã được gửi thành công.')
|
|
]);
|
|
} else {
|
|
$msg = is_array($result) && isset($result['data']) ? $result['data'] : __('Không thể khởi động lại máy ảo');
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $msg
|
|
], 500);
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::error('Lỗi khi reboot VM', [
|
|
'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 khởi động lại máy ảo')
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
|
|
public function reinstallVM(Request $request, vCenter $vCenterService)
|
|
{
|
|
try {
|
|
$user = customer();
|
|
if (!$user) {
|
|
Log::warning('User not logged in for reinstallVM');
|
|
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);
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'invoice_id' => 'required|integer',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
Log::warning('Validation failed for reinstallVM', [
|
|
'errors' => $validator->errors()->toArray()
|
|
]);
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Dữ liệu không hợp lệ'),
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$invoice = Invoice::find($request->invoice_id);
|
|
if (!$invoice) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Không tìm thấy hóa đơn')
|
|
], 404);
|
|
}
|
|
|
|
// Debug: Log product lookup
|
|
$invoiceItem = $invoice->invoiceItem->first();
|
|
$productId = $invoiceItem?->getProductAttribute()?->id;
|
|
$vmware = $productId;
|
|
$osId = $request->os_id;
|
|
$userId = $user->id;
|
|
|
|
// Tìm product và kiểm tra quyền sở hữu
|
|
$vm = ModuleVmware::where('vmware_id', $vmware->vmware_id)
|
|
->where('customer_id', $userId)
|
|
->first();
|
|
|
|
Log::info('VM lookup result', [
|
|
'vm_found' => $vm ? 'yes' : 'no',
|
|
'vm_data' => $vm ? $vm->toArray() : null
|
|
]);
|
|
if (!$vm) {
|
|
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 OS có tồn tại và hợp lệ không
|
|
$os = Os::find($osId);
|
|
if (!$os) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Hệ điều hành không tồn tại')
|
|
], 404);
|
|
}
|
|
|
|
// Kiểm tra OS có được phép cho VPS này không
|
|
$allowedOs = $vm->operatingSystems()->where('os.id', $osId)->first();
|
|
if (!$allowedOs) {
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => __('Hệ điều hành này không được phép cho VPS này')
|
|
], 403);
|
|
}
|
|
|
|
$result = $vCenterService->DeleteVirtualMachine($vm->vmware_id);
|
|
if (is_array($result) && isset($result['error']) && $result['error'] === false) {
|
|
// Cập nhật OS trong database
|
|
$vm->update([
|
|
'os_id' => $osId
|
|
]);
|
|
|
|
Cache::forget('vm_info_' . $vm->vmware_id);
|
|
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.')
|
|
]);
|
|
} else {
|
|
$msg = is_array($result) && isset($result['data']) ? $result['data'] : __('Không thể cài lại máy ảo');
|
|
Log::warning('vCenter call failed', [
|
|
'result' => $result,
|
|
'message' => $msg
|
|
]);
|
|
return response()->json([
|
|
'error' => true,
|
|
'message' => $msg
|
|
], 500);
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::error('Lỗi khi cài lại máy ảo', [
|
|
'user_id' => $user->id ?? 'guest',
|
|
'product_id' => $request->product_id ?? 'not_provided',
|
|
'os_id' => $request->os_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 cài lại máy ảo')
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sanitize input data
|
|
*/
|
|
private function sanitizeInput($input): string
|
|
{
|
|
return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
/**
|
|
* Sanitize output data
|
|
*/
|
|
private function sanitizeOutput($output): string
|
|
{
|
|
return htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
/**
|
|
* Kiểm tra cache có hết hạn chưa
|
|
*/
|
|
private function isCacheExpired($cachedData): bool
|
|
{
|
|
if (!isset($cachedData['last_updated'])) {
|
|
return true;
|
|
}
|
|
|
|
$lastUpdated = \Carbon\Carbon::parse($cachedData['last_updated']);
|
|
return now()->diffInMinutes($lastUpdated) > 2; // Cache 2 phút
|
|
}
|
|
|
|
/**
|
|
* Tìm VPS với eager loading
|
|
*/
|
|
private function findVmwareProduct($productId, $userId)
|
|
{
|
|
return ModuleVmware::with(['ipAddress.host.datacenter.vCenter'])
|
|
->where('vmware_id', $productId)
|
|
->where('customer_id', $userId)
|
|
->first();
|
|
}
|
|
|
|
/**
|
|
* Validate vCenter configuration
|
|
*/
|
|
private function validateVcenterConfiguration($product)
|
|
{
|
|
if (!$product->ipAddress?->host?->datacenter?->vCenter) {
|
|
return null;
|
|
}
|
|
|
|
return $product->ipAddress->host->datacenter->vCenter;
|
|
}
|
|
|
|
/**
|
|
* Lấy snapshot từ vCenter
|
|
*/
|
|
private function fetchSnapshotsFromVCenter($serviceVCenter, $product, $vcenter)
|
|
{
|
|
try {
|
|
// Login to vCenter
|
|
if (!$serviceVCenter->loginServer($vcenter)) {
|
|
return false;
|
|
}
|
|
|
|
// Kiểm tra method tồn tại
|
|
if (!method_exists($serviceVCenter, 'getSnapShot')) {
|
|
Log::error('getSnapShot method không tồn tại trong ServiceVCenter');
|
|
return false;
|
|
}
|
|
|
|
$result = $serviceVCenter->getSnapShot($product);
|
|
|
|
// Retry logic
|
|
if ($result === "Login_Again") {
|
|
if ($serviceVCenter->loginServer($vcenter)) {
|
|
$result = $serviceVCenter->getSnapShot($product);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Validate result
|
|
if (is_string($result) || (isset($result['error']) && $result['error'])) {
|
|
return false;
|
|
}
|
|
|
|
return $result['data'] ?? [];
|
|
} catch (\Exception $e) {
|
|
Log::error('Lỗi khi lấy snapshot từ vCenter', [
|
|
'product_id' => $product->id,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lấy snapshot từ database
|
|
*/
|
|
private function getDatabaseSnapshots($userId, $productId)
|
|
{
|
|
return ModuleSnapshot::where('customer_id', $userId)
|
|
->where('module_vmware_id', $productId)
|
|
->get()
|
|
->keyBy('name');
|
|
}
|
|
|
|
/**
|
|
* Xử lý và sanitize snapshot data
|
|
*/
|
|
private function processSnapshotsData($snapshots, $dbSnapshots): array
|
|
{
|
|
$processed = [];
|
|
|
|
if (!is_array($snapshots)) {
|
|
return $processed;
|
|
}
|
|
|
|
foreach ($snapshots as $snapshot) {
|
|
$snapshotName = $this->sanitizeOutput($snapshot['name'] ?? $snapshot['Name'] ?? 'Unknown');
|
|
$dbSnapshot = $dbSnapshots->get($snapshotName);
|
|
|
|
$processed[] = [
|
|
'name' => $snapshotName,
|
|
'description' => $this->sanitizeOutput($snapshot['description'] ?? $snapshot['Description'] ?? ''),
|
|
'created' => $this->sanitizeOutput($snapshot['created'] ?? $snapshot['Created'] ?? ''),
|
|
'power_state' => $this->sanitizeOutput($snapshot['power_state'] ?? $snapshot['PowerState'] ?? ''),
|
|
'size_mb' => (int) ($snapshot['size_mb'] ?? $snapshot['SizeMB'] ?? 0),
|
|
'size_gb' => (float) ($snapshot['size_gb'] ?? $snapshot['SizeGB'] ?? 0),
|
|
'is_current' => (bool) ($snapshot['is_current'] ?? $snapshot['IsCurrent'] ?? false),
|
|
'is_replay_supported' => (bool) ($snapshot['is_replay_supported'] ?? $snapshot['IsReplaySupported'] ?? false),
|
|
'in_database' => $dbSnapshot ? true : false,
|
|
'db_created_at' => $dbSnapshot ? $dbSnapshot->created_at->format('d/m/Y H:i:s') : null
|
|
];
|
|
}
|
|
|
|
return $processed;
|
|
}
|
|
}
|