Blog / Organiser ses outils Laravel sur un unique sous-domaine

Image used for article Organiser ses outils Laravel sur un unique sous-domaine

Organiser ses outils Laravel sur un unique sous-domaine




TL;DR: Comment organiser les outils de maintenance tels que Laravel Pulse et Laravel Telescope sur un sous-domaine d’un projet Laravel.




Vous trouverez le code source via ce Github Repository. Découvrez-en plus sur CapsulesX ou Bluesky.




Avec les nombreux outils fournis par le framework Laravel, tels que Telescope ou plus récemment Pulse, il est devenu essentiel de les centraliser sur un même tableau de bord. Voici comment regrouper ces outils sur un sous-domaine dédié.




Initialement, une seule route est configurée dans notre projet Laravel vierge.



routes/web.php


<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;


Route::get( '/', fn() => Inertia::render( 'Welcome' ) );




La création d'un sous-domaine regroupant les outils souhaités ne nécessite que quelques lignes dans le fichier web.php. Pour cet article, il est essentiel d'ajouter des variables d'environnement supplémentaires dans le fichier .env ainsi que dans les fichiers de configuration avant de passer à la suite.



.env


APP_DOMAIN=article.test
APP_URL=http://${APP_DOMAIN}
TOOLS_DOMAIN=tools.${APP_DOMAIN}




De même, cela implique des modifications dans les fichiers de configuration associés, tels que app.php, ainsi que la création d'un nouveau fichier de configuration appelé tools.php.



config/app.php


...
	/*
    |--------------------------------------------------------------------------
    | Application Domain
    |--------------------------------------------------------------------------
    |
    | This value is the domain of your application. This value is used when the
    | framework needs to access the domain in routes.
    |
    */

    'domain' => env('APP_DOMAIN'),
...



config/tools.php


<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Tools Domain
    |--------------------------------------------------------------------------
    |
    | This value is the domain of your tools. This value is used when the
    | framework needs to access the domain in routes.
    |
    */

    'domain' => env('TOOLS_DOMAIN'),
];




Il est maintenant temps de configurer les routes associées à ces changements.



routes/web.php


<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;


Route::domain( config( 'tools.domain' ) )->group( function()
{
    Route::get( '/', fn() => Inertia::render( 'Tools' ) )->name( 'tools' );
});

Route::domain( config( 'app.domain' ) )->group( function()
{
    Route::get( '/', fn() => Inertia::render( 'App' ) )->name( 'app' );
});




La commande php artisan route:list offre une vue d'ensemble des routes créées, permettant ainsi de vérifier si tout semble en ordre.


php artisan route:list

GET|HEAD   tools.article.test/ ................................................. tools
GET|HEAD   article.test/ ......................................................... app
...




Pour afficher les routes, il est nécessaire de créer une page dédiée au domaine général et une autre pour le sous-domaine tools. En copiant la page par défaut avec une légère modification du titre, il sera possible de distinguer les deux pages. La page par défaut est la suivante :


<script setup>

import logotype from '/public/assets/capsules-logotype-red-blue-home.svg';

</script>

<template>

    <div class="w-full min-h-screen flex flex-col font-sans text-primary-black">

        <div class="grow mx-8 lg:mx-auto max-w-screen-lg overflow-auto flex flex-col items-center justify-center text-center">

            <img class="w-24 h-24 select-none" v-bind:src="logotype">

            <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

        </div>

    </div>

</template>



resources/js/pages/App.vue


...
<h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

<h2 class="mt-4 text-4xl font-bold select-none header-mode" v-text="'Application'" />
...








resources/js/pages/Tools.vue


...
<h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

<h2 class="mt-4 text-4xl font-bold select-none header-mode" v-text="'Tools'" />
...







Maintenant que l'accès au sous-domaine est établi, il est nécessaire de créer un menu affichant les différents onglets avant d'implémenter les outils. Ce menu peut se trouver à l'extrême gauche avec les boutons Pulse et Horizon, le contenu serait positionné à droite.



