user()->isAdmin()) { abort(403); } $allSettings = Setting::all()->keyBy('key'); // Feature-Toggle-Settings $featureSettings = $allSettings->filter(fn ($s) => str_starts_with($s->key, 'feature_')); // Visibility-Settings $visibilitySettings = $allSettings->filter(fn ($s) => str_starts_with($s->key, 'visibility_')); // Mail-Konfiguration $mailConfig = [ 'mailer' => config('mail.default'), 'host' => config('mail.mailers.smtp.host'), 'port' => config('mail.mailers.smtp.port'), 'username' => config('mail.mailers.smtp.username'), 'password' => config('mail.mailers.smtp.password'), 'encryption' => config('mail.mailers.smtp.scheme', 'tls'), 'from_address' => config('mail.from.address'), 'from_name' => config('mail.from.name'), ]; // Lizenz & Support $supportService = app(SupportApiService::class); $isRegistered = $supportService->isRegistered(); $installationId = $isRegistered ? ($supportService->readInstalled()['installation_id'] ?? null) : null; $updateInfo = Cache::get('support.update_check'); // License-Key $licenseKey = $allSettings['license_key']->value ?? ''; // Aktivitätslog (letzte 100 Einträge) $recentLogs = ActivityLog::with('user')->latest('created_at')->limit(100)->get(); return view('admin.administration.index', compact( 'featureSettings', 'visibilitySettings', 'mailConfig', 'isRegistered', 'installationId', 'updateInfo', 'licenseKey', 'recentLogs' )); } public function updateFeatures(Request $request): RedirectResponse { if (!auth()->user()->isAdmin()) { abort(403); } $inputSettings = $request->input('settings', []); $oldValues = Setting::whereIn('key', array_keys($inputSettings))->pluck('value', 'key')->toArray(); foreach ($inputSettings as $key => $value) { // Nur feature_ und visibility_ Keys akzeptieren if (!str_starts_with($key, 'feature_') && !str_starts_with($key, 'visibility_')) { continue; } $setting = Setting::where('key', $key)->first(); if ($setting) { $setting->update(['value' => $value]); } else { $newSetting = new Setting([ 'label' => $key, 'type' => 'number', 'value' => $value, ]); $newSetting->key = $key; $newSetting->save(); } } Setting::clearCache(); $newValues = Setting::whereIn('key', array_keys($inputSettings))->pluck('value', 'key')->toArray(); ActivityLog::logWithChanges('updated', __('admin.features_saved'), 'Setting', null, $oldValues, $newValues); return back()->with('success', __('admin.features_saved')); } public function updateLicense(Request $request): RedirectResponse { if (!auth()->user()->isAdmin()) { abort(403); } $request->validate([ 'license_key' => ['nullable', 'string', 'max:255'], ]); $oldValue = Setting::get('license_key'); $newValue = strip_tags($request->input('license_key', '')); Setting::set('license_key', $newValue); if ($newValue && $newValue !== $oldValue) { $supportService = app(SupportApiService::class); $result = $supportService->validateLicense($newValue); if ($result && !($result['valid'] ?? false)) { session()->flash('warning', __('admin.license_invalid')); } } ActivityLog::logWithChanges('updated', __('admin.log_settings_updated'), 'Setting', null, ['license_key' => $oldValue], ['license_key' => $newValue]); return back()->with('success', __('admin.settings_saved')); } public function updateMail(Request $request): RedirectResponse { if (!auth()->user()->isAdmin()) { abort(403); } $mailer = $request->input('mail_mailer', 'log'); if ($mailer === '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', ]); $encryption = $request->input('mail_encryption'); $this->updateEnvValues([ 'MAIL_MAILER' => 'smtp', 'MAIL_HOST' => $request->input('mail_host'), 'MAIL_PORT' => $request->input('mail_port'), 'MAIL_USERNAME' => $request->input('mail_username'), 'MAIL_PASSWORD' => $request->input('mail_password'), 'MAIL_FROM_ADDRESS' => $request->input('mail_from_address'), 'MAIL_FROM_NAME' => $request->input('mail_from_name', config('app.name')), 'MAIL_SCHEME' => $encryption === 'none' ? '' : $encryption, ]); } else { $this->updateEnvValues([ 'MAIL_MAILER' => 'log', ]); } Artisan::call('config:clear'); return back()->with('success', __('admin.mail_saved'))->withFragment('mail'); } public function testMail(Request $request): \Illuminate\Http\JsonResponse { if (!auth()->user()->isAdmin()) { return response()->json(['success' => false, 'message' => 'Keine Berechtigung.'], 403); } $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' => __('admin.mail_test_success')]); } catch (\Throwable $e) { return response()->json(['success' => false, 'message' => $e->getMessage()]); } } public function destroyDemoData(Request $request): RedirectResponse { if (!auth()->user()->isAdmin()) { abort(403); } $request->validate([ 'password' => ['required', 'current_password'], ]); DB::table('activity_logs')->delete(); DB::table('comments')->delete(); DB::table('event_player_stats')->delete(); DB::table('event_carpool_passengers')->delete(); DB::table('event_carpools')->delete(); DB::table('event_participants')->delete(); DB::table('event_catering')->delete(); DB::table('event_timekeepers')->delete(); DB::table('event_faq')->delete(); DB::table('event_file')->delete(); DB::table('events')->delete(); DB::table('parent_player')->delete(); DB::table('players')->delete(); DB::table('team_user')->delete(); DB::table('team_file')->delete(); DB::table('teams')->delete(); DB::table('invitation_players')->delete(); DB::table('invitations')->delete(); DB::table('locations')->delete(); DB::table('faq')->delete(); DB::table('users')->where('id', '!=', auth()->id())->delete(); $files = DB::table('files')->get(); foreach ($files as $file) { Storage::disk('private')->delete($file->path); } DB::table('files')->delete(); $adminAvatar = auth()->user()->profile_picture; foreach (Storage::disk('public')->files('avatars') as $avatarFile) { if ($adminAvatar && str_contains($avatarFile, $adminAvatar)) { continue; } Storage::disk('public')->delete($avatarFile); } ActivityLog::log('deleted', __('admin.demo_data_deleted')); return redirect()->route('admin.administration.index') ->with('success', __('admin.demo_data_deleted')); } public function factoryReset(Request $request): RedirectResponse { if (!auth()->user()->isAdmin()) { abort(403); } $request->validate([ 'password' => ['required', 'current_password'], 'confirmation' => ['required', 'in:RESET-BESTÄTIGT'], ]); Storage::disk('private')->deleteDirectory('files'); Storage::disk('public')->deleteDirectory('avatars'); Storage::disk('public')->deleteDirectory('favicon'); Storage::disk('public')->deleteDirectory('logos'); Storage::disk('public')->deleteDirectory('dsgvo'); $driver = DB::getDriverName(); if ($driver === 'sqlite') { DB::statement('PRAGMA foreign_keys = OFF;'); } else { DB::statement('SET FOREIGN_KEY_CHECKS = 0;'); } $tables = [ 'activity_logs', 'comments', 'event_player_stats', 'event_carpool_passengers', 'event_carpools', 'event_participants', 'event_catering', 'event_timekeepers', 'event_faq', 'event_file', 'events', 'parent_player', 'players', 'team_user', 'team_file', 'teams', 'invitation_players', 'invitations', 'locations', 'faq', 'files', 'file_categories', 'settings', 'users', 'sessions', 'cache', 'cache_locks', ]; foreach ($tables as $table) { DB::table($table)->delete(); } if ($driver === 'sqlite') { DB::statement('PRAGMA foreign_keys = ON;'); } else { DB::statement('SET FOREIGN_KEY_CHECKS = 1;'); } $installedFile = storage_path('installed'); if (file_exists($installedFile)) { unlink($installedFile); } Artisan::call('cache:clear'); Artisan::call('config:clear'); Artisan::call('view:clear'); Artisan::call('route:clear'); auth()->logout(); request()->session()->invalidate(); request()->session()->regenerateToken(); return redirect()->route('install.requirements'); } private function updateEnvValues(array $values): void { $envPath = base_path('.env'); $envContent = file_get_contents($envPath); foreach ($values as $key => $value) { if ($value === '' || $value === null) { $replacement = "# {$key}="; } else { $quotedValue = str_contains($value, ' ') || str_contains($value, '#') ? '"' . str_replace('"', '\\"', $value) . '"' : $value; $replacement = "{$key}={$quotedValue}"; } if (preg_match("/^#?\s*{$key}=.*/m", $envContent)) { $envContent = preg_replace("/^#?\s*{$key}=.*/m", $replacement, $envContent); } else { $envContent .= "\n{$replacement}"; } } file_put_contents($envPath, $envContent); } }