update fix deadcode, security

This commit is contained in:
baodev0
2026-09-28 08:26:29 +07:00
parent 3af59bd9c5
commit 82080b5b84
452 changed files with 4857 additions and 26289 deletions
@@ -0,0 +1,242 @@
<?php
namespace App\Http\Controllers\Api\Service\Concerns;
use App\Actions\VM\ChangePortVMAction;
use App\Http\Services\vCenter;
use App\Jobs\VirtualMachine\ChangeIpVMJob;
use App\Models\IpPool;
use App\Models\ModuleVmware;
use App\Models\Service;
use App\Services\VirtualMachine\VmIpReservationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
trait ManagesVmNetwork
{
/**
* GET /v1/system/vm-ip-options
*
* Query: server_name string Service.server_name
* Lists free IP pool entries in the same network as the VM's current IP.
*/
public function ipOptions(Request $request): JsonResponse
{
$validated = $request->validate([
'server_name' => ['required', 'string', 'max:255'],
]);
$service = $this->findOwnedServiceByServerName($validated['server_name'], $request);
if ($service instanceof JsonResponse) {
return $service;
}
$vm = $service->orderItem?->order?->vmWare?->first();
if (!$vm) {
return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404);
}
$networkId = $vm->ipAddress?->module_network_id;
if (!$networkId) {
return response()->json(['success' => false, 'message' => 'Không xác định được mạng của VM.'], 422);
}
$ips = app(VmIpReservationService::class)->freeIpsInNetwork($networkId);
return response()->json([
'success' => true,
'data' => $ips->map(fn (IpPool $ip) => [
'ip_address' => $ip->ip_address,
'gateway' => $ip->gateway,
])->values(),
]);
}
/**
* POST /v1/system/vm-change-ip
*
* Body:
* server_name string Service.server_name (e.g. "xasdf.com.vnx")
* ip_address string New IP to assign — must be a free IP from the VM's network pool
*/
/**
* Dispatches ChangeIpVMJob — returns immediately, runs async.
*/
public function changeIp(Request $request, VmIpReservationService $ipReservation): JsonResponse
{
$validated = $request->validate([
'server_name' => ['required', 'string', 'max:255'],
'ip_address' => ['required', 'ip'],
'dns' => ['nullable', 'ip'],
'guest_user' => ['required', 'string', 'max:255'],
'guest_password' => ['required', 'string', 'max:255'],
]);
$service = $this->findOwnedServiceByServerName($validated['server_name'], $request);
if ($service instanceof JsonResponse) {
return $service;
}
$vm = $service->orderItem?->order?->vmWare?->first();
if (!$vm) {
return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404);
}
$networkId = $vm->ipAddress?->module_network_id;
if (!$networkId) {
return response()->json(['success' => false, 'message' => 'Không xác định được mạng của VM.'], 422);
}
$reservation = $ipReservation->reserveInNetwork($networkId, $validated['ip_address'], $service->id);
if (!$reservation) {
return response()->json(['success' => false, 'message' => 'IP không thuộc pool khả dụng hoặc đã được sử dụng.'], 422);
}
$oldIpPoolId = IpPool::where('module_network_id', $networkId)
->where('service_id', $service->id)
->where('status', 'assigned')
->where('id', '!=', $reservation['ip_pool_id'])
->value('id');
// Auto-detect OS type from the service's order item OS group
$osGroup = strtolower($service->orderItem?->os?->osGroup?->name ?? '');
$osType = (str_contains($osGroup, 'linux') || str_contains($osGroup, 'ubuntu') || str_contains($osGroup, 'centos'))
? 'linux'
: 'windows';
$dns = $validated['dns'] ?? '8.8.8.8';
// ponytail: don't release — lock expires after 5 min while the async job runs
$lock = $this->acquireVmLock($vm, 300);
if (!$lock) {
$ipReservation->releaseByPoolId($reservation['ip_pool_id']);
return $this->vmLocked();
}
ChangeIpVMJob::dispatch(
$vm->id,
$validated['ip_address'],
$reservation['gateway'],
$osType,
$dns,
$validated['guest_user'],
$validated['guest_password'],
$reservation['ip_pool_id'],
$oldIpPoolId,
);
$this->activityLogger->log(
event: 'change_ip',
logName: 'VM',
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' => $reservation['gateway'], 'os_type' => $osType],
);
return response()->json([
'success' => true,
'message' => 'Yêu cầu đổi IP đã được ghi nhận. Hệ thống sẽ xử lý trong vài phút.',
]);
}
/**
* POST /v1/system/vm-change-port
*
* Body:
* product int|string ModuleVmware.id|vmware_id|service_uuid
* new_port int New RDP port (1024–65535)
*/
public function changePort(Request $request, ChangePortVMAction $action): JsonResponse
{
$validated = $request->validate([
'product' => ['required'],
'new_port' => ['required', 'integer', 'min:1024', 'max:65535'],
]);
$vm = $this->resolveVmById($validated['product'], $request);
if ($vm instanceof JsonResponse) {
return $vm;
}
$lock = $this->acquireVmLock($vm);
if (!$lock) {
return $this->vmLocked();
}
try {
$result = $action->execute($vm, (string) $validated['new_port']);
} finally {
$lock->release();
}
// changeRDPPortWindows returns array on success/failure or string on login fail
if ($result === 'Login_Again' || $result === 'Login_Fail') {
return response()->json([
'success' => false,
'message' => 'Không thể đăng nhập vào vCenter.',
], 400);
}
if (is_array($result)) {
$ok = isset($result['success'])
? (bool) $result['success']
: !($result['error'] ?? true);
if ($ok) {
$this->activityLogger->log(
event: 'change_port',
logName: 'VM',
description: "Customer {$request->user()->email} đổi cổng RDP VM: {$vm->name} → port {$validated['new_port']}",
subject: $vm,
newValues: ['new_port' => $validated['new_port']],
);
}
return response()->json([
'success' => $ok,
'message' => $ok ? 'Đổi cổng RDP thành công.' : ($result['message'] ?? 'Đổi cổng thất bại.'),
'data' => $result,
], $ok ? 200 : 400);
}
\Illuminate\Support\Facades\Log::error('changePort: unexpected vCenter response', [
'vm_id' => $vm->id,
'result' => $result,
]);
return response()->json([
'success' => false,
'message' => 'Phản hồi không hợp lệ từ vCenter.',
], 400);
}
/**
* Look up a Service by server_name, verify ownership and active status.
* Returns JsonResponse on error, Service on success.
*/
private function findOwnedServiceByServerName(string $serverName, Request $request): Service|JsonResponse
{
$customer = $request->user();
// VM chain: Service → orderItem → order → vmWare (same as ServiceClientDetailResource)
$service = Service::with([
'orderItem.order.vmWare.vCenter',
'orderItem.order.vmWare.ipAddress.host.datacenter.vCenter',
'orderItem.os.osGroup',
])
->where('server_name', $serverName)
->where('customer_id', $customer->id)
->first();
if (!$service) {
return response()->json(['success' => false, 'message' => 'Không tìm thấy dịch vụ.'], 404);
}
if ($service->status !== 'active') {
return response()->json(['success' => false, 'message' => 'Dịch vụ không ở trạng thái hoạt động, không thể thao tác.'], 403);
}
return $service;
}
}