diff --git a/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php b/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php index 1e32ac3..9037cbb 100644 --- a/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php +++ b/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php @@ -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, diff --git a/app/Http/Controllers/admin/VCenterManagerController.php b/app/Http/Controllers/admin/VCenterManagerController.php index 043251b..10ae696 100644 --- a/app/Http/Controllers/admin/VCenterManagerController.php +++ b/app/Http/Controllers/admin/VCenterManagerController.php @@ -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 } diff --git a/app/Http/Services/vCenter.php b/app/Http/Services/vCenter.php index 8330054..4a30527 100644 --- a/app/Http/Services/vCenter.php +++ b/app/Http/Services/vCenter.php @@ -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 { diff --git a/app/Http/Services/vCenter/DeleteVMService.php b/app/Http/Services/vCenter/DeleteVMService.php new file mode 100644 index 0000000..43065a8 --- /dev/null +++ b/app/Http/Services/vCenter/DeleteVMService.php @@ -0,0 +1,94 @@ +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()]; + } + } +} diff --git a/app/Http/Services/vCenter/PowerShellService.php b/app/Http/Services/vCenter/PowerShellService.php index d1b9bfc..5b5d405 100644 --- a/app/Http/Services/vCenter/PowerShellService.php +++ b/app/Http/Services/vCenter/PowerShellService.php @@ -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(); } diff --git a/app/Http/Services/vCenter/SnapshotService.php b/app/Http/Services/vCenter/SnapshotService.php index 92e3f89..80a8722 100644 --- a/app/Http/Services/vCenter/SnapshotService.php +++ b/app/Http/Services/vCenter/SnapshotService.php @@ -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 diff --git a/app/Jobs/VirtualMachine/ChangeVmPasswordJob.php b/app/Jobs/VirtualMachine/ChangeVmPasswordJob.php index f79f04e..37b55a3 100644 --- a/app/Jobs/VirtualMachine/ChangeVmPasswordJob.php +++ b/app/Jobs/VirtualMachine/ChangeVmPasswordJob.php @@ -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, ])); } diff --git a/app/Mail/Customer/VmPasswordChanged.php b/app/Mail/Customer/VmPasswordChanged.php index 7094264..f6d701c 100644 --- a/app/Mail/Customer/VmPasswordChanged.php +++ b/app/Mail/Customer/VmPasswordChanged.php @@ -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'), diff --git a/database/migrations/2026_07_17_000001_make_customer_id_nullable_in_services_table.php b/database/migrations/2026_07_17_000001_make_customer_id_nullable_in_services_table.php new file mode 100644 index 0000000..c031307 --- /dev/null +++ b/database/migrations/2026_07_17_000001_make_customer_id_nullable_in_services_table.php @@ -0,0 +1,26 @@ +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'); + }); + } +}; diff --git a/database/seeders/Setting.php b/database/seeders/Setting.php index 1edab95..a1ceac0 100644 --- a/database/seeders/Setting.php +++ b/database/seeders/Setting.php @@ -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)] + ); + } } } diff --git a/resources/views/emails/customer/vm-password-changed.blade.php b/resources/views/emails/customer/vm-password-changed.blade.php index 04f7427..e3a1322 100644 --- a/resources/views/emails/customer/vm-password-changed.blade.php +++ b/resources/views/emails/customer/vm-password-changed.blade.php @@ -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 @@