Implement VM deletion functionality and update related services; add confirmation check and logging
This commit is contained in:
@@ -225,6 +225,7 @@ class VCenterController extends Controller
|
||||
'type' => 'virtual_machine',
|
||||
'meta' => [
|
||||
'db_id' => $vm->id,
|
||||
'vmware_db_id' => $vm->vmware?->id,
|
||||
'uuid' => $vm->uuid,
|
||||
'vm_id' => $vm->vm_id,
|
||||
'ip' => $vm->ip,
|
||||
@@ -302,6 +303,7 @@ class VCenterController extends Controller
|
||||
'type' => 'virtual_machine',
|
||||
'meta' => [
|
||||
'db_id' => $vm->id,
|
||||
'vmware_db_id' => $vm->vmware?->id,
|
||||
'uuid' => $vm->uuid,
|
||||
'vm_id' => $vm->vm_id,
|
||||
'ip' => $vm->ip,
|
||||
|
||||
@@ -313,20 +313,20 @@ class VCenterManagerController extends Controller
|
||||
{
|
||||
$vcenterId = $request->input('vcenter_id');
|
||||
$vcenter = $this->vCenterRepo->find($vcenterId);
|
||||
|
||||
|
||||
if (!$vcenter) {
|
||||
return response()->json(['error' => 'vCenter not found'], 404);
|
||||
}
|
||||
|
||||
$vcenterService = new \App\Http\Services\vCenter();
|
||||
$loginResult = $vcenterService->loginServer($vcenter);
|
||||
|
||||
|
||||
if (!$loginResult) {
|
||||
return response()->json(['error' => 'Cannot login to vCenter'], 400);
|
||||
}
|
||||
|
||||
$datacenters = $vcenterService->getAllDatacenter();
|
||||
|
||||
|
||||
if ($datacenters === false || $datacenters === 'Login_Again') {
|
||||
return response()->json(['error' => 'Cannot get datacenters'], 400);
|
||||
}
|
||||
@@ -341,9 +341,9 @@ class VCenterManagerController extends Controller
|
||||
{
|
||||
$vcenterId = $request->input('vcenter_id');
|
||||
$datacenterId = $request->input('datacenter_id');
|
||||
|
||||
|
||||
$vcenter = $this->vCenterRepo->find($vcenterId);
|
||||
|
||||
|
||||
if (!$vcenter) {
|
||||
return response()->json(['error' => 'vCenter not found'], 404);
|
||||
}
|
||||
@@ -352,7 +352,7 @@ class VCenterManagerController extends Controller
|
||||
$vcenterService->loginServer($vcenter);
|
||||
|
||||
$hosts = $vcenterService->getAllHost($datacenterId);
|
||||
|
||||
|
||||
if ($hosts === false || $hosts === 'Login_Again') {
|
||||
return response()->json(['error' => 'Cannot get hosts'], 400);
|
||||
}
|
||||
@@ -367,9 +367,9 @@ class VCenterManagerController extends Controller
|
||||
{
|
||||
$vcenterId = $request->input('vcenter_id');
|
||||
$hostId = $request->input('host_id');
|
||||
|
||||
|
||||
$vcenter = $this->vCenterRepo->find($vcenterId);
|
||||
|
||||
|
||||
if (!$vcenter) {
|
||||
return response()->json(['error' => 'vCenter not found'], 404);
|
||||
}
|
||||
@@ -445,15 +445,15 @@ class VCenterManagerController extends Controller
|
||||
public function powerOn(Request $request)
|
||||
{
|
||||
$vmId = (int) $request->input('vm_id');
|
||||
|
||||
|
||||
$vm = VirtualMachines::find($vmId);
|
||||
|
||||
|
||||
if (!$vm) {
|
||||
return response()->json(['error' => true, 'message' => 'VM not found'], 404);
|
||||
}
|
||||
|
||||
$vmwareId = $vm->vm_id;
|
||||
|
||||
|
||||
if (!$vmwareId) {
|
||||
return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400);
|
||||
}
|
||||
@@ -463,7 +463,7 @@ class VCenterManagerController extends Controller
|
||||
}
|
||||
|
||||
$vcenter = null;
|
||||
|
||||
|
||||
if ($vm->datacenter?->vCenter) {
|
||||
$vcenter = $vm->datacenter->vCenter;
|
||||
}
|
||||
@@ -477,7 +477,7 @@ class VCenterManagerController extends Controller
|
||||
}
|
||||
|
||||
$vCenterService = new \App\Http\Services\vCenter();
|
||||
|
||||
|
||||
if (!$vCenterService->loginServer($vcenter)) {
|
||||
return response()->json(['error' => true, 'message' => 'Cannot login to vCenter'], 400);
|
||||
}
|
||||
@@ -505,15 +505,15 @@ class VCenterManagerController extends Controller
|
||||
public function powerOff(Request $request)
|
||||
{
|
||||
$vmId = (int) $request->input('vm_id');
|
||||
|
||||
|
||||
$vm = VirtualMachines::find($vmId);
|
||||
|
||||
|
||||
if (!$vm) {
|
||||
return response()->json(['error' => true, 'message' => 'VM not found'], 404);
|
||||
}
|
||||
|
||||
$vmwareId = $vm->vm_id;
|
||||
|
||||
|
||||
if (!$vmwareId) {
|
||||
return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400);
|
||||
}
|
||||
@@ -523,7 +523,7 @@ class VCenterManagerController extends Controller
|
||||
}
|
||||
|
||||
$vcenter = null;
|
||||
|
||||
|
||||
if ($vm->datacenter?->vCenter) {
|
||||
$vcenter = $vm->datacenter->vCenter;
|
||||
}
|
||||
@@ -537,7 +537,7 @@ class VCenterManagerController extends Controller
|
||||
}
|
||||
|
||||
$vCenterService = new \App\Http\Services\vCenter();
|
||||
|
||||
|
||||
if (!$vCenterService->loginServer($vcenter)) {
|
||||
return response()->json(['error' => true, 'message' => 'Cannot login to vCenter'], 400);
|
||||
}
|
||||
@@ -683,19 +683,28 @@ class VCenterManagerController extends Controller
|
||||
|
||||
public function suspend(Request $request)
|
||||
{
|
||||
$vmId = $request->input('vm_id');
|
||||
$vmId = (int) $request->input('vm_id');
|
||||
|
||||
$vm = VirtualMachines::where('vm_id', $vmId)->first();
|
||||
// vm_id from frontend = services.vm_id = module_vmware.id (FK)
|
||||
$vmware = ModuleVmware::with('vCenter')->find($vmId);
|
||||
|
||||
if (!$vm) {
|
||||
if (!$vmware) {
|
||||
return response()->json(['error' => true, 'message' => 'VM not found'], 404);
|
||||
}
|
||||
|
||||
if ($vm->power_state === 'SUSPENDED') {
|
||||
$vmwareId = $vmware->vmware_id;
|
||||
|
||||
if (!$vmwareId) {
|
||||
return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400);
|
||||
}
|
||||
|
||||
$vm = VirtualMachines::where('vm_id', $vmwareId)->first();
|
||||
|
||||
if ($vm?->power_state === 'SUSPENDED') {
|
||||
return response()->json(['error' => true, 'message' => 'VM is already suspended'], 400);
|
||||
}
|
||||
|
||||
$vcenter = $vm->datacenter?->vCenter ?? $vm->host?->datacenter?->vCenter;
|
||||
$vcenter = $vmware->vCenter;
|
||||
|
||||
if (!$vcenter) {
|
||||
return response()->json(['error' => true, 'message' => 'vCenter not found'], 400);
|
||||
@@ -709,18 +718,18 @@ class VCenterManagerController extends Controller
|
||||
|
||||
$response = Http::withoutVerifying()
|
||||
->withHeaders(['vmware-api-session-id' => $vCenterService->token])
|
||||
->post("https://{$vCenterService->apiUri}/rest/vcenter/vm/$vmId/power/suspend");
|
||||
->post("https://{$vCenterService->apiUri}/rest/vcenter/vm/$vmwareId/power/suspend");
|
||||
|
||||
if ($response->status() === 200) {
|
||||
$vm->update(['power_state' => 'SUSPENDED']);
|
||||
$this->sendSuspendNotification($vm);
|
||||
$vm?->update(['power_state' => 'SUSPENDED']);
|
||||
$this->sendSuspendNotification($vmware, $vm);
|
||||
return response()->json(['error' => false, 'message' => 'Suspend command sent successfully']);
|
||||
}
|
||||
|
||||
$errorData = $response->json();
|
||||
if (isset($errorData['error_type']) && $errorData['error_type'] === 'ALREADY_IN_DESIRED_STATE') {
|
||||
$vm->update(['power_state' => 'SUSPENDED']);
|
||||
$this->sendSuspendNotification($vm);
|
||||
$vm?->update(['power_state' => 'SUSPENDED']);
|
||||
$this->sendSuspendNotification($vmware, $vm);
|
||||
return response()->json(['error' => false, 'message' => 'VM is already suspended']);
|
||||
}
|
||||
|
||||
@@ -730,19 +739,69 @@ class VCenterManagerController extends Controller
|
||||
], 400);
|
||||
}
|
||||
|
||||
private function sendSuspendNotification(VirtualMachines $vm): void
|
||||
public function deleteVM(Request $request)
|
||||
{
|
||||
$vmware = ModuleVmware::with('customer')
|
||||
->where('vmware_id', $vm->vm_id)
|
||||
->first();
|
||||
$request->validate([
|
||||
'vm_id' => 'required|integer',
|
||||
'confirm_name' => 'required|string',
|
||||
]);
|
||||
|
||||
if (!$vmware || !$vmware->customer || !$vmware->customer->email) {
|
||||
$vmId = (int) $request->input('vm_id');
|
||||
$vm = VirtualMachines::find($vmId);
|
||||
|
||||
if (!$vm) {
|
||||
return response()->json(['error' => true, 'message' => 'VM not found'], 404);
|
||||
}
|
||||
|
||||
if ($request->input('confirm_name') !== $vm->name) {
|
||||
return response()->json(['error' => true, 'message' => "Tên xác nhận không khớp. Vui lòng nhập đúng: {$vm->name}"], 422);
|
||||
}
|
||||
|
||||
if ($vm->power_state !== 'POWERED_OFF') {
|
||||
return response()->json(['error' => true, 'message' => 'Vui lòng tắt máy ảo trước khi xoá'], 400);
|
||||
}
|
||||
|
||||
$vcenter = $vm->datacenter?->vCenter ?? $vm->host?->datacenter?->vCenter;
|
||||
|
||||
if (!$vcenter) {
|
||||
return response()->json(['error' => true, 'message' => 'vCenter not found'], 400);
|
||||
}
|
||||
|
||||
$powerShellService = app(\App\Http\Services\vCenter\PowerShellService::class);
|
||||
|
||||
$q = fn(string $v) => str_replace("'", "''", $v);
|
||||
$vmName = $vm->name;
|
||||
$host = $vcenter->host;
|
||||
$user = $vcenter->user;
|
||||
$password = \App\Http\Facade\SignRSA::DecryptFunc($vcenter->password);
|
||||
|
||||
$command = "& 'C:\\CLISave\\DeleteVM\\deleteVM.ps1' -VmName '" . $q($vmName) . "' -VCenterHost '" . $q($host) . "' -VCenterUser '" . $q($user) . "' -VCenterPassword '" . $q($password) . "'";
|
||||
$cmdResult = $powerShellService->runPsCommand($command);
|
||||
|
||||
if (strpos($cmdResult, 'Login_Fail') !== false) {
|
||||
return response()->json(['error' => true, 'message' => 'SSH Login to PowerShell server failed'], 400);
|
||||
}
|
||||
|
||||
if (strpos($cmdResult, 'SUCCESS') === false) {
|
||||
return response()->json(['error' => true, 'message' => 'Xoá VM thất bại: ' . $cmdResult], 400);
|
||||
}
|
||||
|
||||
$vm->delete();
|
||||
|
||||
return response()->json(['error' => false, 'message' => 'VM đã được xoá thành công']);
|
||||
}
|
||||
|
||||
private function sendSuspendNotification(ModuleVmware $vmware, ?VirtualMachines $vm = null): void
|
||||
{
|
||||
$vmware->loadMissing('customer');
|
||||
|
||||
if (!$vmware->customer || !$vmware->customer->email) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Mail::to($vmware->customer->email)
|
||||
->queue(new VmSuspendedMail($vmware->customer, $vm->name, $vm->vm_id));
|
||||
->queue(new VmSuspendedMail($vmware->customer, $vm?->name ?? $vmware->name, $vmware->vmware_id));
|
||||
} catch (\Throwable) {
|
||||
// Email failure must not block the suspend response
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ class vCenter
|
||||
|
||||
public function CreateSnapShot($product, ?string $snapshotName = null): string|array
|
||||
{
|
||||
return $this->snapshot->CreateSnapShot($product);
|
||||
return $this->snapshot->CreateSnapShot($product, $snapshotName);
|
||||
}
|
||||
public function getSnapShot($product): string|array
|
||||
{
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Services\vCenter;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DeleteVMService
|
||||
{
|
||||
protected Client $client;
|
||||
protected PowerShellService $powerShellService;
|
||||
|
||||
public function __construct(Client $client, PowerShellService $powerShellService)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->powerShellService = $powerShellService;
|
||||
}
|
||||
|
||||
protected function resolveVCenterFromProduct($product): ?\App\Models\vCenter
|
||||
{
|
||||
if ($product->vCenter) {
|
||||
return $product->vCenter;
|
||||
}
|
||||
|
||||
if ($product->ipaddress?->host?->datacenter?->vCenter) {
|
||||
return $product->ipaddress->host->datacenter->vCenter;
|
||||
}
|
||||
|
||||
$service = $product->services()->first();
|
||||
if ($service) {
|
||||
$service->load('vmware.vCenter');
|
||||
if ($service->vmware?->vCenter) {
|
||||
return $service->vmware->vCenter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function deleteVM($product, string $confirmName): array
|
||||
{
|
||||
$vmName = (string) $product->name;
|
||||
|
||||
// ponytail: confirm guard — caller must pass exact vm name to proceed
|
||||
if ($confirmName !== $vmName) {
|
||||
return ['error' => true, 'message' => "Tên server xác nhận không khớp. Vui lòng nhập đúng: $vmName"];
|
||||
}
|
||||
|
||||
$vcenter = $this->resolveVCenterFromProduct($product);
|
||||
if (!$vcenter) {
|
||||
return ['error' => true, 'message' => 'Không tìm thấy thông tin vCenter cho VM'];
|
||||
}
|
||||
|
||||
$loginResult = $this->client->loginServer($vcenter);
|
||||
if (!$loginResult) {
|
||||
return ['error' => true, 'message' => 'Login_Again'];
|
||||
}
|
||||
|
||||
$check = $this->client->checkApiExits();
|
||||
if (!$check) {
|
||||
return ['error' => true, 'message' => 'Login_Again'];
|
||||
}
|
||||
|
||||
$host = $this->client->apiUri;
|
||||
$user = $this->client->getUsername();
|
||||
$password = $this->client->getPassword();
|
||||
|
||||
try {
|
||||
Log::channel('Custom')->info("===================================================================");
|
||||
Log::channel('Custom')->info("Delete VM - $vmName");
|
||||
|
||||
$command = "& 'C:\CLISave\DeleteVM\deleteVM.ps1' -VmName '$vmName' -VCenterHost '$host' -VCenterUser '$user' -VCenterPassword '$password'";
|
||||
Log::channel('Custom')->info("Executing deleteVM command for: $vmName");
|
||||
|
||||
$cmdResult = $this->powerShellService->executePowerCLI($command);
|
||||
Log::channel('Custom')->info("Command Result: " . $cmdResult);
|
||||
|
||||
if (strpos($cmdResult, 'Login_Fail') !== false) {
|
||||
return ['error' => true, 'message' => 'SSH Login to PowerShell server failed'];
|
||||
}
|
||||
|
||||
if (strpos($cmdResult, 'SUCCESS') === false) {
|
||||
return ['error' => true, 'message' => 'PowerShell command failed: ' . $cmdResult];
|
||||
}
|
||||
|
||||
Log::channel('Custom')->info("After run Command |" . now());
|
||||
Log::channel('Custom')->info("===================================================================");
|
||||
|
||||
return ['error' => false];
|
||||
} catch (\Exception $error) {
|
||||
Log::channel('Custom')->error("Delete VM error: " . $error->getMessage());
|
||||
return ['error' => true, 'message' => $error->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,8 @@ class PowerShellService
|
||||
try {
|
||||
$out = $auth->exec($action);
|
||||
$errOut = $auth->getStdError();
|
||||
return $out . ($errOut ? "\n[STDERR]\n" . $errOut : '');
|
||||
$realErr = $errOut && !str_starts_with(trim($errOut), '#< CLIXML');
|
||||
return $out . ($realErr ? "\n[STDERR]\n" . $errOut : '');
|
||||
} catch (\Throwable $e) {
|
||||
return "Error: " . $e->getMessage();
|
||||
}
|
||||
@@ -132,7 +133,9 @@ PS;
|
||||
try {
|
||||
$out = $auth->exec($action);
|
||||
$errOut = $auth->getStdError();
|
||||
return $out . ($errOut ? "\n[STDERR]\n" . $errOut : '');
|
||||
// PowerCLI banner is CLIXML informational records — not an error
|
||||
$realErr = $errOut && !str_starts_with(trim($errOut), '#< CLIXML');
|
||||
return $out . ($realErr ? "\n[STDERR]\n" . $errOut : '');
|
||||
} catch (\Throwable $e) {
|
||||
return "Error: " . $e->getMessage();
|
||||
}
|
||||
|
||||
@@ -47,57 +47,54 @@ class SnapshotService
|
||||
return null;
|
||||
}
|
||||
|
||||
public function CreateSnapShot($product): string|array
|
||||
public function CreateSnapShot($product, ?string $snapshotName = null): string|array
|
||||
{
|
||||
$vcenter = $this->resolveVCenterFromProduct($product);
|
||||
if (!$vcenter) {
|
||||
return ['error' => true, 'message' => 'Không tìm thấy thông tin vCenter cho VM'];
|
||||
}
|
||||
|
||||
$loginResult = $this->client->loginServer($vcenter);
|
||||
if (!$loginResult) {
|
||||
return "Login_Again";
|
||||
}
|
||||
|
||||
$vmName = (string) $product->name;
|
||||
$name = $snapshotName ?? ($vmName . '_' . date('d_m_Y_H_i_s'));
|
||||
|
||||
$check = $this->client->checkApiExits();
|
||||
if (!$check) {
|
||||
return "Login_Again";
|
||||
}
|
||||
|
||||
try {
|
||||
// Note: credentials setup logic from original vCenter.php seemed redundant with Client logic,
|
||||
// but CreateSnapShot was overwriting $this->username/password.
|
||||
// Here we assume Client is used, or we need to update Client.
|
||||
// Client stores token. If we need username/password for something else, Client has them now via getters.
|
||||
// But CreateSnapshot used $product->ipaddress...->vCenter->user/password.
|
||||
Log::channel('Custom')->info("===================================================================");
|
||||
Log::channel('Custom')->info("Create Snapshot with VirtualMachine Name - $vmName - Snap #$name");
|
||||
|
||||
// Re-login to ensure we have the right credentials for the vCenter associated with the product
|
||||
$vcenterModel = $this->resolveVCenterFromProduct($product);
|
||||
$command = "& 'C:\CLISave\createsnapshot.ps1' -vm_name '$vmName' -name '$name'";
|
||||
Log::channel('Custom')->info($command);
|
||||
|
||||
if (!$vcenterModel) {
|
||||
return ['error' => true, 'message' => 'Không tìm thấy thông tin vCenter cho VM'];
|
||||
$cmdResult = $this->powerShellService->executePowerCLI($command);
|
||||
Log::channel('Custom')->info("Command Result: " . $cmdResult);
|
||||
|
||||
if (strpos($cmdResult, 'Login_Fail') !== false) {
|
||||
return ['error' => true, 'message' => 'SSH Login to PowerShell server failed'];
|
||||
}
|
||||
|
||||
$this->client->loginServer($vcenterModel);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ['error' => true, 'message' => 'Lỗi setup credentials: ' . $e->getMessage()];
|
||||
}
|
||||
|
||||
$vmware_name = (string) $product->name;
|
||||
// In original code, it checks checkApiExits(), if true proceed.
|
||||
|
||||
if ($this->client->checkApiExits()) {
|
||||
try {
|
||||
$Snapshot = $vmware_name . "_" . date('d_m_Y_H_i_s');
|
||||
|
||||
Log::channel('Custom')->info("===================================================================");
|
||||
Log::channel('Custom')->info("Create Snapshot with VirtualMachine Name - $vmware_name - Snap #$Snapshot");
|
||||
|
||||
$Command = "\$vm_name = '$vmware_name';\$name = '$Snapshot'; cd /; C:\CLISave\createsnapshot.ps1;";
|
||||
Log::channel('Custom')->info("$Command");
|
||||
|
||||
$this->powerShellService->executePowerCLI($Command);
|
||||
|
||||
$this->clearSnapshotCache($product);
|
||||
|
||||
return [
|
||||
'error' => false,
|
||||
'data' => $Snapshot
|
||||
];
|
||||
} catch (\Exception $error) {
|
||||
return [
|
||||
'error' => true,
|
||||
'message' => $error->getMessage()
|
||||
];
|
||||
if (strpos($cmdResult, '[STDERR]') !== false || stripos($cmdResult, 'error') !== false) {
|
||||
return ['error' => true, 'message' => 'PowerShell command failed: ' . $cmdResult];
|
||||
}
|
||||
|
||||
Log::channel('Custom')->info("After run Command |" . now());
|
||||
Log::channel('Custom')->info("===================================================================");
|
||||
|
||||
$this->clearSnapshotCache($product);
|
||||
|
||||
return ['error' => false, 'data' => $name];
|
||||
} catch (\Exception $error) {
|
||||
Log::channel('Custom')->error("Create Snapshot error: " . $error->getMessage());
|
||||
return ['error' => true, 'message' => $error->getMessage()];
|
||||
}
|
||||
return "Login_Again";
|
||||
}
|
||||
|
||||
private function clearSnapshotCache($product): void
|
||||
|
||||
@@ -99,8 +99,9 @@ class ChangeVmPasswordJob implements ShouldQueue
|
||||
}
|
||||
|
||||
Mail::to($vm->customer->email)->queue(new VmPasswordChanged([
|
||||
'fullname' => $vm->customer->fullname,
|
||||
'vm_name' => $vm->name,
|
||||
'fullname' => $vm->customer->fullname,
|
||||
'vm_name' => $vm->name,
|
||||
'newPassword' => $this->newPassword,
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class VmPasswordChanged extends Mailable
|
||||
'appName' => $data['company_name'] ?? config('app.name', 'The Gioi So'),
|
||||
'fullName' => $data['fullname'] ?? 'Quý khách',
|
||||
'vmName' => $data['vm_name'] ?? '',
|
||||
'newPassword' => $data['newPassword'] ?? '',
|
||||
'loginUrl' => $data['login_url'] ?? rtrim(config('app.frontend_url', ''), '/') . '/dang-nhap',
|
||||
'supportEmail' => $data['support_email'] ?? config('mail.support_email', 'support@thegioiso.com'),
|
||||
'supportPhone' => $data['support_phone'] ?? config('mail.support_phone', '1900.xxxx'),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->dropForeign(['customer_id']);
|
||||
$table->unsignedBigInteger('customer_id')->nullable()->change();
|
||||
$table->foreign('customer_id')->references('id')->on('customers')->onDelete('set null');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->dropForeign(['customer_id']);
|
||||
$table->unsignedBigInteger('customer_id')->nullable(false)->change();
|
||||
$table->foreign('customer_id')->references('id')->on('customers')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -2,97 +2,68 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use App\Models\Setting as SettingModel;
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Setting as SettingWeb;
|
||||
|
||||
class Setting extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
* Seed the application settings without creating duplicate keys.
|
||||
*
|
||||
* @return void
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public function run()
|
||||
public function run(): void
|
||||
{
|
||||
SettingWeb::insert([
|
||||
[
|
||||
'key' => 'setting',
|
||||
'value' => json_encode([
|
||||
'tax' => '',
|
||||
'tel' => '',
|
||||
'favi_icon' => 'https://tgs.com.vn/wp-content/uploads/2020/11/logo-social-01.png',
|
||||
'logo' => 'https://tgs.com.vn/wp-content/uploads/2023/02/cropped-LOGO-THE-GIOI-SO-2023-05M-1-1024x487.png',
|
||||
'email' => 'tgs@gmail.com',
|
||||
'title' => 'Trang Quản Lý Portal',
|
||||
'company' => 'Thế giới số Portal',
|
||||
'bot_id' => env('TELEGRAM_BOT_ID'),
|
||||
'bot_token' => env('TELEGRAM_BOT_TOKEN'),
|
||||
'telegram_group_id' => env('TELEGRAM_GROUP_ID', ''),
|
||||
'status' => 1,
|
||||
'address' => 'HCM',
|
||||
'hotline' => '',
|
||||
'admin_email' => env('ADMIN_NOTIFY_EMAIL', ''),
|
||||
'support' => null,
|
||||
'brand_color' => '#EF6824'
|
||||
], JSON_THROW_ON_ERROR)
|
||||
$settings = [
|
||||
'setting' => [
|
||||
'tax' => null,
|
||||
'tel' => null,
|
||||
'favi_icon' => 'https://tgs.com.vn/wp-content/uploads/2020/11/logo-social-01.png',
|
||||
'logo' => env('APP_LOGO_URL', 'https://tgs.com.vn/wp-content/uploads/2023/02/cropped-LOGO-THE-GIOI-SO-2023-05M-1-1024x487.png'),
|
||||
'email' => env('ADMIN_NOTIFY_EMAIL', 'web@tgs.com.vn'),
|
||||
'title' => 'Trang Quản Lý Portal',
|
||||
'company' => 'Thế giới số Portal',
|
||||
'bot_id' => env('TELEGRAM_BOT_ID'),
|
||||
'bot_token' => env('TELEGRAM_BOT_TOKEN'),
|
||||
'status' => 1,
|
||||
'address' => 'HCM',
|
||||
'support' => null,
|
||||
'brand_color' => '#EF6824',
|
||||
'hotline' => null,
|
||||
'admin_email' => env('ADMIN_NOTIFY_EMAIL', 'web@tgs.com.vn'),
|
||||
'telegram_group_id' => env('TELEGRAM_GROUP_ID'),
|
||||
'inet_api_key' => env('INET_API_KEY'),
|
||||
],
|
||||
[
|
||||
'key' => 'smtp',
|
||||
'value' => json_encode([
|
||||
'transport' => env('MAIL_MAILER', 'smtp'),
|
||||
'host' => env('MAIL_HOST'),
|
||||
'user' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'encryption' => env('MAIL_ENCRYPTION', 'ssl'),
|
||||
'port' => env('MAIL_PORT', 465),
|
||||
'status' => 1
|
||||
], JSON_THROW_ON_ERROR)
|
||||
'smtp' => [
|
||||
'transport' => env('MAIL_MAILER', 'smtp'),
|
||||
'host' => env('MAIL_HOST', 'smtp.gmail.com'),
|
||||
'user' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
||||
'port' => env('MAIL_PORT', 465),
|
||||
'status' => 1,
|
||||
],
|
||||
[
|
||||
'key' => 'banking',
|
||||
'value' => json_encode([
|
||||
'type' => 'MB',
|
||||
'callback' => 'api.callback.MB',
|
||||
'captcha' => '',
|
||||
'user' => '',
|
||||
'password' => '',
|
||||
'status' => 0
|
||||
], JSON_THROW_ON_ERROR)
|
||||
'banking' => [
|
||||
'type' => 'MB',
|
||||
'callback' => 'api.callback.MB',
|
||||
'captcha' => '',
|
||||
'user' => '',
|
||||
'password' => '',
|
||||
'status' => 0,
|
||||
],
|
||||
[
|
||||
'key' => 'domain_pricing',
|
||||
'value' => json_encode([
|
||||
'domains' => [
|
||||
['tld' => '.com', 'setupFee' => 'Miễn phí', 'firstYear' => 320000, 'renewal' => 340000],
|
||||
['tld' => '.net', 'setupFee' => 'Miễn phí', 'firstYear' => 369000, 'renewal' => 409000],
|
||||
['tld' => '.org', 'setupFee' => 'Miễn phí', 'firstYear' => 339000, 'renewal' => 369000],
|
||||
['tld' => '.biz', 'setupFee' => 'Miễn phí', 'firstYear' => 399000, 'renewal' => 409000],
|
||||
['tld' => '.info', 'setupFee' => 'Miễn phí', 'firstYear' => 490000, 'renewal' => 509000],
|
||||
['tld' => '.Pro', 'setupFee' => 'Miễn phí', 'firstYear' => 469000, 'renewal' => 489000],
|
||||
['tld' => '.name', 'setupFee' => 'Miễn phí', 'firstYear' => 259000, 'renewal' => 259000],
|
||||
['tld' => '.ws', 'setupFee' => 'Miễn phí', 'firstYear' => 909000, 'renewal' => 909000],
|
||||
['tld' => '.tv', 'setupFee' => 'Miễn phí', 'firstYear' => 765000, 'renewal' => 819000],
|
||||
['tld' => '.mobi', 'setupFee' => 'Miễn phí', 'firstYear' => 589000, 'renewal' => 589000],
|
||||
['tld' => '.uk', 'setupFee' => 'Miễn phí', 'firstYear' => 537000, 'renewal' => 537000],
|
||||
['tld' => '.asia', 'setupFee' => 'Miễn phí', 'firstYear' => 380000, 'renewal' => 452000],
|
||||
['tld' => '.me', 'setupFee' => 'Miễn phí', 'firstYear' => 1029000, 'renewal' => 1029000],
|
||||
['tld' => '.us', 'setupFee' => 'Miễn phí', 'firstYear' => 323000, 'renewal' => 323000],
|
||||
],
|
||||
'services' => [
|
||||
['name' => 'Chứng thực Rapid SSL', 'setupFee' => 230000, 'maintenance' => 750000, 'note' => '01 Domain'],
|
||||
['name' => 'Chứng thực Quick SSL', 'setupFee' => 230000, 'maintenance' => 2300000, 'note' => '01 Domain'],
|
||||
['name' => 'Chứng thực Wildcard SSL', 'setupFee' => 230000, 'maintenance' => 3300000, 'note' => 'Unlimited Subdomain'],
|
||||
['name' => 'Chứng thực True BusinessID', 'setupFee' => 540000, 'maintenance' => 3900000, 'note' => '01 Domain'],
|
||||
['name' => 'Chứng thực True BusinessID WildCard', 'setupFee' => 900000, 'maintenance' => 15000000, 'note' => 'Unlimited Subdomain'],
|
||||
['name' => 'Đăng ký DNS riêng', 'setupFee' => 200000, 'maintenance' => 'Miễn phí', 'note' => "dns1.tenmiencuaban.com\ndns2.tenmiencuaban.com"],
|
||||
['name' => 'Bảo mật thông tin tên miền', 'setupFee' => 'Miễn phí', 'maintenance' => 90000, 'note' => 'Toàn bộ thông tin Whois tên miền của bạn sẽ được giấu kín hoàn toàn với mọi công cụ Whois'],
|
||||
['name' => 'Khóa tên miền', 'setupFee' => 'Miễn phí', 'maintenance' => 'Miễn phí', 'note' => 'Tăng cường bảo mật cho tên miền'],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR)
|
||||
]
|
||||
]);
|
||||
'email_settings' => [
|
||||
'vm_template_id' => 2,
|
||||
'renewal_template_id' => 6,
|
||||
'news_template_id' => null,
|
||||
'invoice_template_id' => null,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($settings as $key => $value) {
|
||||
SettingModel::updateOrCreate(
|
||||
['key' => $key],
|
||||
['value' => json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
$appName = $appName ?? config('app.name', 'Thế Giới Số');
|
||||
$fullName = $fullName ?? 'Quý khách';
|
||||
$vmName = $vmName ?? '';
|
||||
$newPassword = $newPassword ?? '';
|
||||
$loginUrl = $loginUrl ?? rtrim(config('app.frontend_url', ''), '/') . '/dang-nhap';
|
||||
$supportEmail = $supportEmail ?? 'support@tgs.com.vn';
|
||||
$supportPhone = $supportPhone ?? '1900. 6119';
|
||||
@@ -46,6 +47,9 @@
|
||||
<tr>
|
||||
<td style="padding: 14px 18px; font-family: Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.8; color: #000000;">
|
||||
<div>Máy chủ: <strong style="font-family: 'Menlo','Consolas',monospace; color: #111827;">{{ $vmName }}</strong></div>
|
||||
@if($newPassword)
|
||||
<div>Mật khẩu mới: <strong style="font-family: 'Menlo','Consolas',monospace; color: #111827;">{{ $newPassword }}</strong></div>
|
||||
@endif
|
||||
<div>Trạng thái: <strong style="color: #16a34a;">Đổi mật khẩu thành công ✓</strong></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -596,6 +596,7 @@ Route::prefix('v1')
|
||||
Route::post('vm/restart', 'restart')->middleware('perm:module-vcenter.edit');
|
||||
Route::post('vm/snapshot', 'snapshot')->middleware('perm:module-vcenter.edit');
|
||||
Route::post('vm/suspend', 'suspend')->middleware('perm:module-vcenter.edit');
|
||||
Route::post('vm/delete', 'deleteVM')->middleware('perm:module-vcenter.edit');
|
||||
Route::get('vms/powered-off', 'getPoweredOffVms')->middleware('perm:module-vcenter.view');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user