Add bulk update gateway endpoint to vCenter API routes

- Introduced a new POST route 'bulk-update-gateway' for bulk updating gateways.
- This route is protected by the 'module-vcenter.manager' permission middleware.
This commit is contained in:
baodev0
2026-08-06 15:32:51 +07:00
parent 90f2c97bb4
commit 79f06c2fa2
9 changed files with 5555 additions and 19 deletions
+48 -9
View File
@@ -4,6 +4,7 @@ namespace App\Imports;
use App\Models\IpPool;
use App\Models\ModuleNetwork;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
@@ -13,17 +14,22 @@ use Maatwebsite\Excel\Concerns\WithValidation;
class IpPoolImport implements ToModel, WithHeadingRow, WithValidation, WithUpserts, WithUpsertColumns
{
/** @var array<string, int> */
/** @var array<string, ModuleNetwork> */
private array $networkCache = [];
public function model(array $row)
{
$network = $this->resolveNetwork($row['ten_mang'] ?? null, $row['cidr'] ?? null);
return new IpPool([
// uuid has no DB default and must be set on insert; excluded from upsertColumns() so it's never overwritten
'uuid' => (string) Str::uuid(),
'ip_address' => $row['ip_address'],
'module_network_id' => $this->resolveNetworkId($row['ten_mang'] ?? null, $row['cidr'] ?? null),
'cidr' => $this->parsePrefixLength($row['cidr'] ?? null),
'module_network_id' => $network?->id,
// ip_pools.cidr is NOT NULL; fall back to the network's own prefix (or 24) when the row omits it
'cidr' => $this->parsePrefixLength($row['cidr'] ?? null)
?? $this->parsePrefixLength($network?->network)
?? 24,
'gateway' => $row['gateway'] ?? null,
'netmask' => $row['subnet_mask'] ?? null,
'owner_type' => $row['owner_type'] ?? null,
@@ -31,8 +37,8 @@ class IpPoolImport implements ToModel, WithHeadingRow, WithValidation, WithUpser
'parent_ip' => $row['parent_ip'] ?? null,
'customer_name' => $row['ten_khach_hang'] ?? null,
'service_name' => $row['ten_dich_vu'] ?? null,
'allocated_at' => $row['ngay_cap_phat'] ?: null,
'revoked_at' => $row['ngay_thu_hoi'] ?: null,
'allocated_at' => $this->parseDate($row['ngay_cap_phat'] ?? null),
'revoked_at' => $this->parseDate($row['ngay_thu_hoi'] ?? null),
'allocation_status' => $row['trang_thai'] ?? null,
'contact_email' => $row['contact_email'] ?? null,
'contact_cc' => $row['contact_cc'] ?? null,
@@ -41,7 +47,7 @@ class IpPoolImport implements ToModel, WithHeadingRow, WithValidation, WithUpser
'selling_price' => $row['selling_price'] ?: null,
'additional_info' => $row['thong_tin_bo_sung'] ?? null,
'last_email_type' => $row['last_email_type'] ?? null,
'last_email_at' => $row['last_email_at'] ?: null,
'last_email_at' => $this->parseDate($row['last_email_at'] ?? null),
'updated_by' => $row['updated_by'] ?? null,
'note' => $row['ghi_chu'] ?? null,
]);
@@ -49,16 +55,49 @@ class IpPoolImport implements ToModel, WithHeadingRow, WithValidation, WithUpser
public function rules(): array
{
$multiEmail = function (string $attribute, mixed $value, \Closure $fail) {
foreach (preg_split('/[,;]+/', (string) $value) as $email) {
$email = trim($email);
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$fail("Email không hợp lệ: {$email}");
}
}
};
return [
'ten_mang' => 'required|string|max:255',
'cidr' => 'nullable|string|max:45',
'ip_address' => 'required|ip',
'contact_email' => 'nullable|email',
'contact_email' => ['nullable', 'string', $multiEmail],
'contact_cc' => ['nullable', 'string', $multiEmail],
'monthly_cost' => 'nullable|numeric',
'selling_price' => 'nullable|numeric',
];
}
/**
* CSV dates come as "dd/mm/yyyy" or "dd/mm/yyyy H:i:s" (Vietnamese sheet export).
*/
private function parseDate(?string $value): ?string
{
$value = trim((string) $value);
if ($value === '') {
return null;
}
try {
return Carbon::createFromFormat('d/m/Y H:i:s', $value)->toDateTimeString();
} catch (\Throwable) {
// not a datetime, try date-only below
}
try {
return Carbon::createFromFormat('d/m/Y', $value)->startOfDay()->toDateTimeString();
} catch (\Throwable) {
return null;
}
}
/**
* CSV gives CIDR as "/22"; the ip_pools.cidr column only stores the prefix length.
*/
@@ -83,7 +122,7 @@ class IpPoolImport implements ToModel, WithHeadingRow, WithValidation, WithUpser
];
}
private function resolveNetworkId(?string $name, ?string $cidr): ?int
private function resolveNetwork(?string $name, ?string $cidr): ?ModuleNetwork
{
if (!$name) {
return null;
@@ -99,6 +138,6 @@ class IpPoolImport implements ToModel, WithHeadingRow, WithValidation, WithUpser
?? ($cidr ? ModuleNetwork::where('network', $cidr)->first() : null)
?? ModuleNetwork::create(['name' => $name, 'network' => $cidr]);
return $this->networkCache[$key] = $network->id;
return $this->networkCache[$key] = $network;
}
}