Stabilize discovery workflow

This commit is contained in:
2026-05-29 17:16:46 +08:00
parent 15772bc17e
commit 3981ffdf5e
40 changed files with 2039 additions and 211 deletions
+96
View File
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Domovoy\Controllers;
use Domovoy\Repositories\DiscoveredHostRepository;
use Domovoy\Repositories\NetworkRangeRepository;
use Domovoy\Services\Inventory\DeviceService;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class DiscoveriesController
{
public function __construct(
private DiscoveredHostRepository $discoveredHostRepository,
private NetworkRangeRepository $networkRangeRepository,
private DeviceService $deviceService
) {
}
public function index(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$filters = $this->filters($request);
$ranges = $this->networkRangeRepository->findAll();
$findings = $this->discoveredHostRepository->findFiltered(
$filters['range_id'],
$filters['scan_job_id'],
$filters['status'],
200,
$filters['sort'],
$filters['dir']
);
ob_start();
$username = $_SESSION['username'] ?? 'User';
require dirname(__DIR__, 2) . '/templates/discoveries/index.php';
$body = ob_get_clean();
$response->getBody()->write($body);
return $response;
}
public function table(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$filters = $this->filters($request);
$findings = $this->discoveredHostRepository->findFiltered(
$filters['range_id'],
$filters['scan_job_id'],
$filters['status'],
200,
$filters['sort'],
$filters['dir']
);
ob_start();
require dirname(__DIR__, 2) . '/templates/discoveries/_table.php';
$body = ob_get_clean();
$response->getBody()->write($body);
return $response;
}
public function merge(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$data = $request->getParsedBody();
$hostId = isset($data['host_id']) ? (int)$data['host_id'] : 0;
$deviceId = isset($data['device_id']) ? (int)$data['device_id'] : 0;
if ($hostId > 0 && $deviceId > 0) {
$this->deviceService->mergeDiscoveredHost($hostId, $deviceId);
}
$referer = $request->getHeaderLine('Referer') ?: '/discoveries';
return $response
->withHeader('Location', $referer)
->withStatus(302);
}
/**
* @return array{range_id: ?int, scan_job_id: ?int, status: ?string, sort: string, dir: string}
*/
private function filters(ServerRequestInterface $request): array
{
$query = $request->getQueryParams();
$status = (string)($query['status'] ?? 'new');
$sort = (string)($query['sort'] ?? 'ip');
$dir = strtolower((string)($query['dir'] ?? 'asc')) === 'desc' ? 'desc' : 'asc';
return [
'range_id' => isset($query['range_id']) && $query['range_id'] !== '' ? (int)$query['range_id'] : null,
'scan_job_id' => isset($query['scan_job_id']) && $query['scan_job_id'] !== '' ? (int)$query['scan_job_id'] : null,
'status' => $status === 'all' ? null : $status,
'sort' => $sort,
'dir' => $dir,
];
}
}
+48 -1
View File
@@ -29,8 +29,8 @@ class DiscoveryController
ob_start();
$username = $_SESSION['username'] ?? 'User';
$ranges = $this->networkRangeRepository->findAll();
$activeJobs = $this->scanJobRepository->findActive();
$scanJobs = $this->scanJobRepository->findRecent(10);
$newHosts = $this->discoveredHostRepository->findByStatus('new', 20);
require dirname(__DIR__, 2) . '/templates/discovery/index.php';
$body = ob_get_clean();
$response->getBody()->write($body);
@@ -80,6 +80,53 @@ class DiscoveryController
->withStatus(302);
}
public function activeJobs(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$activeJobs = $this->scanJobRepository->findActive();
ob_start();
require dirname(__DIR__, 2) . '/templates/discovery/_active_jobs.php';
$body = ob_get_clean();
$response->getBody()->write($body);
return $response;
}
public function scanHistory(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$scanJobs = $this->scanJobRepository->findRecent(10);
ob_start();
require dirname(__DIR__, 2) . '/templates/discovery/_scan_history.php';
$body = ob_get_clean();
$response->getBody()->write($body);
return $response;
}
public function pauseJob(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
$job = $this->scanJobRepository->findById((int)$args['id']);
if ($job !== null && $job->status === 'running') {
$this->scanJobRepository->updateStatus($job->id, 'paused');
}
return $this->activeJobs($request, $response);
}
public function resumeJob(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
$job = $this->scanJobRepository->findById((int)$args['id']);
if ($job !== null && $job->status === 'paused') {
$this->scanJobRepository->updateStatus($job->id, 'pending');
}
return $this->activeJobs($request, $response);
}
public function cancelJob(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
{
$job = $this->scanJobRepository->findById((int)$args['id']);
if ($job !== null && in_array($job->status, ['pending', 'running', 'paused'], true)) {
$this->scanJobRepository->updateStatus($job->id, 'cancelled');
}
return $this->activeJobs($request, $response);
}
private function createScanJob(string $type, int $rangeId): void
{
$job = new \Domovoy\Models\ScanJob();
+31 -4
View File
@@ -78,17 +78,44 @@ class NetworkRangeController
private function isValidCidr(string $cidr): bool
{
if (!str_contains($cidr, '/')) {
if (!preg_match('#^(\d+\.\d+\.\d+\.\d+)/(\d{1,2})$#', $cidr, $matches)) {
return false;
}
[$ip, $mask] = explode('/', $cidr, 2);
$ip = $matches[1];
$maskInt = (int)$matches[2];
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return false;
}
$maskInt = (int)$mask;
return $maskInt >= 8 && $maskInt <= 32;
if ($maskInt < 8 || $maskInt > 32) {
return false;
}
$long = ip2long($ip);
if ($long === false) {
return false;
}
$value = (int)sprintf('%u', $long);
return $this->isInRange($value, '10.0.0.0', 8)
|| $this->isInRange($value, '172.16.0.0', 12)
|| $this->isInRange($value, '192.168.0.0', 16)
|| $this->isInRange($value, '169.254.0.0', 16)
|| $this->isInRange($value, '127.0.0.0', 8);
}
private function isInRange(int $ip, string $network, int $mask): bool
{
$networkLong = ip2long($network);
if ($networkLong === false) {
return false;
}
$networkValue = (int)sprintf('%u', $networkLong);
$maskValue = (0xFFFFFFFF << (32 - $mask)) & 0xFFFFFFFF;
return ($ip & $maskValue) === ($networkValue & $maskValue);
}
}