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([ 'ids' => 'required|array|min:1', 'ids.*' => 'integer', ]); $ids = $request->ids; $pools = IpPool::whereIn('id', $ids)->get(); if ($pools->isEmpty()) { return response()->json(['error' => true, 'message' => 'Không tìm thấy IP Pool nào'], 404); } // Block deletion if any IP is currently assigned $inUseIps = ModuleIpAddress::whereIn('ip', $pools->pluck('ip_address')) ->where('status', 1) ->pluck('ip') ->toArray(); if (!empty($inUseIps)) { return response()->json([ 'error' => true, 'message' => 'Không thể xoá — các IP sau đang được gán cho máy chủ: ' . implode(', ', $inUseIps), ], 422); } foreach ($pools as $p) { ModuleIpAddress::where('ip', $p->ip_address) ->where('module_network_id', $p->module_network_id) ->delete(); } $deleted = IpPool::whereIn('id', $ids)->delete(); return response()->json([ 'error' => false, 'message' => "Đã xoá {$deleted} IP Pool thành công", 'deleted' => $deleted, ]); } public function importPreview(Request $request, IpPoolSheetImporter $importer): JsonResponse { $request->validate(['file' => self::IMPORT_FILE_RULE]); return response()->json(['error' => false, 'data' => $importer->preview($request->file('file'))]); } public function import(Request $request, IpPoolSheetImporter $importer): JsonResponse { $request->validate(['file' => self::IMPORT_FILE_RULE]); $summary = $importer->import($request->file('file')); return response()->json([ 'error' => false, 'message' => "Đã thêm {$summary['create']}, cập nhật {$summary['update']} IP" . ($summary['error_count'] ? ", bỏ qua {$summary['error_count']} dòng lỗi" : ''), 'data' => $summary, ]); } /** * Backfill/repair the module_ip_address mirror for every ip_pools row. * Existing mirror rows only get gateway/netmask refreshed — status/vm_name * (assigned VM state) is left untouched. Missing rows are created with * status derived from the current ip_pools.status. */ public function sync(): JsonResponse { $created = 0; $updated = 0; $hostByNet = ModuleNetwork::pluck('module_host_id', 'id'); IpPool::whereNotNull('module_network_id')->chunkById(500, function ($pools) use (&$created, &$updated, $hostByNet) { foreach ($pools as $pool) { $hostId = $hostByNet[$pool->module_network_id] ?? null; $netmask = $pool->netmask ?: '255.255.255.0'; $mia = ModuleIpAddress::where('ip', $pool->ip_address) ->where('module_network_id', $pool->module_network_id) ->first(); if ($mia) { $mia->update([ 'module_host_id' => $hostId, 'gateway' => $pool->gateway, 'netmask' => $netmask, ]); $updated++; } else { ModuleIpAddress::create([ 'ip' => $pool->ip_address, 'module_network_id' => $pool->module_network_id, 'module_host_id' => $hostId, 'gateway' => $pool->gateway, 'netmask' => $netmask, 'status' => $pool->status === 'assigned' ? 1 : 0, ]); $created++; } } }); return response()->json([ 'error' => false, 'message' => "Đã đồng bộ: tạo mới {$created}, cập nhật {$updated}", 'created' => $created, 'updated' => $updated, ]); } }