lthn.io/app/Website/Api/Boot.php
Claude 3f294340b2
feat: UpdateDnsRecords Action, Prometheus metrics, JSON validation
- UpdateDnsRecords Action: controller method now one-liner, all DNS
  logic in Action with activity logging and edit lock.
- Prometheus metrics at /v1/metrics: chain_height, alias_count,
  claims_pending, dns_tickets, gateways_live. Grafana-ready.
- ValidateJsonRequest middleware: enforces application/json on POST,
  64KB body size limit. Applied to all /v1/* API routes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 12:12:07 +01:00

71 lines
2.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Website\Api;
use Core\Events\DomainResolving;
use Core\Events\WebRoutesRegistering;
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
/**
* api.lthn.io — REST API.
*
* Registers all /v1/* API routes. In production, Traefik routes
* api.lthn.io traffic here. The honeypot null-routes API payloads
* sent to the web domain, so these routes MUST NOT be on lthn.io.
*
* DomainResolving handles the homepage (/) — only on api.lthn.io.
* API routes register unconditionally since Blesta connects via
* host.docker.internal in homelab.
*/
class Boot extends ServiceProvider
{
public static array $domains = [
'/^api\.lthn\.io$/',
'/^api\.lthn\.sh$/',
'/^api\.(test|localhost)$/',
];
public static array $listens = [
DomainResolving::class => 'onDomain',
WebRoutesRegistering::class => 'onWebRoutes',
];
public function register(): void {}
public function boot(): void
{
$this->loadViewsFrom(__DIR__ . '/Views', 'api');
foreach (static::$listens as $event => $method) {
Event::listen($event, [$this, $method]);
}
}
public function onDomain(DomainResolving $event): void
{
foreach (static::$domains as $pattern) {
if (preg_match($pattern, $event->domain)) {
$event->resolve($this);
return;
}
}
}
public function onWebRoutes(WebRoutesRegistering $event): void
{
// API routes — no CSRF, no session needed
$event->routes(fn () => Route::prefix('v1')
->withoutMiddleware(ValidateCsrfToken::class)
->middleware('json.validate')
->group(__DIR__ . '/Routes/api.php'));
// Homepage scoped to API domain via middleware
$event->routes(fn () => Route::middleware('domain:api.lthn.io,api.lthn.sh')
->group(__DIR__ . '/Routes/home.php'));
}
}