Files
portal-api/app/Http/Controllers/frontend/ServiceController.php
T
2025-08-10 22:19:22 +07:00

1550 lines
59 KiB
PHP

<?php
namespace App\Http\Controllers\frontend;
use App\Http\Controllers\Controller;
use App\Http\Requests\Vm\SnapShotRequest;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Repositories\Invoice\InvoiceRepositoryInterface;
use App\Http\Services\vCenter;
use App\Models\ModuleSnapshot;
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ụ.');
}
// Kiểm tra quyền truy cập - chỉ cho phép customer sở hữu
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.');
}
// Lấy thông tin VM nếu có - WITH CACHING AND BACKGROUND UPDATE
$vmInfo = null;
if ($invoice->vm_id) {
$vmInfo = $this->getVMInfoFromVCenter($invoice, $vCenterService);
// Dispatch background job to update VM info if cache is stale
$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)
{
// Cache key for this VM info
$cacheKey = "vm_info_{$invoice->vm_id}";
$cacheDuration = 300; // 5 minutes
// Try to get from cache first
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;
}
// Login vào vCenter - with timeout
$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;
}
// Lấy thông tin VM từ vCenter API - with timeout handling
$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', // Có thể lấy từ guest info nếu cần
'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, vCenter $serviceVCenter)
{
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.');
}
$validator = $request->validated();
$productId = $validator['product_id'];
$userId = $user->id;
$product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')
->where('vmware_id', $productId)
->where('customer_id', $userId)
->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");
}
if ($product->status != 1) {
return redirect()->back()->with('error', 'Chỉ có thể tạo snapshot cho máy ảo đ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)
->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ũ.");
}
}
// 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()
]);
return redirect()->back()->with('error', 'Lỗi hệ thống khi tạo snapshot: ' . $e->getMessage());
}
}
/**
* 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);
}
}
/**
* Reset VM password (Windows/Linux) via vCenter helpers
*/
public function resetVMPassword(Request $request, vCenter $serviceVCenter)
{
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',
'new_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;
// Load product and ensure ownership
$product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')
->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);
}
$vcenter = $product->ipAddress->host->datacenter->vCenter ?? null;
if (!$vcenter) {
return response()->json([
'error' => true,
'message' => __('Cấu hình vCenter không hợp lệ'),
], 400);
}
// Login to vCenter
if (!$serviceVCenter->loginServer($vcenter)) {
return response()->json([
'error' => true,
'message' => __('Không thể kết nối đến vCenter'),
], 500);
}
// Determine OS family
$osFamily = strtoupper($product->os->osGroup->name ?? '');
$isWindows = str_contains($osFamily, 'WINDOWS');
// Prepare data
$ip = $product->ipAddress->ip ?? null;
if (!$ip) {
return response()->json([
'error' => true,
'message' => __('Không tìm thấy IP của máy ảo'),
], 400);
}
$payload = [
'ip' => $ip,
'user' => $isWindows ? 'administrator' : 'root',
'password' => $product->password ?? 'TGS@123', // fallback if field exists; adjust per model
'new_password' => $newPassword,
];
$ok = $isWindows
? $serviceVCenter->change_password_windows($payload)
: $serviceVCenter->change_password_linux($payload);
if (!$ok) {
return response()->json([
'error' => true,
'message' => __('Không thể đặt lại mật khẩu. Vui lòng thử lại sau.'),
], 500);
}
return response()->json([
'error' => false,
'message' => __('Đặt lại mật khẩu thành công'),
]);
} catch (\Exception $e) {
Log::error('Lỗi reset VM password', [
'product_id' => $request->product_id ?? null,
'error' => $e->getMessage(),
]);
return response()->json([
'error' => true,
'message' => __('Có lỗi xảy ra khi đặt lại mật khẩu'),
], 500);
}
}
/**
* Change Remote Desktop port (Windows only)
*/
public function changeRemotePort(Request $request, vCenter $serviceVCenter)
{
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',
'port' => 'required|integer|min:1|max:65535',
]);
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 = (int) $request->port;
$product = ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'os.osGroup')
->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);
}
$osFamily = strtoupper($product->os->osGroup->name ?? '');
if (!str_contains($osFamily, 'WINDOWS')) {
return response()->json([
'error' => true,
'message' => __('Chỉ hỗ trợ Windows'),
], 400);
}
$vcenter = $product->ipAddress->host->datacenter->vCenter ?? null;
if (!$vcenter) {
return response()->json([
'error' => true,
'message' => __('Cấu hình vCenter không hợp lệ'),
], 400);
}
if (!$serviceVCenter->loginServer($vcenter)) {
return response()->json([
'error' => true,
'message' => __('Không thể kết nối đến vCenter'),
], 500);
}
// NOTE: Implement the actual change RDP port over WinRM/PowerShell here.
// Placeholder success until service method is available.
return response()->json([
'error' => false,
'message' => __('Yêu cầu đổi port RDP đã được ghi nhận'),
]);
} catch (\Exception $e) {
Log::error('Lỗi changeRemotePort', [
'product_id' => $request->product_id ?? null,
'error' => $e->getMessage(),
]);
return response()->json([
'error' => true,
'message' => __('Có lỗi xảy ra khi đổi port'),
], 500);
}
}
/**
* 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);
Log::info('Khách yêu cầu gia hạn dịch vụ', [
'user_id' => $user->id,
'product_vmware_id' => $productId,
'months' => $months,
]);
// TODO: implement actual renewal billing/workflow
return response()->json([
'error' => false,
'message' => __('Yêu cầu gia hạn đã được ghi nhận'),
]);
} 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) {
// Clear VM info cache after stopping VM
\Cache::forget('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 {
// 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');
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);
}
// 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,
'message' => __('Không tìm thấy hóa đơn')
], 404);
}
// 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)
->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);
}
// 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.')
]);
} 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;
}
}