validateAllocation($request); return response()->json([ 'error' => false, 'data' => $allocation->previewAllocation($data['ids'], $this->allocationCustomer($data), $data), ]); } public function allocate(Request $request, IpAllocationService $allocation): JsonResponse { $data = $this->validateAllocation($request); $count = $allocation->allocate($data['ids'], $this->allocationCustomer($data), $data, $request->user('admin')); return response()->json(['error' => false, 'message' => "Đã cấp phát {$count} IP và gửi email cho khách hàng"]); } public function revocationPreview(Request $request, IpAllocationService $allocation): JsonResponse { $data = $this->validateRevocation($request); return response()->json(['error' => false, 'data' => $allocation->previewRevocation($data['ids'], $data)]); } public function revoke(Request $request, IpAllocationService $allocation): JsonResponse { $data = $this->validateRevocation($request, withRecipients: true); $recipients = collect($data['recipients'])->keyBy('key')->all(); $count = $allocation->revoke($data['ids'], $data, $recipients, $request->user('admin')); return response()->json(['error' => false, 'message' => "Đã thu hồi {$count} IP và gửi email thông báo"]); } public function release(Request $request, IpReleaseService $release): JsonResponse { $data = $request->validate([ 'ids' => 'required|array|min:1|max:1000', 'ids.*' => 'integer', 'note' => 'nullable|string|max:2000', ]); $count = $release->release($data['ids'], $data['note'] ?? null, $request->user('admin')); return response()->json(['error' => false, 'message' => "Đã giải phóng {$count} IP khỏi dịch vụ/VM"]); } public function logs(int $id): JsonResponse { $pool = IpPool::withTrashed()->findOrFail($id); return response()->json([ 'error' => false, 'data' => $pool->logs()->with('user:id,username,fullname')->limit(200)->get(), ]); } public function allLogs(Request $request): JsonResponse { $data = $request->validate([ 'filter' => 'nullable|string|max:255', 'action' => 'nullable|array', 'action.*' => 'in:' . implode(',', [IpPoolLog::ALLOCATED, IpPoolLog::REVOKED, IpPoolLog::VM_ASSIGNED, IpPoolLog::VM_RELEASED]), 'date_from' => 'nullable|date', 'date_to' => 'nullable|date', 'per_page' => 'nullable|integer|min:1|max:100', ]); $paginator = IpPoolLog::with('user:id,username,fullname') ->when($data['filter'] ?? null, function ($query, string $filter) { $like = '%' . addcslashes($filter, '%_\\') . '%'; $query->where(fn ($q) => $q->where('ip_address', 'like', $like) ->orWhere('customer_name', 'like', $like) ->orWhere('service_name', 'like', $like) ->orWhere('vm_name', 'like', $like) ->orWhere('email_to', 'like', $like) ->orWhere('email_cc', 'like', $like) ->orWhere('email_bcc', 'like', $like)); }) ->when($data['action'] ?? null, fn ($query, array $actions) => $query->whereIn('action', $actions)) ->when($data['date_from'] ?? null, fn ($query, string $from) => $query->whereDate('created_at', '>=', $from)) ->when($data['date_to'] ?? null, fn ($query, string $to) => $query->whereDate('created_at', '<=', $to)) ->latest('id') ->paginate($data['per_page'] ?? 20); return response()->json([ 'error' => false, 'data' => [ 'items' => $paginator->items(), 'total' => $paginator->total(), 'per_page' => $paginator->perPage(), 'current_page' => $paginator->currentPage(), ], ]); } private function validateAllocation(Request $request): array { return $request->validate([ 'ids' => 'required|array|min:1|max:1000', 'ids.*' => 'integer', 'customer_uuid' => 'required|string|exists:customers,uuid', 'to' => ['required', 'string', 'max:1000', $this->multiEmailRule()], 'cc' => ['nullable', 'string', 'max:1000', $this->multiEmailRule()], 'date' => 'required|date', 'service_name' => 'nullable|string|max:255', 'services' => 'nullable|array', 'services.*' => 'nullable|string|max:255', 'note' => 'nullable|string|max:2000', 'subject' => 'nullable|string|max:255', ...MailAttachments::rules(), ]); } private function validateRevocation(Request $request, bool $withRecipients = false): array { return $request->validate([ 'ids' => 'required|array|min:1|max:1000', 'ids.*' => 'integer', 'date' => 'required|date', 'note' => 'nullable|string|max:2000', 'recipients' => $withRecipients ? 'required|array|min:1' : 'prohibited', 'recipients.*.key' => 'required|string', 'recipients.*.to' => ['nullable', 'string', 'max:1000', $this->multiEmailRule()], 'recipients.*.cc' => ['nullable', 'string', 'max:1000', $this->multiEmailRule()], 'recipients.*.subject' => 'nullable|string|max:255', ]); } private function allocationCustomer(array $data): Customer { return Customer::where('uuid', $data['customer_uuid'])->firstOrFail(); } }