Remplace l'édition directe du .env par des réglages en base de données
L'édition de fichier nécessitait que le process web ait un accès en écriture sur .env — problématique en prod (permissions, plusieurs instances). À la place : - Table `settings` (clé/valeur) + SettingsRegistry, une liste blanche explicite des réglages modifiables (mail, OnlyOffice, GLPI, nom/URL de l'app) mappés vers leur chemin de config Laravel. - Volontairement exclu : tout ce qui touche à l'infrastructure (APP_KEY, DB_*, SESSION_DRIVER, CACHE_STORE, QUEUE_CONNECTION...) — ces valeurs sont nécessaires avant même qu'une connexion BDD existe, donc une surcharge en base ne serait pas fiable et reste réservée au .env. - AppServiceProvider applique les surcharges au boot (cache 5 min, invalidé immédiatement à l'enregistrement) ; le reste du code continue de lire config() normalement, sans changement ailleurs. - Tant qu'aucun réglage n'est enregistré, le comportement reste identique au .env actuel — transition sans rupture. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
da589d0627
commit
c35833ed0b
@@ -133,7 +133,7 @@
|
||||
</a>
|
||||
<a href="{{ route('settings.env') }}"
|
||||
class="block rounded px-3 py-2 hover:bg-slate-800 {{ request()->routeIs('settings.env') ? 'bg-slate-800 text-white' : '' }}">
|
||||
Configuration (.env)
|
||||
Configuration
|
||||
</a>
|
||||
</div>
|
||||
@endrole
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<?php
|
||||
|
||||
use App\Services\EnvFileManager;
|
||||
use App\Models\Setting;
|
||||
use App\Services\SettingsRegistry;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Livewire\Component;
|
||||
|
||||
new class extends Component
|
||||
{
|
||||
/** @var array<int, array<string, mixed>> */
|
||||
public array $lines = [];
|
||||
|
||||
/** @var array<string, string> */
|
||||
public array $values = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->loadLines();
|
||||
$this->loadValues();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
@@ -26,38 +25,51 @@ new class extends Component
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
app(EnvFileManager::class)->update($this->values);
|
||||
} catch (\Throwable $e) {
|
||||
session()->flash('error', $e->getMessage());
|
||||
foreach (SettingsRegistry::configMap() as $envKey => $path) {
|
||||
$value = trim($this->values[$envKey] ?? '');
|
||||
|
||||
Setting::updateOrCreate(['key' => $envKey], ['value' => $value === '' ? null : $value]);
|
||||
}
|
||||
|
||||
Cache::forget(SettingsRegistry::CACHE_KEY);
|
||||
|
||||
session()->flash('status', 'Configuration enregistrée. Les nouvelles valeurs sont actives immédiatement.');
|
||||
|
||||
$this->loadValues();
|
||||
}
|
||||
|
||||
public function resetToDefault(string $envKey): void
|
||||
{
|
||||
if (! array_key_exists($envKey, SettingsRegistry::configMap())) {
|
||||
return;
|
||||
}
|
||||
|
||||
session()->flash('status', 'Configuration enregistrée. Les nouvelles valeurs seront actives dès la prochaine requête.');
|
||||
Setting::where('key', $envKey)->delete();
|
||||
Cache::forget(SettingsRegistry::CACHE_KEY);
|
||||
|
||||
$this->loadLines();
|
||||
session()->flash('status', "Valeur par défaut restaurée pour {$envKey}.");
|
||||
|
||||
$this->loadValues();
|
||||
}
|
||||
|
||||
protected function loadLines(): void
|
||||
protected function loadValues(): void
|
||||
{
|
||||
$this->lines = app(EnvFileManager::class)->read();
|
||||
$this->values = [];
|
||||
$stored = Setting::query()->pluck('value', 'key');
|
||||
|
||||
foreach ($this->lines as $line) {
|
||||
if ($line['type'] === 'var' && ! $line['protected']) {
|
||||
$this->values[$line['key']] = $line['value'];
|
||||
}
|
||||
foreach (SettingsRegistry::configMap() as $envKey => $path) {
|
||||
$this->values[$envKey] = $stored[$envKey] ?? (string) (config($path) ?? '');
|
||||
}
|
||||
}
|
||||
};
|
||||
?>
|
||||
|
||||
<div class="max-w-3xl">
|
||||
<h1 class="mb-2 text-2xl font-semibold text-slate-900">Configuration (.env)</h1>
|
||||
<h1 class="mb-2 text-2xl font-semibold text-slate-900">Configuration</h1>
|
||||
<p class="mb-6 text-sm text-slate-500">
|
||||
Modifie directement les variables d'environnement de l'application. Les changements
|
||||
prennent effet à la prochaine requête — aucun redémarrage n'est nécessaire.
|
||||
Ces réglages sont stockés en base de données et prennent effet immédiatement, sans
|
||||
redémarrage. Les paramètres d'infrastructure (base de données, clé de chiffrement,
|
||||
pilote de session…) ne sont pas modifiables ici — ils doivent être changés directement
|
||||
dans le fichier <code class="rounded bg-slate-100 px-1 py-0.5">.env</code> du serveur.
|
||||
</p>
|
||||
|
||||
@if (session('status'))
|
||||
@@ -66,60 +78,52 @@ new class extends Component
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('error'))
|
||||
<div class="mb-4 rounded border border-red-200 bg-red-50 px-4 py-2 text-sm text-red-700">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@error('values')
|
||||
<div class="mb-4 rounded border border-red-200 bg-red-50 px-4 py-2 text-sm text-red-700">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
|
||||
<form wire:submit="save" class="space-y-1 rounded border border-slate-200 bg-white p-6">
|
||||
@foreach ($lines as $line)
|
||||
@if ($line['type'] === 'comment')
|
||||
@if ($line['text'] !== '')
|
||||
<p class="mb-1 mt-5 text-xs font-semibold uppercase tracking-wide text-slate-400 first:mt-0">
|
||||
{{ $line['text'] }}
|
||||
</p>
|
||||
@endif
|
||||
@elseif ($line['type'] === 'var')
|
||||
<div class="flex items-center gap-3 py-1.5">
|
||||
<label for="env-{{ $line['key'] }}" class="w-56 shrink-0 truncate font-mono text-xs text-slate-600" title="{{ $line['key'] }}">
|
||||
{{ $line['key'] }}
|
||||
</label>
|
||||
<form wire:submit="save" class="space-y-8 rounded border border-slate-200 bg-white p-6">
|
||||
@foreach (\App\Services\SettingsRegistry::SETTINGS as $section => $keys)
|
||||
<div>
|
||||
<p class="mb-3 text-xs font-semibold uppercase tracking-wide text-slate-400">{{ $section }}</p>
|
||||
|
||||
@if ($line['protected'])
|
||||
<div class="flex flex-1 items-center gap-2">
|
||||
<input type="text" value="{{ $line['sensitive'] ? '••••••••••••' : $line['value'] }}" disabled
|
||||
class="w-full rounded border-slate-200 bg-slate-50 font-mono text-sm text-slate-400">
|
||||
<span class="shrink-0 rounded-full bg-slate-100 px-2 py-0.5 text-xs font-medium text-slate-500" title="Non modifiable ici — nécessite un accès direct au serveur">
|
||||
🔒 lecture seule
|
||||
</span>
|
||||
</div>
|
||||
@elseif ($line['sensitive'])
|
||||
<div x-data="{ show: false }" class="flex flex-1 items-center gap-2">
|
||||
<input :type="show ? 'text' : 'password'" id="env-{{ $line['key'] }}"
|
||||
wire:model="values.{{ $line['key'] }}" autocomplete="off"
|
||||
class="w-full rounded border-slate-300 font-mono text-sm shadow-sm focus:border-slate-500 focus:ring-slate-500">
|
||||
<button type="button" x-on:click="show = !show"
|
||||
class="shrink-0 text-xs text-slate-500 hover:text-slate-900">
|
||||
<span x-text="show ? 'Masquer' : 'Révéler'"></span>
|
||||
<div class="space-y-1.5">
|
||||
@foreach ($keys as $envKey => $meta)
|
||||
<div class="flex items-center gap-3 py-1">
|
||||
<label for="setting-{{ $envKey }}" class="w-48 shrink-0 text-sm text-slate-700">
|
||||
{{ $meta['label'] }}
|
||||
</label>
|
||||
|
||||
@if (\App\Services\SettingsRegistry::isSensitive($envKey))
|
||||
<div x-data="{ show: false }" class="flex flex-1 items-center gap-2">
|
||||
<input :type="show ? 'text' : 'password'" id="setting-{{ $envKey }}"
|
||||
wire:model="values.{{ $envKey }}" autocomplete="off"
|
||||
class="w-full rounded border-slate-300 font-mono text-sm shadow-sm focus:border-slate-500 focus:ring-slate-500">
|
||||
<button type="button" x-on:click="show = !show"
|
||||
class="shrink-0 text-xs text-slate-500 hover:text-slate-900">
|
||||
<span x-text="show ? 'Masquer' : 'Révéler'"></span>
|
||||
</button>
|
||||
</div>
|
||||
@else
|
||||
<input type="text" id="setting-{{ $envKey }}"
|
||||
wire:model="values.{{ $envKey }}"
|
||||
class="flex-1 rounded border-slate-300 font-mono text-sm shadow-sm focus:border-slate-500 focus:ring-slate-500">
|
||||
@endif
|
||||
|
||||
<button type="button" wire:click="resetToDefault('{{ $envKey }}')"
|
||||
wire:confirm="Revenir à la valeur par défaut de {{ $envKey }} (celle du .env) ?"
|
||||
class="shrink-0 text-xs text-slate-400 hover:text-slate-700">
|
||||
Réinitialiser
|
||||
</button>
|
||||
</div>
|
||||
@else
|
||||
<input type="text" id="env-{{ $line['key'] }}"
|
||||
wire:model="values.{{ $line['key'] }}"
|
||||
class="flex-1 rounded border-slate-300 font-mono text-sm shadow-sm focus:border-slate-500 focus:ring-slate-500">
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<div class="flex items-center gap-3 pt-5">
|
||||
<div class="flex items-center gap-3 border-t border-slate-100 pt-6">
|
||||
<button type="submit" wire:loading.attr="disabled" wire:target="save"
|
||||
class="inline-flex items-center gap-2 rounded bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-700 disabled:cursor-not-allowed disabled:opacity-60">
|
||||
<svg wire:loading wire:target="save" class="h-4 w-4 animate-spin" viewBox="0 0 24 24" fill="none">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<x-layouts.app title="Configuration (.env)">
|
||||
<x-layouts.app title="Configuration">
|
||||
<livewire:settings.env />
|
||||
</x-layouts.app>
|
||||
|
||||
Reference in New Issue
Block a user