*/ public array $diagnostics = []; public function mount(): void { $this->fillForm(); $this->runDiagnostics(); } protected function fillForm(): void { $paths = $this->autoDetectPaths(); $this->data = [ 'alert_email_enabled' => $this->getSettingBool('alert_email_enabled'), 'alert_email_address' => $this->getSetting('alert_email_address', ''), 'alert_discord_enabled' => $this->getSettingBool('alert_discord_enabled'), 'alert_discord_webhook_url' => $this->getSetting('alert_discord_webhook_url', ''), 'discord_webhook_ranks' => json_decode($this->getSetting('discord_webhook_ranks', '[]'), true) ?? [], 'alert_emulator_enabled' => $this->getSettingBool('alert_emulator_enabled', true), 'alert_ddos_enabled' => $this->getSettingBool('alert_ddos_enabled', true), 'alert_ddos_threshold' => (int) $this->getSetting('alert_ddos_threshold', '100'), 'alert_ddos_auto_block' => $this->getSettingBool('alert_ddos_auto_block'), 'alert_errors_enabled' => $this->getSettingBool('alert_errors_enabled', true), 'alert_error_threshold' => $this->getSetting('alert_error_threshold', '10'), 'alert_min_severity' => $this->getSetting('alert_min_severity', AlertSeverity::ERROR->value), 'emulator_github_url' => $this->getSetting('emulator_github_url', ''), 'emulator_source_repo' => $this->getSetting('emulator_source_repo', ''), 'emulator_jar_direct_url' => $this->getSetting('emulator_jar_direct_url', ''), 'emulator_jar_path' => $this->getSetting('emulator_jar_path', $paths['emulator_jar_path']), 'emulator_source_path' => $this->getSetting('emulator_source_path', $paths['emulator_source_path']), 'emulator_service_name' => $this->getSetting('emulator_service_name', 'arcturus'), 'emulator_github_branch' => $this->getSetting('emulator_github_branch', 'main'), 'emulator_database_host' => $this->getSetting('emulator_database_host', '127.0.0.1'), 'emulator_database_port' => $this->getSetting('emulator_database_port', '3306'), 'emulator_database_name' => $this->getSetting('emulator_database_name', ''), 'emulator_database_username' => $this->getSetting('emulator_database_username', ''), 'emulator_database_password' => $this->getSetting('emulator_database_password', ''), 'emulator_version' => $this->getSetting('emulator_version', 'Onbekend'), 'auto_update_enabled' => $this->getSettingBool('auto_update_enabled'), 'auto_update_schedule' => $this->getSetting('auto_update_schedule', '03:00'), 'auto_update_days' => $this->getSetting('auto_update_days', '0,6'), 'nitro_client_path' => $this->getSetting('nitro_client_path', $paths['nitro_client_path']), 'nitro_renderer_path' => $this->getSetting('nitro_renderer_path', $paths['nitro_renderer_path']), 'nitro_build_path' => $this->getSetting('nitro_build_path', $paths['nitro_build_path']), 'nitro_webroot' => $this->getSetting('nitro_webroot', $paths['nitro_webroot']), 'gamedata_path' => $this->getSetting('gamedata_path', $paths['gamedata_path']), 'nitro_github_branch' => $this->getSetting('nitro_github_branch', 'main'), 'nitro_github_url' => $this->getSetting('nitro_github_url', ''), 'nitro_site_url' => $this->getSetting('nitro_site_url', $this->getCurrentSiteUrl()), 'nitro_auto_update_configs' => $this->getSettingBool('nitro_auto_update_configs'), 'nitro_auto_update_enabled' => $this->getSettingBool('nitro_auto_update_enabled'), 'nitro_auto_update_schedule' => $this->getSetting('nitro_auto_update_schedule', '03:00'), 'nitro_auto_update_days' => $this->getSetting('nitro_auto_update_days', '0,6'), 'hotel_alert_message' => '', ]; } public function form(Schema $schema): Schema { return $schema ->components([ Section::make('🎯 Live Status') ->description('Real-time hotel statistieken') ->icon('heroicon-o-heart') ->columns(4) ->schema([ Placeholder::make('online_users') ->label('Online') ->content(fn (): HtmlString => $this->getCardHtml('Online', $this->getOnlineUsersCount(), '#22c55e', 'heroicon-o-users')), Placeholder::make('emulator_status') ->label('Emulator') ->content(fn (): HtmlString => $this->getCardHtml('Emulator', $this->getEmulatorStatusText(), $this->getEmulatorStatusColor(), 'heroicon-o-server')), Placeholder::make('database_status') ->label('Database') ->content(fn (): HtmlString => $this->getCardHtml('Database', $this->isDatabaseOnline() ? 'Online' : 'Offline', $this->isDatabaseOnline() ? '#22c55e' : '#ef4444', 'heroicon-o-circle-stack')), Placeholder::make('server_load') ->label('Load') ->content(fn (): HtmlString => $this->getCardHtml('Load', $this->getServerLoad(), '#3b82f6', 'heroicon-o-cpu-chip')), ]), Section::make('📊 Server Informatie') ->description('Gedetailleerde server status') ->icon('heroicon-o-information-circle') ->schema([ Placeholder::make('server_info') ->label('') ->content(fn () => $this->renderServerInfoView()), ]), Section::make('🩺 Systeem Gezondheid') ->description('Automatische systeem diagnostiek') ->icon('heroicon-o-heart') ->afterHeader([ Action::make('refresh_diagnostics') ->label('Vernieuwen') ->icon('heroicon-o-arrow-path') ->color('info') ->action('refreshDiagnostics'), ]) ->schema([ Placeholder::make('diagnostics') ->label('') ->content(fn (): HtmlString => $this->renderDiagnostics()), ]), Section::make('🏨 Hotel Status') ->description('Emulator en Nitro status') ->icon('heroicon-o-building-office') ->schema([ Placeholder::make('hotel_status') ->label('') ->content(fn () => $this->renderHotelStatusView()), ]), Section::make('📢 Hotel Alert') ->description('Stuur een bericht naar alle online gebruikers') ->icon('heroicon-o-megaphone') ->schema([ Placeholder::make('alert_form') ->label('') ->content(fn () => view('filament.components.commandocentrum.alert-form')), ]), Section::make('📜 Emulator Logs') ->description('Live emulator log viewer') ->icon('heroicon-o-document-text') ->columnSpanFull() ->schema([ Placeholder::make('emulator_logs') ->label('') ->content(fn () => view('filament.components.emulator-log-viewer')), ]), Section::make('🖥️ Emulator Control') ->description('Volledige emulator controle') ->icon('heroicon-o-server') ->columns(3) ->afterHeader([ Action::make('start_emulator') ->label('Start') ->icon('heroicon-o-play') ->color('success') ->action('startEmulator'), Action::make('stop_emulator') ->label('Stop') ->icon('heroicon-o-stop') ->color('danger') ->action('stopEmulator'), Action::make('restart_emulator') ->label('Restart') ->icon('heroicon-o-arrow-path') ->color('warning') ->action('restartEmulator'), Action::make('check_emulator') ->label('Check') ->icon('heroicon-o-check-circle') ->color('info') ->action('checkEmulator'), ]) ->schema([ Placeholder::make('emulator_info') ->label('') ->content(fn () => $this->renderEmulatorInfoView()), ]), Section::make('🔄 Emulator Updates') ->description('Configureer en update de emulator') ->icon('heroicon-o-arrow-down-circle') ->afterHeader([ Action::make('check_updates') ->label('Check Updates') ->color('info') ->action('checkEmulatorUpdates'), Action::make('build_emulator') ->label('🔨 Build') ->color('success') ->action('buildEmulator'), Action::make('run_sql') ->label('SQL Updates') ->color('purple') ->action('runSqlUpdates'), Action::make('save_emulator') ->label('Opslaan') ->color('primary') ->action('saveEmulator'), ]) ->schema([ Placeholder::make('emulator_settings') ->label('') ->content(fn () => $this->renderEmulatorSettingsView()), ]), Section::make('💾 Emulator Backups') ->description('Bekijk en herstel emulator backups') ->icon('heroicon-s-archive-box') ->schema([ Placeholder::make('backups_list') ->label('') ->content(fn () => $this->renderBackupsListView()), ]), Section::make('📦 Nitro Client') ->description('Configureer en update Nitro') ->icon('heroicon-o-cloud-arrow-down') ->afterHeader([ Action::make('detect_paths') ->label('🔍 Auto Detect') ->color('success') ->action('detectAndSavePaths'), Action::make('check_nitro') ->label('Check') ->color('info') ->action('checkNitroUpdates'), Action::make('build_nitro') ->label('Build') ->color('pink') ->action('buildNitro'), Action::make('generate_configs') ->label('Genereer Configs') ->color('indigo') ->action('generateNitroConfigs'), Action::make('save_nitro') ->label('Opslaan') ->color('primary') ->action('saveNitro'), ]) ->schema([ Placeholder::make('nitro_settings') ->label('') ->content(fn () => $this->renderNitroSettingsView()), ]), Section::make('⚙️ Automatische Updates') ->description('Configureer automatische updates') ->icon('heroicon-o-clock') ->columns(2) ->afterHeader([ Action::make('save_auto') ->label('Opslaan') ->color('primary') ->action('saveAutoUpdate'), ]) ->schema([ Toggle::make('auto_update_enabled') ->label('Automatische Updates Inschakelen'), TextInput::make('auto_update_schedule') ->label('Schema (HH:MM)'), TextInput::make('auto_update_days') ->label('Dagen (0-6)'), ]), Section::make('👔 Kleding Sync') ->description('Sync catalogus kleding uit FigureMap') ->icon('heroicon-o-user') ->afterHeader([ Action::make('sync_clothing') ->label('🔄 Sync') ->color('success') ->action('syncClothing'), ]) ->schema([ Placeholder::make('clothing_status') ->label('') ->content(fn () => $this->renderClothingStatusView()), ]), Section::make('🔔 Meldingen') ->description('E-mail en Discord alerts') ->icon('heroicon-o-bell') ->columns(2) ->afterHeader([ Action::make('save_alerts') ->label('Opslaan') ->color('primary') ->action('saveAlerts'), Action::make('test_discord') ->label('Test Discord') ->color('info') ->action('testDiscord'), ]) ->schema([ Toggle::make('alert_email_enabled') ->label('E-mail Meldingen'), TextInput::make('alert_email_address') ->label('E-mail Adres') ->email() ->columnSpanFull(), Toggle::make('alert_discord_enabled') ->label('Discord Meldingen'), TextInput::make('alert_discord_webhook_url') ->label('Webhook URL') ->columnSpanFull(), Select::make('discord_webhook_ranks') ->label('Ranks die Discord notificatie krijgen') ->multiple() ->options(fn () => WebsitePermission::query()->pluck('permission', 'min_rank')->mapWithKeys(fn ($perm, $rank) => [$rank => "Rank {$rank} ({$perm})"])->toArray()) ->helperText('Laat leeg voor alleen staff (min_staff_rank)'), ]), Section::make('📊 Update Geschiedenis') ->description('Laatste systeem updates') ->icon('heroicon-o-clock') ->schema([ Placeholder::make('history') ->label('') ->content(fn () => $this->renderUpdateHistoryView()), ]), Section::make('🔐 Social Login (v1.4)') ->description('Enable social login providers') ->icon('heroicon-o-user-circle') ->schema([ Toggle::make('social_login_google_enabled') ->label('Google Login') ->helperText('Allow users to login with Google'), TextInput::make('social_login_google_client_id') ->label('Google Client ID') ->helperText('From Google Cloud Console'), TextInput::make('social_login_google_client_secret') ->label('Google Client Secret') ->type('password'), Toggle::make('social_login_discord_enabled') ->label('Discord Login') ->helperText('Allow users to login with Discord'), TextInput::make('social_login_discord_client_id') ->label('Discord Client ID') ->helperText('From Discord Developer Portal'), TextInput::make('social_login_discord_client_secret') ->label('Discord Client Secret') ->type('password'), Toggle::make('social_login_github_enabled') ->label('GitHub Login') ->helperText('Allow users to login with GitHub'), TextInput::make('social_login_github_client_id') ->label('GitHub Client ID') ->helperText('From GitHub Developer Settings'), TextInput::make('social_login_github_client_secret') ->label('GitHub Client Secret') ->type('password'), ]) ->columns(2), Section::make('👔 Staff Activity Log') ->description('Recent staff activities in the housekeeping (v1.2)') ->icon('heroicon-o-user-group') ->schema([ Placeholder::make('staff_activity') ->label('') ->content(fn () => $this->renderStaffActivityView()), ]), ]); } private function renderServerInfoView(): \Illuminate\Contracts\View\View { $load = sys_getloadavg(); return view('filament.components.commandocentrum.server-info', [ 'phpVersion' => phpversion(), 'laravelVersion' => app()->version(), 'memoryUsage' => round(memory_get_usage() / 1024 / 1024, 2), 'memoryLimit' => ini_get('memory_limit'), 'diskUsage' => $this->runCommand("df -h /var/www | tail -1 | awk '{print \$3 \"/\" \$2}'") ?: 'N/B', 'uptime' => $this->runCommand('uptime -p 2>/dev/null') ?: 'N/B', 'load1' => $load ? number_format($load[0], 2) : 'N/A', 'load5' => $load ? number_format($load[1], 2) : 'N/A', 'load15' => $load ? number_format($load[2], 2) : 'N/A', ]); } private function renderHotelStatusView(): \Illuminate\Contracts\View\View { $serviceName = $this->getSetting('emulator_service_name', 'emulator'); $serviceStatus = $this->runCommand('systemctl is-active ' . escapeshellarg($serviceName) . ' 2>/dev/null') ?: 'inactive'; $serviceColor = $serviceStatus === 'active' ? '#22c55e' : '#ef4444'; $nitroClientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client'); $nitroRendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer'); $nitroWebroot = $this->getSetting('nitro_webroot', '/var/www/Client'); $clientCommit = $this->getGitCommit($nitroClientPath); $rendererCommit = $this->getGitCommit($nitroRendererPath); $clientExists = $this->checkPathExists($nitroClientPath); $rendererExists = $this->checkPathExists($nitroRendererPath); $clientColor = $clientExists ? '#22c55e' : '#ef4444'; $rendererColor = $rendererExists ? '#22c55e' : '#ef4444'; $clientText = $clientExists ? '✓ ' . substr($clientCommit, 0, 7) : '✗ Niet gevonden'; $rendererText = $rendererExists ? '✓ ' . substr($rendererCommit, 0, 7) : '✗ Niet gevonden'; $webrootText = $rendererExists ? '✓ ' . basename($nitroWebroot) : '✗ Niet gevonden'; $webrootColor = $rendererExists ? '#22c55e' : '#ef4444'; return view('filament.components.commandocentrum.hotel-status', [ 'serviceStatus' => $serviceStatus, 'serviceColor' => $serviceColor, 'onlineUsers' => $this->getOnlineUsersCount(), 'emulatorStatus' => $this->getEmulatorStatusText(), 'dbStatus' => $this->isDatabaseOnline() ? 'Online' : 'Offline', 'dbColor' => $this->isDatabaseOnline() ? '#22c55e' : '#ef4444', 'clientExists' => $clientExists, 'clientColor' => $clientColor, 'clientText' => $clientText, 'rendererExists' => $rendererExists, 'rendererColor' => $rendererColor, 'rendererText' => $rendererText, 'webrootText' => $webrootText, 'webrootColor' => $webrootColor, ]); } private function renderEmulatorInfoView(): \Illuminate\Contracts\View\View { $status = $this->getEmulatorStatusText(); return view('filament.components.commandocentrum.emulator-info', [ 'version' => $this->getSetting('emulator_version', 'Onbekend'), 'serviceName' => $this->getSetting('emulator_service_name', 'arcturus'), 'status' => $status, 'color' => $status === 'Online' ? '#22c55e' : '#ef4444', ]); } private function renderEmulatorSettingsView(): \Illuminate\Contracts\View\View { return view('filament.components.commandocentrum.emulator-settings', [ 'emulatorBranchesHtml' => $this->getEmulatorBranchesHtml(), 'emulatorStatusHtml' => $this->renderEmulatorStatusView()->render(), ]); } private function renderEmulatorStatusView(): \Illuminate\Contracts\View\View { $serviceName = $this->getSetting('emulator_service_name', 'arcturus'); $jarPath = $this->getSetting('emulator_jar_path', '/var/www/Emulator'); $sourcePath = $this->getSetting('emulator_source_path', '/var/www/emulator-source'); $githubUrl = $this->getSetting('emulator_github_url', ''); $branch = $this->getSetting('emulator_github_branch', 'main'); $jarExists = $this->fileExists($jarPath); $sourceExists = $this->fileExists($sourcePath); $sourceCommit = $this->getGitCommit($sourcePath); $remoteVersion = $githubUrl !== '' && $githubUrl !== '0' ? $this->getRemoteCommit($githubUrl, $branch) : 'N/A'; $canBuild = false; $checkDirs = [ $sourcePath, $sourcePath . '/Emulator', $sourcePath . '/Emulator/Emulator', $sourcePath . '/emulator', $sourcePath . '/emulator/emulator', ]; foreach ($checkDirs as $dir) { $check = $this->runCommand('test -f ' . escapeshellarg($dir . '/pom.xml') . ' && echo yes'); if ($check && trim($check) === 'yes') { $canBuild = true; break; } } return view('filament.components.commandocentrum.emulator-status', [ 'emulatorOnline' => $this->getEmulatorStatusText() === 'Online', 'jarExists' => $jarExists, 'serviceName' => $serviceName, 'sourceCommit' => $sourceCommit, 'remoteVersion' => $remoteVersion, 'canBuild' => $canBuild, 'jarPath' => $jarPath, 'sourcePath' => $sourcePath, ]); } private function renderNitroSettingsView(): \Illuminate\Contracts\View\View { return view('filament.components.commandocentrum.nitro-settings', [ 'nitroBranchesHtml' => $this->getNitroBranchesHtml(), 'nitroStatusHtml' => $this->renderNitroStatusView()->render(), ]); } private function renderNitroStatusView(): \Illuminate\Contracts\View\View { $clientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client'); $rendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer'); $webroot = $this->getSetting('nitro_webroot', '/var/www/Client'); $clientGithubUrl = $this->getSetting('nitro_github_url', ''); $rendererGithubUrl = $this->getSetting('nitro_renderer_github_url', 'https://github.com/duckietm/Nitro_Render_V3'); $clientCommit = $this->getGitCommit($clientPath); $rendererCommit = $this->getGitCommit($rendererPath); $clientRemote = $clientGithubUrl !== '' && $clientGithubUrl !== '0' ? $this->getRemoteCommit($clientGithubUrl, $this->getSetting('nitro_github_branch', 'main')) : 'N/A'; $rendererRemote = $rendererGithubUrl !== '' && $rendererGithubUrl !== '0' ? $this->getRemoteCommit($rendererGithubUrl, $this->getSetting('nitro_renderer_github_branch', 'main')) : 'N/A'; return view('filament.components.commandocentrum.nitro-status', [ 'clientExists' => $this->checkPathExists($clientPath), 'rendererExists' => $this->checkPathExists($rendererPath), 'webrootExists' => $this->checkPathExists($webroot), 'clientCommit' => $clientCommit, 'rendererCommit' => $rendererCommit, 'clientRemote' => $clientRemote, 'rendererRemote' => $rendererRemote, ]); } private function renderBackupsListView(): \Illuminate\Contracts\View\View { try { $backups = app(EmulatorControlAction::class)->getBackups(); } catch (Exception) { $backups = []; } return view('filament.components.commandocentrum.backups-list', [ 'backups' => $backups, ]); } private function renderClothingStatusView(): \Illuminate\Contracts\View\View { try { $count = DB::table('catalog_clothing')->count(); } catch (Exception) { $count = 0; } return view('filament.components.commandocentrum.clothing-status', [ 'clothingCount' => $count, ]); } private function renderStaffActivityView(): \Illuminate\Contracts\View\View { try { $activities = StaffActivity::with('user:id,username,look') ->orderByDesc('created_at') ->limit(20) ->get(); } catch (Exception) { $activities = collect(); } return view('filament.components.commandocentrum.staff-activity', [ 'activities' => $activities, ]); } private function renderUpdateHistoryView(): \Illuminate\Contracts\View\View { try { $history = app(UpdateHistoryService::class)->getRecent(10); } catch (Exception) { $history = []; } return view('filament.components.commandocentrum.update-history', [ 'history' => $history, ]); } private function getSetting(string $key, string $default = ''): string { try { return app(SettingsService::class)->getOrDefault($key, $default); } catch (Exception) { return $default; } } private function getSettingBool(string $key, bool $default = false): bool { try { $value = app(SettingsService::class)->getOrDefault($key, $default ? '1' : '0'); return in_array($value, ['1', 'true', 'yes'], true); } catch (Exception) { return $default; } } private function getCurrentSiteUrl(): string { try { return config('app.url', 'https://epicnabbo.nl'); } catch (Exception) { return 'https://epicnabbo.nl'; } } private function autoDetectPaths(): array { $autoDetect = AutoDetectService::getInstance(); $autoDetect->clearCache(); return [ 'nitro_client_path' => $autoDetect->detectNitroClientPath(), 'nitro_renderer_path' => $autoDetect->detectNitroRendererPath(), 'nitro_build_path' => $autoDetect->detectNitroBuildPath(), 'nitro_webroot' => $autoDetect->detectNitroWebroot(), 'gamedata_path' => $autoDetect->detectGamedataPath(), 'emulator_jar_path' => $autoDetect->detectEmulatorJarPath(), 'emulator_source_path' => $autoDetect->detectEmulatorSourcePath(), ]; } private function getCardHtml(string $label, string $value, string $color, string $icon): HtmlString { $iconSvg = match ($icon) { 'heroicon-o-users' => '', 'heroicon-o-server' => '', 'heroicon-o-circle-stack' => '', 'heroicon-o-cpu-chip' => '', default => '', }; return new HtmlString(<<
{$iconSvg}
{$label}
{$value}
HTML); } private function getOnlineUsersCount(): string { try { return (string) DB::connection('mysql')->table('users')->where('online', '=', '1')->count(); } catch (Exception) { return 'N/B'; } } private function getEmulatorStatusText(): string { try { $rcon = new RconService; if ($rcon->isConnected()) { return 'Online'; } $response = Http::timeout(2)->get('http://127.0.0.1:3000/api/status'); return $response->successful() ? 'Online' : 'Offline'; } catch (Exception) { return 'Offline'; } } private function getEmulatorStatusColor(): string { return $this->getEmulatorStatusText() === 'Online' ? '#22c55e' : '#ef4444'; } private function isDatabaseOnline(): bool { try { DB::connection('mysql')->select('SELECT 1'); return true; } catch (Exception) { return false; } } private function getServerLoad(): string { try { $load = sys_getloadavg(); return $load ? number_format($load[0], 2) : 'N/B'; } catch (Exception) { return 'N/B'; } } private function getEmulatorBranchesHtml(): string { $githubUrl = $this->getSetting('emulator_github_url', ''); $currentBranch = $this->data['emulator_github_branch'] ?? 'main'; $branches = app(GitHubService::class)->getBranches($githubUrl); $html = ''; foreach ($branches as $branch) { $selected = $branch === $currentBranch ? 'selected' : ''; $html .= ''; } return $html; } private function getNitroBranchesHtml(): string { $githubUrl = $this->getSetting('nitro_github_url', ''); $currentBranch = $this->data['nitro_github_branch'] ?? 'main'; $branches = app(GitHubService::class)->getBranches($githubUrl); $html = ''; foreach ($branches as $branch) { $selected = $branch === $currentBranch ? 'selected' : ''; $html .= ''; } return $html; } private function getRemoteCommit(string $githubUrl, string $branch = 'main'): string { $commit = app(GitHubService::class)->getLatestCommit($githubUrl, $branch); return $commit ?? 'N/A'; } private function getGitCommit(string $path): string { if (! $this->fileExists($path)) { return 'N/A'; } $subdirs = ['', 'Emulator', 'emulator', 'src', 'client']; foreach ($subdirs as $subdir) { $fullPath = $subdir !== '' ? $path . '/' . $subdir : $path; $gitPath = $fullPath . '/.git'; if ($this->dirExists($gitPath)) { $headFile = $gitPath . '/HEAD'; $headContent = $this->readFile($headFile); if ($headContent) { if (str_contains($headContent, 'ref:')) { preg_match('/ref: refs\/heads\/(\S+)/', $headContent, $matches); if (isset($matches[1])) { $branchRef = $gitPath . '/refs/heads/' . $matches[1]; $branchContent = $this->readFile($branchRef); if ($branchContent) { return trim($branchContent); } } } else { return trim($headContent); } } } } return 'N/A'; } private function checkPathExists(string $path): bool { return $this->fileExists($path); } public function sendHotelAlert(): void { $result = app(EmulatorControlAction::class)->sendAlert($this->data['hotel_alert_message'] ?? ''); $this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger'); if ($result['success']) { $this->data['hotel_alert_message'] = ''; } } public function startEmulator(): void { $result = app(EmulatorControlAction::class)->start(); $this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger'); } public function stopEmulator(): void { $result = app(EmulatorControlAction::class)->stop(); $this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger'); } public function restartEmulator(): void { $result = app(EmulatorControlAction::class)->restart(); $this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger'); } public function checkEmulator(): void { try { Artisan::call('monitor:emulator', ['--notify-online' => true]); $rconService = new RconService; if ($rconService->isConnected()) { $this->notify('Success', 'Emulator is online en reageert!', 'success'); } else { $this->notify('Warning', 'Emulator is niet bereikbaar via RCON', 'warning'); } $this->fillForm(); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } public function checkEmulatorUpdates(): void { $result = app(EmulatorControlAction::class)->update(); $this->notify($result['success'] ?? false ? 'Success' : 'Error', $result['message'] ?? $result['error'] ?? 'Onbekende fout', ($result['success'] ?? false) ? 'success' : 'danger'); Cache::forget('all_updates_check'); $this->fillForm(); } public function buildEmulator(): void { $result = app(EmulatorControlAction::class)->build(); $this->notify($result['success'] ?? false ? 'Success' : 'Error', $result['message'] ?? $result['error'] ?? 'Onbekende fout', ($result['success'] ?? false) ? 'success' : 'danger'); } public function runSqlUpdates(): void { $result = app(EmulatorControlAction::class)->runSqlUpdates(); $this->notify($result['success'] ?? false ? 'Success' : 'Error', $result['message'] ?? 'Onbekende fout', ($result['success'] ?? false) ? 'success' : 'danger'); } public function restoreBackup(string $backupName): void { $result = app(EmulatorControlAction::class)->restoreBackup($backupName); $this->notify($result['success'] ? 'Success' : 'Error', $result['message'] ?? $result['error'] ?? 'Onbekende fout', $result['success'] ? 'success' : 'danger'); $this->fillForm(); } public function saveEmulator(): void { try { $settings = app(SettingsService::class); $settings->set('emulator_github_url', $this->data['emulator_github_url'] ?? ''); $settings->set('emulator_jar_direct_url', $this->data['emulator_jar_direct_url'] ?? ''); $settings->set('emulator_jar_path', $this->data['emulator_jar_path'] ?? '/root/emulator'); $settings->set('emulator_source_repo', $this->data['emulator_source_repo'] ?? ''); $settings->set('emulator_source_path', $this->data['emulator_source_path'] ?? '/var/www/emulator-source'); $settings->set('emulator_github_branch', $this->data['emulator_github_branch'] ?? 'main'); $settings->set('emulator_database_host', $this->data['emulator_database_host'] ?? '127.0.0.1'); $settings->set('emulator_database_name', $this->data['emulator_database_name'] ?? ''); $settings->set('emulator_service_name', $this->data['emulator_service_name'] ?? 'arcturus'); $this->notify('Success', 'Emulator instellingen opgeslagen!', 'success'); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } public function checkNitroUpdates(): void { $clientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client'); $rendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer'); $branch = $this->getSetting('nitro_github_branch', 'main'); $result = app(NitroControlAction::class)->pullUpdates($clientPath, $rendererPath, $branch); $this->notify('Success', $result['message'], 'success'); $this->fillForm(); } public function buildNitro(): void { $clientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client'); $rendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer'); $branch = $this->getSetting('nitro_github_branch', 'main'); $result = app(NitroControlAction::class)->build($clientPath, $rendererPath, $branch); $this->notify($result['success'] ? 'Success' : 'Warning', $result['message'], $result['success'] ? 'success' : 'warning'); } public function generateNitroConfigs(): void { $siteUrl = $this->getSetting('nitro_site_url', $this->getCurrentSiteUrl()); $webroot = $this->getSetting('nitro_webroot', '/var/www/Client'); $gamedataPath = $this->getSetting('gamedata_path', '/var/www/Gamedata'); $result = app(NitroControlAction::class)->generateConfigs($siteUrl, $webroot, $gamedataPath); $this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger'); $this->fillForm(); } public function syncClothing(): void { try { $catalogService = app(CatalogService::class); $result = $catalogService->syncCatalogClothing(); $message = '👔 Kleding Sync Resultaat:' . PHP_EOL; $message .= '• Toegevoegd: ' . $result['inserted'] . PHP_EOL; $message .= '• Totaal: ' . $result['total']; $this->notify('Success', $message, 'success'); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } public function detectAndSavePaths(): void { try { $paths = $this->autoDetectPaths(); $settings = app(SettingsService::class); $settings->set('nitro_client_path', $paths['nitro_client_path']); $settings->set('nitro_renderer_path', $paths['nitro_renderer_path']); $settings->set('nitro_build_path', $paths['nitro_build_path']); $settings->set('nitro_webroot', $paths['nitro_webroot']); $settings->set('gamedata_path', $paths['gamedata_path']); $settings->set('emulator_jar_path', $paths['emulator_jar_path']); $settings->set('emulator_source_path', $paths['emulator_source_path']); $this->fillForm(); $this->notify('Success', 'Paths gedetecteerd en opgeslagen!', 'success'); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } public function saveNitro(): void { try { $settings = app(SettingsService::class); $settings->set('nitro_client_path', $this->data['nitro_client_path'] ?? '/var/www/atomcms/nitro-client'); $settings->set('nitro_renderer_path', $this->data['nitro_renderer_path'] ?? '/var/www/atomcms/nitro-renderer'); $settings->set('nitro_build_path', $this->data['nitro_build_path'] ?? '/var/www/atomcms/nitro-client/dist'); $settings->set('nitro_webroot', $this->data['nitro_webroot'] ?? '/var/www/Client'); $settings->set('gamedata_path', $this->data['gamedata_path'] ?? '/var/www/Gamedata'); $settings->set('nitro_github_url', $this->data['nitro_github_url'] ?? ''); $settings->set('nitro_github_branch', $this->data['nitro_github_branch'] ?? 'main'); $settings->set('nitro_site_url', $this->data['nitro_site_url'] ?? ''); $this->notify('Success', 'Nitro instellingen opgeslagen!', 'success'); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } public function saveAutoUpdate(): void { try { $settings = app(SettingsService::class); $settings->set('auto_update_enabled', ($this->data['auto_update_enabled'] ?? false) ? '1' : '0'); $settings->set('auto_update_schedule', $this->data['auto_update_schedule'] ?? '03:00'); $settings->set('auto_update_days', $this->data['auto_update_days'] ?? '0,6'); $this->notify('Success', 'Auto update instellingen opgeslagen!', 'success'); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } public function saveAlerts(): void { try { $settings = app(SettingsService::class); $settings->set('alert_email_enabled', ($this->data['alert_email_enabled'] ?? false) ? '1' : '0'); $settings->set('alert_email_address', $this->data['alert_email_address'] ?? ''); $settings->set('alert_discord_enabled', ($this->data['alert_discord_enabled'] ?? false) ? '1' : '0'); $settings->set('alert_discord_webhook_url', $this->data['alert_discord_webhook_url'] ?? ''); $settings->set('discord_webhook_ranks', json_encode($this->data['discord_webhook_ranks'] ?? [])); $this->notify('Success', 'Meldingen opgeslagen!', 'success'); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } public function testDiscord(): void { try { $webhookUrl = $this->data['alert_discord_webhook_url'] ?? ''; if (empty($webhookUrl)) { $this->notify('Error', 'Webhook URL is leeg', 'danger'); return; } Http::post($webhookUrl, ['content' => '✅ Test van Atom CMS Commandocentrum']); $this->notify('Success', 'Test bericht verzonden!', 'success'); } catch (Exception $e) { $this->notify('Error', $e->getMessage(), 'danger'); } } private function notify(string $title, string $message, string $color): void { Notification::make() ->title($title) ->body($message) ->color($color) ->send(); } public function refreshDiagnostics(): void { $this->runDiagnostics(); $this->notify('Success', 'Diagnostiek vernieuwd', 'success'); } private function runDiagnostics(): void { $runner = app(DiagnosticRunner::class); $this->diagnostics = $runner->runAll(); } private function renderDiagnostics(): HtmlString { if ($this->diagnostics === []) { $this->runDiagnostics(); } $errors = array_filter($this->diagnostics, fn ($r) => $r->status === 'error'); $warnings = array_filter($this->diagnostics, fn ($r) => $r->status === 'warning'); $ok = array_filter($this->diagnostics, fn ($r) => $r->status === 'ok'); $errorCount = count($errors); $warningCount = count($warnings); $okCount = count($ok); $overallStatus = $errorCount > 0 ? 'error' : ($warningCount > 0 ? 'warning' : 'ok'); $overallColor = match ($overallStatus) { 'error' => '#ef4444', 'warning' => '#f59e0b', default => '#22c55e', }; $overallLabel = match ($overallStatus) { 'error' => 'Kritieke Problemen', 'warning' => 'Waarschuwingen', default => 'Gezond', }; $html = '
'; // Summary cards $html .= '
'; $html .= $this->getSummaryCardHtml('Gezond', $okCount, '#22c55e', 'heroicon-o-check-circle'); $html .= $this->getSummaryCardHtml('Waarschuwingen', $warningCount, '#f59e0b', 'heroicon-o-exclamation-triangle'); $html .= $this->getSummaryCardHtml('Fouten', $errorCount, '#ef4444', 'heroicon-o-x-circle'); $html .= '
'; // Overall status banner $html .= '
'; $html .= '
'; $html .= 'Systeem Status: {$overallLabel}'; $html .= '
'; // Detailed results if ($errorCount > 0 || $warningCount > 0) { $html .= '
'; foreach ($this->diagnostics as $result) { if ($result->status === 'ok') { continue; } $color = $result->status === 'error' ? '#ef4444' : '#f59e0b'; $icon = $result->status === 'error' ? '' : ''; $html .= '
'; $html .= '
' . $icon . '
'; $html .= '
'; $html .= '
' . e($result->name) . '
'; $html .= '
' . e($result->message) . '
'; if ($result->fix) { $html .= '
💡 ' . e($result->fix) . '
'; } $html .= '
'; } $html .= '
'; } $html .= '
'; return new HtmlString($html); } private function getSummaryCardHtml(string $label, int $count, string $color, string $icon): string { $iconSvg = match ($icon) { 'heroicon-o-check-circle' => '', 'heroicon-o-exclamation-triangle' => '', 'heroicon-o-x-circle' => '', default => '', }; return <<
{$iconSvg}
{$label}
{$count}
HTML; } private function runCommand(string $command, int $timeout = 10): ?string { $result = Process::timeout($timeout)->run($command); return $result->successful() ? trim($result->output()) : null; } private function fileExists(string $path): bool { return $this->runCommand('test -e ' . escapeshellarg($path) . ' && echo yes') === 'yes'; } private function dirExists(string $path): bool { return $this->runCommand('test -d ' . escapeshellarg($path) . ' && echo yes') === 'yes'; } private function readFile(string $path): ?string { return $this->runCommand('cat ' . escapeshellarg($path) . ' 2>/dev/null'); } }