From ed5cfcd275a4b0d83fec7cdfcfff7788e1bad832 Mon Sep 17 00:00:00 2001 From: yann64 Date: Fri, 5 Jun 2026 07:17:32 +0200 Subject: [PATCH] Fix wizard : suppression totale des sous-processus exec() dans install() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - APP_KEY : généré directement en PHP (random_bytes) + écrit dans .env + propagé via config() et putenv() → évite le bug de pattern-matching de key:generate (la clé en mémoire ≠ clé dans le .env réécrit par writeEnv) - DB_* + APP_KEY : putenv() écrase l'env OS hérité au boot (pgsql/temp-key) pour que tout sous-processus futur hérite des bonnes valeurs - optimize supprimé de l'installation : config:cache re-boostrappe l'app via bootstrap/app.php dans un contexte où l'Encrypter peut lever MissingAppKeyException ; optimize:clear seul suffit — Laravel reconstruit ses caches à la première requête - key:generate converti en Artisan::call() puis remplacé par génération PHP directe Co-Authored-By: Claude Sonnet 4.6 --- app/Http/Controllers/SetupController.php | 62 ++++++++++++++++-------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php index 042d415..8e84465 100644 --- a/app/Http/Controllers/SetupController.php +++ b/app/Http/Controllers/SetupController.php @@ -128,8 +128,6 @@ class SetupController extends Controller $steps = []; $success = true; - $php = $this->phpBinary(); - $artisan = $php . ' ' . escapeshellarg(base_path('artisan')); // 1. Écriture du .env try { @@ -140,24 +138,44 @@ class SetupController extends Controller $success = false; } - // 2. Génération de la clé APP_KEY + // 2. Génération de la clé APP_KEY — directement en PHP, sans passer par key:generate. + // + // Artisan key:generate remplace APP_KEY= dans le .env grâce à un + // pattern regex. Mais writeEnv() vient d'écrire APP_KEY= (vide) alors qu'en mémoire + // la clé est celle de l'auto-création (TEMP_KEY) → le pattern ne matche pas → la + // clé reste vide dans le .env et la config:cache en hérite. + // Solution : générer la clé nous-mêmes, l'écrire directement dans le .env, et la + // propager en mémoire + env OS dès maintenant. + $appKey = null; if ($success) { - [$ok, $out] = $this->artisanRun($artisan, 'key:generate --force'); - $steps[] = ['ok' => $ok, 'label' => 'Génération de la clé de chiffrement (APP_KEY)', 'error' => $ok ? null : $out]; - if (! $ok) $success = false; + try { + $appKey = 'base64:' . base64_encode(random_bytes(32)); + $envPath = base_path('.env'); + $env = file_get_contents($envPath); + $env = preg_replace('/^APP_KEY=.*/m', 'APP_KEY=' . $appKey, $env); + file_put_contents($envPath, $env); + config(['app.key' => $appKey]); + $steps[] = ['ok' => true, 'label' => 'Génération de la clé de chiffrement (APP_KEY)']; + } catch (\Exception $e) { + $steps[] = ['ok' => false, 'label' => 'Génération de la clé de chiffrement (APP_KEY)', 'error' => $e->getMessage()]; + $success = false; + } } - // 2b. Reconfiguration de la connexion BDD dans le processus courant. + // 2b. Reconfiguration de la connexion BDD — processus courant ET sous-processus. // - // Problème : public/index.php charge le .env auto-créé (pgsql) au premier boot - // et appelle putenv('DB_CONNECTION=pgsql'). Les sous-processus exec() héritent - // cet env OS. phpdotenv en mode immutable (défaut Laravel) refuse d'écraser une - // variable déjà présente dans l'env → le nouveau .env (mysql) est ignoré par le - // subprocess de migration qui continue à tenter une connexion pgsql. - // - // Solution : exécuter les migrations via Artisan::call() dans le processus courant - // après avoir écrasé la config BDD en mémoire — pas de subprocess, pas d'héritage. + // putenv() écrase l'env OS hérité au boot (pgsql + TEMP_KEY) pour que tous les + // sous-processus futurs (config:cache interne à optimize…) reçoivent les bonnes + // valeurs. config() + DB::purge() reconfigure le processus courant en mémoire. if ($success) { + putenv("APP_KEY={$appKey}"); + putenv("DB_CONNECTION={$dbData['driver']}"); + putenv("DB_HOST={$dbData['host']}"); + putenv("DB_PORT={$dbData['port']}"); + putenv("DB_DATABASE={$dbData['database']}"); + putenv("DB_USERNAME={$dbData['username']}"); + putenv('DB_PASSWORD=' . ($dbData['password'] ?? '')); + $connConfig = $dbData['driver'] === 'pgsql' ? ['driver' => 'pgsql', 'host' => $dbData['host'], 'port' => (int) $dbData['port'], 'database' => $dbData['database'], 'username' => $dbData['username'], @@ -169,8 +187,8 @@ class SetupController extends Controller 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true]; config([ - 'database.default' => $dbData['driver'], - "database.connections.{$dbData['driver']}" => $connConfig, + 'database.default' => $dbData['driver'], + "database.connections.{$dbData['driver']}" => $connConfig, ]); DB::purge($dbData['driver']); @@ -214,10 +232,14 @@ class SetupController extends Controller } } - // 6. Optimisation des caches + // 6. Nettoyage des caches + // optimize:clear supprime tout cache résiduel (config, routes, vues, events). + // On n'appelle PAS optimize : config:cache re-boostrappe l'app depuis bootstrap/app.php + // dans un contexte qui peut ne pas avoir accès à notre APP_KEY via putenv, ce qui + // provoque MissingAppKeyException. Laravel reconstruit ses caches à la première + // requête — pas besoin de les préchauffer pendant l'installation. if ($success) { - $this->artisanRun($artisan, 'optimize:clear'); - $this->artisanRun($artisan, 'optimize'); + Artisan::call('optimize:clear'); } // 7. Marquage installation