Refactor VM creation and configuration process

- Updated VpsServiceResource to return product UUID instead of ID.
- Modified VirtualMachineService to utilize product data for CPU, RAM, and disk settings.
- Replaced AssignIpToGuestJob with UpdateVmConfigJob in CreateVirtualMachineJob to streamline VM configuration after cloning.
- Adjusted AssignIpToGuestJob to remove unnecessary UpdateVmConfigJob dispatch and focus on IP assignment.
- Enhanced UpdateVmConfigJob to handle CPU/RAM configuration and power cycling more effectively.
- Introduced ChangeIpVMJob for handling customer-initiated IP changes asynchronously.
- Updated InvoiceRepository to set invoice due date based on maximum months instead of a fixed 7 days.
- Improved web design quote PDF layout for better readability.
- Added Times-Roman font metrics for PDF generation.
This commit is contained in:
baodev0
2026-07-24 09:29:43 +07:00
parent ccf95114f6
commit 3ed33ea7e4
10 changed files with 767 additions and 127 deletions
@@ -2,14 +2,13 @@
namespace App\Http\Controllers\Api\Service;
use App\Actions\VM\ChangeIpVMAction;
use App\Actions\VM\ChangePortVMAction;
use App\Actions\VM\PowerOffVMAction;
use App\Actions\VM\PowerOnVMAction;
use App\Http\Controllers\Api\Service\Concerns\ResolvesVm;
use App\Http\Controllers\Controller;
use App\Http\Services\Telegram;
use App\Http\Services\vCenter;
use App\Jobs\VirtualMachine\ChangeIpVMJob;
use App\Jobs\VirtualMachine\ChangeVmPasswordJob;
use App\Models\ModuleVmware;
use App\Models\Os;
@@ -19,7 +18,6 @@ use App\Services\VmProvisionService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class VmController extends Controller
{
@@ -124,7 +122,10 @@ class VmController extends Controller
* ip_address string New IP to assign
* gateway string (optional) Gateway — falls back to existing IP record
*/
public function changeIp(Request $request, ChangeIpVMAction $action): JsonResponse
/**
* Dispatches ChangeIpVMJob — returns immediately, runs async.
*/
public function changeIp(Request $request): JsonResponse
{
$validated = $request->validate([
'server_name' => ['required', 'string', 'max:255'],
@@ -165,87 +166,35 @@ class VmController extends Controller
: 'windows';
$gateway = $validated['gateway'] ?? '';
$dns = $validated['dns'] ?? '8.8.8.8';
$dns = $validated['dns'] ?? '8.8.8.8';
$lock = $this->acquireVmLock($vm, 120);
// ponytail: don't release — lock expires after 5 min while the async job runs
$lock = $this->acquireVmLock($vm, 300);
if (!$lock) {
return $this->vmLocked();
}
try {
$raw = $action->execute(
$vm,
$validated['ip_address'],
$gateway,
$osType,
$dns,
$validated['guest_user'],
$validated['guest_password']
);
} finally {
$lock->release();
}
// Always log the raw PowerShell output for debugging
Log::channel('Custom')->info('[changeIp] raw output', [
'server_name' => $validated['server_name'],
'ip_address' => $validated['ip_address'],
'gateway' => $gateway,
'os_type' => $osType,
'vm_name' => $vm->name,
'vmware_id' => $vm->vmware_id,
'raw' => $raw,
]);
$telegram = new Telegram();
if ($raw === 'Login_Again' || $raw === 'vCenter_Not_Found') {
$errMsg = $raw === 'vCenter_Not_Found'
? 'Không tìm thấy vCenter cho VM này.'
: 'Không thể đăng nhập vào vCenter.';
$telegram->SendErrorMessage("❌ Đổi IP thất bại [{$osType}]\n"
. "🖥 VM: {$vm->name} ({$vm->vmware_id})\n"
. "📍 IP mới: {$validated['ip_address']}\n"
. "🔗 GW: {$gateway}\n"
. "🚫 Lỗi: {$errMsg}");
return response()->json(['success' => false, 'message' => $errMsg], 400);
}
if (str_starts_with((string) $raw, 'Error:') || str_contains((string) $raw, '[STDERR]')) {
$telegram->SendErrorMessage("❌ Đổi IP thất bại [{$osType}]\n"
. "🖥 VM: {$vm->name} ({$vm->vmware_id})\n"
. "📍 IP mới: {$validated['ip_address']}\n"
. "🔗 GW: {$gateway}\n"
. "📋 Output:\n" . mb_substr((string) $raw, 0, 800));
return response()->json(['success' => false, 'message' => 'Lệnh đổi IP thất bại. Admin đã được thông báo.', 'data' => ['raw' => $raw]], 400);
}
// Empty raw = script chạy không có output → coi là thành công (PowerShell script không echo)
// Nhưng vẫn gửi cảnh báo để theo dõi
if (trim((string) $raw) === '') {
$telegram->SendMessage("⚠️ Đổi IP [{$osType}] - Script chạy nhưng không có output\n"
. "🖥 VM: {$vm->name} ({$vm->vmware_id})\n"
. "📍 IP mới: {$validated['ip_address']}\n"
. "🔗 GW: {$gateway}\n"
. "ℹ️ Kiểm tra lại VM để xác nhận IP đã thay đổi chưa.");
}
ChangeIpVMJob::dispatch(
$vm->id,
$validated['ip_address'],
$gateway,
$osType,
$dns,
$validated['guest_user'],
$validated['guest_password'],
);
$this->activityLogger->log(
event: 'change_ip',
logName: 'VM',
description: "Customer {$request->user()->email} đổi IP VM: {$vm->name} → {$validated['ip_address']}",
description: "Customer {$request->user()->email} yêu cầu đổi IP VM: {$vm->name} → {$validated['ip_address']}",
subject: $vm,
newValues: ['ip_address' => $validated['ip_address'], 'gateway' => $gateway, 'os_type' => $osType],
);
return response()->json([
'success' => true,
'message' => 'Đã gửi lệnh đổi IP.',
'data' => ['raw' => $raw],
'message' => 'Yêu cầu đổi IP đã được ghi nhận. Hệ thống sẽ xử lý trong vài phút.',
]);
}