'required|string|in:A,AAAA,CNAME,TXT,MX,NS,SRV', 'name' => 'required|string|max:255', 'value' => 'required|string', 'ttl' => 'nullable|integer|min:60', 'priority' => 'nullable|integer|min:0', 'weight' => 'nullable|integer|min:0', 'port' => 'nullable|integer|min:0', 'service_name' => 'nullable|string|max:255', 'protocol' => 'nullable|string|max:50', ]; public function __construct( private readonly DnsDomainRepositoryInterface $domainRepository, private readonly DnsRecordRepositoryInterface $recordRepository, private readonly ServerService $serverService, ) {} // ─── Domains ───────────────────────────────────────────────────────────── public function index(Request $request): JsonResponse { $perPage = min((int) $request->get('per_page', 15), 500); $filter = $request->get('filter'); $status = $request->get('status'); $customerName = trim((string) $request->get('customer_name', '')); $query = $this->domainRepository->model ->with(['server', 'customer']) ->newQuery(); if ($filter) { $query->where('domain_name', 'like', "%{$filter}%"); } if ($status !== null && $status !== '') { $query->where('is_created', (int) $status); } if ($customerName !== '') { $query->whereHas('customer', function ($q) use ($customerName) { $q->where('fullname', 'like', "%{$customerName}%"); }); } $paginator = $query->orderByDesc('id')->paginate($perPage); $items = $paginator->getCollection()->map(fn($domain) => $this->formatDomain($domain)); return response()->json([ 'error' => false, 'data' => [ 'items' => $items, 'total' => $paginator->total(), 'per_page' => $paginator->perPage(), 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), ], ]); } public function show(int $id): JsonResponse { $domain = $this->domainRepository->model ->with(['server', 'customer']) ->find($id); if (!$domain) { return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$id"], 404); } return response()->json([ 'error' => false, 'data' => $this->formatDomain($domain), ]); } public function store(Request $request): JsonResponse { $request->validate([ 'domain_name' => 'required|string|max:255', 'dns_manager_server_id' => 'required|integer|exists:dns_manager_servers,id', 'customer_ids' => 'nullable|array', 'customer_ids.*' => 'integer|exists:customers,id', 'is_created' => 'nullable|integer|in:0,1,2', ]); DB::beginTransaction(); try { $domain = $this->domainRepository->create([ 'domain_name' => $request->domain_name, 'dns_manager_server_id' => $request->dns_manager_server_id, 'is_created' => $request->is_created ?? 0, ]); if (!empty($request->customer_ids)) { $domain->customer()->sync($request->customer_ids); } DB::commit(); return response()->json([ 'error' => false, 'data' => ['id' => $domain->id, 'domain_name' => $domain->domain_name], ], 201); } catch (\Exception $e) { DB::rollBack(); return response()->json(['error' => true, 'message' => $e->getMessage()], 500); } } public function update(int $id, Request $request): JsonResponse { $domain = $this->domainRepository->find($id); if (!$domain) { return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$id"], 404); } $request->validate([ 'domain_name' => 'sometimes|required|string|max:255', 'dns_manager_server_id' => 'nullable|integer|exists:dns_manager_servers,id', 'customer_ids' => 'nullable|array', 'customer_ids.*' => 'integer|exists:customers,id', 'is_created' => 'nullable|integer|in:0,1,2', ]); DB::beginTransaction(); try { $this->domainRepository->update($id, $request->only([ 'domain_name', 'dns_manager_server_id', 'is_created', ])); // Sync customers (empty array = detach all) if ($request->has('customer_ids')) { $domain->customer()->sync($request->customer_ids ?? []); } DB::commit(); return $this->show($id); } catch (\Exception $e) { DB::rollBack(); return response()->json(['error' => true, 'message' => $e->getMessage()], 500); } } public function destroy(int $id): JsonResponse { $domain = $this->domainRepository->find($id); if (!$domain) { return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$id"], 404); } DB::beginTransaction(); try { $domain->customer()->detach(); $domain->record()->delete(); $this->domainRepository->delete($id); DB::commit(); return response()->json(['error' => false, 'message' => 'Đã xoá domain thành công']); } catch (\Exception $e) { DB::rollBack(); return response()->json(['error' => true, 'message' => $e->getMessage()], 500); } } // ─── DNS Records ───────────────────────────────────────────────────────── // ─── Helpers ───────────────────────────────────────────────────────────── private function formatDomain($domain): array { return [ 'id' => $domain->id, 'domain_name' => $domain->domain_name, 'dns_manager_server_id' => $domain->dns_manager_server_id, 'server_name' => $domain->server?->name, 'customer_ids' => $domain->customer->pluck('id')->toArray(), 'customers' => $domain->customer->map(fn($c) => [ 'id' => $c->id, 'name' => $c->fullname, 'email' => $c->email, ])->values()->toArray(), 'is_created' => $domain->is_created, 'created_at' => $domain->created_at, 'updated_at' => $domain->updated_at, ]; } // ─── Sync from Plesk ───────────────────────────────────────────────────── public function syncDns(): JsonResponse { dispatch(new SyncDnsFromPlesk()); return response()->json([ 'error' => false, 'message' => 'Đã đưa lệnh đồng bộ DNS vào hàng đợi, quá trình sẽ chạy nền.', ]); } // ─── DNS Records ───────────────────────────────────────────────────────── public function dnsIndex(int $domainId): JsonResponse { $domain = $this->domainRepository->find($domainId); if (!$domain) { return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$domainId"], 404); } $records = $this->recordRepository->model ->where('dns_manager_domain_id', $domainId) ->orderBy('type') ->get(); return response()->json(['error' => false, 'data' => $records]); } public function dnsStore(int $domainId, Request $request): JsonResponse { $domain = $this->domainRepository->find($domainId); if (!$domain) { return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$domainId"], 404); } $validated = $request->validate(self::RECORD_RULES); if (!$this->serverService->addServerById($domain->dns_manager_server_id)) { return response()->json(['error' => true, 'message' => 'Không tìm thấy server DNS'], 404); } $result = $this->serverService ->handlePayload(['domain' => $domain->domain_name, ...$validated]) ->addRecord(); if ($result['error']) { return response()->json(['error' => true, 'message' => $result['message'] ?? 'Tạo bản ghi DNS trên Plesk thất bại'], 422); } $record = $this->recordRepository->create([ 'dns_manager_domain_id' => $domainId, 'type' => $validated['type'], 'type_valid' => $validated['type'], 'name' => $validated['name'], 'value' => $validated['value'], 'ttl' => $validated['ttl'] ?? 300, 'priority' => $validated['priority'] ?? null, 'weight' => $validated['weight'] ?? null, 'port' => $validated['port'] ?? null, 'service_name' => $validated['service_name'] ?? null, 'protocol' => $validated['protocol'] ?? null, 'plesk_id' => $result['data']['id'] ?? null, ]); return response()->json(['error' => false, 'data' => $record], 201); } public function dnsUpdate(int $domainId, int $recordId, Request $request): JsonResponse { $domain = $this->domainRepository->find($domainId); if (!$domain) { return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$domainId"], 404); } $record = $this->recordRepository->model ->where('dns_manager_domain_id', $domainId) ->find($recordId); if (!$record) { return response()->json(['error' => true, 'message' => "Không tìm thấy bản ghi DNS #$recordId"], 404); } $validated = $request->validate(self::RECORD_RULES); // ponytail: records created before Plesk sync was wired up have no plesk_id, update locally only if ($record->plesk_id) { if (!$this->serverService->addServerById($domain->dns_manager_server_id)) { return response()->json(['error' => true, 'message' => 'Không tìm thấy server DNS'], 404); } $result = $this->serverService ->handlePayload(['domain' => $domain->domain_name, ...$validated]) ->updateRecord($record->plesk_id); if ($result['error']) { return response()->json(['error' => true, 'message' => $result['message'] ?? 'Cập nhật bản ghi DNS trên Plesk thất bại'], 422); } } $this->recordRepository->update($recordId, [ 'type' => $validated['type'], 'type_valid' => $validated['type'], 'name' => $validated['name'], 'value' => $validated['value'], 'ttl' => $validated['ttl'] ?? 300, 'priority' => $validated['priority'] ?? null, 'weight' => $validated['weight'] ?? null, 'port' => $validated['port'] ?? null, 'service_name' => $validated['service_name'] ?? null, 'protocol' => $validated['protocol'] ?? null, ]); return response()->json(['error' => false, 'data' => $this->recordRepository->find($recordId)]); } public function dnsDestroy(int $domainId, int $recordId): JsonResponse { $domain = $this->domainRepository->find($domainId); if (!$domain) { return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$domainId"], 404); } $record = $this->recordRepository->model ->where('dns_manager_domain_id', $domainId) ->find($recordId); if (!$record) { return response()->json(['error' => true, 'message' => "Không tìm thấy bản ghi DNS #$recordId"], 404); } if ($record->plesk_id) { if (!$this->serverService->addServerById($domain->dns_manager_server_id)) { return response()->json(['error' => true, 'message' => 'Không tìm thấy server DNS'], 404); } $result = $this->serverService->deleteRecord($record->plesk_id); if ($result['error']) { return response()->json(['error' => true, 'message' => $result['message'] ?? 'Xoá bản ghi DNS trên Plesk thất bại'], 422); } } $this->recordRepository->delete($recordId); return response()->json(['error' => false, 'message' => 'Đã xoá bản ghi DNS']); } }