update layout control vm

This commit is contained in:
Pham Bao
2025-08-10 22:07:52 +07:00
parent cb0e263f44
commit 3ef83f8662
6 changed files with 110 additions and 0 deletions
@@ -739,6 +739,61 @@ class ServiceController extends Controller
}
}
/**
* 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);
}
}
/**
* Secure remove snapshot using session authentication - NO BEARER TOKEN
*/
+7
View File
@@ -6153,6 +6153,13 @@
}
}
}
.hover\:border-blue-300 {
&:hover {
@media (hover: hover) {
border-color: var(--color-blue-300);
}
}
}
.hover\:border-gray-300 {
&:hover {
@media (hover: hover) {
@@ -0,0 +1,30 @@
@props(['invoice'])
<flux:modal name="change-ip" class="md:w-[28rem]">
<div class="space-y-6">
<div>
<flux:heading size="lg">{{ __('Đổi địa chỉ IP') }}</flux:heading>
<flux:text class="mt-2">{{ __('Nhập địa chỉ IP mới mà bạn muốn sử dụng.') }}</flux:text>
</div>
<form x-data="{ submitting: false }" x-on:submit.prevent="if (submitting) return; submitting = true; $el.submit()" method="POST" action="{{ route('service.change.ip') }}">
@csrf
<input type="hidden" name="product_id" value="{{ $invoice->vm_id }}">
<flux:field>
<flux:label>{{ __('Địa chỉ IP mới') }}</flux:label>
<flux:input name="ip" type="text" placeholder="103.42.xx.xx" required pattern="^(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})$" />
<flux:description>{{ __('Vui lòng nhập IPv4 hợp lệ. Yêu cầu sẽ được xử lý nếu khả dụng.') }}</flux:description>
</flux:field>
<div class="flex gap-2 justify-end mt-6">
<flux:modal.close>
<flux:button variant="ghost">{{ __('Hủy') }}</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="primary" color="orange" x-bind:disabled="submitting">{{ __('Đồng ý') }}</flux:button>
</div>
</form>
</div>
</flux:modal>
@@ -0,0 +1,15 @@
{{-- Change IP Trigger --}}
<flux:modal.trigger name="change-ip">
<div class="bg-gray-50 hover:bg-gray-100 rounded-lg p-4 text-center cursor-pointer transition-colors duration-200 border border-gray-200 hover:border-blue-300 w-full">
<div class="flex flex-col items-center">
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mb-3">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 12c2.485 0 4.5-2.015 4.5-4.5S14.485 3 12 3 7.5 5.015 7.5 7.5 9.515 12 12 12zm0 0c-3.314 0-6 2.239-6 5v1h12v-1c0-2.761-2.686-5-6-5z" />
</svg>
</div>
<span class="text-sm font-medium text-gray-700">{{ __('Đổi IP') }}</span>
</div>
</div>
</flux:modal.trigger>
@@ -44,6 +44,7 @@
@if ($isPoweredOn)
@include('frontend.service.partials.control-buttons.snapshot-button')
@include('frontend.service.partials.control-buttons.reset-password-button')
@include('frontend.service.partials.control-buttons.change-ip-button')
@endif
</div>
@@ -60,6 +61,7 @@
'invoice' => $invoice,
'isWindows' => $isWindows,
])
@include('frontend.service.partials.change-ip-modal', ['invoice' => $invoice])
@include('frontend.service.partials.change-remote-port-modal', ['invoice' => $invoice])
@endif
</div>
+1
View File
@@ -521,6 +521,7 @@ Route::middleware("CustomerLoginCheck")->group(function () {
Route::post('secure/reinstall-vm', 'reinstallVM')->name('service.reinstall.vm');
Route::post('secure/reset-vm-password', 'resetVMPassword')->name('service.reset.vm.password');
Route::post('secure/change-remote-port', 'changeRemotePort')->name('service.change.remote.port');
Route::post('secure/change-ip', 'changeIP')->name('service.change.ip');
Route::post('secure/start-vm', 'startVM')->name('service.start.vm');
Route::post('secure/stop-vm', 'stopVM')->name('service.stop.vm');
Route::post('secure/reboot-vm', 'rebootVM')->name('service.reboot.vm');