resources/js/pages/Tools.vue


<script setup>

import { Link } from '@inertiajs/vue3';

import logotype from '/public/assets/capsules-logotype-red-blue.svg';
import pulse from '/public/assets/tools/pulse.svg';
import telescope from '/public/assets/tools/telescope.svg';


const props = defineProps( { service : { type : String } } );

</script>

<template>

    <div class="w-full h-screen flex">

        <div class="p-4 h-full bg-white">

            <div class="space-y-8">

                <img class="h-8 w-8" v-bind:src="logotype">

                <div class="flex flex-col space-y-1">

                    <Link class="py-2 rounded-md" v-bind:class=" props.service === 'pulse' ? 'bg-slate-100' : 'hover:bg-slate-50' " href="/pulse" v-bind:replace="true" as="button"><img class="mx-2 h-4 w-4" v-bind:src="pulse"></link>

                    <Link class="py-2 rounded-md" v-bind:class=" props.service === 'telescope' ? 'bg-slate-100' : 'hover:bg-slate-50' " href="/telescope" v-bind:replace="true" as="button"><img class="mx-2 h-4 w-4" v-bind:src="telescope"></link>
                </div>

            </div>

        </div>

        <div class="grow overflow-auto">

            <div class="h-full flex">

                <div class="w-full flex flex-col items-center justify-center">

                    <img class="w-24 h-24 select-none" v-bind:src="logotype">

                    <h1 class="mt-4 text-6xl font-bold select-none header-mode" v-text="'Capsules Codes'" />

                    <h2 class="mt-4 text-4xl font-bold select-none header-mode" v-text=" props.service ? props.service : 'Tools'" />

                </div>

            </div>

        </div>

    </div>

</template>
  • Deux liens, sous forme de composants Link, sont maintenant disponibles pour accéder aux différents outils. Actuellement, la page affiche un écran d'accueil "Tools" lorsqu'aucun service n'est chargé.




Il est nécessaire d'implémenter deux routes dans le fichier web.php ainsi que dans le contrôleur ToolsController.php pour activer nos outils.



routes/web.php


use App\Http\Controllers\ToolsController;


Route::domain( config( 'tools.domain' ) )->group( function()
{
    Route::get( 'pulse', [ ToolsController::class, 'pulse' ] )->name( 'tools.pulse' );
    Route::get( 'telescope', [ ToolsController::class, 'telescope' ] )->name( 'tools.telescope' );

	Route::get( '{any}', fn() => redirect()->route( 'tools.pulse' ) )->where( 'any', '.*' );
});
  • Désormais, il n'est plus nécessaire de maintenir la route /. Une route de redirection a été mise en place pour rediriger vers l'outil de choix dans le cas où aucune URL ne correspond.



app/Http/Controllers/ToolsController.php


<?php

namespace App\Http\Controllers;

use Inertia\Inertia;
use Inertia\Response;


class ToolsController extends Controller
{
    public function pulse() : Response
    {
        return Inertia::render( 'Tools', [ 'service' => 'pulse' ] );
    }

    public function telescope() : Response
    {
        return Inertia::render( 'Tools', [ 'service' => 'telescope' ] );
    }
}








Il ne reste plus qu'à installer Pulse et Telescope. Pour cela, une base de données est nécessaire. Les instructions d'installation sont résumées plus bas.



.env


DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tools
DB_USERNAME=root
DB_PASSWORD=


Laravel Pulse 
> composer require laravel/pulse
> php artisan vendor:publish --provider="Laravel\\Pulse\\PulseServiceProvider"
> php artisan migrate

Laravel Telescope
> composer require laravel/telescope
> php artisan telescope:install
> php artisan migrate




Une fois ces outils installés, les chemins /pulse et /telescope donnent accès directement aux outils. Il faut donc les modifier, ce qui peut être réalisé depuis leurs fichiers de configuration respectifs. Il suffit alors de modifier les chemins dans le fichier .env.



.env


