*/
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(__('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.emulator_updates'))
->description(__('commandocentrum.emulator_updates_desc'))
->icon('heroicon-o-arrow-down-circle')
->afterHeader([
Action::make('check_updates')
->label(__('commandocentrum.check_updates'))
->color('info')
->action('checkEmulatorUpdates'),
Action::make('build_emulator')
->label('🔨 ' . __('commandocentrum.build'))
->color('success')
->action('buildEmulator'),
Action::make('run_sql')
->label(__('commandocentrum.sql_updates'))
->color('purple')
->action('runSqlUpdates'),
Action::make('save_emulator')
->label(__('commandocentrum.save'))
->color('primary')
->action('saveEmulator'),
])
->schema([
Placeholder::make('emulator_settings')
->label('')
->content(fn () => $this->renderEmulatorSettingsView()),
]),
Section::make(__('commandocentrum.emulator_backups'))
->description(__('commandocentrum.emulator_backups_desc'))
->icon('heroicon-s-archive-box')
->schema([
Placeholder::make('backups_list')
->label('')
->content(fn () => $this->renderBackupsListView()),
]),
Section::make(__('commandocentrum.nitro_client'))
->description(__('commandocentrum.nitro_client_desc'))
->icon('heroicon-o-cloud-arrow-down')
->afterHeader([
Action::make('detect_paths')
->label('🔍 ' . __('commandocentrum.auto_detect'))
->color('success')
->action('detectAndSavePaths'),
Action::make('check_nitro')
->label(__('commandocentrum.check'))
->color('info')
->action('checkNitroUpdates'),
Action::make('build_nitro')
->label(__('commandocentrum.build'))
->color('pink')
->action('buildNitro'),
Action::make('generate_configs')
->label(__('commandocentrum.generate_configs'))
->color('indigo')
->action('generateNitroConfigs'),
Action::make('save_nitro')
->label(__('commandocentrum.save'))
->color('primary')
->action('saveNitro'),
])
->schema([
Placeholder::make('nitro_settings')
->label('')
->content(fn () => $this->renderNitroSettingsView()),
]),
Section::make(__('commandocentrum.auto_updates'))
->description(__('commandocentrum.auto_updates_desc'))
->icon('heroicon-o-clock')
->columns(2)
->afterHeader([
Action::make('save_auto')
->label(__('commandocentrum.save'))
->color('primary')
->action('saveAutoUpdate'),
])
->schema([
Toggle::make('auto_update_enabled')
->label(__('commandocentrum.enable_auto_updates')),
TextInput::make('auto_update_schedule')
->label(__('commandocentrum.schedule')),
TextInput::make('auto_update_days')
->label(__('commandocentrum.days')),
]),
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.update_history'))
->description(__('commandocentrum.update_history_desc'))
->icon('heroicon-o-clock')
->schema([
Placeholder::make('history')
->label('')
->content(fn () => $this->renderUpdateHistoryView()),
]),
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(): \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) : '✗ ' . __('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(): \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' => '