This commit is contained in:
baodev0
2026-07-25 15:18:48 +07:00
parent 306e65e503
commit 8a451e434e
9 changed files with 341 additions and 1929 deletions
@@ -3,17 +3,32 @@
namespace App\Http\Controllers\Api\Admin\DnsManager;
use App\Http\Controllers\Controller;
use App\Jobs\DnsManager\SyncDnsFromPlesk;
use App\Repositories\DnsManager\Domain\DnsDomainRepositoryInterface;
use App\Repositories\DnsManager\Record\DnsRecordRepositoryInterface;
use App\Services\ServerService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class DomainController extends Controller
{
private const RECORD_RULES = [
'type' => '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 ─────────────────────────────────────────────────────────────
@@ -192,6 +207,18 @@ class DomainController extends Controller
];
}
// ─── 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
@@ -218,21 +245,33 @@ class DomainController extends Controller
return response()->json(['error' => true, 'message' => "Không tìm thấy domain #$domainId"], 404);
}
$request->validate([
'type' => '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',
]);
$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' => $request->type,
'name' => $request->name,
'value' => $request->value,
'ttl' => $request->ttl ?? 300,
'priority' => $request->priority,
'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);
@@ -240,6 +279,12 @@ class DomainController extends Controller
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);
@@ -248,21 +293,47 @@ class DomainController extends Controller
return response()->json(['error' => true, 'message' => "Không tìm thấy bản ghi DNS #$recordId"], 404);
}
$request->validate([
'type' => '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',
]);
$validated = $request->validate(self::RECORD_RULES);
$this->recordRepository->update($recordId, $request->only(['type', 'name', 'value', 'ttl', 'priority']));
// 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);
@@ -271,6 +342,18 @@ class DomainController extends Controller
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']);
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\Admin\DnsManager;
use App\Http\Controllers\Controller;
use App\Repositories\DnsManager\Record\DnsRecordRepositoryInterface;
use App\Services\ServerService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -11,6 +12,7 @@ class RecordController extends Controller
{
public function __construct(
private readonly DnsRecordRepositoryInterface $recordRepository,
private readonly ServerService $serverService,
) {}
public function index(Request $request): JsonResponse
@@ -106,6 +108,23 @@ class RecordController extends Controller
'protocol' => $validated['protocol'] ?? null,
];
// ponytail: records created before Plesk sync was wired up have no plesk_id, update locally only
if ($record->plesk_id) {
$domain = $record->domain;
if (!$domain || !$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($id, $payload);
return $this->show($id);
@@ -7,6 +7,7 @@ use App\Repositories\DnsManager\Server\DnsServerRepositoryInterface;
use App\Services\ServerService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
class ServerController extends Controller
@@ -82,7 +83,7 @@ class ServerController extends Controller
'name' => $validated['name'],
'ip_address' => $validated['ip_address'],
'username' => $validated['username'],
'password' => encrypt($validated['password']),
'password' => Crypt::encryptString($validated['password']),
'api_key' => $validated['api_key'] ?? null,
'status' => $validated['status'] ?? 'activated',
]);
@@ -125,7 +126,7 @@ class ServerController extends Controller
->toArray();
if ($request->filled('password')) {
$payload['password'] = encrypt((string) $request->password);
$payload['password'] = Crypt::encryptString((string) $request->password);
}
$this->serverRepository->update($id, $payload);
@@ -161,12 +162,12 @@ class ServerController extends Controller
return response()->json(['error' => true, 'message' => "Không tìm thấy server #$id"], 404);
}
$connected = $this->serverService->checkConnectionByServer($server);
$meta = $this->serverService->checkConnectionByServer($server);
return response()->json([
'error' => !$connected,
'data' => ['connected' => $connected],
'message' => $connected
'error' => $meta === false,
'data' => ['connected' => $meta !== false, 'server' => $meta ?: null],
'message' => $meta !== false
? 'Kết nối server thành công.'
: 'Không thể kết nối tới server. Vui lòng kiểm tra lại IP hoặc tài khoản.',
]);
@@ -72,6 +72,42 @@ class MediaController extends Controller
]);
}
public function destroy(string $id): JsonResponse
{
$path = $this->resolvePathById($id);
if ($path === null) {
return response()->json(['error' => true, 'message' => 'File không tồn tại.'], 404);
}
Storage::disk('local')->delete($path);
return response()->json(['error' => false, 'message' => 'Xoá file thành công.']);
}
public function bulkDestroy(Request $request): JsonResponse
{
$ids = $request->validate(['ids' => ['required', 'array'], 'ids.*' => ['string']])['ids'];
$paths = collect($ids)
->map(fn (string $id) => $this->resolvePathById($id))
->filter()
->values();
Storage::disk('local')->delete($paths->all());
return response()->json([
'error' => false,
'message' => "Đã xoá {$paths->count()} file.",
]);
}
private function resolvePathById(string $id): ?string
{
return collect(Storage::disk('local')->allFiles())
->first(fn (string $path) => sha1($path) === $id);
}
private function formatFile(string $path): array
{
$disk = Storage::disk('local');