PULSE_PATH=pulse-custom-path
TELESCOPE_PATH=telescope-custom-path




Il faut maintenant injecter les chemins depuis le ToolsController dans le composant Tools. Ce dernier chargera ensuite la balise <iframe> avec l'URL donné.



app\Http\Controllers\ToolsController.php


<?php

namespace App\Http\Controllers;

use Inertia\Inertia;
use Inertia\Response;


class ToolsController extends Controller
{
    public function pulse() : Response
    {
        return Inertia::render( 'Tools', [ 'service' => 'pulse', 'url' => redirect()->to( config( 'pulse.path' ) )->getTargetUrl() ] );
    }

    public function telescope() : Response
    {
        return Inertia::render( 'Tools', [ 'service' => 'telescope', 'url' => redirect()->to( config( 'telescope.path' ) )->getTargetUrl() ] );
    }
}




resources/js/pages/Tools.vue


<script setup>

import { Link } from '@inertiajs/vue3';

import logotype from '/public/assets/capsules-logotype-red-blue.svg';
import pulse from '/public/assets/tools/pulse.svg';
import telescope from '/public/assets/tools/telescope.svg';


const props = defineProps( { service : { type : String, required : true }, url : { type : String, required : true } } );

</script>

<template>

    <div class="w-full h-screen flex">

        <div class="p-4 h-full bg-white">

            <div class="space-y-8">

                <img class="h-8 w-8" v-bind:src="logotype">

                <div class="flex flex-col space-y-1">

                    <Link class="py-2 rounded-md" v-bind:class=" props.service === 'pulse' ? 'bg-slate-100' : 'hover:bg-slate-50' " href="/pulse" v-bind:replace="true" as="button"><img class="mx-2 h-4 w-4" v-bind:src="pulse"></link>

                    <Link class="py-2 rounded-md" v-bind:class=" props.service === 'telescope' ? 'bg-slate-100' : 'hover:bg-slate-50' " href="/telescope" v-bind:replace="true" as="button"><img class="mx-2 h-4 w-4" v-bind:src="telescope"></link>
                </div>

            </div>

        </div>

        <div class="grow overflow-auto">

            <iframe class="w-full h-full" v-bind:src="props.url" />

        </div>

    </div>

</template>








Les outils sont maintenant accessibles via le menu latéral ! 🎉






Plusieurs informations supplémentaires destinées à étayer cet article :



  • Plusieurs éléments peuvent restreindre l'accès au sous-domaine. Par exemple, l'ajout d'un middleware auth.basic à une route déclenchera l'ouverture d'une fenêtre modale de connexion.
 Route::get( 'pulse', [ ToolsController::class, 'pulse' ] )->middleware( 'auth.basic' )->name( 'tools.pulse' );




  • Un autre example est l’implémentation d’un Gate auprès des différents ServiceProvider tel que le TelescopeServiceProvider.php vérifiant si l’utilisateur est connecté via Auth::check().
protected function gate() : void
{
    Gate::define( 'viewTelescope', fn() => Auth::check() );
}




  • Le cookie de session étant associé à son nom de domaine, pour accéder au cookie de session indépendamment du sous-domaine, il est nécessaire d'ajouter la variable d'environnement SESSION_DOMAIN au fichier .env, en associant le nom de domaine global avec un point . devant.
SESSION_DOMAIN=".article.test"




  • Certaines URL ne peuvent pas être incluses dans une balise <iframe> en raison de l'en-tête X-Frame-Options: SAMEORIGIN. Si vous en êtes le propriétaire, vous pouvez ajouter l'en-tête suivant à la directive Content-Security-Policy dans la configuration Nginx de votre outil intégrable pour valider l'origine : add_header Content-Security-Policy "frame-ancestors 'self' {website-url};". Si vous ne possédez pas l'URL, il est recommandé d'utiliser un lien externe.
public function google() : RedirectResponse
{
   return Redirect::away( "http://google.com" );
}




Ravi d’avoir pu aider !

v1.4.0

Icône XIcône Github