Итерация 2: сканирование сети
- Phinx миграции: network_ranges, scan_jobs, discovered_hosts, audit_log - Models: NetworkRange, ScanJob, DiscoveredHost - Repositories с CRUD для всех сущностей - NetworkScanner: ping sweep + TCP connect + ARP table + reverse DNS - PingScanner, TcpPortScanner (curl_multi для параллелизма), ArpTableReader - HostFingerprintService: vendor detection + confidence scoring - ScanJobRunner + CLI worker (bin/run-scan-worker.php) - Discovery UI: управление диапазонами, запуск скана, таблица находок - NetworkRangeController с валидацией CIDR - Обновлён public/index.php с DI конфигурацией для новых сервисов
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domovoy\Services\Jobs;
|
||||
|
||||
use Domovoy\Models\ScanJob;
|
||||
use Domovoy\Repositories\ScanJobRepository;
|
||||
use Domovoy\Services\Discovery\NetworkScanner;
|
||||
|
||||
class ScanJobRunner
|
||||
{
|
||||
private ScanJobRepository $scanJobRepository;
|
||||
private NetworkScanner $networkScanner;
|
||||
|
||||
public function __construct(
|
||||
ScanJobRepository $scanJobRepository,
|
||||
NetworkScanner $networkScanner
|
||||
) {
|
||||
$this->scanJobRepository = $scanJobRepository;
|
||||
$this->networkScanner = $networkScanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single pending scan job.
|
||||
*/
|
||||
public function runNext(): ?ScanJob
|
||||
{
|
||||
$pending = $this->scanJobRepository->findPending();
|
||||
if (empty($pending)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$job = $pending[0];
|
||||
$this->execute($job);
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all pending scan jobs in a loop.
|
||||
*
|
||||
* @param callable|null $onProgress Called after each job: fn(ScanJob $job)
|
||||
*/
|
||||
public function runAll(?callable $onProgress = null): int
|
||||
{
|
||||
$count = 0;
|
||||
while (true) {
|
||||
$job = $this->runNext();
|
||||
if ($job === null) {
|
||||
break;
|
||||
}
|
||||
$count++;
|
||||
if ($onProgress !== null) {
|
||||
$onProgress($job);
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function execute(ScanJob $job): void
|
||||
{
|
||||
$job->status = 'running';
|
||||
$job->startedAt = new \DateTimeImmutable();
|
||||
$this->scanJobRepository->save($job);
|
||||
|
||||
try {
|
||||
switch ($job->type) {
|
||||
case 'network_discovery':
|
||||
$this->runNetworkDiscovery($job);
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException("Unknown scan job type: {$job->type}");
|
||||
}
|
||||
|
||||
$job->status = 'done';
|
||||
$job->finishedAt = new \DateTimeImmutable();
|
||||
} catch (\Throwable $e) {
|
||||
$job->status = 'failed';
|
||||
$job->errorMessage = $e->getMessage();
|
||||
$job->finishedAt = new \DateTimeImmutable();
|
||||
}
|
||||
|
||||
$this->scanJobRepository->save($job);
|
||||
}
|
||||
|
||||
private function runNetworkDiscovery(ScanJob $job): void
|
||||
{
|
||||
// For MVP, we scan all enabled network ranges if no specific range set
|
||||
// TODO: filter by specific range when network_range_id is set
|
||||
$settings = require dirname(__DIR__, 3) . '/../phinx.php';
|
||||
|
||||
// Use the ranges from database via NetworkRangeRepository
|
||||
// For now, this is handled by the controller that creates the job with a specific range
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user