- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er) - Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link - Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest - Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen) - Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
687 lines
27 KiB
PHP
687 lines
27 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use App\Enums\UserRole;
|
||
use App\Models\Setting;
|
||
use App\Models\User;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Artisan;
|
||
use Illuminate\Support\Facades\Hash;
|
||
use Illuminate\Support\Facades\Log;
|
||
|
||
class InstallerController extends Controller
|
||
{
|
||
/**
|
||
* Check if app is already installed.
|
||
*/
|
||
public static function isInstalled(): bool
|
||
{
|
||
return file_exists(storage_path('installed'));
|
||
}
|
||
|
||
// ─── Step 1: System Requirements ───────────────────────
|
||
|
||
public function requirements()
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$checks = $this->runRequirementChecks();
|
||
|
||
return view('installer.steps.requirements', [
|
||
'currentStep' => 1,
|
||
'checks' => $checks,
|
||
'allPassed' => collect($checks)->where('required', true)->every(fn ($c) => $c['passed']),
|
||
]);
|
||
}
|
||
|
||
// ─── Step 2: Database ──────────────────────────────────
|
||
|
||
public function database()
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
return view('installer.steps.database', [
|
||
'currentStep' => 2,
|
||
'dbDriver' => old('db_driver', session('installer.db_driver', 'sqlite')),
|
||
]);
|
||
}
|
||
|
||
public function storeDatabase(Request $request)
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$driver = $request->input('db_driver', 'sqlite');
|
||
|
||
if ($driver === 'mysql') {
|
||
$request->validate([
|
||
'db_host' => 'required|string',
|
||
'db_port' => 'required|integer|min:1|max:65535',
|
||
'db_database' => 'required|string',
|
||
'db_username' => 'required|string',
|
||
'db_password' => 'nullable|string',
|
||
]);
|
||
|
||
// Test MySQL connection before writing config
|
||
$testResult = $this->testMysqlConnection(
|
||
$request->input('db_host'),
|
||
(int) $request->input('db_port'),
|
||
$request->input('db_database'),
|
||
$request->input('db_username'),
|
||
$request->input('db_password', ''),
|
||
);
|
||
|
||
if ($testResult !== true) {
|
||
Log::error('Installer: DB connection failed', ['error' => $testResult]);
|
||
return back()->withInput()
|
||
->with('error', 'Datenbankverbindung fehlgeschlagen. Bitte Zugangsdaten pruefen.');
|
||
}
|
||
}
|
||
|
||
// Write DB config to .env
|
||
$this->updateEnvValues($this->buildDbEnvValues($driver, $request));
|
||
|
||
// For SQLite: ensure database file exists with secure permissions
|
||
if ($driver === 'sqlite') {
|
||
$dbPath = database_path('database.sqlite');
|
||
if (! file_exists($dbPath)) {
|
||
touch($dbPath);
|
||
}
|
||
chmod($dbPath, 0640);
|
||
}
|
||
|
||
// Clear config cache so new .env values take effect
|
||
Artisan::call('config:clear');
|
||
|
||
// Set the runtime DB config for this request (since .env was just written)
|
||
if ($driver === 'sqlite') {
|
||
config([
|
||
'database.default' => 'sqlite',
|
||
'database.connections.sqlite.database' => database_path('database.sqlite'),
|
||
]);
|
||
} else {
|
||
config([
|
||
'database.default' => 'mysql',
|
||
'database.connections.mysql.host' => $request->input('db_host', '127.0.0.1'),
|
||
'database.connections.mysql.port' => $request->input('db_port', '3306'),
|
||
'database.connections.mysql.database' => $request->input('db_database'),
|
||
'database.connections.mysql.username' => $request->input('db_username'),
|
||
'database.connections.mysql.password' => $request->input('db_password', ''),
|
||
]);
|
||
}
|
||
|
||
// Run migrations
|
||
try {
|
||
Artisan::call('migrate', ['--force' => true]);
|
||
} catch (\Exception $e) {
|
||
Log::error('Installer: Migration failed', ['error' => $e->getMessage()]);
|
||
return back()->withInput()
|
||
->with('error', 'Migration fehlgeschlagen. Details im Laravel-Log.');
|
||
}
|
||
|
||
// Generate APP_KEY now (modifies .env — must happen before finalize)
|
||
if (empty(config('app.key')) || config('app.key') === 'base64:') {
|
||
Artisan::call('key:generate', ['--force' => true]);
|
||
}
|
||
|
||
// Store state in session
|
||
session(['installer.db_driver' => $driver]);
|
||
session(['installer.db_configured' => true]);
|
||
|
||
return redirect()->route('install.app');
|
||
}
|
||
|
||
// ─── Step 3: App Configuration ─────────────────────────
|
||
|
||
public function app()
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
if (! session('installer.db_configured')) {
|
||
return redirect()->route('install.database')
|
||
->with('error', 'Bitte zuerst die Datenbank konfigurieren.');
|
||
}
|
||
|
||
return view('installer.steps.app', [
|
||
'currentStep' => 3,
|
||
]);
|
||
}
|
||
|
||
public function storeApp(Request $request)
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$request->validate([
|
||
'app_name' => 'required|string|max:100',
|
||
'app_slogan' => 'nullable|string|max:255',
|
||
'app_url' => 'required|url',
|
||
'admin_name' => 'required|string|max:255',
|
||
'admin_email' => 'required|email|max:255',
|
||
'admin_password' => ['required', 'string', \Illuminate\Validation\Rules\Password::min(8)->letters()->numbers(), 'confirmed'],
|
||
]);
|
||
|
||
// Write APP_NAME + APP_URL to .env now (triggers dev-server restart —
|
||
// safe here because we redirect immediately after)
|
||
$appName = $request->input('app_name');
|
||
$this->updateEnvValues([
|
||
'APP_NAME' => '"' . str_replace('"', '\\"', $appName) . '"',
|
||
'APP_URL' => $request->input('app_url'),
|
||
]);
|
||
|
||
session([
|
||
'installer.app_name' => $appName,
|
||
'installer.app_slogan' => $request->input('app_slogan'),
|
||
'installer.app_url' => $request->input('app_url'),
|
||
'installer.admin_name' => $request->input('admin_name'),
|
||
'installer.admin_email' => $request->input('admin_email'),
|
||
// Passwort sofort hashen (nicht Klartext in Session speichern).
|
||
// Der 'hashed' Cast im User-Model erkennt via Hash::isHashed()
|
||
// dass der Wert bereits gehasht ist und hasht NICHT doppelt.
|
||
'installer.admin_password_hash' => Hash::make($request->input('admin_password')),
|
||
'installer.app_configured' => true,
|
||
]);
|
||
|
||
return redirect()->route('install.mail');
|
||
}
|
||
|
||
// ─── Step 4: E-Mail Configuration ───────────────────────
|
||
|
||
public function mail()
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
if (! session('installer.app_configured')) {
|
||
return redirect()->route('install.app')
|
||
->with('error', 'Bitte zuerst die App-Einstellungen konfigurieren.');
|
||
}
|
||
|
||
$defaults = $this->getDefaultPasswordResetTexts();
|
||
|
||
return view('installer.steps.mail', [
|
||
'currentStep' => 4,
|
||
'defaultPwResetDe' => $defaults['de'],
|
||
]);
|
||
}
|
||
|
||
public function storeMail(Request $request)
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$mailMode = $request->input('mail_mode', 'log');
|
||
|
||
if ($mailMode === 'smtp') {
|
||
$request->validate([
|
||
'mail_host' => 'required|string|max:255',
|
||
'mail_port' => 'required|integer|min:1|max:65535',
|
||
'mail_username' => 'required|string|max:255',
|
||
'mail_password' => 'required|string|max:255',
|
||
'mail_from_address' => 'required|email|max:255',
|
||
'mail_from_name' => 'nullable|string|max:255',
|
||
'mail_encryption' => 'required|in:tls,ssl,none',
|
||
]);
|
||
|
||
session([
|
||
'installer.mail_mode' => 'smtp',
|
||
'installer.mail_host' => $request->input('mail_host'),
|
||
'installer.mail_port' => $request->input('mail_port'),
|
||
'installer.mail_username' => $request->input('mail_username'),
|
||
'installer.mail_password' => $request->input('mail_password'),
|
||
'installer.mail_from_address' => $request->input('mail_from_address'),
|
||
'installer.mail_from_name' => $request->input('mail_from_name', ''),
|
||
'installer.mail_encryption' => $request->input('mail_encryption'),
|
||
]);
|
||
} else {
|
||
session(['installer.mail_mode' => 'log']);
|
||
}
|
||
|
||
session([
|
||
'installer.password_reset_email_de' => $request->input('password_reset_email_de', ''),
|
||
'installer.mail_configured' => true,
|
||
]);
|
||
|
||
return redirect()->route('install.finalize');
|
||
}
|
||
|
||
public function testMail(Request $request): \Illuminate\Http\JsonResponse
|
||
{
|
||
if (self::isInstalled()) {
|
||
return response()->json(['success' => false, 'message' => 'Bereits installiert.']);
|
||
}
|
||
|
||
$request->validate([
|
||
'mail_host' => 'required|string|max:255',
|
||
'mail_port' => 'required|integer|min:1|max:65535',
|
||
'mail_username' => 'required|string|max:255',
|
||
'mail_password' => 'required|string|max:255',
|
||
'mail_encryption' => 'required|in:tls,ssl,none',
|
||
]);
|
||
|
||
try {
|
||
$encryption = $request->input('mail_encryption');
|
||
$tls = ($encryption !== 'none');
|
||
$transport = new \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
|
||
$request->input('mail_host'),
|
||
(int) $request->input('mail_port'),
|
||
$tls,
|
||
);
|
||
$transport->setUsername($request->input('mail_username'));
|
||
$transport->setPassword($request->input('mail_password'));
|
||
$transport->start();
|
||
$transport->stop();
|
||
|
||
return response()->json(['success' => true, 'message' => 'SMTP-Verbindung erfolgreich!']);
|
||
} catch (\Throwable $e) {
|
||
return response()->json(['success' => false, 'message' => $e->getMessage()]);
|
||
}
|
||
}
|
||
|
||
// ─── Step 5: Finalize ──────────────────────────────────
|
||
|
||
public function finalize()
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
if (! session('installer.mail_configured')) {
|
||
return redirect()->route('install.mail')
|
||
->with('error', 'Bitte zuerst die E-Mail-Einstellungen konfigurieren.');
|
||
}
|
||
|
||
return view('installer.steps.finalize', [
|
||
'currentStep' => 5,
|
||
'appName' => session('installer.app_name'),
|
||
'appSlogan' => session('installer.app_slogan'),
|
||
'adminEmail' => session('installer.admin_email'),
|
||
'adminName' => session('installer.admin_name'),
|
||
'dbDriver' => session('installer.db_driver', 'sqlite'),
|
||
'installed' => false,
|
||
]);
|
||
}
|
||
|
||
public function storeFinalize(Request $request)
|
||
{
|
||
if (self::isInstalled()) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$installDemo = $request->boolean('install_demo');
|
||
|
||
// Pruefen ob alle Session-Daten vorhanden sind
|
||
$requiredSessionKeys = [
|
||
'installer.admin_email', 'installer.admin_name',
|
||
'installer.admin_password_hash', 'installer.app_name',
|
||
];
|
||
foreach ($requiredSessionKeys as $key) {
|
||
if (empty(session($key))) {
|
||
return back()->with('error', "Session-Daten verloren ('{$key}' fehlt). Bitte die Installation erneut ab Schritt 2 durchfuehren.");
|
||
}
|
||
}
|
||
|
||
// Datenbankverbindung sicherstellen (wurde in Schritt 2 konfiguriert via .env)
|
||
try {
|
||
\Illuminate\Support\Facades\DB::connection()->getPdo();
|
||
} catch (\Exception $e) {
|
||
return back()->with('error', 'Datenbankverbindung fehlgeschlagen: ' . $e->getMessage());
|
||
}
|
||
|
||
try {
|
||
$appName = session('installer.app_name');
|
||
|
||
// 1. Create admin user (guaranteed ID 1 on fresh DB)
|
||
$admin = User::updateOrCreate(
|
||
['email' => session('installer.admin_email')],
|
||
[
|
||
'name' => session('installer.admin_name'),
|
||
'password' => session('installer.admin_password_hash'),
|
||
]
|
||
);
|
||
$admin->is_active = true;
|
||
$admin->role = UserRole::Admin;
|
||
$admin->save();
|
||
|
||
// 2. Run required seeders (Settings + FileCategories)
|
||
Artisan::call('db:seed', [
|
||
'--class' => 'Database\\Seeders\\SettingsSeeder',
|
||
'--force' => true,
|
||
]);
|
||
Artisan::call('db:seed', [
|
||
'--class' => 'Database\\Seeders\\FileCategorySeeder',
|
||
'--force' => true,
|
||
]);
|
||
|
||
// 3. Override settings with installer values
|
||
Setting::set('app_name', $appName);
|
||
$slogan = session('installer.app_slogan');
|
||
if ($slogan) {
|
||
Setting::set('app_slogan', '<p><em>' . e($slogan) . '</em></p>');
|
||
}
|
||
|
||
// 4. Mail-Konfiguration in .env schreiben
|
||
$mailMode = session('installer.mail_mode', 'log');
|
||
if ($mailMode === 'smtp') {
|
||
$mailEncryption = session('installer.mail_encryption', 'tls');
|
||
$this->updateEnvValues([
|
||
'MAIL_MAILER' => 'smtp',
|
||
'MAIL_HOST' => session('installer.mail_host'),
|
||
'MAIL_PORT' => session('installer.mail_port'),
|
||
'MAIL_USERNAME' => session('installer.mail_username'),
|
||
'MAIL_PASSWORD' => session('installer.mail_password'),
|
||
'MAIL_FROM_ADDRESS' => session('installer.mail_from_address'),
|
||
'MAIL_FROM_NAME' => session('installer.mail_from_name', $appName),
|
||
'MAIL_SCHEME' => $mailEncryption === 'none' ? '' : $mailEncryption,
|
||
]);
|
||
} else {
|
||
$this->updateEnvValues([
|
||
'MAIL_MAILER' => 'log',
|
||
]);
|
||
}
|
||
|
||
// 5. Passwort-Reset E-Mail-Texte setzen
|
||
$customDe = session('installer.password_reset_email_de', '');
|
||
$defaults = $this->getDefaultPasswordResetTexts();
|
||
|
||
// DE: Benutzer-Text aus Installer oder Default
|
||
$deText = (strip_tags($customDe) !== '') ? $customDe : $defaults['de'];
|
||
Setting::set('password_reset_email_de', $deText);
|
||
|
||
// Andere Sprachen: Default-Texte setzen
|
||
foreach (['en', 'pl', 'ru', 'ar', 'tr'] as $locale) {
|
||
Setting::set('password_reset_email_' . $locale, $defaults[$locale]);
|
||
}
|
||
|
||
// 6. Optionally run DemoDataSeeder
|
||
if ($installDemo) {
|
||
Artisan::call('db:seed', [
|
||
'--class' => 'Database\\Seeders\\DemoDataSeeder',
|
||
'--force' => true,
|
||
]);
|
||
}
|
||
|
||
// 7. Create storage symlink
|
||
try {
|
||
Artisan::call('storage:link');
|
||
} catch (\Exception $e) {
|
||
// May already exist
|
||
}
|
||
|
||
// 8. Clear all caches
|
||
Artisan::call('config:clear');
|
||
Artisan::call('cache:clear');
|
||
Artisan::call('view:clear');
|
||
Artisan::call('route:clear');
|
||
try {
|
||
Setting::clearCache();
|
||
} catch (\Exception $e) {
|
||
// Cache may already be cleared
|
||
}
|
||
|
||
// 9. Mark as installed
|
||
$installedPath = storage_path('installed');
|
||
file_put_contents($installedPath, json_encode([
|
||
'installed_at' => now()->toIso8601String(),
|
||
'version' => config('app.version'),
|
||
'php_version' => PHP_VERSION,
|
||
'db_driver' => session('installer.db_driver', 'sqlite'),
|
||
], JSON_PRETTY_PRINT));
|
||
chmod($installedPath, 0600);
|
||
|
||
// 9b. Opt-in registration with support backend
|
||
if ($request->boolean('register_installation')) {
|
||
try {
|
||
$supportService = app(\App\Services\SupportApiService::class);
|
||
$supportService->register([
|
||
'app_name' => $appName,
|
||
'app_url' => session('installer.app_url'),
|
||
'app_version' => config('app.version'),
|
||
'php_version' => PHP_VERSION,
|
||
'db_driver' => session('installer.db_driver', 'sqlite'),
|
||
'installed_at' => now()->toIso8601String(),
|
||
]);
|
||
} catch (\Exception $e) {
|
||
Log::warning('Installation registration failed: ' . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
// 9c. Store license key if provided
|
||
$licenseKey = $request->input('license_key');
|
||
if ($licenseKey) {
|
||
Setting::set('license_key', trim($licenseKey));
|
||
}
|
||
|
||
// 10. Store completion info in session, then clean up installer data
|
||
$completionData = [
|
||
'installed' => true,
|
||
'install_demo' => $installDemo,
|
||
'admin_email' => session('installer.admin_email'),
|
||
'admin_name' => session('installer.admin_name'),
|
||
];
|
||
|
||
session()->forget([
|
||
'installer.db_driver', 'installer.db_configured',
|
||
'installer.app_name', 'installer.app_slogan',
|
||
'installer.app_url', 'installer.admin_name',
|
||
'installer.admin_email', 'installer.admin_password_hash',
|
||
'installer.app_configured',
|
||
'installer.mail_mode', 'installer.mail_host',
|
||
'installer.mail_port', 'installer.mail_username',
|
||
'installer.mail_password', 'installer.mail_from_address',
|
||
'installer.mail_from_name', 'installer.mail_encryption',
|
||
'installer.password_reset_email_de', 'installer.mail_configured',
|
||
]);
|
||
|
||
session(['installer.completed' => $completionData]);
|
||
|
||
return redirect()->route('install.complete');
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error('Installer: Installation failed', [
|
||
'error' => $e->getMessage(),
|
||
'file' => $e->getFile() . ':' . $e->getLine(),
|
||
'trace' => $e->getTraceAsString(),
|
||
]);
|
||
|
||
// Waehrend der Installation ist kein Laravel-Log per FTP leicht zugaenglich.
|
||
// Daher zeigen wir die Fehlermeldung direkt an.
|
||
$errorDetail = $e->getMessage();
|
||
$errorFile = basename($e->getFile()) . ':' . $e->getLine();
|
||
|
||
return back()->with('error', "Installation fehlgeschlagen: {$errorDetail} (in {$errorFile})");
|
||
}
|
||
}
|
||
|
||
// ─── Completion Page ───────────────────────────────────
|
||
|
||
public function complete()
|
||
{
|
||
$data = session('installer.completed');
|
||
|
||
if (! $data) {
|
||
return redirect('/login');
|
||
}
|
||
|
||
// Clear the completion data so this page can't be revisited
|
||
session()->forget('installer.completed');
|
||
|
||
return view('installer.steps.finalize', [
|
||
'currentStep' => 5,
|
||
'installed' => true,
|
||
'installDemo' => $data['install_demo'] ?? false,
|
||
'adminEmail' => $data['admin_email'] ?? '',
|
||
'adminName' => $data['admin_name'] ?? '',
|
||
'appName' => null,
|
||
'dbDriver' => null,
|
||
]);
|
||
}
|
||
|
||
// ─── Private Helpers ───────────────────────────────────
|
||
|
||
private function runRequirementChecks(): array
|
||
{
|
||
$checks = [];
|
||
|
||
// PHP version
|
||
$checks[] = [
|
||
'name' => 'PHP Version >= 8.2',
|
||
'current' => PHP_VERSION,
|
||
'passed' => version_compare(PHP_VERSION, '8.2.0', '>='),
|
||
'required' => true,
|
||
];
|
||
|
||
// Required PHP extensions
|
||
foreach (['pdo', 'pdo_sqlite', 'mbstring', 'openssl', 'tokenizer', 'xml', 'ctype', 'fileinfo', 'dom'] as $ext) {
|
||
$checks[] = [
|
||
'name' => "PHP Extension: {$ext}",
|
||
'current' => extension_loaded($ext) ? 'Geladen' : 'Fehlt',
|
||
'passed' => extension_loaded($ext),
|
||
'required' => true,
|
||
];
|
||
}
|
||
|
||
// Optional: pdo_mysql
|
||
$checks[] = [
|
||
'name' => 'PHP Extension: pdo_mysql (nur für MySQL)',
|
||
'current' => extension_loaded('pdo_mysql') ? 'Geladen' : 'Fehlt',
|
||
'passed' => extension_loaded('pdo_mysql'),
|
||
'required' => false,
|
||
];
|
||
|
||
// Directory permissions
|
||
$dirs = [
|
||
'storage/' => storage_path(),
|
||
'storage/app/' => storage_path('app'),
|
||
'storage/framework/cache/' => storage_path('framework/cache'),
|
||
'storage/framework/sessions/' => storage_path('framework/sessions'),
|
||
'storage/framework/views/' => storage_path('framework/views'),
|
||
'storage/logs/' => storage_path('logs'),
|
||
'bootstrap/cache/' => base_path('bootstrap/cache'),
|
||
'database/' => database_path(),
|
||
];
|
||
foreach ($dirs as $label => $path) {
|
||
$checks[] = [
|
||
'name' => "Schreibberechtigung: {$label}",
|
||
'current' => is_writable($path) ? 'Schreibbar' : 'Nicht schreibbar',
|
||
'passed' => is_writable($path),
|
||
'required' => true,
|
||
];
|
||
}
|
||
|
||
// .env file
|
||
$checks[] = [
|
||
'name' => '.env Datei',
|
||
'current' => file_exists(base_path('.env')) ? 'Vorhanden' : 'Fehlt',
|
||
'passed' => file_exists(base_path('.env')),
|
||
'required' => true,
|
||
];
|
||
|
||
return $checks;
|
||
}
|
||
|
||
private function testMysqlConnection(string $host, int $port, string $database, string $username, string $password): true|string
|
||
{
|
||
try {
|
||
new \PDO(
|
||
"mysql:host={$host};port={$port};dbname={$database}",
|
||
$username,
|
||
$password,
|
||
[\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_TIMEOUT => 5]
|
||
);
|
||
|
||
return true;
|
||
} catch (\PDOException $e) {
|
||
return $e->getMessage();
|
||
}
|
||
}
|
||
|
||
private function buildDbEnvValues(string $driver, Request $request): array
|
||
{
|
||
if ($driver === 'sqlite') {
|
||
return [
|
||
'DB_CONNECTION' => 'sqlite',
|
||
'DB_HOST' => '',
|
||
'DB_PORT' => '',
|
||
'DB_DATABASE' => '',
|
||
'DB_USERNAME' => '',
|
||
'DB_PASSWORD' => '',
|
||
];
|
||
}
|
||
|
||
$password = $request->input('db_password', '');
|
||
|
||
return [
|
||
'DB_CONNECTION' => 'mysql',
|
||
'DB_HOST' => $request->input('db_host', '127.0.0.1'),
|
||
'DB_PORT' => $request->input('db_port', '3306'),
|
||
'DB_DATABASE' => $request->input('db_database'),
|
||
'DB_USERNAME' => $request->input('db_username'),
|
||
'DB_PASSWORD' => $password !== '' ? '"' . str_replace('"', '\\"', $password) . '"' : '',
|
||
];
|
||
}
|
||
|
||
private function getDefaultPasswordResetTexts(): array
|
||
{
|
||
return [
|
||
'de' => '<p>Hallo {name},</p><p>du hast eine Passwort-Zuruecksetzung fuer dein Konto bei {app_name} angefordert. Klicke auf den Button unten, um ein neues Passwort zu vergeben.</p><p>Falls du diese Anfrage nicht gestellt hast, kannst du diese E-Mail ignorieren.</p>',
|
||
'en' => '<p>Hello {name},</p><p>You requested a password reset for your account at {app_name}. Click the button below to set a new password.</p><p>If you did not request this, you can safely ignore this email.</p>',
|
||
'pl' => '<p>Witaj {name},</p><p>Otrzymalismy prosbe o zresetowanie hasla do Twojego konta w {app_name}. Kliknij przycisk ponizej, aby ustawic nowe haslo.</p><p>Jesli nie prosiles o zmiane hasla, zignoruj te wiadomosc.</p>',
|
||
'ru' => '<p>Здравствуйте {name},</p><p>Вы запросили сброс пароля для вашей учетной записи в {app_name}. Нажмите кнопку ниже, чтобы установить новый пароль.</p><p>Если вы не запрашивали сброс пароля, просто проигнорируйте это письмо.</p>',
|
||
'ar' => '<p>مرحباً {name}،</p><p>لقد تلقينا طلباً لإعادة تعيين كلمة المرور لحسابك في {app_name}. انقر على الزر أدناه لتعيين كلمة مرور جديدة.</p><p>إذا لم تطلب ذلك، يمكنك تجاهل هذا البريد الإلكتروني.</p>',
|
||
'tr' => '<p>Merhaba {name},</p><p>{app_name} hesabiniz icin sifre sifirlama talebinde bulundunuz. Yeni bir sifre belirlemek icin asagidaki butona tiklayin.</p><p>Bu talebi siz yapmadiysan, bu e-postayi goerurmezden gelebilirsiniz.</p>',
|
||
];
|
||
}
|
||
|
||
private function updateEnvValues(array $values): void
|
||
{
|
||
$envPath = base_path('.env');
|
||
$envContent = file_get_contents($envPath);
|
||
|
||
foreach ($values as $key => $value) {
|
||
// Empty values: comment out the line
|
||
if ($value === '' || $value === null) {
|
||
$pattern = "/^{$key}=.*/m";
|
||
if (preg_match($pattern, $envContent)) {
|
||
$envContent = preg_replace($pattern, "# {$key}=", $envContent);
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// Newline-Injection verhindern und Werte quoten (T07)
|
||
$value = str_replace(["\n", "\r", "\0"], '', $value);
|
||
if (!preg_match('/^".*"$/', $value)) {
|
||
$value = '"' . str_replace('"', '\\"', $value) . '"';
|
||
}
|
||
|
||
$replacement = "{$key}={$value}";
|
||
$pattern = "/^{$key}=.*/m";
|
||
|
||
if (preg_match($pattern, $envContent)) {
|
||
$envContent = preg_replace($pattern, $replacement, $envContent);
|
||
} else {
|
||
// Also check for commented-out version
|
||
$commentPattern = "/^#\s*{$key}=.*/m";
|
||
if (preg_match($commentPattern, $envContent)) {
|
||
$envContent = preg_replace($commentPattern, $replacement, $envContent);
|
||
} else {
|
||
$envContent .= "\n{$replacement}";
|
||
}
|
||
}
|
||
}
|
||
|
||
file_put_contents($envPath, $envContent);
|
||
}
|
||
}
|