diff --git a/app/Http/Middleware/DomainScope.php b/app/Http/Middleware/DomainScope.php new file mode 100644 index 0000000..e018028 --- /dev/null +++ b/app/Http/Middleware/DomainScope.php @@ -0,0 +1,45 @@ +group(...) + * Route::middleware('domain:docs.lthn.io,docs.test')->group(...) + */ +class DomainScope +{ + public function handle(Request $request, Closure $next, string ...$domains): mixed + { + $host = $request->getHost(); + + foreach ($domains as $domain) { + // Exact match + if ($host === $domain) { + return $next($request); + } + + // Wildcard match (e.g., *.lthn.io) + if (str_starts_with($domain, '*.')) { + $suffix = substr($domain, 1); // .lthn.io + if (str_ends_with($host, $suffix)) { + return $next($request); + } + } + } + + abort(404); + } +} diff --git a/app/Mod/Chain/Boot.php b/app/Mod/Chain/Boot.php index 4c27764..7e4d34b 100644 --- a/app/Mod/Chain/Boot.php +++ b/app/Mod/Chain/Boot.php @@ -31,6 +31,7 @@ class Boot app()->singleton(WalletRpc::class); app('router')->aliasMiddleware('auth.api', \App\Http\Middleware\ApiTokenAuth::class); + app('router')->aliasMiddleware('domain', \App\Http\Middleware\DomainScope::class); } public function onConsole(ConsoleBooting $event): void diff --git a/app/Website/Api/Boot.php b/app/Website/Api/Boot.php index 77d11f7..09742f6 100644 --- a/app/Website/Api/Boot.php +++ b/app/Website/Api/Boot.php @@ -63,13 +63,8 @@ class Boot extends ServiceProvider ->withoutMiddleware(ValidateCsrfToken::class) ->group(__DIR__ . '/Routes/api.php')); - // Homepage only on API domain - $host = $_SERVER['HTTP_HOST'] ?? ''; - foreach (static::$domains as $pattern) { - if (preg_match($pattern, $host)) { - $event->routes(fn () => Route::group([], __DIR__ . '/Routes/home.php')); - return; - } - } + // Homepage scoped to API domain via middleware + $event->routes(fn () => Route::middleware('domain:api.lthn.io,api.lthn.sh') + ->group(__DIR__ . '/Routes/home.php')); } } diff --git a/app/Website/Docs/Boot.php b/app/Website/Docs/Boot.php index b6b5b93..b58db4f 100644 --- a/app/Website/Docs/Boot.php +++ b/app/Website/Docs/Boot.php @@ -5,23 +5,20 @@ declare(strict_types=1); namespace Website\Docs; use Core\Events\DomainResolving; -use Core\Events\WebRoutesRegistering; use Illuminate\Support\Facades\Event; -use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; /** * docs.lthn.io — Lethean documentation site. * - * Renders markdown docs with sidebar navigation and search. - * On docs.lthn.io: serves at root. On lthn.io: not mounted - * (API docs live at /docs via the Lethean Website module). + * Routes disabled in shared app — catch-all /{section}/{page?} conflicts + * with lthn.io routes even with domain middleware (Laravel matches routes + * before middleware runs). Deploy as own container to enable. */ class Boot extends ServiceProvider { public static array $domains = [ '/^docs\.lthn\.io$/', - '/^docs\.(test|localhost)$/', ]; public static array $listens = [ @@ -48,8 +45,4 @@ class Boot extends ServiceProvider } } } - - // Routes disabled — docs.lthn.io catch-all /{section}/{page?} - // conflicts with other routes when registered unconditionally. - // Re-enable when Octane domain middleware is implemented. } diff --git a/app/Website/Explorer/Boot.php b/app/Website/Explorer/Boot.php index b19b8ef..7e4bc9f 100644 --- a/app/Website/Explorer/Boot.php +++ b/app/Website/Explorer/Boot.php @@ -10,9 +10,10 @@ use Illuminate\Support\ServiceProvider; /** * explorer.lthn.io — block explorer subdomain. - * Routes disabled — Mod/Explorer handles /explorer prefix on lthn.io. - * Subdomain routing re-enabled when explorer.lthn.io is deployed - * with proper Octane domain middleware. + * + * Root-level routes disabled to prevent conflict with Lethean homepage. + * Mod/Explorer handles /explorer prefix on lthn.io. + * When deployed to its own container, re-enable routes with domain middleware. */ class Boot extends ServiceProvider {