You've already forked Atomcms-edit
2f30a058a4
- Replace all hardcoded Dutch/English strings with __() translation calls - Update 13 Blade components to use translation keys - Update Commandocentrum.php controller with translation calls - Add comprehensive Dutch (nl.json) and English (en.json) translations - 150+ translation keys for UI labels, messages, and notifications - Supports all 21 languages available in the lang/ directory
1195 lines
57 KiB
PHP
Executable File
1195 lines
57 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Pages\Monitoring;
|
|
|
|
use App\Actions\Commandocentrum\EmulatorControlAction;
|
|
use App\Actions\Commandocentrum\NitroControlAction;
|
|
use App\Enums\AlertSeverity;
|
|
use App\Models\Miscellaneous\WebsitePermission;
|
|
use App\Models\StaffActivity;
|
|
use App\Services\AutoDetectService;
|
|
use App\Services\CatalogService;
|
|
use App\Services\Diagnostics\DiagnosticRunner;
|
|
use App\Services\GitHubService;
|
|
use App\Services\RconService;
|
|
use App\Services\SettingsService;
|
|
use App\Services\UpdateHistoryService;
|
|
use BackedEnum;
|
|
use Carbon\Carbon;
|
|
use Exception;
|
|
use Filament\Actions\Action;
|
|
use Filament\Forms\Components\Placeholder;
|
|
use Filament\Forms\Components\Select;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Forms\Components\Toggle;
|
|
use Filament\Forms\Concerns\InteractsWithForms;
|
|
use Filament\Forms\Contracts\HasForms;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Filament\Schemas\Components\Section;
|
|
use Filament\Schemas\Schema;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Process;
|
|
use Illuminate\Support\HtmlString;
|
|
use UnitEnum;
|
|
|
|
use function __;
|
|
|
|
final class Commandocentrum extends Page implements HasForms
|
|
{
|
|
use InteractsWithForms;
|
|
|
|
#[\Override]
|
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-command-line';
|
|
|
|
#[\Override]
|
|
protected static string|UnitEnum|null $navigationGroup = 'Commandocentrum';
|
|
|
|
#[\Override]
|
|
protected static ?string $navigationLabel = 'Commandocentrum';
|
|
|
|
#[\Override]
|
|
protected static ?string $title = 'Commandocentrum';
|
|
|
|
#[\Override]
|
|
protected static ?string $slug = 'commandocentrum';
|
|
|
|
#[\Override]
|
|
protected string $view = 'filament.pages.monitoring.commandocentrum';
|
|
|
|
public array $data = [];
|
|
|
|
public string $catalogSyncUrl = '';
|
|
|
|
/** @var array<\App\Services\Diagnostics\DiagnosticResult> */
|
|
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' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />',
|
|
'heroicon-o-server' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />',
|
|
'heroicon-o-circle-stack' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />',
|
|
'heroicon-o-cpu-chip' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />',
|
|
default => '',
|
|
};
|
|
|
|
return new HtmlString(<<<HTML
|
|
<div style="background:#fff;border-radius:16px;padding:24px;border:1px solid #e2e8f0;box-shadow:0 1px 3px rgba(0,0,0,0.06);transition:all 0.3s ease;cursor:default;" onmouseover="this.style.transform='translateY(-4px)';this.style.boxShadow='0 12px 24px rgba(0,0,0,0.1)';this.style.borderColor='{$color}40';" onmouseout="this.style.transform='translateY(0)';this.style.boxShadow='0 1px 3px rgba(0,0,0,0.06)';this.style.borderColor='#e2e8f0';">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
|
<div style="display:flex;align-items:center;gap:10px;">
|
|
<div style="background:{$color}15;padding:10px;border-radius:12px;">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="{$color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
{$iconSvg}
|
|
</svg>
|
|
</div>
|
|
<span style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:#94a3b8;">{$label}</span>
|
|
</div>
|
|
<div style="width:8px;height:8px;border-radius:50%;background:{$color};animation:pulse 2s infinite;"></div>
|
|
</div>
|
|
<div style="font-size:36px;font-weight:800;color:#1e293b;line-height:1;">{$value}</div>
|
|
</div>
|
|
<style>
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
</style>
|
|
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 = '<option value="main">main</option>';
|
|
foreach ($branches as $branch) {
|
|
$selected = $branch === $currentBranch ? 'selected' : '';
|
|
$html .= '<option value="' . $branch . '" ' . $selected . '>' . $branch . '</option>';
|
|
}
|
|
|
|
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 = '<option value="main">main</option>';
|
|
foreach ($branches as $branch) {
|
|
$selected = $branch === $currentBranch ? 'selected' : '';
|
|
$html .= '<option value="' . $branch . '" ' . $selected . '>' . $branch . '</option>';
|
|
}
|
|
|
|
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'] ? __('commandocentrum.success') : __('commandocentrum.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'] ? __('commandocentrum.success') : __('commandocentrum.error'), $result['message'], $result['success'] ? 'success' : 'danger');
|
|
}
|
|
|
|
public function stopEmulator(): void
|
|
{
|
|
$result = app(EmulatorControlAction::class)->stop();
|
|
$this->notify($result['success'] ? __('commandocentrum.success') : __('commandocentrum.error'), $result['message'], $result['success'] ? 'success' : 'danger');
|
|
}
|
|
|
|
public function restartEmulator(): void
|
|
{
|
|
$result = app(EmulatorControlAction::class)->restart();
|
|
$this->notify($result['success'] ? __('commandocentrum.success') : __('commandocentrum.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(__('commandocentrum.success'), __('commandocentrum.emulator_online'), 'success');
|
|
} else {
|
|
$this->notify(__('commandocentrum.warning'), __('commandocentrum.emulator_unreachable'), 'warning');
|
|
}
|
|
$this->fillForm();
|
|
} catch (Exception $e) {
|
|
$this->notify(__('commandocentrum.error'), $e->getMessage(), 'danger');
|
|
}
|
|
}
|
|
|
|
public function checkEmulatorUpdates(): void
|
|
{
|
|
$result = app(EmulatorControlAction::class)->update();
|
|
$this->notify($result['success'] ?? false ? __('commandocentrum.success') : __('commandocentrum.error'), $result['message'] ?? $result['error'] ?? __('commandocentrum.unknown'), ($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 ? __('commandocentrum.success') : __('commandocentrum.error'), $result['message'] ?? $result['error'] ?? __('commandocentrum.unknown'), ($result['success'] ?? false) ? 'success' : 'danger');
|
|
}
|
|
|
|
public function runSqlUpdates(): void
|
|
{
|
|
$result = app(EmulatorControlAction::class)->runSqlUpdates();
|
|
$this->notify($result['success'] ?? false ? __('commandocentrum.success') : __('commandocentrum.error'), $result['message'] ?? __('commandocentrum.unknown'), ($result['success'] ?? false) ? 'success' : 'danger');
|
|
}
|
|
|
|
public function restoreBackup(string $backupName): void
|
|
{
|
|
$result = app(EmulatorControlAction::class)->restoreBackup($backupName);
|
|
$this->notify($result['success'] ? __('commandocentrum.success') : __('commandocentrum.error'), $result['message'] ?? $result['error'] ?? __('commandocentrum.unknown'), $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(__('commandocentrum.success'), __('commandocentrum.emulator_settings_saved'), 'success');
|
|
} catch (Exception $e) {
|
|
$this->notify(__('commandocentrum.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(__('commandocentrum.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'] ? __('commandocentrum.success') : __('commandocentrum.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'] ? __('commandocentrum.success') : __('commandocentrum.error'), $result['message'], $result['success'] ? 'success' : 'danger');
|
|
$this->fillForm();
|
|
}
|
|
|
|
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 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(__('commandocentrum.success'), __('commandocentrum.paths_detected'), 'success');
|
|
} catch (Exception $e) {
|
|
$this->notify(__('commandocentrum.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(__('commandocentrum.success'), __('commandocentrum.nitro_settings_saved'), 'success');
|
|
} catch (Exception $e) {
|
|
$this->notify(__('commandocentrum.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(__('commandocentrum.success'), __('commandocentrum.auto_update_saved'), '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 = $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 = '<div style="display:flex;flex-direction:column;gap:16px;">';
|
|
|
|
// Summary cards
|
|
$html .= '<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px;">';
|
|
$html .= $this->getSummaryCardHtml(__('commandocentrum.healthy'), $okCount, '#22c55e', 'heroicon-o-check-circle');
|
|
$html .= $this->getSummaryCardHtml(__('commandocentrum.warnings'), $warningCount, '#f59e0b', 'heroicon-o-exclamation-triangle');
|
|
$html .= $this->getSummaryCardHtml(__('commandocentrum.errors'), $errorCount, '#ef4444', 'heroicon-o-x-circle');
|
|
$html .= '</div>';
|
|
|
|
// Overall status banner
|
|
$html .= '<div style="background:{$overallColor}15;border:1px solid {$overallColor}30;border-radius:12px;padding:16px;display:flex;align-items:center;gap:12px;">';
|
|
$html .= '<div style="width:12px;height:12px;border-radius:50%;background:{$overallColor};"></div>';
|
|
$html .= '<span style="font-weight:700;color:{$overallColor};font-size:16px;">' . __('commandocentrum.system_status') . ': {$overallLabel}</span>';
|
|
$html .= '</div>';
|
|
|
|
// Detailed results
|
|
if ($errorCount > 0 || $warningCount > 0) {
|
|
$html .= '<div style="display:flex;flex-direction:column;gap:8px;">';
|
|
foreach ($this->diagnostics as $result) {
|
|
if ($result->status === 'ok') {
|
|
continue;
|
|
}
|
|
$color = $result->status === 'error' ? '#ef4444' : '#f59e0b';
|
|
$icon = $result->status === 'error'
|
|
? '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="' . $color . '" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>'
|
|
: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="' . $color . '" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>';
|
|
|
|
$html .= '<div style="background:#fff;border:1px solid ' . $color . '30;border-radius:10px;padding:14px 16px;display:flex;align-items:flex-start;gap:12px;">';
|
|
$html .= '<div style="flex-shrink:0;margin-top:2px;">' . $icon . '</div>';
|
|
$html .= '<div style="flex:1;">';
|
|
$html .= '<div style="font-weight:600;color:#1e293b;font-size:14px;">' . e($result->name) . '</div>';
|
|
$html .= '<div style="color:#64748b;font-size:13px;margin-top:2px;">' . e($result->message) . '</div>';
|
|
if ($result->fix) {
|
|
$html .= '<div style="background:#f8fafc;border-radius:6px;padding:8px 12px;margin-top:8px;font-size:12px;color:#475569;font-family:monospace;">💡 ' . e($result->fix) . '</div>';
|
|
}
|
|
$html .= '</div></div>';
|
|
}
|
|
$html .= '</div>';
|
|
}
|
|
|
|
$html .= '</div>';
|
|
|
|
return new HtmlString($html);
|
|
}
|
|
|
|
private function getSummaryCardHtml(string $label, int $count, string $color, string $icon): string
|
|
{
|
|
$iconSvg = match ($icon) {
|
|
'heroicon-o-check-circle' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />',
|
|
'heroicon-o-exclamation-triangle' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />',
|
|
'heroicon-o-x-circle' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" />',
|
|
default => '',
|
|
};
|
|
|
|
return <<<HTML
|
|
<div style="background:#fff;border-radius:12px;padding:16px;border:1px solid #e2e8f0;box-shadow:0 1px 3px rgba(0,0,0,0.06);">
|
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;">
|
|
<div style="background:{$color}15;padding:8px;border-radius:10px;">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="{$color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
{$iconSvg}
|
|
</svg>
|
|
</div>
|
|
<span style="font-size:12px;font-weight:600;color:#64748b;">{$label}</span>
|
|
</div>
|
|
<div style="font-size:28px;font-weight:800;color:{$color};line-height:1;">{$count}</div>
|
|
</div>
|
|
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');
|
|
}
|
|
}
|