update port remote change

This commit is contained in:
pbaodev
2025-08-25 01:45:38 +07:00
parent 197fe413e5
commit 1384a2ec1c
7 changed files with 349 additions and 84 deletions
@@ -9,6 +9,7 @@ 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\ModuleVmware;
use App\Models\Order;
@@ -600,85 +601,44 @@ class ServiceController extends Controller
/**
* Change Remote Desktop port (Windows only)
*/
public function changeRemotePort(Request $request, vCenter $serviceVCenter)
public function changeRemotePort(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',
'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);
$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: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);
$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.');
}
/**
+124
View File
@@ -425,6 +425,76 @@ PS;
return "Error: " . $e->getMessage();
}
}
protected function CommandChangePort(string $cmd, bool $expectJson = false)
{
$auth = $this->LoginAuth();
if (!$auth)
return "Login_Fail";
$esc = fn(string $v) => str_replace("'", "''", $v);
$server = $esc($this->apiUri ?? '');
$user = $esc($this->username ?? '');
$pass = $esc($this->password ?? '');
// PowerShell script template
$psTemplate = <<<'PS'
$ProgressPreference = 'SilentlyContinue'
$InformationPreference = 'SilentlyContinue'
$WarningPreference = 'SilentlyContinue'
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
Import-Module VMware.PowerCLI -ErrorAction Stop
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$false | Out-Null
Connect-VIServer -Server '<SERVER>' -User '<USER>' -Password '<PASS>' | Out-Null
& {
<CMD>
}
PS;
$psScript = strtr($psTemplate, [
'<SERVER>' => $server,
'<USER>' => $user,
'<PASS>' => $pass,
'<CMD>' => $cmd,
]);
// Encode UTF-16LE cho -EncodedCommand
$encoded = base64_encode(mb_convert_encoding($psScript, 'UTF-16LE', 'UTF-8'));
$action = "powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encoded";
try {
$stream = ssh2_exec($auth, $action);
if (!$stream)
return "ssh2_exec failed";
$err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
stream_set_blocking($stream, true);
stream_set_blocking($err, true);
$out = stream_get_contents($stream) ?: '';
$errOut = stream_get_contents($err) ?: '';
if ($expectJson) {
// CẮT JSON cuối: {...} hoặc [...]
$trimmed = trim($out);
// đơn giản: bắt khối { ... } hoặc [ ... ] ở cuối chuỗi
if (preg_match('~(\{.*\}|\[.*\])\s*$~s', $trimmed, $m)) {
return $m[1];
}
// không tìm thấy → trả nguyên STDOUT (để caller log raw)
return $trimmed;
}
// Trường hợp không kỳ vọng JSON: giữ nguyên hành vi cũ (append STDERR)
return $out . ($errOut ? "\n[STDERR]\n" . $errOut : '');
} catch (\Throwable $e) {
return "Error: " . $e->getMessage();
}
}
public function resetGuestPasswordViaPs1(string $vmName, string $guestUser, string $guestPassword, string $newPassword, string $targetUser = 'Administrator'): array|string
{
// File ps1 chạy được ngoài PowerShell: bạn đã test ok
@@ -461,6 +531,60 @@ PS;
return $data ?: $raw; // nếu chưa parse được, trả raw để debug
}
public function changeRDPPortWindows(string $vmName, string $guestUser, string $guestPassword, string $newPort): array|string
{
$psFile = "C:\\CLISave\\AssignIP\\change-rdp-port.ps1";
$q = fn(string $v) => str_replace("'", "''", $v);
// Dùng -VMName (string). Alias -VMNames cũng ok, nhưng mình set đúng tên tham số.
$args = " -VMName '" . $q($vmName) . "'";
$args .= " -GuestUser '" . $q($guestUser) . "'";
$args .= " -GuestPassword '" . $q($guestPassword) . "'"; // khớp tên tham số trong script 1-VM
$args .= " -NewPort " . (int) $newPort;
$cmd = "& '$psFile'$args";
// Nếu đã có phiên bản CommandRSP($cmd, bool $expectJson) → bật expectJson để không trộn STDERR
$raw = method_exists($this, 'CommandRSP')
? ($this->CommandChangePort($cmd, true) ?? '')
: '';
if ($raw === 'Login_Fail' || $raw === false || $raw === null) {
return "Login_Fail";
}
$raw = trim($raw);
// CẮT LẤY KHỐI JSON CUỐI (object hoặc array) nếu có noise
if (!preg_match('~(\{(?:[^{}]|(?1))*\}|\[(?:[^\[\]]|(?0))*\])\s*$~s', $raw, $m)) {
// fallback đơn giản hơn
if (preg_match('~(\{.*\}|\[.*\])\s*$~s', $raw, $m)) {
$raw = $m[1];
}
} else {
$raw = $m[1];
}
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return [
'success' => false,
'error' => 'JSON parsing failed',
'raw_output' => $raw,
'json_error' => json_last_error_msg()
];
}
// Script 1-VM trả về 1 object, nhưng nếu lỡ trả mảng thì lấy phần tử đầu
if (isset($data[0])) {
$data = $data[0];
}
return $data;
}
private function getAllNetwork()
{
$check = $this->checkApiExits();