Container.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\App;
  4. use App\Application\Admin\AllowlistController;
  5. use App\Application\Admin\AppSettingsController;
  6. use App\Application\Admin\AuditController;
  7. use App\Application\Admin\CategoriesController;
  8. use App\Application\Admin\ConfigController;
  9. use App\Application\Admin\ConsumersController;
  10. use App\Application\Admin\IpsController;
  11. use App\Application\Admin\JobsAdminController;
  12. use App\Application\Admin\MaintenanceController;
  13. use App\Application\Admin\ManualBlocksController;
  14. use App\Application\Admin\MeController;
  15. use App\Application\Admin\PoliciesController;
  16. use App\Application\Admin\ReportersController;
  17. use App\Application\Admin\StatsController;
  18. use App\Application\Admin\TokensController;
  19. use App\Application\Admin\UsersController;
  20. use App\Application\Auth\AuthController;
  21. use App\Application\Internal\JobsController;
  22. use App\Application\Jobs\CleanupAuditJob;
  23. use App\Application\Jobs\CleanupExpiredManualBlocksJob;
  24. use App\Application\Jobs\EnrichPendingJob;
  25. use App\Application\Jobs\RecomputeScoresJob;
  26. use App\Application\Jobs\RefreshGeoipJob;
  27. use App\Application\Jobs\TickJob;
  28. use App\Application\Public\BlocklistController;
  29. use App\Application\Public\DocsController;
  30. use App\Application\Public\ReportController;
  31. use App\Domain\Audit\AuditEmitter;
  32. use App\Domain\Auth\Role;
  33. use App\Domain\Auth\TokenHasher;
  34. use App\Domain\Auth\TokenIssuer;
  35. use App\Domain\Enrichment\EnrichmentService;
  36. use App\Domain\Reputation\BlocklistBuilder;
  37. use App\Domain\Reputation\EffectiveStatusService;
  38. use App\Domain\Reputation\PairScorer;
  39. use App\Domain\Settings\AppSettings;
  40. use App\Domain\Time\Clock;
  41. use App\Domain\Time\SystemClock;
  42. use App\Infrastructure\Allowlist\AllowlistRepository;
  43. use App\Infrastructure\Audit\AuditRepository;
  44. use App\Infrastructure\Audit\DbAuditEmitter;
  45. use App\Infrastructure\Auth\RoleMappingRepository;
  46. use App\Infrastructure\Auth\ServiceTokenBootstrap;
  47. use App\Infrastructure\Auth\TokenRepository;
  48. use App\Infrastructure\Auth\UserRepository;
  49. use App\Infrastructure\Category\CategoryRepository;
  50. use App\Infrastructure\Consumer\ConsumerRepository;
  51. use App\Infrastructure\Db\ConnectionFactory;
  52. use App\Infrastructure\Enrichment\Downloaders\DbipDownloader;
  53. use App\Infrastructure\Enrichment\Downloaders\GeoIpDownloader;
  54. use App\Infrastructure\Enrichment\Downloaders\IPinfoDownloader;
  55. use App\Infrastructure\Enrichment\Downloaders\MaxMindDownloader;
  56. use App\Infrastructure\Enrichment\IpinfoRecordAdapter;
  57. use App\Infrastructure\Enrichment\MaxMindRecordAdapter;
  58. use App\Infrastructure\Enrichment\MmdbEnrichmentService;
  59. use App\Infrastructure\Enrichment\RecordAdapter;
  60. use App\Infrastructure\Http\JsonErrorHandler;
  61. use App\Infrastructure\Http\Middleware\AuditContextMiddleware;
  62. use App\Infrastructure\Http\Middleware\ImpersonationMiddleware;
  63. use App\Infrastructure\Http\Middleware\InternalNetworkMiddleware;
  64. use App\Infrastructure\Http\Middleware\InternalTokenMiddleware;
  65. use App\Infrastructure\Http\Middleware\RateLimitMiddleware;
  66. use App\Infrastructure\Http\Middleware\TokenAuthenticationMiddleware;
  67. use App\Infrastructure\Http\RateLimiter;
  68. use App\Infrastructure\Jobs\JobLockRepository;
  69. use App\Infrastructure\Jobs\JobRegistry;
  70. use App\Infrastructure\Jobs\JobRunner;
  71. use App\Infrastructure\Jobs\JobRunRepository;
  72. use App\Infrastructure\ManualBlock\ManualBlockRepository;
  73. use App\Infrastructure\Policy\PolicyRepository;
  74. use App\Infrastructure\Reporter\ReporterRepository;
  75. use App\Infrastructure\Reputation\BlocklistCache;
  76. use App\Infrastructure\Reputation\CidrEvaluatorFactory;
  77. use App\Infrastructure\Reputation\DashboardStatsRepository;
  78. use App\Infrastructure\Reputation\IpEnrichmentRepository;
  79. use App\Infrastructure\Reputation\IpHistoryRepository;
  80. use App\Infrastructure\Reputation\IpScoreRepository;
  81. use App\Infrastructure\Reputation\ReportRepository;
  82. use App\Infrastructure\Settings\DbAppSettings;
  83. use function DI\autowire;
  84. use DI\ContainerBuilder;
  85. use function DI\factory;
  86. use Doctrine\DBAL\Connection;
  87. use GuzzleHttp\Client as GuzzleClient;
  88. use GuzzleHttp\ClientInterface as GuzzleClientInterface;
  89. use Monolog\Formatter\JsonFormatter;
  90. use Monolog\Handler\StreamHandler;
  91. use Monolog\Logger;
  92. use Psr\Container\ContainerInterface;
  93. use Psr\Http\Message\ResponseFactoryInterface;
  94. use Psr\Log\LoggerInterface;
  95. use Slim\Psr7\Factory\ResponseFactory;
  96. /**
  97. * Builds the api's DI container.
  98. *
  99. * M04 additions: ReporterRepository / ConsumerRepository / CategoryRepository /
  100. * ReportRepository / IpScoreRepository, the PairScorer + Clock pair, and the
  101. * RateLimiter singleton plus its middleware. The rate-limit settings flow
  102. * through `settings` so tests can override capacity/refill cleanly.
  103. */
  104. final class Container
  105. {
  106. /**
  107. * @param array<string, mixed>|null $settings Optional override (tests pass in fixtures).
  108. */
  109. public static function build(?array $settings = null): ContainerInterface
  110. {
  111. $settings ??= require __DIR__ . '/../../config/settings.php';
  112. $builder = new ContainerBuilder();
  113. $builder->useAutowiring(true);
  114. $builder->addDefinitions([
  115. 'settings' => $settings,
  116. 'settings.db' => $settings['db'],
  117. 'settings.ui_service_token' => $settings['ui_service_token'] ?? '',
  118. 'settings.app_env' => $settings['app_env'] ?? 'production',
  119. 'settings.log_level' => $settings['log_level'] ?? \Monolog\Level::Info,
  120. 'settings.oidc_default_role' => $settings['oidc_default_role'] ?? Role::Viewer,
  121. 'settings.score_hard_cutoff_days' => (int) ($settings['score_hard_cutoff_days'] ?? 365),
  122. 'settings.rate_limit_per_second' => (int) ($settings['rate_limit_per_second'] ?? 60),
  123. 'settings.internal_job_token' => (string) ($settings['internal_job_token'] ?? ''),
  124. 'settings.internal_cidr_allowlist' => (string) ($settings['internal_cidr_allowlist'] ?? ''),
  125. 'settings.api_docs_public' => (bool) ($settings['api_docs_public'] ?? false),
  126. 'settings.job_recompute_max_runtime_seconds' => (int) ($settings['job_recompute_max_runtime_seconds'] ?? 240),
  127. 'settings.job_recompute_max_rows_per_tick' => (int) ($settings['job_recompute_max_rows_per_tick'] ?? 5000),
  128. 'settings.job_audit_retention_days' => (int) ($settings['job_audit_retention_days'] ?? 180),
  129. 'settings.cidr_evaluator_ttl_seconds' => (int) ($settings['cidr_evaluator_ttl_seconds'] ?? 60),
  130. 'settings.blocklist_cache_ttl_seconds' => (int) ($settings['blocklist_cache_ttl_seconds'] ?? 30),
  131. 'settings.geoip' => $settings['geoip'] ?? [
  132. 'enabled' => true,
  133. 'provider' => 'dbip',
  134. 'country_db' => '/data/geoip/country.mmdb',
  135. 'asn_db' => '/data/geoip/asn.mmdb',
  136. 'maxmind_license_key' => '',
  137. 'ipinfo_token' => '',
  138. 'refresh_interval_days' => 7,
  139. ],
  140. ConnectionFactory::class => factory(static function (ContainerInterface $c): ConnectionFactory {
  141. /** @var array{driver: string, sqlite_path: string, mysql_host: string, mysql_port: int, mysql_database: string, mysql_username: string, mysql_password: string} $db */
  142. $db = $c->get('settings.db');
  143. return new ConnectionFactory($db);
  144. }),
  145. Connection::class => factory(static function (ContainerInterface $c): Connection {
  146. /** @var ConnectionFactory $factory */
  147. $factory = $c->get(ConnectionFactory::class);
  148. return $factory->create();
  149. }),
  150. LoggerInterface::class => factory(static function (ContainerInterface $c): LoggerInterface {
  151. $logger = new Logger('api');
  152. /** @var \Monolog\Level $level */
  153. $level = $c->get('settings.log_level');
  154. $handler = new StreamHandler('php://stdout', $level);
  155. $handler->setFormatter(new JsonFormatter());
  156. $logger->pushHandler($handler);
  157. $logger->pushProcessor(new \App\Infrastructure\Logging\SecretScrubbingProcessor());
  158. return $logger;
  159. }),
  160. ResponseFactoryInterface::class => autowire(ResponseFactory::class),
  161. Clock::class => autowire(SystemClock::class),
  162. TokenHasher::class => autowire(),
  163. TokenIssuer::class => autowire(),
  164. TokenRepository::class => autowire(),
  165. RoleMappingRepository::class => autowire(),
  166. UserRepository::class => autowire(),
  167. ReporterRepository::class => autowire(),
  168. ConsumerRepository::class => autowire(),
  169. CategoryRepository::class => autowire(),
  170. ReportRepository::class => autowire(),
  171. IpScoreRepository::class => autowire(),
  172. ManualBlockRepository::class => autowire(),
  173. AllowlistRepository::class => autowire(),
  174. PolicyRepository::class => autowire(),
  175. IpEnrichmentRepository::class => autowire(),
  176. IpHistoryRepository::class => autowire(),
  177. DashboardStatsRepository::class => autowire(),
  178. CidrEvaluatorFactory::class => factory(static function (ContainerInterface $c): CidrEvaluatorFactory {
  179. /** @var ManualBlockRepository $manual */
  180. $manual = $c->get(ManualBlockRepository::class);
  181. /** @var AllowlistRepository $allow */
  182. $allow = $c->get(AllowlistRepository::class);
  183. /** @var Clock $clock */
  184. $clock = $c->get(Clock::class);
  185. /** @var LoggerInterface $logger */
  186. $logger = $c->get(LoggerInterface::class);
  187. /** @var int $ttl */
  188. $ttl = $c->get('settings.cidr_evaluator_ttl_seconds');
  189. return new CidrEvaluatorFactory($manual, $allow, $clock, $logger, $ttl);
  190. }),
  191. EffectiveStatusService::class => autowire(),
  192. BlocklistBuilder::class => autowire(),
  193. BlocklistCache::class => factory(static function (ContainerInterface $c): BlocklistCache {
  194. /** @var BlocklistBuilder $builder */
  195. $builder = $c->get(BlocklistBuilder::class);
  196. /** @var Clock $clock */
  197. $clock = $c->get(Clock::class);
  198. /** @var int $ttl */
  199. $ttl = $c->get('settings.blocklist_cache_ttl_seconds');
  200. return new BlocklistCache($builder, $clock, $ttl);
  201. }),
  202. ServiceTokenBootstrap::class => autowire(),
  203. TokenAuthenticationMiddleware::class => autowire(),
  204. ImpersonationMiddleware::class => autowire(),
  205. AuditContextMiddleware::class => autowire(),
  206. \App\Infrastructure\Http\Middleware\RequestBodySizeLimitMiddleware::class => factory(static function (ContainerInterface $c): \App\Infrastructure\Http\Middleware\RequestBodySizeLimitMiddleware {
  207. /** @var ResponseFactoryInterface $rf */
  208. $rf = $c->get(ResponseFactoryInterface::class);
  209. // SEC_REVIEW F69: 256 KiB global cap. Per-endpoint
  210. // caps (e.g. the 4 KiB `metadata` cap on /report)
  211. // layer on top.
  212. return new \App\Infrastructure\Http\Middleware\RequestBodySizeLimitMiddleware($rf, 256 * 1024);
  213. }),
  214. AuditRepository::class => autowire(),
  215. AuditEmitter::class => autowire(DbAuditEmitter::class),
  216. AppSettings::class => autowire(DbAppSettings::class),
  217. PairScorer::class => factory(static function (ContainerInterface $c): PairScorer {
  218. /** @var ReportRepository $reports */
  219. $reports = $c->get(ReportRepository::class);
  220. /** @var CategoryRepository $categories */
  221. $categories = $c->get(CategoryRepository::class);
  222. /** @var Clock $clock */
  223. $clock = $c->get(Clock::class);
  224. /** @var int $cutoff */
  225. $cutoff = $c->get('settings.score_hard_cutoff_days');
  226. return new PairScorer($reports, $categories, $clock, $cutoff);
  227. }),
  228. RateLimiter::class => factory(static function (ContainerInterface $c): RateLimiter {
  229. /** @var Clock $clock */
  230. $clock = $c->get(Clock::class);
  231. /** @var int $perSecond */
  232. $perSecond = $c->get('settings.rate_limit_per_second');
  233. $perSecond = max(1, $perSecond);
  234. return new RateLimiter($clock, (float) $perSecond, (float) ($perSecond * 2));
  235. }),
  236. RateLimitMiddleware::class => autowire(),
  237. JobLockRepository::class => autowire(),
  238. JobRunRepository::class => autowire(),
  239. JobRunner::class => factory(static function (ContainerInterface $c): JobRunner {
  240. /** @var JobLockRepository $locks */
  241. $locks = $c->get(JobLockRepository::class);
  242. /** @var JobRunRepository $runs */
  243. $runs = $c->get(JobRunRepository::class);
  244. /** @var Clock $clock */
  245. $clock = $c->get(Clock::class);
  246. /** @var LoggerInterface $logger */
  247. $logger = $c->get(LoggerInterface::class);
  248. return new JobRunner($locks, $runs, $clock, $logger);
  249. }),
  250. RecomputeScoresJob::class => factory(static function (ContainerInterface $c): RecomputeScoresJob {
  251. /** @var ReportRepository $reports */
  252. $reports = $c->get(ReportRepository::class);
  253. /** @var IpScoreRepository $ipScores */
  254. $ipScores = $c->get(IpScoreRepository::class);
  255. /** @var PairScorer $scorer */
  256. $scorer = $c->get(PairScorer::class);
  257. /** @var int $maxRuntime */
  258. $maxRuntime = $c->get('settings.job_recompute_max_runtime_seconds');
  259. /** @var int $maxRows */
  260. $maxRows = $c->get('settings.job_recompute_max_rows_per_tick');
  261. return new RecomputeScoresJob($reports, $ipScores, $scorer, $maxRuntime, $maxRows);
  262. }),
  263. CleanupAuditJob::class => factory(static function (ContainerInterface $c): CleanupAuditJob {
  264. /** @var Connection $conn */
  265. $conn = $c->get(Connection::class);
  266. /** @var int $days */
  267. $days = $c->get('settings.job_audit_retention_days');
  268. return new CleanupAuditJob($conn, $days);
  269. }),
  270. CleanupExpiredManualBlocksJob::class => autowire(),
  271. GuzzleClientInterface::class => factory(static function (): GuzzleClientInterface {
  272. // SEC_REVIEW F50: lock the GeoIP downloader's HTTP client
  273. // down. `allow_redirects` is now a tight whitelist —
  274. // HTTPS-only, ≤3 redirects, no Referer header. The
  275. // PrivateHostGuardMiddleware refuses to dial loopback /
  276. // link-local / RFC1918 / metadata-service hosts even
  277. // after a redirect rewrites the URL.
  278. $stack = \GuzzleHttp\HandlerStack::create();
  279. $stack->push(\App\Infrastructure\Enrichment\Downloaders\PrivateHostGuardMiddleware::factory());
  280. return new GuzzleClient([
  281. 'handler' => $stack,
  282. 'connect_timeout' => 10,
  283. 'timeout' => 120,
  284. 'http_errors' => true,
  285. 'allow_redirects' => [
  286. 'max' => 3,
  287. 'protocols' => ['https'],
  288. 'strict' => true,
  289. 'referer' => false,
  290. 'track_redirects' => false,
  291. ],
  292. 'headers' => [
  293. 'User-Agent' => 'irdb-geoip-refresh/1.0',
  294. ],
  295. ]);
  296. }),
  297. RecordAdapter::class => factory(static function (ContainerInterface $c): RecordAdapter {
  298. /** @var array{provider: string} $g */
  299. $g = $c->get('settings.geoip');
  300. return $g['provider'] === 'ipinfo'
  301. ? new IpinfoRecordAdapter()
  302. : new MaxMindRecordAdapter();
  303. }),
  304. MmdbEnrichmentService::class => factory(static function (ContainerInterface $c): MmdbEnrichmentService {
  305. /** @var array{country_db: string, asn_db: string} $g */
  306. $g = $c->get('settings.geoip');
  307. /** @var RecordAdapter $adapter */
  308. $adapter = $c->get(RecordAdapter::class);
  309. /** @var Clock $clock */
  310. $clock = $c->get(Clock::class);
  311. /** @var LoggerInterface $logger */
  312. $logger = $c->get(LoggerInterface::class);
  313. return new MmdbEnrichmentService($g['country_db'], $g['asn_db'], $adapter, $clock, $logger);
  314. }),
  315. EnrichmentService::class => factory(static function (ContainerInterface $c): EnrichmentService {
  316. /** @var MmdbEnrichmentService $svc */
  317. $svc = $c->get(MmdbEnrichmentService::class);
  318. return $svc;
  319. }),
  320. GeoIpDownloader::class => factory(static function (ContainerInterface $c): GeoIpDownloader {
  321. /** @var array{provider: string, maxmind_license_key: string, ipinfo_token: string} $g */
  322. $g = $c->get('settings.geoip');
  323. /** @var GuzzleClientInterface $http */
  324. $http = $c->get(GuzzleClientInterface::class);
  325. /** @var Clock $clock */
  326. $clock = $c->get(Clock::class);
  327. return match ($g['provider']) {
  328. 'maxmind' => new MaxMindDownloader($http, $g['maxmind_license_key']),
  329. 'ipinfo' => new IPinfoDownloader($http, $g['ipinfo_token']),
  330. default => new DbipDownloader($http, $clock),
  331. };
  332. }),
  333. EnrichPendingJob::class => factory(static function (ContainerInterface $c): EnrichPendingJob {
  334. /** @var EnrichmentService $svc */
  335. $svc = $c->get(EnrichmentService::class);
  336. /** @var IpEnrichmentRepository $repo */
  337. $repo = $c->get(IpEnrichmentRepository::class);
  338. return new EnrichPendingJob($svc, $repo);
  339. }),
  340. RefreshGeoipJob::class => factory(static function (ContainerInterface $c): RefreshGeoipJob {
  341. /** @var GeoIpDownloader $downloader */
  342. $downloader = $c->get(GeoIpDownloader::class);
  343. /** @var MmdbEnrichmentService $svc */
  344. $svc = $c->get(MmdbEnrichmentService::class);
  345. /** @var IpEnrichmentRepository $repo */
  346. $repo = $c->get(IpEnrichmentRepository::class);
  347. /** @var array{country_db: string, asn_db: string, refresh_interval_days: int} $g */
  348. $g = $c->get('settings.geoip');
  349. return new RefreshGeoipJob(
  350. $downloader,
  351. $svc,
  352. $repo,
  353. $g['country_db'],
  354. $g['asn_db'],
  355. (int) $g['refresh_interval_days'],
  356. );
  357. }),
  358. TickJob::class => factory(static function (ContainerInterface $c): TickJob {
  359. /** @var JobRunner $runner */
  360. $runner = $c->get(JobRunner::class);
  361. /** @var JobRunRepository $runs */
  362. $runs = $c->get(JobRunRepository::class);
  363. // Closure indirection: TickJob iterates JobRegistry at
  364. // run() time, after the registry is fully populated. A
  365. // direct JobRegistry dependency would create a build-time
  366. // cycle (registry → tick → registry).
  367. $resolver = static fn (): array => $c->get(JobRegistry::class)->all();
  368. return new TickJob($resolver, $runner, $runs);
  369. }),
  370. JobRegistry::class => factory(static function (ContainerInterface $c): JobRegistry {
  371. $registry = new JobRegistry();
  372. /** @var RecomputeScoresJob $recompute */
  373. $recompute = $c->get(RecomputeScoresJob::class);
  374. /** @var CleanupAuditJob $cleanup */
  375. $cleanup = $c->get(CleanupAuditJob::class);
  376. /** @var CleanupExpiredManualBlocksJob $cleanupExpired */
  377. $cleanupExpired = $c->get(CleanupExpiredManualBlocksJob::class);
  378. /** @var EnrichPendingJob $enrich */
  379. $enrich = $c->get(EnrichPendingJob::class);
  380. /** @var RefreshGeoipJob $refresh */
  381. $refresh = $c->get(RefreshGeoipJob::class);
  382. /** @var TickJob $tick */
  383. $tick = $c->get(TickJob::class);
  384. $registry->register($recompute);
  385. $registry->register($cleanup);
  386. $registry->register($cleanupExpired);
  387. $registry->register($enrich);
  388. $registry->register($refresh);
  389. $registry->register($tick);
  390. return $registry;
  391. }),
  392. JobsController::class => autowire(),
  393. InternalNetworkMiddleware::class => factory(static function (ContainerInterface $c): InternalNetworkMiddleware {
  394. /** @var ResponseFactoryInterface $rf */
  395. $rf = $c->get(ResponseFactoryInterface::class);
  396. /** @var string $raw */
  397. $raw = $c->get('settings.internal_cidr_allowlist');
  398. return new InternalNetworkMiddleware($rf, InternalNetworkMiddleware::parseCidrList($raw));
  399. }),
  400. InternalTokenMiddleware::class => factory(static function (ContainerInterface $c): InternalTokenMiddleware {
  401. /** @var ResponseFactoryInterface $rf */
  402. $rf = $c->get(ResponseFactoryInterface::class);
  403. /** @var string $token */
  404. $token = $c->get('settings.internal_job_token');
  405. return new InternalTokenMiddleware($rf, $token);
  406. }),
  407. JsonErrorHandler::class => factory(static function (ContainerInterface $c): JsonErrorHandler {
  408. /** @var ResponseFactoryInterface $factory */
  409. $factory = $c->get(ResponseFactoryInterface::class);
  410. /** @var LoggerInterface $logger */
  411. $logger = $c->get(LoggerInterface::class);
  412. return new JsonErrorHandler(
  413. $factory,
  414. $logger,
  415. $c->get('settings.app_env') === 'development',
  416. );
  417. }),
  418. AuthController::class => factory(static function (ContainerInterface $c): AuthController {
  419. /** @var Role|null $role */
  420. $role = $c->get('settings.oidc_default_role');
  421. /** @var UserRepository $users */
  422. $users = $c->get(UserRepository::class);
  423. /** @var AuditEmitter $audit */
  424. $audit = $c->get(AuditEmitter::class);
  425. /** @var Connection $conn */
  426. $conn = $c->get(Connection::class);
  427. return new AuthController($users, $role ?? Role::Viewer, $audit, $conn);
  428. }),
  429. MeController::class => autowire(),
  430. ReportersController::class => autowire(),
  431. ConsumersController::class => autowire(),
  432. TokensController::class => autowire(),
  433. ReportController::class => autowire(),
  434. ManualBlocksController::class => autowire(),
  435. AllowlistController::class => autowire(),
  436. PoliciesController::class => autowire(),
  437. BlocklistController::class => autowire(),
  438. IpsController::class => autowire(),
  439. StatsController::class => autowire(),
  440. CategoriesController::class => autowire(),
  441. UsersController::class => autowire(),
  442. AuditController::class => autowire(),
  443. JobsAdminController::class => autowire(),
  444. MaintenanceController::class => autowire(),
  445. AppSettingsController::class => autowire(),
  446. ConfigController::class => factory(static function (ContainerInterface $c): ConfigController {
  447. /** @var array<string, mixed> $settings */
  448. $settings = $c->get('settings');
  449. return new ConfigController($settings);
  450. }),
  451. DocsController::class => factory(static function (): DocsController {
  452. return new DocsController(__DIR__ . '/../../public/openapi.yaml');
  453. }),
  454. ]);
  455. return $builder->build();
  456. }
  457. }