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:
@@ -15,8 +15,11 @@ use Maatwebsite\Excel\Validators\ValidationException;
|
||||
|
||||
class IpPoolController extends Controller
|
||||
{
|
||||
/**
|
||||
* Descriptive/CSV-import fields — plain pass-through, unlike gateway/cidr/netmask/
|
||||
* status/service_id which have defaulting + module_ip_address mirror logic below.
|
||||
*/
|
||||
private const EDITABLE_FIELDS = [
|
||||
'gateway', 'netmask', 'service_id', 'status',
|
||||
'owner_type', 'ip_role', 'parent_ip', 'customer_name', 'service_name',
|
||||
'allocated_at', 'revoked_at', 'allocation_status',
|
||||
'contact_email', 'contact_cc', 'contact_phone',
|
||||
@@ -24,9 +27,25 @@ class IpPoolController extends Controller
|
||||
'last_email_type', 'last_email_at', 'updated_by', 'note',
|
||||
];
|
||||
|
||||
/**
|
||||
* contact_email/contact_cc may hold multiple addresses separated by comma/semicolon.
|
||||
*/
|
||||
private function multiEmailRule(): \Closure
|
||||
{
|
||||
return 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}");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = min((int) $request->get('per_page', 15), 500);
|
||||
// 'all' lets the admin grid load the full dataset once and filter/sort client-side.
|
||||
$perPage = $request->boolean('all') ? 20000 : min((int) $request->get('per_page', 15), 500);
|
||||
$filter = $request->get('filter');
|
||||
$networkId = $request->get('network_id');
|
||||
|
||||
@@ -133,6 +152,10 @@ class IpPoolController extends Controller
|
||||
'netmask' => 'nullable|string|max:45',
|
||||
'status' => 'nullable|in:free,assigned,reserved',
|
||||
'service_id' => 'nullable|integer|exists:services,id',
|
||||
'contact_email' => ['nullable', 'string', $this->multiEmailRule()],
|
||||
'contact_cc' => ['nullable', 'string', $this->multiEmailRule()],
|
||||
'monthly_cost' => 'nullable|numeric',
|
||||
'selling_price' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
$networkId = (int) $request->module_network_id;
|
||||
@@ -174,6 +197,7 @@ class IpPoolController extends Controller
|
||||
$hostId = ModuleNetwork::find($networkId)?->module_host_id;
|
||||
$netmask = $request->netmask ?: '255.255.255.0';
|
||||
$cidr = $request->cidr ?? 24;
|
||||
$extra = $request->only(self::EDITABLE_FIELDS);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
@@ -187,6 +211,7 @@ class IpPoolController extends Controller
|
||||
'netmask' => $netmask,
|
||||
'status' => $request->status ?: 'free',
|
||||
'service_id' => $request->service_id,
|
||||
...$extra,
|
||||
]);
|
||||
|
||||
// Mirror into module_ip_address so both tables stay in sync.
|
||||
@@ -229,6 +254,10 @@ class IpPoolController extends Controller
|
||||
'netmask' => 'nullable|string|max:45',
|
||||
'status' => 'nullable|in:free,assigned,reserved',
|
||||
'service_id' => 'nullable|integer|exists:services,id',
|
||||
'contact_email' => ['nullable', 'string', $this->multiEmailRule()],
|
||||
'contact_cc' => ['nullable', 'string', $this->multiEmailRule()],
|
||||
'monthly_cost' => 'nullable|numeric',
|
||||
'selling_price' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
if (!filter_var($request->ip_address, FILTER_VALIDATE_IP)) {
|
||||
@@ -256,6 +285,7 @@ class IpPoolController extends Controller
|
||||
try {
|
||||
$pool->update([
|
||||
...$request->only(['ip_address', 'module_network_id', 'gateway', 'cidr', 'service_id']),
|
||||
...$request->only(self::EDITABLE_FIELDS),
|
||||
'netmask' => $netmask,
|
||||
'status' => $request->status ?: $pool->status,
|
||||
]);
|
||||
@@ -312,6 +342,44 @@ class IpPoolController extends Controller
|
||||
return response()->json(['error' => false, 'message' => 'Đã xoá IP Pool thành công']);
|
||||
}
|
||||
|
||||
public function bulkUpdateGateway(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array|min:1',
|
||||
'ids.*' => 'integer',
|
||||
'gateway' => 'required|string|max:45',
|
||||
]);
|
||||
|
||||
$pools = IpPool::whereIn('id', $request->ids)->get(['id', 'ip_address', 'module_network_id']);
|
||||
|
||||
if ($pools->isEmpty()) {
|
||||
return response()->json(['error' => true, 'message' => 'Không tìm thấy IP Pool nào'], 404);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
IpPool::whereIn('id', $pools->pluck('id'))->update(['gateway' => $request->gateway]);
|
||||
|
||||
// Mirror into module_ip_address, grouped by network to keep this to a handful of queries.
|
||||
foreach ($pools->groupBy('module_network_id') as $networkId => $group) {
|
||||
ModuleIpAddress::whereIn('ip', $group->pluck('ip_address'))
|
||||
->where('module_network_id', $networkId)
|
||||
->update(['gateway' => $request->gateway]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json(['error' => true, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'message' => "Đã cập nhật gateway cho {$pools->count()} IP",
|
||||
'updated' => $pools->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function bulkDelete(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
@@ -354,6 +422,42 @@ class IpPoolController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function import(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'file' => 'required|file|mimes:csv,txt|max:10240',
|
||||
]);
|
||||
|
||||
$countBefore = IpPool::withTrashed()->count();
|
||||
|
||||
try {
|
||||
Excel::import(new IpPoolImport(), $request->file('file'));
|
||||
} catch (ValidationException $e) {
|
||||
$errors = [];
|
||||
foreach ($e->failures() as $failure) {
|
||||
$errors[] = 'Dòng ' . $failure->row() . ': ' . implode(', ', $failure->errors());
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => 'Import thất bại',
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['error' => true, 'message' => 'Import thất bại: ' . $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
$countAfter = IpPool::withTrashed()->count();
|
||||
|
||||
IpPool::resyncAllContacts();
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'message' => 'Import thành công',
|
||||
'created' => max(0, $countAfter - $countBefore),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backfill/repair the module_ip_address mirror for every ip_pools row.
|
||||
* Existing mirror rows only get gateway/netmask refreshed — status/vm_name
|
||||
|
||||
Reference in New Issue
Block a user