*/
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'),
'nitro_emulator_path' => $this->getSetting('nitro_emulator_path', '/var/www/emulator'),
'nitro_emulator_service' => $this->getSetting('nitro_emulator_service', 'emulator'),
'nitro_db_name' => $this->getSetting('nitro_db_name', 'habbo'),
'nitro_sql_dir' => $this->getSetting('nitro_sql_dir', '/var/www/emulator/Database Updates'),
'nitro_backup_dir' => $this->getSetting('nitro_backup_dir', '/var/www/emulator/Database Updates/backups'),
'nitro_gamedata_dir' => $this->getSetting('nitro_gamedata_dir', '/var/www/Gamedata/config'),
'nitro_client_dir' => $this->getSetting('nitro_client_dir', '/var/www/Nitro-V3/public/configuration'),
'nitro_client_src' => $this->getSetting('nitro_client_src', '/var/www/Nitro-V3'),
'nitro_renderer_src' => $this->getSetting('nitro_renderer_src', '/var/www/Nitro_Render_V3'),
'hotel_alert_message' => '',
];
}
public function form(Schema $schema): Schema
{
return $schema
->components([
Section::make(__('commandocentrum.live_status'))
->description(__('commandocentrum.live_status_desc'))
->icon('heroicon-o-heart')
->columns(4)
->schema([
Placeholder::make('online_users')
->label(__('commandocentrum.online'))
->content(fn (): HtmlString => $this->getCardHtml(__('commandocentrum.online'), $this->getOnlineUsersCount(), '#22c55e', 'heroicon-o-users')),
Placeholder::make('emulator_status')
->label(__('commandocentrum.emulator'))
->content(fn (): HtmlString => $this->getCardHtml(__('commandocentrum.emulator'), $this->getEmulatorStatusText(), $this->getEmulatorStatusColor(), 'heroicon-o-server')),
Placeholder::make('database_status')
->label(__('commandocentrum.database'))
->content(fn (): HtmlString => $this->getCardHtml(__('commandocentrum.database'), $this->isDatabaseOnline() ? __('commandocentrum.online') : __('commandocentrum.offline'), $this->isDatabaseOnline() ? '#22c55e' : '#ef4444', 'heroicon-o-circle-stack')),
Placeholder::make('server_load')
->label(__('commandocentrum.load'))
->content(fn (): HtmlString => $this->getCardHtml(__('commandocentrum.load'), $this->getServerLoad(), '#3b82f6', 'heroicon-o-cpu-chip')),
]),
Section::make(__('commandocentrum.server_info'))
->description(__('commandocentrum.server_info_desc'))
->icon('heroicon-o-information-circle')
->schema([
Placeholder::make('server_info')
->label('')
->content(fn () => $this->renderServerInfoView()),
]),
Section::make(__('commandocentrum.system_health'))
->description(__('commandocentrum.system_health_desc'))
->icon('heroicon-o-heart')
->afterHeader([
Action::make('refresh_diagnostics')
->label(__('commandocentrum.refresh'))
->icon('heroicon-o-arrow-path')
->color('info')
->action('refreshDiagnostics'),
])
->schema([
Placeholder::make('diagnostics')
->label('')
->content(fn (): HtmlString => $this->renderDiagnostics()),
]),
Section::make(__('commandocentrum.hotel_status'))
->description(__('commandocentrum.hotel_status_desc'))
->icon('heroicon-o-building-office')
->schema([
Placeholder::make('hotel_status')
->label('')
->content(fn () => $this->renderHotelStatusView()),
]),
Section::make(__('commandocentrum.hotel_alert'))
->description(__('commandocentrum.hotel_alert_desc'))
->icon('heroicon-o-megaphone')
->schema([
Placeholder::make('alert_form')
->label('')
->content(fn () => view('filament.components.commandocentrum.alert-form')),
]),
Section::make(__('commandocentrum.emulator_logs'))
->description(__('commandocentrum.emulator_logs_desc'))
->icon('heroicon-o-document-text')
->columnSpanFull()
->schema([
Placeholder::make('emulator_logs')
->label('')
->content(fn () => view('filament.components.emulator-log-viewer')),
]),
Section::make(__('commandocentrum.emulator_control'))
->description(__('commandocentrum.emulator_control_desc'))
->icon('heroicon-o-server')
->columns(3)
->afterHeader([
Action::make('start_emulator')
->label(__('commandocentrum.start'))
->icon('heroicon-o-play')
->color('success')
->action('startEmulator'),
Action::make('stop_emulator')
->label(__('commandocentrum.stop'))
->icon('heroicon-o-stop')
->color('danger')
->action('stopEmulator'),
Action::make('restart_emulator')
->label(__('commandocentrum.restart'))
->icon('heroicon-o-arrow-path')
->color('warning')
->action('restartEmulator'),
Action::make('check_emulator')
->label(__('commandocentrum.check'))
->icon('heroicon-o-check-circle')
->color('info')
->action('checkEmulator'),
])
->schema([
Placeholder::make('emulator_info')
->label('')
->content(fn () => $this->renderEmulatorInfoView()),
]),
Section::make(__('commandocentrum.nitro_update'))
->description(__('commandocentrum.nitro_update_desc'))
->icon('heroicon-o-arrow-path')
->afterHeader([
Action::make('configure_nitro')
->label(__('commandocentrum.configure_nitro'))
->icon('heroicon-o-cog-6-tooth')
->color('primary')
->form([
\Filament\Forms\Components\TextInput::make('nitro_emulator_path')
->label(__('commandocentrum.nitro_emulator_path'))
->default(fn () => $this->getSetting('nitro_emulator_path', '/var/www/emulator')),
\Filament\Forms\Components\TextInput::make('nitro_emulator_service')
->label(__('commandocentrum.nitro_emulator_service'))
->default(fn () => $this->getSetting('nitro_emulator_service', 'emulator')),
\Filament\Forms\Components\TextInput::make('nitro_db_name')
->label(__('commandocentrum.nitro_db_name'))
->default(fn () => $this->getSetting('nitro_db_name', 'habbo')),
\Filament\Forms\Components\TextInput::make('nitro_sql_dir')
->label(__('commandocentrum.nitro_sql_dir'))
->default(fn () => $this->getSetting('nitro_sql_dir', '/var/www/emulator/Database Updates')),
\Filament\Forms\Components\TextInput::make('nitro_backup_dir')
->label(__('commandocentrum.nitro_backup_dir'))
->default(fn () => $this->getSetting('nitro_backup_dir', '/var/www/emulator/Database Updates/backups')),
\Filament\Forms\Components\TextInput::make('nitro_gamedata_dir')
->label(__('commandocentrum.nitro_gamedata_dir'))
->default(fn () => $this->getSetting('nitro_gamedata_dir', '/var/www/Gamedata/config')),
\Filament\Forms\Components\TextInput::make('nitro_client_dir')
->label(__('commandocentrum.nitro_client_dir'))
->default(fn () => $this->getSetting('nitro_client_dir', '/var/www/Nitro-V3/public/configuration')),
\Filament\Forms\Components\TextInput::make('nitro_client_src')
->label(__('commandocentrum.nitro_client_src'))
->default(fn () => $this->getSetting('nitro_client_src', '/var/www/Nitro-V3')),
\Filament\Forms\Components\TextInput::make('nitro_renderer_src')
->label(__('commandocentrum.nitro_renderer_src'))
->default(fn () => $this->getSetting('nitro_renderer_src', '/var/www/Nitro_Render_V3')),
])
->action(function (array $data): void {
$settings = app(SettingsService::class);
$settings->set('nitro_emulator_path', $data['nitro_emulator_path'] ?? '/var/www/emulator');
$settings->set('nitro_emulator_service', $data['nitro_emulator_service'] ?? 'emulator');
$settings->set('nitro_db_name', $data['nitro_db_name'] ?? 'habbo');
$settings->set('nitro_sql_dir', $data['nitro_sql_dir'] ?? '/var/www/emulator/Database Updates');
$settings->set('nitro_backup_dir', $data['nitro_backup_dir'] ?? '/var/www/emulator/Database Updates/backups');
$settings->set('nitro_gamedata_dir', $data['nitro_gamedata_dir'] ?? '/var/www/Gamedata/config');
$settings->set('nitro_client_dir', $data['nitro_client_dir'] ?? '/var/www/Nitro-V3/public/configuration');
$settings->set('nitro_client_src', $data['nitro_client_src'] ?? '/var/www/Nitro-V3');
$settings->set('nitro_renderer_src', $data['nitro_renderer_src'] ?? '/var/www/Nitro_Render_V3');
$this->fillForm();
Notification::make()
->title(__('commandocentrum.success'))
->body(__('commandocentrum.nitro_config_saved'))
->color('success')
->send();
}),
Action::make('run_nitro_update')
->label(__('commandocentrum.run_update'))
->icon('heroicon-o-play')
->color('success')
->action('runUpdateNitrov3'),
])
->schema([
Placeholder::make('nitro_config_summary')
->label('')
->content(fn () => view('filament.components.commandocentrum.nitro-config-summary')),
]),
Section::make(__('commandocentrum.clothing_sync'))
->description(__('commandocentrum.clothing_sync_desc'))
->icon('heroicon-o-user')
->afterHeader([
Action::make('sync_clothing')
->label('🔄 ' . __('commandocentrum.sync'))
->color('success')
->action('syncClothing'),
])
->schema([
Placeholder::make('clothing_status')
->label('')
->content(fn () => $this->renderClothingStatusView()),
]),
Section::make(__('commandocentrum.notifications'))
->description(__('commandocentrum.notifications_desc'))
->icon('heroicon-o-bell')
->columns(2)
->afterHeader([
Action::make('save_alerts')
->label(__('commandocentrum.save'))
->color('primary')
->action('saveAlerts'),
Action::make('test_discord')
->label(__('commandocentrum.test_discord'))
->color('info')
->action('testDiscord'),
])
->schema([
Toggle::make('alert_email_enabled')
->label(__('commandocentrum.email_notifications')),
TextInput::make('alert_email_address')
->label(__('commandocentrum.email_address'))
->email()
->columnSpanFull(),
Toggle::make('alert_discord_enabled')
->label(__('commandocentrum.discord_notifications')),
TextInput::make('alert_discord_webhook_url')
->label(__('commandocentrum.webhook_url'))
->columnSpanFull(),
Select::make('discord_webhook_ranks')
->label(__('commandocentrum.discord_ranks'))
->multiple()
->options(fn () => WebsitePermission::query()->pluck('permission', 'min_rank')->mapWithKeys(fn ($perm, $rank) => [$rank => "Rank {$rank} ({$perm})"])->toArray())
->helperText(__('commandocentrum.discord_ranks_helper')),
]),
Section::make(__('commandocentrum.social_login'))
->description(__('commandocentrum.social_login_desc'))
->icon('heroicon-o-user-circle')
->schema([
Toggle::make('social_login_google_enabled')
->label(__('commandocentrum.google_login'))
->helperText(__('commandocentrum.google_login_helper')),
TextInput::make('social_login_google_client_id')
->label(__('commandocentrum.google_client_id'))
->helperText(__('commandocentrum.google_client_id_helper')),
TextInput::make('social_login_google_client_secret')
->label(__('commandocentrum.google_client_secret'))
->type('password'),
Toggle::make('social_login_discord_enabled')
->label(__('commandocentrum.discord_login'))
->helperText(__('commandocentrum.discord_login_helper')),
TextInput::make('social_login_discord_client_id')
->label(__('commandocentrum.discord_client_id'))
->helperText(__('commandocentrum.discord_client_id_helper')),
TextInput::make('social_login_discord_client_secret')
->label(__('commandocentrum.discord_client_secret'))
->type('password'),
Toggle::make('social_login_github_enabled')
->label(__('commandocentrum.github_login'))
->helperText(__('commandocentrum.github_login_helper')),
TextInput::make('social_login_github_client_id')
->label(__('commandocentrum.github_client_id'))
->helperText(__('commandocentrum.github_client_id_helper')),
TextInput::make('social_login_github_client_secret')
->label(__('commandocentrum.github_client_secret'))
->type('password'),
])
->columns(2),
Section::make(__('commandocentrum.staff_activity'))
->description(__('commandocentrum.staff_activity_desc'))
->icon('heroicon-o-user-group')
->schema([
Placeholder::make('staff_activity')
->label('')
->content(fn () => $this->renderStaffActivityView()),
]),
]);
}
private function renderServerInfoView(): 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(): 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) : '✗ ' . __('commandocentrum.not_found');
$rendererText = $rendererExists ? '✓ ' . substr($rendererCommit, 0, 7) : '✗ ' . __('commandocentrum.not_found');
$webrootText = $rendererExists ? '✓ ' . basename($nitroWebroot) : '✗ ' . __('commandocentrum.not_found');
$webrootColor = $rendererExists ? '#22c55e' : '#ef4444';
return view('filament.components.commandocentrum.hotel-status', [
'serviceStatus' => $serviceStatus,
'serviceColor' => $serviceColor,
'onlineUsers' => $this->getOnlineUsersCount(),
'emulatorStatus' => $this->getEmulatorStatusText(),
'dbStatus' => $this->isDatabaseOnline() ? __('commandocentrum.online') : __('commandocentrum.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(): 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 renderClothingStatusView(): View
{
try {
$count = DB::table('catalog_clothing')->count();
} catch (Exception) {
$count = 0;
}
return view('filament.components.commandocentrum.clothing-status', [
'clothingCount' => $count,
]);
}
private function renderStaffActivityView(): 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 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' => '
' . e($output) . '') ->color($color) ->persistent() ->send(); } catch (Exception $e) { $this->notify(__('commandocentrum.error'), $e->getMessage(), 'danger'); } } 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(__('commandocentrum.success'), __('commandocentrum.emulator_settings_saved'), 'success'); } catch (Exception $e) { $this->notify(__('commandocentrum.error'), $e->getMessage(), 'danger'); } } public function syncClothing(): void { try { $catalogService = app(CatalogService::class); $result = $catalogService->syncCatalogClothing(); $message = '👔 ' . __('commandocentrum.clothing_items') . ':' . PHP_EOL; $message .= '• Toegevoegd: ' . $result['inserted'] . PHP_EOL; $message .= '• Totaal: ' . $result['total']; $this->notify(__('commandocentrum.success'), $message, 'success'); } catch (Exception $e) { $this->notify(__('commandocentrum.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(__('commandocentrum.success'), __('commandocentrum.alerts_saved'), 'success'); } catch (Exception $e) { $this->notify(__('commandocentrum.error'), $e->getMessage(), 'danger'); } } public function testDiscord(): void { try { $webhookUrl = $this->data['alert_discord_webhook_url'] ?? ''; if (empty($webhookUrl)) { $this->notify(__('commandocentrum.error'), __('commandocentrum.webhook_empty'), 'danger'); return; } Http::post($webhookUrl, ['content' => '✅ Test van Atom CMS Commandocentrum']); $this->notify(__('commandocentrum.success'), __('commandocentrum.test_sent'), 'success'); } catch (Exception $e) { $this->notify(__('commandocentrum.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(__('commandocentrum.success'), __('commandocentrum.diagnostics_refreshed'), 'success'); } private function runDiagnostics(): void { $runner = app(DiagnosticRunner::class); $this->diagnostics = array_map(fn (DiagnosticResult $r) => [ 'name' => $r->name, 'status' => $r->status, 'message' => $r->message, 'fix' => $r->fix, ], $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' => __('commandocentrum.critical_issues'), 'warning' => __('commandocentrum.warnings'), default => __('commandocentrum.healthy'), }; $html = '