Files
portal-api/app/Models/ModuleVmware.php
T
baodev0 c917e3bf5e feat: Add IT Support catalog and related migrations, seeders, and email templates
- Created migration to add 'group_label' to 'product_type_fields' table.
- Implemented ItSupportCatalogSeeder to populate IT Support features and tiers.
- Added ProductCategorySeeder for initial product categories.
- Developed ProductMockupSeeder for mockup products in various categories.
- Introduced ProductTypeFieldSeeder to define fields for different product types.
- Created email templates for VPS service information and service renewal reminders.
2026-08-26 16:19:38 +07:00

134 lines
4.1 KiB
PHP

<?php
namespace App\Models;
use App\Traits\LogsActivity;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ModuleVmware extends Model
{
use HasFactory, LogsActivity;
protected $table = 'module_vmware';
protected $fillable = [
'name',
'module_ip_address',
'port',
'order_id',
'first_payment',
'next_payment',
'next_due_date',
'next_invoice',
'user',
'password',
'hostname',
'vmware_id',
'vcenter_id',
'os_id',
'customer_id',
'status',
'service_id',
'ip',
'dns',
'plain_password',
'virtual_machine_id',
'cpu',
'ram_mb',
'disk_gb',
];
// NOTE: password must NOT use 'hashed' cast — it is a VM guest credential
// used in plaintext by PowerShell scripts (Invoke-VMScript) via SSH/PowerCLI.
// plain_password uses Laravel's 'encrypted' cast (APP_KEY, AES-256-CBC): stored
// encrypted at rest, transparently decrypted on read — no code elsewhere changes.
protected $casts = [
'plain_password' => 'encrypted',
];
public function customer()
{
return $this->belongsTo(\App\Models\Customer::class);
}
public function ipAddress()
{
return $this->belongsTo(\App\Models\ModuleIpAddress::class, 'module_ip_address', 'id');
}
public function vCenter()
{
return $this->belongsTo(\App\Models\vCenter::class, 'vcenter_id', 'id');
}
public function os()
{
return $this->belongsTo(\App\Models\Os::class);
}
public function osGroup()
{
return $this->belongsTo(\App\Models\OsGroup::class);
}
public function order()
{
return $this->belongsTo(\App\Models\Order::class);
}
public function virtualMachine()
{
return $this->hasOne(\App\Models\VirtualMachines::class, 'vm_id', 'vmware_id');
}
public function services()
{
return $this->hasMany(\App\Models\Service::class, 'vm_id', 'vmware_id');
}
/** Direct link via service_id FK (new). */
public function service()
{
return $this->belongsTo(\App\Models\Service::class, 'service_id');
}
public function getStatus()
{
return match ($this->status) {
1 => '<span class="badge bg-success">Active</span>',
2 => '<span class="badge bg-secondary">Cancel</span>',
default => '<span class="badge bg-danger">Pending</span>',
};
}
/**
* `virtual_machine_id`/`cpu`/`ram_mb` are only written when a service is
* created directly against a known vCenter inventory row (admin "create
* service" flow) or after a manual config change (UpdateVmConfigJob) —
* the normal auto-provisioning pipeline never sets them, since the VM
* isn't synced into `virtual_machines` yet at creation time. Backfill
* them here from the live `vmware_id` ↔ `virtual_machines.vm_id` match
* so API responses always expose the real link/spec (needed by the
* admin UI to show CPU/RAM and enable power on/off/restart/console).
*
* `disk_gb` has no source anywhere in the app (vCenter sync doesn't
* track disk size) — it stays null until captured some other way.
*/
public static function resolveVirtualMachineIds(iterable $vmwares): void
{
$candidates = collect($vmwares)->filter(fn ($vm) => $vm && $vm->vmware_id
&& (!$vm->virtual_machine_id || !$vm->cpu || !$vm->ram_mb));
if ($candidates->isEmpty()) {
return;
}
$inventory = \App\Models\VirtualMachines::whereIn('vm_id', $candidates->pluck('vmware_id')->unique())
->get(['id', 'vm_id', 'cpu', 'ram_mb'])
->keyBy('vm_id');
foreach ($candidates as $vm) {
$match = $inventory->get($vm->vmware_id);
if (!$match) {
continue;
}
$vm->virtual_machine_id ??= $match->id;
$vm->cpu ??= $match->cpu;
$vm->ram_mb ??= $match->ram_mb;
}
}
}