diff --git a/app/Actions/Commandocentrum/EmulatorControlAction.php b/app/Actions/Commandocentrum/EmulatorControlAction.php
index ee69904..4ec5824 100755
--- a/app/Actions/Commandocentrum/EmulatorControlAction.php
+++ b/app/Actions/Commandocentrum/EmulatorControlAction.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Actions\Commandocentrum;
-use App\Services\EmulatorUpdateService;
use App\Services\RconService;
use App\Services\SettingsService;
use Illuminate\Support\Facades\Process;
@@ -13,7 +12,6 @@ class EmulatorControlAction
{
public function __construct(
private readonly SettingsService $settings,
- private readonly EmulatorUpdateService $updateService,
) {}
public function start(): array
@@ -59,29 +57,4 @@ class EmulatorControlAction
return ['success' => true, 'message' => 'Alert verstuurd naar alle gebruikers!'];
}
-
- public function build(): array
- {
- return $this->updateService->buildFromSource();
- }
-
- public function update(): array
- {
- return $this->updateService->updateEmulator();
- }
-
- public function runSqlUpdates(): array
- {
- return $this->updateService->runSqlUpdates();
- }
-
- public function getBackups(): array
- {
- return $this->updateService->getBackupList();
- }
-
- public function restoreBackup(string $backupName): array
- {
- return $this->updateService->restoreBackup($backupName);
- }
}
diff --git a/app/Actions/Commandocentrum/NitroControlAction.php b/app/Actions/Commandocentrum/NitroControlAction.php
deleted file mode 100755
index 9159913..0000000
--- a/app/Actions/Commandocentrum/NitroControlAction.php
+++ /dev/null
@@ -1,119 +0,0 @@
-runGitPull($clientPath, $branch);
- $this->runGitPull($rendererPath, $branch);
-
- return ['success' => true, 'message' => 'Nitro bijgewerkt van GitHub'];
- }
-
- public function build(string $clientPath, string $rendererPath, string $branch): array
- {
- $this->runGitPull($clientPath, $branch);
- $this->runGitPull($rendererPath, $branch);
-
- Process::timeout(120)->run('cd ' . escapeshellarg($clientPath) . ' && sudo -u www-data npm install 2>&1');
- Process::timeout(120)->run('cd ' . escapeshellarg($rendererPath) . ' && sudo -u www-data npm install 2>&1');
-
- $exitCode = Artisan::call('build:theme');
-
- return [
- 'success' => $exitCode === 0,
- 'message' => $exitCode === 0 ? 'Nitro build succesvol!' : 'Build gestart - controleer handmatig',
- ];
- }
-
- public function generateConfigs(string $siteUrl, string $webroot, string $gamedataPath): array
- {
- if (! filter_var($siteUrl, FILTER_VALIDATE_URL)) {
- return ['success' => false, 'message' => 'Voer een geldige URL in'];
- }
-
- $existingConfigs = $this->readExistingConfigs($webroot, $gamedataPath);
-
- $exitCode = Artisan::call('app:generate-nitro-configs', ['--site-url' => $siteUrl]);
-
- if ($existingConfigs !== [] && $exitCode === 0) {
- $this->mergeExistingConfigs($webroot, $existingConfigs);
- }
-
- $this->settings->set('nitro_last_checked', now()->toIso8601String());
-
- return [
- 'success' => $exitCode === 0,
- 'message' => $exitCode === 0 ? 'Configs gegenereerd & bestaande instellingen behouden!' : 'Config gegenereerd (controleer handmatig)',
- ];
- }
-
- private function runGitPull(string $path, string $branch): void
- {
- Process::timeout(60)->run('cd ' . escapeshellarg($path) . ' && sudo -u www-data git pull origin ' . escapeshellarg($branch) . ' 2>&1');
- }
-
- private function readExistingConfigs(string $webroot, string $gamedataPath): array
- {
- $configs = [];
- $files = ['renderer-config.json', 'ui-config.json', 'UITexts.json'];
-
- foreach ($files as $file) {
- $path = $webroot . '/' . $file;
- $content = @file_get_contents($path);
- if ($content) {
- $decoded = json_decode($content, true);
- if (json_last_error() === JSON_ERROR_NONE) {
- $configs[$file] = $decoded;
- }
- }
- }
-
- if ($gamedataPath !== '') {
- $gamedataConfigs = [
- 'ExternalTexts.json' => $gamedataPath . '/config/ExternalTexts.json',
- 'FurnitureData.json' => $gamedataPath . '/config/FurnitureData.json',
- 'ProductData.json' => $gamedataPath . '/config/ProductData.json',
- 'FigureData.json' => $gamedataPath . '/config/FigureData.json',
- ];
-
- foreach ($gamedataConfigs as $key => $path) {
- $content = @file_get_contents($path);
- if ($content) {
- $decoded = json_decode($content, true);
- if (json_last_error() === JSON_ERROR_NONE) {
- $configs['gamedata.' . $key] = $decoded;
- }
- }
- }
- }
-
- return $configs;
- }
-
- private function mergeExistingConfigs(string $webroot, array $existingConfigs): void
- {
- if (isset($existingConfigs['renderer-config.json'])) {
- $newPath = $webroot . '/renderer-config.json';
- $newContent = @file_get_contents($newPath);
- $newConfig = json_decode($newContent, true);
-
- if ($newConfig && json_last_error() === JSON_ERROR_NONE) {
- $merged = array_merge($existingConfigs['renderer-config.json'], $newConfig);
- @file_put_contents($newPath, json_encode($merged, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
- }
- }
- }
-}
diff --git a/app/Console/Commands/AutoUpdateCommand.php b/app/Console/Commands/AutoUpdateCommand.php
deleted file mode 100755
index 056cc69..0000000
--- a/app/Console/Commands/AutoUpdateCommand.php
+++ /dev/null
@@ -1,150 +0,0 @@
-info('Automatische update check...');
-
- $isForced = $this->option('force');
- $sqlOnly = $this->option('sql-only');
- $emuOnly = $this->option('emu-only');
-
- if (! $isForced && ! $this->isScheduledTime()) {
- $this->line('Niet de geplande tijd, overslaan.');
-
- return Command::SUCCESS;
- }
-
- if (! $sqlOnly) {
- $this->runEmulatorUpdate($alertService, $updateService);
- }
-
- if (! $emuOnly) {
- $this->runSqlUpdates($alertService, $updateService);
- }
-
- return Command::SUCCESS;
- }
-
- private function isScheduledTime(): bool
- {
- $enabled = setting('auto_update_enabled', true);
-
- if (! $enabled) {
- return true;
- }
-
- $scheduleTime = setting('auto_update_schedule', '03:00');
- $scheduleDays = setting('auto_update_days', '0,1,2,3,4,5,6');
-
- $now = now();
- $currentTime = $now->format('H:i');
- $currentDay = (int) $now->dayOfWeek;
-
- $allowedDays = array_map(intval(...), explode(',', $scheduleDays));
-
- if (! in_array($currentDay, $allowedDays)) {
- return false;
- }
-
- if ($currentTime !== $scheduleTime) {
- $minuteDiff = abs(strtotime($currentTime) - strtotime((string) $scheduleTime)) / 60;
- if ($minuteDiff > 5) {
- return false;
- }
- }
-
- return true;
- }
-
- private function runEmulatorUpdate(AlertService $alertService, EmulatorUpdateService $updateService): void
- {
- $check = $updateService->checkForUpdates();
-
- if (! ($check['update_available'] ?? false)) {
- $this->line('Emulator is al up-to-date.');
-
- return;
- }
-
- $this->warn('Nieuwe emulator versie beschikbaar: ' . ($check['latest_version'] ?? 'onbekend'));
- $this->info('Emulator updaten...');
-
- $result = $updateService->updateEmulator();
-
- if ($result['success']) {
- $this->info('Emulator succesvol geรผpdatet!');
- $alertService->sendEmulatorUpdate($result['version'] ?? 'onbekend', $result['message'] ?? '');
- Log::info('[AutoUpdate] Emulator updated to v' . ($result['version'] ?? 'unknown'));
- } else {
- $this->error('Emulator update mislukt: ' . ($result['error'] ?? 'onbekende fout'));
- $alertService->send(
- AlertType::EMULATOR_ERROR,
- 'Emulator Auto-Update Mislukt: ' . ($result['error'] ?? 'Onbekende fout'),
- [],
- AlertChannel::DISCORD,
- );
- }
-
- Cache::put(self::CACHE_KEY_LAST_EMU_UPDATE, now()->toIso8601String());
- }
-
- private function runSqlUpdates(AlertService $alertService, EmulatorUpdateService $updateService): void
- {
- $check = $updateService->checkForSqlUpdates();
-
- if (! ($check['has_updates'] ?? false)) {
- $this->line('Geen SQL updates beschikbaar.');
-
- return;
- }
-
- $this->warn($check['message']);
- $this->info('SQL updates uitvoeren...');
-
- $result = $updateService->runSqlUpdates();
-
- if ($result['success'] && ! empty($result['files_run'])) {
- $count = count($result['files_run']);
- $this->info("{$count} SQL updates succesvol uitgevoerd!");
- $alertService->sendSqlUpdate($count, $result['message'] ?? '');
- Log::info('[AutoUpdate] SQL updates completed', ['files' => $result['files_run']]);
- } elseif (! empty($result['errors'])) {
- $this->warn('SQL updates met fouten: ' . implode(', ', $result['errors']));
- $alertService->send(
- AlertType::CRITICAL_ERROR,
- 'SQL Updates Met Fouten: ' . implode(', ', $result['errors']),
- ['files' => $result['errors']],
- AlertChannel::DISCORD,
- );
- }
-
- Cache::put(self::CACHE_KEY_LAST_SQL_UPDATE, now()->toIso8601String());
- }
-}
diff --git a/app/Console/Commands/EmulatorUpdateCommand.php b/app/Console/Commands/EmulatorUpdateCommand.php
deleted file mode 100755
index a24c5a8..0000000
--- a/app/Console/Commands/EmulatorUpdateCommand.php
+++ /dev/null
@@ -1,172 +0,0 @@
-option('repair')) {
- return $this->repairEmulator($updateService, $alertService);
- }
-
- if (! $updateService->isConfigured()) {
- $this->error('Geen GitHub URL geconfigureerd voor emulator updates.');
- $this->info('Configureer dit in Filament > Settings > Emulator');
-
- return Command::FAILURE;
- }
-
- $this->info('Emulator update service gestart...');
-
- $checkResult = $updateService->checkForUpdates();
-
- if (isset($checkResult['error'])) {
- $this->error($checkResult['error']);
-
- return Command::FAILURE;
- }
-
- $this->table(
- ['Property', 'Value'],
- [
- ['Huidige Versie', $checkResult['current_version']],
- ['Nieuwste Versie', $checkResult['latest_version']],
- ['Update Beschikbaar', $checkResult['update_available'] ? 'JA' : 'NEE'],
- ['Type', $checkResult['update_type'] ?? 'N/A'],
- ],
- );
-
- if ($this->option('check')) {
- return Command::SUCCESS;
- }
-
- $force = $this->option('force');
- $rebuild = $this->option('rebuild');
-
- if (! $checkResult['update_available'] && ! $force && ! $rebuild) {
- $this->info('Emulator is al up-to-date!');
-
- return Command::SUCCESS;
- }
-
- if (! $checkResult['update_available'] && ($force || $rebuild)) {
- $this->warn('Geen nieuwe versie beschikbaar, maar force/rebuild aangevraagd.');
- }
-
- if (! $force && ! $rebuild && ! $this->confirm('Wil je de emulator updaten naar v' . $checkResult['latest_version'] . '?')) {
- $this->info('Update geannuleerd.');
-
- return Command::SUCCESS;
- }
-
- $this->info('Emulator wordt geรผpdatet...');
-
- try {
- if ($rebuild || $checkResult['type'] === 'source_build') {
- $this->info('Build vanaf source...');
- $result = $updateService->buildFromSource($force);
- } else {
- $result = $updateService->updateEmulator();
- }
-
- if ($result['success']) {
- $this->info($result['message'] ?? 'Emulator succesvol geรผpdatet!');
- $alertService->send(
- AlertType::EMULATOR_ONLINE,
- 'Emulator succesvol geรผpdatet naar v' . ($result['version'] ?? 'onbekend'),
- [
- 'version' => $result['version'] ?? 'onbekend',
- 'jar' => $result['jar'] ?? 'N/A',
- ],
- AlertChannel::DISCORD,
- );
- Log::info('[EmulatorUpdateCommand] Update successful', $result);
-
- return Command::SUCCESS;
- }
-
- $this->error('Update mislukt: ' . ($result['error'] ?? 'Onbekende fout'));
- $alertService->send(
- AlertType::EMULATOR_ERROR,
- 'Emulator update mislukt: ' . ($result['error'] ?? 'Onbekende fout'),
- [],
- AlertChannel::DISCORD,
- );
- Log::error('[EmulatorUpdateCommand] Update failed', $result);
-
- return Command::FAILURE;
-
- } catch (\Exception $e) {
- $this->error('Update exception: ' . $e->getMessage());
- $alertService->send(
- AlertType::CRITICAL_ERROR,
- 'Emulator update exception: ' . $e->getMessage(),
- [],
- AlertChannel::DISCORD,
- );
- Log::error('[EmulatorUpdateCommand] Exception', ['error' => $e->getMessage()]);
-
- return Command::FAILURE;
- }
- }
-
- private function repairEmulator(EmulatorUpdateService $updateService, AlertService $alertService): int
- {
- $this->warn('๐ง Emulator repair modus gestart...');
- $this->info('Dit zal de emulator status controleren en proberen te repareren.');
-
- try {
- $repairResult = $updateService->repairEmulator();
-
- if ($repairResult['success']) {
- $this->info('โ
Repair succesvol!');
- foreach ($repairResult['actions'] ?? [] as $action) {
- $this->line(' - ' . $action);
- }
- $alertService->send(
- AlertType::EMULATOR_ONLINE,
- 'Emulator gerepareerd',
- ['actions' => $repairResult['actions'] ?? []],
- AlertChannel::DISCORD,
- );
-
- return Command::SUCCESS;
- }
-
- $this->error('โ Repair mislukt: ' . ($repairResult['error'] ?? 'Onbekende fout'));
- $alertService->send(
- AlertType::EMULATOR_ERROR,
- 'Emulator repair mislukt: ' . ($repairResult['error'] ?? 'Onbekende fout'),
- [],
- AlertChannel::DISCORD,
- );
-
- return Command::FAILURE;
-
- } catch (\Exception $e) {
- $this->error('โ Repair exception: ' . $e->getMessage());
-
- return Command::FAILURE;
- }
- }
-}
diff --git a/app/Console/Commands/GenerateNitroConfigs.php b/app/Console/Commands/GenerateNitroConfigs.php
deleted file mode 100755
index adf0bfd..0000000
--- a/app/Console/Commands/GenerateNitroConfigs.php
+++ /dev/null
@@ -1,796 +0,0 @@
-option('site-url') ?? setting('nitro_site_url', config('app.url', 'https://epicnabbo.nl'));
-
- $this->info('๐ง Nitro Config Generator');
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
-
- // Check 1: Validate URL
- $this->addCheck(function () use ($siteUrl) {
- if (empty($siteUrl) || ! filter_var($siteUrl, FILTER_VALIDATE_URL)) {
- throw new \Exception("Invalid URL: {$siteUrl}");
- }
- $this->line(" โ URL: {$siteUrl}");
- });
-
- $nitroService = new NitroUpdateService;
- $status = $nitroService->getStatus();
- $webroot = $status['webroot'] ?? '/var/www/Client';
- $buildPath = $status['build_path'] ?? '/var/www/atomcms/nitro-client/dist';
-
- // Check 2: Webroot exists
- $this->addCheck(function () use ($webroot) {
- $result = Process::timeout(5)->run('test -d ' . escapeshellarg((string) $webroot));
- if ($result->exitCode() !== 0) {
- throw new \Exception("Webroot does not exist: {$webroot}");
- }
- $this->line(" โ Webroot exists: {$webroot}");
- });
-
- // Check 3: Webroot is writable
- $this->addCheck(function () use ($webroot) {
- $result = Process::timeout(5)->run('test -w ' . escapeshellarg((string) $webroot));
- if ($result->exitCode() !== 0) {
- throw new \Exception("Webroot is not writable: {$webroot}");
- }
- $this->line(' โ Webroot is writable');
- });
-
- // Check 4: Build path exists
- $this->addCheck(function () use ($buildPath) {
- $result = Process::timeout(5)->run('test -d ' . escapeshellarg((string) $buildPath));
- if ($result->exitCode() !== 0) {
- throw new \Exception("Build path does not exist: {$buildPath}");
- }
- $this->line(" โ Build path exists: {$buildPath}");
- });
-
- // Check both buildPath and webroot for example files
- $this->addCheck(function () use ($buildPath, $webroot) {
- // Map .example to .json fallback
- $fileMap = [
- 'renderer-config.example' => 'renderer-config.json',
- 'ui-config.example' => 'ui-config.json',
- 'UITexts.example' => 'UITexts.example',
- ];
-
- $allFound = true;
-
- foreach ($fileMap as $exampleFile => $jsonFile) {
- // Check .example file first
- $buildPathResult = Process::timeout(5)->run('test -f ' . escapeshellarg($buildPath . '/' . $exampleFile));
- $webrootResult = Process::timeout(5)->run('test -f ' . escapeshellarg($webroot . '/' . $exampleFile));
-
- if ($buildPathResult->exitCode() === 0) {
- $this->line(" โ Found in build path: {$exampleFile}");
- } elseif ($webrootResult->exitCode() === 0) {
- $this->line(" โ Found in webroot: {$exampleFile}");
- } elseif ($jsonFile !== $exampleFile) {
- // Check json fallback for .example files
- $jsonBuildResult = Process::timeout(5)->run('test -f ' . escapeshellarg($buildPath . '/' . $jsonFile));
- $jsonWebrootResult = Process::timeout(5)->run('test -f ' . escapeshellarg($webroot . '/' . $jsonFile));
- if ($jsonBuildResult->exitCode() === 0) {
- $this->line(" โ Found in build path: {$jsonFile}");
- } elseif ($jsonWebrootResult->exitCode() === 0) {
- $this->line(" โ Found in webroot: {$jsonFile}");
- } else {
- $this->warn(" โ Missing: {$exampleFile} or {$jsonFile}");
- $allFound = false;
- }
- } else {
- $this->warn(" โ Missing: {$exampleFile}");
- $allFound = false;
- }
- }
-
- if (! $allFound) {
- throw new \Exception('Some example files are missing');
- }
- });
-
- // Check 6: Check disk space
- $this->addCheck(function () use ($webroot) {
- $result = Process::timeout(10)->run("df -h {$webroot} | tail -1 | awk '{print $4}'");
- if ($result->successful()) {
- $free = trim($result->output());
- $this->line(" โ Free space: {$free}");
- }
- });
-
- // Check 7: Validate bundled/config gamedata files
- $this->addCheck(function () {
- $bundledConfigDir = '/var/www/Gamedata/bundled/config';
- $configDir = '/var/www/Gamedata/config';
- $requiredFiles = ['HabboAvatarActions.json', 'FurnitureData.json', 'ExternalTexts.json', 'ProductData.json', 'FigureData.json', 'FigureMap.json', 'EffectMap.json'];
-
- // Create bundled/config if it doesn't exist
- if (! is_dir($bundledConfigDir)) {
- mkdir($bundledConfigDir, 0755, true);
- $this->line(" ๐ Created directory: {$bundledConfigDir}");
- }
-
- // Copy missing files from config to bundled/config
- foreach ($requiredFiles as $file) {
- $targetPath = $bundledConfigDir . '/' . $file;
- if (! file_exists($targetPath)) {
- $sourcePath = $configDir . '/' . $file;
- if (file_exists($sourcePath)) {
- copy($sourcePath, $targetPath);
- $this->line(" ๐ Copied: {$file} to bundled/config");
- }
- }
- }
-
- $this->line(' โ Gamedata bundled/config files ready');
- });
-
- // Check 8: Validate existing config files
- $this->addCheck(function () use ($webroot) {
- $configFiles = ['renderer-config.json', 'ui-config.json', 'UITexts.json'];
- foreach ($configFiles as $file) {
- $path = $webroot . '/' . $file;
- $result = Process::timeout(5)->run('test -f ' . escapeshellarg($path));
- if ($result->exitCode() === 0) {
- // Validate JSON
- $jsonCheck = Process::timeout(5)->run('python3 -c "import json; json.load(open(\'' . $path . '\'))" 2>&1 || echo "INVALID"');
- if (str_contains($jsonCheck->output(), 'INVALID')) {
- $this->warn(" โ Invalid JSON: {$file}");
- } else {
- $this->line(" โ Valid JSON: {$file}");
- }
- } else {
- $this->line(" - New file: {$file}");
- }
- }
- });
-
- // Run all checks
- $this->line('');
- $this->info('Running pre-flight checks...');
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
-
- try {
- foreach ($this->checks as $check) {
- $check();
- }
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
- $this->info('โ
All checks passed!');
- } catch (\Exception $e) {
- $this->error('โ Check failed: ' . $e->getMessage());
- $this->newLine();
- $this->error('Please fix the issue before generating configs.');
-
- return 1;
- }
-
- // Generate configs
- $this->newLine();
- $this->info('Generating configuration files...');
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
-
- $protocol = str_starts_with((string) $siteUrl, 'https') ? 'https' : 'http';
- $host = preg_replace('/^https?:\/\//', '', (string) $siteUrl);
-
- try {
- // Step 0: Sync latest example files from GitHub
- $this->syncExampleFromGithub($buildPath, $webroot);
-
- $rendererConfig = $this->generateRendererConfig($protocol, $host, $buildPath, $webroot);
- $uiConfig = $this->generateUiConfig($protocol, $host, $buildPath, $webroot);
-
- // Compare with existing deployed configs
- $this->compareConfigs($rendererConfig, 'renderer-config', $webroot);
- $this->compareConfigs($uiConfig, 'ui-config', $webroot);
-
- // Validate generated JSON
- $this->validateJson($rendererConfig, 'renderer-config');
- $this->validateJson($uiConfig, 'ui-config');
-
- $tempFile = '/tmp/nitro_config_' . uniqid();
- file_put_contents($tempFile . '_renderer', json_encode($rendererConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
- file_put_contents($tempFile . '_ui', json_encode($uiConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
-
- // Copy generated configs
- $commands = [
- 'cp "' . $tempFile . '_renderer" "' . $webroot . '/renderer-config.json"',
- 'cp "' . $tempFile . '_ui" "' . $webroot . '/ui-config.json"',
- ];
-
- // Copy UITexts.json if it exists (check both buildPath and webroot)
- $uitextsInBuild = Process::timeout(5)->run('test -f "' . $buildPath . '/UITexts.example"')->exitCode() === 0;
- $uitextsInWebroot = Process::timeout(5)->run('test -f "' . $webroot . '/UITexts.example"')->exitCode() === 0;
- if ($uitextsInBuild) {
- $commands[] = 'cp "' . $buildPath . '/UITexts.example" "' . $webroot . '/UITexts.json"';
- $this->line(' โ Generated: UITexts.json (from build path)');
- } elseif ($uitextsInWebroot) {
- $commands[] = 'cp "' . $webroot . '/UITexts.example" "' . $webroot . '/UITexts.json"';
- $this->line(' โ Generated: UITexts.json (from webroot)');
- }
-
- $commands[] = 'rm "' . $tempFile . '_renderer" "' . $tempFile . '_ui"';
-
- Process::timeout(10)->run(implode(' && ', $commands));
-
- // Set proper ownership
- Process::timeout(10)->run("chown www-data:www-data {$webroot}/renderer-config.json {$webroot}/ui-config.json {$webroot}/UITexts.json 2>/dev/null");
-
- setting('nitro_config_generated_at', now()->toIso8601String());
-
- $this->line(' โ Generated: renderer-config.json');
- $this->line(' โ Generated: ui-config.json');
-
- // Verify generated files
- $this->verifyGeneratedFiles($webroot);
-
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
- $this->info('โ
Configs generated successfully!');
-
- Log::info('[NitroConfig] Generated successfully', [
- 'webroot' => $webroot,
- 'host' => $host,
- ]);
-
- return 0;
- } catch (\Exception $e) {
- $this->error('โ Generation failed: ' . $e->getMessage());
- Log::error('[NitroConfig] Generation failed', ['error' => $e->getMessage()]);
-
- return 1;
- }
- }
-
- private function addCheck(callable $check): void
- {
- $this->checks[] = function () use ($check) {
- $check();
- };
- }
-
- private function validateJson(array $data, string $name): void
- {
- $encoded = json_encode($data, JSON_THROW_ON_ERROR);
- $decoded = json_decode($encoded, true);
-
- if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
- throw new \Exception("Invalid JSON generated for {$name}");
- }
-
- $this->line(" โ Validated: {$name}");
- }
-
- private function verifyGeneratedFiles(string $webroot): void
- {
- $files = ['renderer-config.json', 'ui-config.json', 'UITexts.json'];
- $allValid = true;
-
- foreach ($files as $file) {
- $path = $webroot . '/' . $file;
- $result = Process::timeout(5)->run('test -f ' . escapeshellarg($path));
-
- if ($result->exitCode() === 0) {
- $size = Process::timeout(5)->run('stat -c%s ' . escapeshellarg($path));
- $sizeStr = trim($size->output()) . ' bytes';
- $this->line(" โ Verified: {$file} ({$sizeStr})");
- } else {
- $this->warn(" โ Missing: {$file}");
- $allValid = false;
- }
- }
-
- if (! $allValid) {
- throw new \Exception('Some files were not generated');
- }
- }
-
- private function generateRendererConfig(string $protocol, string $host, string $buildPath, string $webroot): array
- {
- $httpProtocol = $protocol === 'https' ? 'https' : 'http';
- $wssProtocol = $protocol === 'https' ? 'wss' : 'ws';
- $wsHost = 'ws.' . $host;
-
- $rendererConfig = [];
-
- // Check build path first, then webroot for .example file
- $examplePath = $buildPath . '/renderer-config.example';
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($examplePath))->exitCode() !== 0) {
- $examplePath = $webroot . '/renderer-config.example';
- }
-
- // Fallback to .json version (newer Nitro-V3 format)
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($examplePath))->exitCode() !== 0) {
- $jsonPath = $buildPath . '/renderer-config.json';
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($jsonPath))->exitCode() === 0) {
- $examplePath = $jsonPath;
- } else {
- $jsonPath = $webroot . '/renderer-config.json';
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($jsonPath))->exitCode() === 0) {
- $examplePath = $jsonPath;
- }
- }
- }
-
- // Load COMPLETE example file as base
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($examplePath))->exitCode() === 0) {
- $content = file_get_contents($examplePath);
- // Fix invalid escape sequences (literal \n, \r, \t in JSON values)
- $content = $this->fixInvalidJsonEscapeSequences($content);
- $rendererConfig = @json_decode($content, true) ?: [];
- $this->line(' โ Loaded renderer config from: ' . basename($examplePath) . ' (' . count($rendererConfig) . ' keys)');
- } else {
- $this->warn(' โ renderer-config.example/json not found');
-
- return [];
- }
-
- // Recursively replace ALL URLs in the entire config
- $rendererConfig = $this->replaceUrlsRecursively($rendererConfig, $httpProtocol, $wssProtocol, $host, $wsHost);
-
- // Auto-detect asset directory names on disk and fix paths
- $rendererConfig = $this->autoDetectAssetPaths($rendererConfig, $webroot);
-
- // Special handling for socket.url - use ws subdomain
- if (isset($rendererConfig['socket.url'])) {
- $rendererConfig['socket.url'] = $wssProtocol . '://ws.' . $host;
- }
-
- // Auto-detect local asset paths from Gamedata directory
- $gamedataBase = $httpProtocol . '://' . $host;
-
- // Check what directories exist in Gamedata and set URLs accordingly
- $gamedataPath = '/var/www/Gamedata';
- if (is_dir($gamedataPath)) {
- // Check for bundled directory - serve via /gamedata/bundled (nginx alias)
- if (is_dir($gamedataPath . '/bundled')) {
- $rendererConfig['asset.url'] = $gamedataBase . '/gamedata/bundled';
- }
- // JSON config files are in /gamedata/config/
- if (is_dir($gamedataPath . '/config')) {
- $rendererConfig['gamedata.url'] = $gamedataBase . '/gamedata/config';
- }
- // Check for c_images directory - serve via /gamedata/c_images
- if (is_dir($gamedataPath . '/c_images')) {
- $rendererConfig['image.library.url'] = $gamedataBase . '/gamedata/c_images/';
- // Use icons folder for furni icons
- if (is_dir($gamedataPath . '/icons')) {
- $rendererConfig['hof.furni.url'] = $gamedataBase . '/gamedata/icons';
- } else {
- $rendererConfig['hof.furni.url'] = $gamedataBase . '/gamedata/c_images/dcr/hof_furni';
- }
- }
- // Fix furni icon path - icons are directly in hof.furni folder, not in icons subfolder
- if (isset($rendererConfig['furni.asset.icon.url'])) {
- $rendererConfig['furni.asset.icon.url'] = '${hof.furni.url}/%libname%%param%_icon.png';
- }
- // Fix sound machine samples path - sounds are in /gamedata/sounds/
- if (isset($rendererConfig['external.samples.url'])) {
- $rendererConfig['external.samples.url'] = $gamedataBase . '/gamedata/sounds/sound_machine_sample_%sample%.mp3';
- }
- // Check for images directory
- if (is_dir($gamedataPath . '/images')) {
- $rendererConfig['images.url'] = $gamedataBase . '/gamedata/images';
- }
- }
-
- // Add missing keys that might not be in the example
- if (! isset($rendererConfig['external.plugins'])) {
- $rendererConfig['external.plugins'] = [];
- }
-
- // Add YouTube API key from settings
- $youtubeApiKey = setting('youtube_api_key', '');
- if (! empty($youtubeApiKey)) {
- $rendererConfig['youtube.api.key'] = $youtubeApiKey;
- $this->line(' โ Added YouTube API key to renderer config');
- }
-
- // Ensure pet.types matches the exact required list in order
- if (isset($rendererConfig['pet.types'])) {
- $requiredPetTypes = [
- 'dog',
- 'cat',
- 'croco',
- 'terrier',
- 'bear',
- 'pig',
- 'lion',
- 'rhino',
- 'spider',
- 'turtle',
- 'chicken',
- 'frog',
- 'dragon',
- 'monster',
- 'monkey',
- 'horse',
- 'monsterplant',
- 'bunnyeaster',
- 'bunnyevil',
- 'bunnydepressed',
- 'bunnylove',
- 'pigeongood',
- 'pigeonevil',
- 'demonmonkey',
- 'bearbaby',
- 'terrierbaby',
- 'gnome',
- 'leprechaun',
- 'kittenbaby',
- 'puppybaby',
- 'pigletbaby',
- 'haloompa',
- 'fools',
- 'pterosaur',
- 'velociraptor',
- 'cow',
- 'dragondog',
- 'pkmshaymin2',
- 'LeetEendjes',
- 'pkmnentei',
- 'squirtle',
- 'LeetBH',
- 'LeetCaviaaa',
- 'LeetFantj',
- 'LeetHotelMario',
- 'LeetUil',
- 'LeetWolf',
- 'pokemon_mewblu',
- 'LeetBB',
- 'LeetHotelMari1',
- 'LeetMewtw',
- 'LeetPikachu',
- 'LeetYos',
- 'LeetE',
- 'LeetMewt1',
- 'LeetPen',
- 'slendermn',
- 'pkmnPAPI0',
- 'pkmnPAPI1',
- 'pkmnPAPI2',
- 'pkmnPAPI3',
- 'pkmnPAPI4',
- 'pokmn_mew',
- 'pkmashhhpet',
- 'pkmbeautfly',
- 'pkmcelebipe',
- 'pkmdarkraip',
- 'pkmeeveepet',
- 'pkmjirachip',
- 'pkmpichupet',
- 'pkmriolupet',
- 'pkmshayminp',
- 'pkmtogepipe',
- 'pkmvictinip',
- 'slenderm1',
- 'LeetEendj16',
- 'pkmnente1',
- 'LeetBa',
- 'babymeisje',
- 'babyBH',
- 'bb_hbx',
- 'LeetUi1',
- ];
-
- $rendererConfig['pet.types'] = $requiredPetTypes;
- $this->line(' โ Set pet.types to exact required list');
- }
-
- return $rendererConfig;
- }
-
- private function generateUiConfig(string $protocol, string $host, string $buildPath, string $webroot): array
- {
- $httpProtocol = $protocol === 'https' ? 'https' : 'http';
- $wssProtocol = $protocol === 'https' ? 'wss' : 'ws';
- $wsHost = 'ws.' . $host;
-
- $uiConfig = [];
-
- // Check build path first, then webroot for .example file
- $examplePath = $buildPath . '/ui-config.example';
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($examplePath))->exitCode() !== 0) {
- $examplePath = $webroot . '/ui-config.example';
- }
-
- // Fallback to .json version (newer Nitro-V3 format)
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($examplePath))->exitCode() !== 0) {
- $jsonPath = $buildPath . '/ui-config.json';
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($jsonPath))->exitCode() === 0) {
- $examplePath = $jsonPath;
- } else {
- $jsonPath = $webroot . '/ui-config.json';
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($jsonPath))->exitCode() === 0) {
- $examplePath = $jsonPath;
- }
- }
- }
-
- // Load COMPLETE example file as base
- if (Process::timeout(5)->run('test -f ' . escapeshellarg($examplePath))->exitCode() === 0) {
- $content = file_get_contents($examplePath);
- // Fix invalid escape sequences
- $content = $this->fixInvalidJsonEscapeSequences($content);
- $uiConfig = @json_decode($content, true) ?: [];
- $this->line(' โ Loaded ui config from: ' . basename($examplePath) . ' (' . count($uiConfig) . ' keys)');
- } else {
- $this->warn(' โ ui-config.example/json not found');
-
- return [];
- }
-
- // Recursively replace ALL URLs in the entire config
- $uiConfig = $this->replaceUrlsRecursively($uiConfig, $httpProtocol, $wssProtocol, $host, $wsHost);
-
- return $uiConfig;
- }
-
- private function fixInvalidJsonEscapeSequences(string $content): string
- {
- // The example file has literal backslash + any letter in JSON values
- // which breaks JSON. We need to escape all of these.
-
- $backslash = chr(92);
- $letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
-
- foreach ($letters as $letter) {
- $content = str_replace($backslash . $letter, $backslash . $backslash . $letter, $content);
- }
-
- return $content;
- }
-
- private function replaceUrlsRecursively(array $config, string $httpProtocol, string $wsProtocol, string $host, string $wsHost): array
- {
- foreach ($config as &$value) {
- if (is_array($value)) {
- // Handle nested arrays (like navigator.room.models)
- if ($this->isAssociativeArray($value)) {
- $value = $this->replaceUrlsRecursively($value, $httpProtocol, $wsProtocol, $host, $wsHost);
- } else {
- // Handle arrays of URLs
- $value = array_map(function ($item) use ($httpProtocol, $wsProtocol, $host, $wsHost) {
- if (is_string($item)) {
- return $this->replaceUrl($item, $httpProtocol, $wsProtocol, $host, $wsHost);
- }
-
- return $item;
- }, $value);
- }
- } elseif (is_string($value)) {
- $value = $this->replaceUrl($value, $httpProtocol, $wsProtocol, $host, $wsHost);
- }
- }
-
- return $config;
- }
-
- private function replaceUrl(string $url, string $httpProtocol, string $wsProtocol, string $host, string $wsHost): string
- {
- // Replace ws/wss URLs with ws subdomain
- if (str_starts_with($url, 'ws://') || str_starts_with($url, 'wss://')) {
- $path = parse_url($url, PHP_URL_PATH) ?? '/';
-
- return $wsProtocol . '://' . $wsHost . ($path !== '/' ? $path : '');
- }
-
- // Replace localhost in all URLs
- $url = preg_replace('#https?://localhost(?::\d+)?#', $httpProtocol . '://' . $host, $url);
- $url = preg_replace('#wss?://localhost(?::\d+)?#', $wsProtocol . '://' . $wsHost, (string) $url);
- $url = preg_replace('#localhost(?::\d+)?#', $host, (string) $url);
-
- // Fix broken escape sequences in URL paths (from invalid JSON in example files)
- // Pattern: /public\nitro-assets\gamedata or any variation
- $url = preg_replace('#/public\\n[a-z_-]+\\\\gamedata#i', '/gamedata', (string) $url);
- $url = preg_replace('#/public\\\\[a-z_-]+\\\\gamedata#i', '/gamedata', (string) $url);
- $url = preg_replace('#/nitro-assets\\\\gamedata#i', '/gamedata', (string) $url);
- $url = preg_replace('#/nitro\\\\[a-z_-]+#i', '/gamedata', (string) $url);
-
- // Fix known asset path patterns
- $url = str_replace('/public/nitro-assets/gamedata', '/gamedata', $url);
- $url = str_replace('/swf/gamedata', '/gamedata', $url);
-
- // Clean up any remaining backslashes in URLs
- $url = str_replace('\\', '/', $url);
-
- // Clean up any remaining double slashes (but keep protocol slashes)
- $url = preg_replace('#([^:])//+#', '$1/', $url);
-
- return $url;
- }
-
- private function isAssociativeArray(array $arr): bool
- {
- if ($arr === []) {
- return false;
- }
-
- return array_keys($arr) !== range(0, count($arr) - 1);
- }
-
- private function autoDetectAssetPaths(array $config, string $webroot): array
- {
- // Check multiple possible gamedata locations
- $possiblePaths = [
- $webroot . '/gamedata',
- '/var/www/Gamedata',
- '/var/www/gamedata',
- ];
- $gamedataPath = array_find($possiblePaths, fn ($path) => is_dir($path));
-
- if (! $gamedataPath) {
- return $config;
- }
-
- $assetDir = opendir($gamedataPath);
- $actualDirs = [];
- while (($entry = readdir($assetDir)) !== false) {
- if ($entry !== '.' && $entry !== '..' && is_dir($gamedataPath . '/' . $entry)) {
- $actualDirs[strtolower($entry)] = $entry;
- }
- }
- closedir($assetDir);
-
- $pathChecks = [
- 'pet.asset.url' => 'pets',
- 'furni.asset.url' => 'furniture',
- 'avatar.asset.url' => 'clothes',
- 'avatar.asset.effect.url' => 'effect',
- 'generic.asset.url' => 'generic_custom',
- ];
-
- foreach ($config as $key => &$value) {
- if (! is_string($value) || ! isset($pathChecks[$key])) {
- continue;
- }
-
- $expectedDir = $pathChecks[$key];
- $lowerExpected = strtolower($expectedDir);
- $actualName = null;
-
- // Special case: "figure" is often used instead of "clothes" for avatars
- if ($lowerExpected === 'clothes' && isset($actualDirs['figure'])) {
- $actualName = $actualDirs['figure'];
- } elseif (isset($actualDirs[$lowerExpected])) {
- $actualName = $actualDirs[$lowerExpected];
- } else {
- foreach ($actualDirs as $actualLower => $actual) {
- if (str_starts_with($actualLower, rtrim($lowerExpected, 's')) ||
- str_starts_with(rtrim($actualLower, 's'), rtrim($lowerExpected, 's'))) {
- $actualName = $actual;
- break;
- }
- }
- }
-
- if ($actualName && $actualName !== $expectedDir) {
- $value = str_replace("/{$expectedDir}/", "/{$actualName}/", $value);
- $this->line(" ๐ Auto-detected: {$key} -> /{$actualName}/ (was /{$expectedDir}/)");
- }
- }
-
- return $config;
- }
-
- private function syncExampleFromGithub(string $buildPath, string $webroot): void
- {
- $this->info('Syncing latest examples from GitHub...');
-
- $rendererRepo = setting('nitro_github_url', '');
- $repo = $this->parseRepoFromUrl($rendererRepo);
- if (! $repo) {
- $this->warn(' โ No GitHub repo configured, skipping sync');
-
- return;
- }
-
- $branch = setting('nitro_github_branch', 'main');
-
- $examples = [
- 'renderer-config.example' => 'renderer-config.example',
- 'ui-config.example' => 'ui-config.example',
- 'UITexts.example' => 'UITexts.json',
- ];
-
- foreach (array_keys($examples) as $remoteFile) {
- $tempFile = '/tmp/' . $remoteFile . '_' . uniqid();
- $fetched = false;
-
- // Try multiple paths: root, public/, nitro-client/dist/, Nitro-V3 paths
- $paths = ['', 'public/', 'nitro-client/dist/', 'dist/', 'src/', 'assets/'];
- foreach ($paths as $path) {
- $url = "https://raw.githubusercontent.com/{$repo}/{$branch}/{$path}{$remoteFile}";
- $result = Process::timeout(15)->run("curl -sL -o {$tempFile} '{$url}'");
-
- if ($result->successful() && file_exists($tempFile) && filesize($tempFile) > 10) {
- $content = file_get_contents($tempFile);
- $data = @json_decode($content, true);
-
- if (is_array($data) && $data !== []) {
- // Save to both buildPath and webroot
- file_put_contents($buildPath . '/' . $remoteFile, $content);
- file_put_contents($webroot . '/' . $remoteFile, $content);
- $this->line(" โ Synced: {$remoteFile} (" . count($data) . " keys from {$path}{$remoteFile})");
- $fetched = true;
- break;
- }
- }
- @unlink($tempFile);
- }
-
- if (! $fetched) {
- $this->line(" - Skipped: {$remoteFile} (not found in any path)");
- }
- }
- }
-
- private function parseRepoFromUrl(string $url): ?string
- {
- if (preg_match('/github\.com\/([^\/]+\/[^\/\?#]+)/', $url, $matches)) {
- return rtrim($matches[1], '/');
- }
-
- return null;
- }
-
- private function compareConfigs(array $generated, string $name, string $webroot): void
- {
- $currentPath = $webroot . '/' . $name . '.json';
- if (! file_exists($currentPath)) {
- $this->line(" โน {$name}: Geen bestaande config โ nieuwe generatie");
-
- return;
- }
-
- $current = @json_decode(file_get_contents($currentPath), true);
- if (! is_array($current)) {
- $this->warn(" โ {$name}: Bestaande config is ongeldig JSON");
-
- return;
- }
-
- $newKeys = array_diff(array_keys($generated), array_keys($current));
- $removedKeys = array_diff(array_keys($current), array_keys($generated));
- $changedKeys = [];
-
- foreach (array_intersect(array_keys($generated), array_keys($current)) as $key) {
- if ($generated[$key] !== $current[$key]) {
- $changedKeys[] = $key;
- }
- }
-
- if ($newKeys !== []) {
- $this->line(" ๐ {$name}: " . count($newKeys) . ' nieuwe key(s): ' . implode(', ', array_slice($newKeys, 0, 10)));
- if (count($newKeys) > 10) {
- $this->line(' ... en ' . (count($newKeys) - 10) . ' meer');
- }
- }
- if ($removedKeys !== []) {
- $this->line(" ๐ {$name}: " . count($removedKeys) . ' verwijderde key(s): ' . implode(', ', array_slice($removedKeys, 0, 5)));
- }
- if ($changedKeys !== []) {
- $this->line(" ๐ {$name}: " . count($changedKeys) . ' gewijzigde key(s): ' . implode(', ', array_slice($changedKeys, 0, 5)));
- }
- if ($newKeys === [] && $removedKeys === [] && $changedKeys === []) {
- $this->line(" โ {$name}: Geen wijzigingen");
- }
- }
-}
diff --git a/app/Console/Commands/NitroUpdateCommand.php b/app/Console/Commands/NitroUpdateCommand.php
deleted file mode 100755
index eeae9a2..0000000
--- a/app/Console/Commands/NitroUpdateCommand.php
+++ /dev/null
@@ -1,272 +0,0 @@
-info('Nitro update check...');
-
- if ($this->option('diagnose')) {
- return $this->diagnose($nitroService);
- }
-
- if ($this->option('repair')) {
- return $this->repair($alertService, $nitroService);
- }
-
- $isForced = $this->option('force');
- $buildOnly = $this->option('build-only');
- $full = $this->option('full');
-
- if ($buildOnly) {
- return $this->buildOnly($alertService, $nitroService);
- }
-
- if (! $isForced && ! $this->isScheduledTime()) {
- $this->line('Niet de geplande tijd, overslaan.');
-
- return Command::SUCCESS;
- }
-
- $repo = setting('nitro_github_url', 'duckietm/Nitro-V3 (default)');
- $this->info("GitHub: {$repo}");
-
- if ($full) {
- return $this->fullReinstall($alertService, $nitroService);
- }
-
- $status = $nitroService->getStatus();
-
- $this->info('Client: ' . ($status['client_installed'] ? 'โ
' : 'โ'));
- $this->info('Renderer: ' . ($status['renderer_installed'] ? 'โ
' : 'โ'));
- $this->info('Build: ' . ($status['build_exists'] ? 'โ
' : 'โ'));
- $this->info('Deployed: ' . ($status['deployed'] ? 'โ
' : 'โ'));
-
- if (! $status['client_installed'] || ! $status['renderer_installed']) {
- $this->warn('Nitro niet volledig geรฏnstalleerd. Voer --full uit voor volledige installatie.');
- if ($this->confirm('Wil je nu volledig installeren?')) {
- return $this->fullReinstall($alertService, $nitroService);
- }
-
- return Command::SUCCESS;
- }
-
- $this->line('โ
Nitro client is up-to-date.');
-
- if ($isForced) {
- $this->warn('Force update aangevraagd...');
-
- return $this->fullReinstall($alertService, $nitroService);
- }
-
- Cache::put(self::CACHE_KEY_LAST_UPDATE, now()->toIso8601String());
-
- return Command::SUCCESS;
- }
-
- private function diagnose(NitroUpdateService $nitroService): int
- {
- $this->info('๐ Nitro diagnose...');
- $diagnosis = $nitroService->diagnose();
-
- $this->newLine();
- $this->info('=== Controle Resultaten ===');
-
- $checks = $diagnosis['checks'] ?? [];
- foreach ($checks as $key => $value) {
- if (is_bool($value)) {
- $this->line(($value ? 'โ
' : 'โ') . ' ' . $key);
- } elseif (is_array($value)) {
- $this->line($key . ': ' . count($value) . ' items');
- } else {
- $this->line($key . ': ' . $value);
- }
- }
-
- if (! empty($diagnosis['issues'])) {
- $this->newLine();
- $this->error('=== Problemen ===');
- foreach ($diagnosis['issues'] as $issue) {
- $this->line('โ ' . $issue);
- }
- }
-
- if (! empty($diagnosis['recommendations'])) {
- $this->newLine();
- $this->info('=== Aanbevelingen ===');
- foreach ($diagnosis['recommendations'] as $rec) {
- $this->line('๐ก ' . $rec);
- }
- }
-
- return Command::SUCCESS;
- }
-
- private function repair(AlertService $alertService, NitroUpdateService $nitroService): int
- {
- $this->warn('๐ง Nitro repair modus gestart...');
- $this->info('Dit zal de Nitro installatie controleren en repareren.');
-
- try {
- $repairResult = $nitroService->repair();
-
- if ($repairResult['success']) {
- $this->info('โ
Repair succesvol!');
- foreach ($repairResult['actions'] ?? [] as $action) {
- $this->line(' - ' . $action);
- }
- $alertService->send(
- AlertType::EMULATOR_UPDATE,
- 'Nitro client gerepareerd',
- ['actions' => $repairResult['actions'] ?? []],
- AlertChannel::DISCORD,
- );
-
- return Command::SUCCESS;
- }
-
- $this->error('โ Repair mislukt: ' . ($repairResult['error'] ?? 'Onbekende fout'));
- if (! empty($repairResult['actions'])) {
- $this->line('Uitgevoerde acties:');
- foreach ($repairResult['actions'] as $action) {
- $this->line(' - ' . $action);
- }
- }
- if (! empty($repairResult['errors'])) {
- $this->line('Fouten:');
- foreach ($repairResult['errors'] as $error) {
- $this->line(' โ ' . $error);
- }
- }
- $alertService->send(
- AlertType::EMULATOR_ERROR,
- 'Nitro repair mislukt: ' . ($repairResult['error'] ?? 'Onbekende fout'),
- [],
- AlertChannel::DISCORD,
- );
-
- return Command::FAILURE;
-
- } catch (\Exception $e) {
- $this->error('โ Repair exception: ' . $e->getMessage());
-
- return Command::FAILURE;
- }
- }
-
- private function buildOnly(AlertService $alertService, NitroUpdateService $nitroService): int
- {
- $this->info('Build en deploy uitvoeren...');
-
- $buildResult = $nitroService->buildClient();
-
- if (! $buildResult['success']) {
- $this->error('Build mislukt: ' . ($buildResult['error'] ?? 'Onbekend'));
-
- return Command::FAILURE;
- }
-
- $nitroService->deployClient();
- $nitroService->generateConfigs();
-
- $this->info('Client succesvol gedeployed!');
- $alertService->send(
- AlertType::EMULATOR_UPDATE,
- 'Nitro client opnieuw gedeployed',
- [],
- AlertChannel::DISCORD,
- );
-
- return Command::SUCCESS;
- }
-
- private function fullReinstall(AlertService $alertService, NitroUpdateService $nitroService): int
- {
- $this->warn('Volledige reinstall wordt uitgevoerd...');
-
- try {
- $result = $nitroService->updateNitro();
-
- if ($result['success']) {
- $this->info('Nitro succesvol opnieuw geรฏnstalleerd!');
- $alertService->send(
- AlertType::EMULATOR_UPDATE,
- 'Nitro client succesvol geรผpdatet en gedeployed',
- [],
- AlertChannel::DISCORD,
- );
-
- return Command::SUCCESS;
- }
-
- $this->error('Reinstall mislukt: ' . ($result['error'] ?? 'Onbekende fout'));
- $alertService->send(
- AlertType::CRITICAL_ERROR,
- 'Nitro Update Mislukt: ' . ($result['error'] ?? 'Onbekende fout'),
- [],
- AlertChannel::DISCORD,
- );
-
- return Command::FAILURE;
-
- } catch (\Exception $e) {
- $this->error('Reinstall exception: ' . $e->getMessage());
-
- return Command::FAILURE;
- }
- }
-
- private function isScheduledTime(): bool
- {
- $scheduleTime = setting('nitro_auto_update_schedule', '03:00');
- $scheduleDays = setting('nitro_auto_update_days', '0,6');
- $enabled = setting('nitro_auto_update_enabled', false);
-
- if (! $enabled) {
- return false;
- }
-
- $now = now();
- $currentTime = $now->format('H:i');
- $currentDay = (int) $now->dayOfWeek;
-
- $allowedDays = array_map(intval(...), explode(',', $scheduleDays));
-
- if (! in_array($currentDay, $allowedDays)) {
- return false;
- }
-
- if ($currentTime !== $scheduleTime) {
- $minuteDiff = abs(strtotime($currentTime) - strtotime((string) $scheduleTime)) / 60;
- if ($minuteDiff > 5) {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/app/Console/Commands/SwitchNitroBranch.php b/app/Console/Commands/SwitchNitroBranch.php
deleted file mode 100755
index 07995f4..0000000
--- a/app/Console/Commands/SwitchNitroBranch.php
+++ /dev/null
@@ -1,49 +0,0 @@
-option('branch') ?? 'main';
-
- $this->info("๐ Switching Nitro to branch: {$branch}");
-
- try {
- $nitroService = new NitroUpdateService;
- $result = $nitroService->updateNitro(true);
-
- if ($result['success'] ?? false) {
- $this->info("โ
Switched to {$branch} successfully!");
- $this->info($result['message'] ?? '');
- Log::info('[NitroSwitch] Success', ['branch' => $branch, 'message' => $result['message'] ?? '']);
- } else {
- $this->error('โ Switch failed: ' . ($result['error'] ?? 'Unknown error'));
- Log::error('[NitroSwitch] Failed', ['branch' => $branch, 'error' => $result['error'] ?? 'Unknown']);
- }
-
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- return ($result['success'] ?? false) ? 0 : 1;
- } catch (\Exception $e) {
- $this->error('โ Exception: ' . $e->getMessage());
- Log::error('[NitroSwitch] Exception', ['branch' => $branch, 'error' => $e->getMessage()]);
-
- return 1;
- }
- }
-}
diff --git a/app/Console/Commands/SystemHealthCommand.php b/app/Console/Commands/SystemHealthCommand.php
deleted file mode 100755
index c34900a..0000000
--- a/app/Console/Commands/SystemHealthCommand.php
+++ /dev/null
@@ -1,251 +0,0 @@
-option('json');
- $this->option('details');
-
- $health = $this->performHealthCheck($emuService, $nitroService);
-
- if ($json) {
- $this->line(json_encode($health, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
-
- return $health['status'] === 'healthy' ? 0 : 1;
- }
-
- $this->displayHealthCheck($health);
-
- return $health['status'] === 'healthy' ? 0 : 1;
- }
-
- private function performHealthCheck(EmulatorUpdateService $emuService, NitroUpdateService $nitroService): array
- {
- $checks = [];
- $issues = [];
- $warnings = [];
-
- $checks['timestamp'] = now()->toIso8601String();
-
- $checks['php'] = [
- 'version' => PHP_VERSION,
- 'status' => 'ok',
- ];
-
- $checks['system'] = [
- 'os' => PHP_OS,
- 'user' => posix_getpwuid(posix_geteuid())['name'] ?? 'unknown',
- ];
-
- $diskFree = @disk_free_space('/');
- $diskTotal = @disk_total_space('/');
- $diskPercent = $diskTotal > 0 ? round(($diskFree / $diskTotal) * 100, 1) : 0;
- $checks['disk'] = [
- 'free' => $this->formatBytes($diskFree),
- 'total' => $this->formatBytes($diskTotal),
- 'percent_free' => $diskPercent,
- 'status' => $diskPercent > 10 ? 'ok' : 'critical',
- ];
- if ($diskPercent < 10) {
- $issues[] = 'Disk space critically low: ' . $diskPercent . '% free';
- } elseif ($diskPercent < 20) {
- $warnings[] = 'Disk space low: ' . $diskPercent . '% free';
- }
-
- try {
- $emuDiagnosis = $emuService->diagnose();
- $checks['emulator'] = [
- 'configured' => $emuDiagnosis['checks']['is_configured'] ?? false,
- 'jar_exists' => $emuDiagnosis['checks']['jar_exists'] ?? false,
- 'service_running' => $emuDiagnosis['checks']['service_running'] ?? false,
- 'db_connected' => $emuDiagnosis['checks']['emulator_db_connected'] ?? false,
- 'status' => empty($emuDiagnosis['issues']) ? 'ok' : 'issues',
- 'issues' => $emuDiagnosis['issues'] ?? [],
- ];
-
- if (! empty($emuDiagnosis['issues'])) {
- foreach ($emuDiagnosis['issues'] as $issue) {
- $issues[] = 'Emulator: ' . $issue;
- }
- }
- } catch (\Exception $e) {
- $checks['emulator'] = [
- 'status' => 'error',
- 'error' => $e->getMessage(),
- ];
- $issues[] = 'Emulator check failed: ' . $e->getMessage();
- }
-
- try {
- $nitroDiagnosis = $nitroService->diagnose();
- $checks['nitro'] = [
- 'client_installed' => $nitroDiagnosis['checks']['client_installed'] ?? false,
- 'renderer_installed' => $nitroDiagnosis['checks']['renderer_installed'] ?? false,
- 'deployed' => $nitroDiagnosis['checks']['deployed'] ?? false,
- 'status' => empty($nitroDiagnosis['issues']) ? 'ok' : 'issues',
- 'issues' => $nitroDiagnosis['issues'] ?? [],
- ];
-
- if (! empty($nitroDiagnosis['issues'])) {
- foreach ($nitroDiagnosis['issues'] as $issue) {
- $issues[] = 'Nitro: ' . $issue;
- }
- }
- } catch (\Exception $e) {
- $checks['nitro'] = [
- 'status' => 'error',
- 'error' => $e->getMessage(),
- ];
- $issues[] = 'Nitro check failed: ' . $e->getMessage();
- }
-
- try {
- $sqlDiagnosis = $emuService->diagnoseSqlUpdates();
- $checks['sql_updates'] = [
- 'table_exists' => $sqlDiagnosis['table_exists'] ?? false,
- 'applied' => $sqlDiagnosis['applied_count'] ?? 0,
- 'pending' => $sqlDiagnosis['pending_count'] ?? 0,
- 'status' => ($sqlDiagnosis['pending_count'] ?? 0) > 0 ? 'pending' : 'ok',
- ];
-
- if (($sqlDiagnosis['pending_count'] ?? 0) > 0) {
- $warnings[] = $sqlDiagnosis['pending_count'] . ' SQL updates pending';
- }
- } catch (\Exception $e) {
- $checks['sql_updates'] = [
- 'status' => 'error',
- 'error' => $e->getMessage(),
- ];
- }
-
- $webserverCheck = Process::timeout(5)->run('which nginx || which apache2 || which httpd');
- $checks['webserver'] = [
- 'installed' => $webserverCheck->successful(),
- 'status' => $webserverCheck->successful() ? 'ok' : 'unknown',
- ];
-
- $mysqlCheck = Process::timeout(5)->run('which mysql || which mariadb');
- $checks['database'] = [
- 'client_installed' => $mysqlCheck->successful(),
- 'status' => $mysqlCheck->successful() ? 'ok' : 'unknown',
- ];
-
- $nodeCheck = Process::timeout(5)->run('which node && which yarn');
- $checks['node'] = [
- 'installed' => $nodeCheck->successful(),
- 'status' => $nodeCheck->successful() ? 'ok' : 'missing',
- ];
- if (! $nodeCheck->successful()) {
- $warnings[] = 'Node.js/Yarn niet geรฏnstalleerd';
- }
-
- $status = $issues === [] ? 'healthy' : ($warnings === [] ? 'warning' : 'degraded');
-
- return [
- 'status' => $status,
- 'checks' => $checks,
- 'issues' => $issues,
- 'warnings' => $warnings,
- ];
- }
-
- private function displayHealthCheck(array $health): void
- {
- $status = $health['status'];
-
- $statusIcon = match ($status) {
- 'healthy' => 'โ
',
- 'warning' => 'โ ๏ธ',
- 'degraded' => 'โ',
- default => 'โ',
- };
-
- $this->info("{$statusIcon} Systeem Gezondheid: " . strtoupper($status));
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
-
- $checks = $health['checks'] ?? [];
-
- $this->line('๐ฆ Systeem:');
- $this->line(' PHP: ' . ($checks['php']['version'] ?? '?'));
- $this->line(' User: ' . ($checks['system']['user'] ?? '?'));
-
- $diskStatus = $checks['disk']['status'] ?? '?';
- $diskIcon = $diskStatus === 'ok' ? 'โ
' : 'โ';
- $this->line(" {$diskIcon} Disk: " . ($checks['disk']['percent_free'] ?? '?') . '% vrij');
-
- $emuStatus = $checks['emulator']['status'] ?? '?';
- $emuIcon = $emuStatus === 'ok' ? 'โ
' : 'โ ๏ธ';
- $this->line(" {$emuIcon} Emulator: " . ucfirst($emuStatus));
-
- $nitroStatus = $checks['nitro']['status'] ?? '?';
- $nitroIcon = $nitroStatus === 'ok' ? 'โ
' : 'โ ๏ธ';
- $this->line(" {$nitroIcon} Nitro: " . ucfirst($nitroStatus));
-
- $sqlStatus = $checks['sql_updates']['status'] ?? '?';
- $sqlIcon = $sqlStatus === 'ok' ? 'โ
' : 'โ ๏ธ';
- $this->line(" {$sqlIcon} SQL Updates: " . ucfirst($sqlStatus) . ' (' . ($checks['sql_updates']['applied'] ?? 0) . ' toegepast)');
-
- $nodeStatus = $checks['node']['status'] ?? '?';
- $nodeIcon = $nodeStatus === 'ok' ? 'โ
' : 'โ';
- $this->line(" {$nodeIcon} Node.js: " . ucfirst($nodeStatus));
-
- if (! empty($health['issues'])) {
- $this->line('');
- $this->error('โ Problemen:');
- foreach ($health['issues'] as $issue) {
- $this->line(' - ' . $issue);
- }
- }
-
- if (! empty($health['warnings'])) {
- $this->line('');
- $this->warn('โ ๏ธ Waarschuwingen:');
- foreach ($health['warnings'] as $warning) {
- $this->line(' - ' . $warning);
- }
- }
-
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
-
- if ($status === 'healthy') {
- $this->info('โ
Alles werkt correct!');
- } else {
- $this->warn('Run "php artisan system:repair" om problemen op te lossen');
- }
- }
-
- private function formatBytes(?float $bytes): string
- {
- if ($bytes === null) {
- return 'N/A';
- }
-
- $units = ['B', 'KB', 'MB', 'GB', 'TB'];
- $i = 0;
-
- while ($bytes >= 1024 && $i < count($units) - 1) {
- $bytes /= 1024;
- $i++;
- }
-
- return round($bytes, 2) . ' ' . $units[$i];
- }
-}
diff --git a/app/Console/Commands/SystemRepairCommand.php b/app/Console/Commands/SystemRepairCommand.php
deleted file mode 100755
index a545cb0..0000000
--- a/app/Console/Commands/SystemRepairCommand.php
+++ /dev/null
@@ -1,213 +0,0 @@
-info('๐ง System Repair Service gestart...');
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
-
- $checkOnly = $this->option('check');
- $force = $this->option('force');
- $full = $this->option('full');
- $emuOnly = $this->option('emu');
- $nitroOnly = $this->option('nitro');
-
- $results = [
- 'emulator' => null,
- 'nitro' => null,
- ];
-
- if (! $nitroOnly) {
- $results['emulator'] = $this->repairEmulator($emuService, $checkOnly, $force, $full);
- }
-
- if (! $emuOnly) {
- $results['nitro'] = $this->repairNitro($nitroService, $checkOnly, $force, $full);
- }
-
- Cache::put(self::CACHE_KEY_LAST_REPAIR, now()->toIso8601String());
-
- $emuOk = $results['emulator'] === null || ($results['emulator']['success'] ?? false);
- $nitroOk = $results['nitro'] === null || ($results['nitro']['success'] ?? false);
-
- $this->line('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
- if ($emuOk && $nitroOk) {
- $this->info('โ
Alle systemen OK');
-
- return Command::SUCCESS;
- }
-
- return Command::FAILURE;
- }
-
- private function ensureBaseDirectories(): void
- {
- $basePaths = [
- '/var/www',
- '/var/www/atomcms',
- storage_path('app'),
- ];
-
- foreach ($basePaths as $path) {
- if (! is_dir($path)) {
- @mkdir($path, 0755, true);
- }
- }
-
- Process::timeout(5)->run('chown -R www-data:www-data /var/www/atomcms 2>/dev/null || true');
- Process::timeout(5)->run('chmod -R 755 /var/www/atomcms 2>/dev/null || true');
- }
-
- private function repairEmulator(EmulatorUpdateService $service, bool $checkOnly, bool $force, bool $full): array
- {
- $this->info('Emulator controleren...');
-
- $this->ensureBaseDirectories();
-
- try {
- $diagnosis = $service->diagnose();
-
- if (empty($diagnosis['issues']) && ! $force && ! $full) {
- $this->line(' โ
Emulator is OK');
-
- return ['success' => true, 'status' => 'ok'];
- }
-
- if ($checkOnly) {
- $this->warn(' โ ๏ธ Emulator problemen gevonden:');
- foreach ($diagnosis['issues'] ?? [] as $issue) {
- $this->line(' - ' . $issue);
- }
- foreach ($diagnosis['recommendations'] ?? [] as $rec) {
- $this->line(' ๐ก ' . $rec);
- }
-
- return ['success' => false, 'status' => 'issues_found', 'issues' => $diagnosis['issues']];
- }
-
- $this->warn(' ๐ง Emulator wordt gerepareerd...');
-
- if ($full) {
- $this->line(' ๐ฆ Volledige reset...');
- $repairResult = $service->repairEmulator();
- } else {
- $repairResult = $service->repairEmulator();
- }
-
- if ($repairResult['success']) {
- $this->info(' โ
Emulator gerepareerd!');
- foreach ($repairResult['actions'] ?? [] as $action) {
- $this->line(' - ' . $action);
- }
-
- return ['success' => true, 'status' => 'repaired', 'actions' => $repairResult['actions']];
- }
-
- $this->error(' โ Emulator repair mislukt: ' . ($repairResult['error'] ?? 'Onbekend'));
- if (! empty($repairResult['actions'])) {
- $this->line(' Uitgevoerde acties:');
- foreach ($repairResult['actions'] as $action) {
- $this->line(' - ' . $action);
- }
- }
-
- return ['success' => false, 'status' => 'failed', 'error' => $repairResult['error']];
-
- } catch (\Exception $e) {
- $this->error(' โ Emulator exception: ' . $e->getMessage());
- Log::error('[SystemRepair] Emulator exception', ['error' => $e->getMessage()]);
-
- return ['success' => false, 'status' => 'exception', 'error' => $e->getMessage()];
- }
- }
-
- private function repairNitro(NitroUpdateService $service, bool $checkOnly, bool $force, bool $full): array
- {
- $this->info('Nitro controleren...');
-
- $this->ensureBaseDirectories();
-
- try {
- $diagnosis = $service->diagnose();
-
- if (empty($diagnosis['issues']) && ! $force && ! $full) {
- $this->line(' โ
Nitro is OK');
-
- return ['success' => true, 'status' => 'ok'];
- }
-
- if ($checkOnly) {
- $this->warn(' โ ๏ธ Nitro problemen gevonden:');
- foreach ($diagnosis['issues'] ?? [] as $issue) {
- $this->line(' - ' . $issue);
- }
- foreach ($diagnosis['recommendations'] ?? [] as $rec) {
- $this->line(' ๐ก ' . $rec);
- }
-
- return ['success' => false, 'status' => 'issues_found', 'issues' => $diagnosis['issues']];
- }
-
- $this->warn(' ๐ง Nitro wordt gerepareerd...');
-
- if ($full) {
- $this->line(' ๐ฆ Volledige reset...');
- $repairResult = $service->updateNitro();
- } else {
- $repairResult = $service->repair();
- }
-
- if ($repairResult['success']) {
- $this->info(' โ
Nitro gerepareerd!');
- foreach ($repairResult['actions'] ?? [] as $action) {
- $this->line(' - ' . $action);
- }
-
- return ['success' => true, 'status' => 'repaired', 'actions' => $repairResult['actions']];
- }
-
- $this->error(' โ Nitro repair mislukt: ' . ($repairResult['error'] ?? 'Onbekend'));
- if (! empty($repairResult['actions'])) {
- $this->line(' Uitgevoerde acties:');
- foreach ($repairResult['actions'] as $action) {
- $this->line(' - ' . $action);
- }
- }
-
- return ['success' => false, 'status' => 'failed', 'error' => $repairResult['error']];
-
- } catch (\Exception $e) {
- $this->error(' โ Nitro exception: ' . $e->getMessage());
- Log::error('[SystemRepair] Nitro exception', ['error' => $e->getMessage()]);
-
- return ['success' => false, 'status' => 'exception', 'error' => $e->getMessage()];
- }
- }
-}
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index afa8406..7d0c3e3 100755
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -4,16 +4,10 @@ declare(strict_types=1);
namespace App\Console;
-use App\Console\Commands\AutoUpdateCommand;
use App\Console\Commands\DDoSDetectionCommand;
use App\Console\Commands\EmulatorMonitorCommand;
-use App\Console\Commands\EmulatorUpdateCommand;
use App\Console\Commands\FixCodeCommand;
-use App\Console\Commands\GenerateNitroConfigs;
-use App\Console\Commands\NitroUpdateCommand;
use App\Console\Commands\SystemCheckCommand;
-use App\Console\Commands\SystemHealthCommand;
-use App\Console\Commands\SystemRepairCommand;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -28,9 +22,6 @@ class Kernel extends ConsoleKernel
$schedule->command('maintenance:check-scheduled')->everyMinute()->withoutOverlapping();
$schedule->command('monitor:emulator')->everyMinute()->withoutOverlapping();
$schedule->command('monitor:ddos')->everyFiveMinutes()->withoutOverlapping();
- $schedule->command('update:auto')->everyMinute()->withoutOverlapping();
- $schedule->command('nitro:auto')->everyMinute()->withoutOverlapping();
- $schedule->command('system:repair')->everyTenMinutes()->withoutOverlapping();
}
#[\Override]
@@ -42,12 +33,6 @@ class Kernel extends ConsoleKernel
$this->commands[] = FixCodeCommand::class;
$this->commands[] = EmulatorMonitorCommand::class;
$this->commands[] = DDoSDetectionCommand::class;
- $this->commands[] = EmulatorUpdateCommand::class;
- $this->commands[] = AutoUpdateCommand::class;
- $this->commands[] = NitroUpdateCommand::class;
- $this->commands[] = SystemRepairCommand::class;
- $this->commands[] = SystemHealthCommand::class;
- $this->commands[] = GenerateNitroConfigs::class;
require base_path('routes/console.php');
}
diff --git a/app/Filament/Pages/Monitoring/AlertSettings.php b/app/Filament/Pages/Monitoring/AlertSettings.php
index 2448c96..ca66ae7 100755
--- a/app/Filament/Pages/Monitoring/AlertSettings.php
+++ b/app/Filament/Pages/Monitoring/AlertSettings.php
@@ -10,12 +10,8 @@ use App\Enums\AlertType;
use App\Models\Miscellaneous\AlertLog;
use App\Models\Miscellaneous\WebsiteSetting;
use App\Services\AlertService;
-use App\Services\EmulatorUpdateService;
-use App\Services\NitroUpdateService;
use App\Services\RconService;
use App\Services\SettingsService;
-use App\Services\SystemFixService;
-use App\Services\UpdateHistoryService;
use Carbon\Carbon;
use Filament\Actions\Action;
use Filament\Forms\Components\Placeholder;
@@ -91,20 +87,6 @@ final class AlertSettings extends Page implements HasForms
'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', '/var/www/atomcms/nitro-client'),
- 'nitro_renderer_path' => $this->getSetting('nitro_renderer_path', '/var/www/atomcms/nitro-renderer'),
- 'nitro_build_path' => $this->getSetting('nitro_build_path', '/var/www/atomcms/nitro-client/dist'),
- 'nitro_webroot' => $this->getSetting('nitro_webroot', '/var/www/Client'),
- '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' => '',
];
}
@@ -363,375 +345,11 @@ final class AlertSettings extends Page implements HasForms
])
->default(AlertSeverity::ERROR->value),
]),
- Section::make('Emulator Updates (.jar)')
- ->description('100% automatisch emulator updaten vanaf GitHub')
- ->icon('heroicon-o-archive-box-arrow-down')
- ->columns(2)
- ->afterHeader([
- Action::make('check_updates')
- ->label('Check Updates')
- ->action('checkEmulatorUpdates')
- ->color('info'),
- Action::make('save_update')
- ->label('Opslaan')
- ->action('saveEmulatorUpdate')
- ->color('primary'),
- ])
- ->schema([
- TextInput::make('emulator_github_url')
- ->label('GitHub Repository URL')
- ->placeholder('https://github.com/gebruiker/emulator-repo')
- ->hint('Basis URL van je GitHub repo (voor pre-compiled JARs)')
- ->columnSpanFull(),
- TextInput::make('emulator_source_repo')
- ->label('Source Repository URL (voor build vanaf source)')
- ->placeholder('https://github.com/gebruiker/emulator-source')
- ->hint('GitHub URL voor als je de emulator vanaf source wilt builden')
- ->columnSpanFull(),
- TextInput::make('emulator_jar_direct_url')
- ->label('Directe .jar URL (optioneel)')
- ->placeholder('https://github.com/user/repo/raw/main/Latest_Compiled_Version/emulator.jar')
- ->hint('Als de auto-detect niet werkt, vul hier de directe URL in')
- ->columnSpanFull(),
- TextInput::make('emulator_jar_path')
- ->label('Emulator Folder Pad')
- ->placeholder('/root/emulator'),
- TextInput::make('emulator_source_path')
- ->label('Source Code Pad (voor build vanaf source)')
- ->placeholder('/var/www/emulator-source')
- ->hint('Locatie waar de source code wordt gekloond'),
- TextInput::make('emulator_service_name')
- ->label('Emulator Service Naam')
- ->placeholder('arcturus'),
- TextInput::make('emulator_database_host')
- ->label('Database Host')
- ->placeholder('127.0.0.1'),
- TextInput::make('emulator_database_port')
- ->label('Database Port')
- ->placeholder('3306')
- ->default('3306'),
- TextInput::make('emulator_database_name')
- ->label('Database Naam')
- ->placeholder('habbo'),
- TextInput::make('emulator_database_username')
- ->label('Database Gebruiker')
- ->placeholder('root'),
- TextInput::make('emulator_database_password')
- ->label('Database Wachtwoord')
- ->password()
- ->placeholder('โขโขโขโขโขโข'),
- Select::make('emulator_github_branch')
- ->label('Branch')
- ->options(function () {
- $url = setting('emulator_github_url', '');
- $repo = $this->parseRepoFromUrl($url);
- if (! $repo) {
- return ['main' => 'main'];
- }
- $branches = $this->fetchBranches($repo);
- if ($branches === []) {
- return ['main' => 'main'];
- }
- return $branches;
- })
- ->default('main')
- ->hint('Branches worden automatisch herkend vanuit GitHub'),
- Placeholder::make('installed_jar')
- ->label('')
- ->content(function () {
- $label = '
';
- $label .= '
';
- $label .= '
Huidige .jar';
- $label .= '
';
- $label .= '
';
- return new HtmlString($label . $this->getInstalledJarHtml());
- }),
- Placeholder::make('current_version')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '
';
- $label .= '
Versie';
- $label .= '
';
- $label .= '
';
- return new HtmlString($label . $this->getEmulatorVersionHtml());
- }),
- Placeholder::make('update_status')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '
';
- $label .= '
Update Status';
- $label .= '
';
- $label .= '
';
- return new HtmlString($label . $this->getUpdateStatusHtml());
- }),
- Action::make('run_update')
- ->label('๐ Update Nu (100% Automatisch)')
- ->action('runFullUpdate')
- ->color('success')
- ->visible(fn () => $this->isUpdateAvailable()),
- Action::make('reset_update_date')
- ->label('Reset Update Datum')
- ->action('resetUpdateDate')
- ->color('warning')
- ->icon('heroicon-o-arrow-path'),
- Action::make('force_check')
- ->label('๐ Force Check')
- ->action('forceUpdateCheck')
- ->color('info')
- ->icon('heroicon-o-magnifying-glass'),
- Action::make('switch_to_main')
- ->label('๐ Switch to Branch')
- ->action('switchEmulatorBranch')
- ->color('warning')
- ->icon('heroicon-o-arrow-uturn-left')
- ->visible(fn () => strtolower((string) setting('emulator_github_branch', 'main')) !== strtolower((string) setting('emulator_installed_branch', 'main'))),
- Placeholder::make('debug_status')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '';
- $label .= 'Debug Info';
- $label .= '';
- $label .= '
';
- return new HtmlString($label . $this->getDebugStatusHtml());
- })
- ->columnSpanFull(),
- ]),
- Section::make('Automatische Updates')
- ->description('Stel een automatische schema in voor emulator en SQL updates')
- ->icon('heroicon-o-clock')
- ->columns(3)
- ->afterHeader([
- Action::make('save_auto_update')
- ->label('Opslaan')
- ->action('saveAutoUpdate')
- ->color('primary'),
- ])
- ->schema([
- Toggle::make('auto_update_enabled')
- ->label('Automatische Updates Inschakelen')
- ->columnSpan(3),
- TextInput::make('auto_update_schedule')
- ->label('Tijd (HH:MM)')
- ->placeholder('03:00')
- ->columnSpan(1),
- TextInput::make('auto_update_days')
- ->label('Dagen (0-6, kommagescheiden)')
- ->placeholder('0,6')
- ->hint('0 = Zondag, 1 = Maandag, ..., 6 = Zaterdag')
- ->columnSpan(2),
- Placeholder::make('auto_update_next')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '';
- $label .= 'Volgende Geplande Update';
- $label .= '';
- $label .= '
';
-
- return new HtmlString($label . $this->getNextAutoUpdateHtml());
- }),
- ]),
- Section::make('Database Updates (SQL)')
- ->description('Automatisch SQL updates uitvoeren van GitHub')
- ->icon('heroicon-o-server')
- ->columns(2)
- ->afterHeader([
- Action::make('check_sql_updates')
- ->label('Check SQL Updates')
- ->action('checkSqlUpdates')
- ->color('info'),
- Action::make('run_sql_updates')
- ->label('Run SQL Updates')
- ->action('runSqlUpdates')
- ->color('warning'),
- Action::make('fix_all')
- ->label('Auto Fix All')
- ->action('fixAll')
- ->color('success')
- ->icon('heroicon-o-wrench'),
- ])
- ->schema([
- Placeholder::make('sql_status')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '
';
- $label .= '
SQL Status';
- $label .= '
';
- $label .= '
';
-
- return new HtmlString($label . $this->getSqlStatusHtml());
- }),
- Placeholder::make('sql_last_update')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '
';
- $label .= '
Laatste SQL Update';
- $label .= '
';
- $label .= '
';
-
- return new HtmlString($label . $this->getSqlLastUpdateHtml());
- }),
- ]),
- Section::make('Nitro Client Updates')
- ->description('Automatisch Nitro client en renderer updaten en builden')
- ->icon('heroicon-o-device-phone-mobile')
- ->columns(2)
- ->afterHeader([
- Action::make('check_nitro_updates')
- ->label('Check Updates')
- ->action('checkNitroUpdates')
- ->color('info'),
- Action::make('build_nitro')
- ->label('Build & Deploy')
- ->action('buildNitro')
- ->color('warning'),
- Action::make('save_nitro')
- ->label('Opslaan')
- ->action('saveNitro')
- ->color('primary'),
- Action::make('switch_nitro_main')
- ->label('๐ Switch to Branch')
- ->action('switchNitroBranch')
- ->color('warning')
- ->icon('heroicon-o-arrow-uturn-left')
- ->visible(fn () => strtolower((string) setting('nitro_github_branch', 'main')) !== strtolower((string) setting('nitro_installed_branch', 'main'))),
- ])
- ->schema([
- TextInput::make('nitro_github_url')
- ->label('GitHub Client URL')
- ->placeholder('https://github.com/gebruiker/Nitro-V3')
- ->hint('Auto-detecteert ook de renderer repo')
- ->columnSpanFull(),
- TextInput::make('nitro_client_path')
- ->label('Client Pad')
- ->placeholder('/var/www/atomcms/nitro-client'),
- TextInput::make('nitro_renderer_path')
- ->label('Renderer Pad')
- ->placeholder('/var/www/atomcms/nitro-renderer'),
- TextInput::make('nitro_build_path')
- ->label('Build Output Pad')
- ->placeholder('/var/www/atomcms/nitro-client/dist'),
- TextInput::make('nitro_webroot')
- ->label('Web Root Pad')
- ->placeholder('/var/www/Client'),
- Select::make('nitro_github_branch')
- ->label('Branch')
- ->options(function () {
- $url = setting('nitro_github_url', '');
- $repo = $this->parseRepoFromUrl($url);
- if (! $repo) {
- return ['main' => 'main'];
- }
- $branches = $this->fetchBranches($repo);
- if ($branches === []) {
- return ['main' => 'main'];
- }
-
- return $branches;
- })
- ->default('main')
- ->hint('Branches worden automatisch herkend vanuit GitHub'),
- Placeholder::make('nitro_status')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '
';
- $label .= '
Status';
- $label .= '
';
- $label .= '
';
-
- return new HtmlString($label . $this->getNitroStatusHtml());
- }),
- Placeholder::make('nitro_auto_schedule')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '
';
- $label .= '
Auto Update Schema';
- $label .= '
';
- $label .= '
';
-
- return new HtmlString($label . $this->getNitroScheduleHtml());
- }),
- Toggle::make('nitro_auto_update_enabled')
- ->label('Automatische Updates Inschakelen')
- ->columnSpanFull(),
- TextInput::make('nitro_auto_update_schedule')
- ->label('Tijd (HH:MM)')
- ->placeholder('03:00')
- ->columnSpan(1),
- TextInput::make('nitro_auto_update_days')
- ->label('Dagen (0-6, kommagescheiden)')
- ->placeholder('0,6')
- ->hint('0 = Zondag, 1 = Maandag, ..., 6 = Zaterdag')
- ->columnSpan(2),
- Placeholder::make('nitro_auto_update_next')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '';
- $label .= 'Volgende Geplande Update';
- $label .= '';
- $label .= '
';
-
- return new HtmlString($label . $this->getNitroAutoUpdateNextHtml());
- }),
- ]),
- Section::make('Nitro Config Generator')
- ->description('Genereer en beheer Nitro client configuraties automatisch')
- ->icon('heroicon-o-cog-6-tooth')
- ->columns(2)
- ->afterHeader([
- Action::make('generate_nitro_configs')
- ->label('Genereer Configs')
- ->action('generateNitroConfigs')
- ->color('success'),
- Action::make('save_nitro_url')
- ->label('Opslaan URL')
- ->action('saveNitroUrl')
- ->color('primary'),
- ])
- ->schema([
- TextInput::make('nitro_site_url')
- ->label('Website URL')
- ->placeholder('https://epicnabbo.nl')
- ->hint('Basis URL voor alle client configuraties')
- ->columnSpanFull(),
- Toggle::make('nitro_auto_update_configs')
- ->label('Automatisch configs bijwerken na deploy')
- ->helperText('Bij elke deploy worden de URLs automatisch bijgewerkt'),
- Placeholder::make('nitro_config_status')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= '
';
- $label .= '
Config Status';
- $label .= '
';
- $label .= '
';
-
- return new HtmlString($label . $this->getNitroConfigStatusHtml());
- }),
- Placeholder::make('nitro_config_preview')
- ->label('')
- ->content(function () {
- $label = '';
- $label .= 'Actuele Configs';
- $label .= '';
- $label .= '
';
-
- return new HtmlString($label . $this->getNitroConfigPreviewHtml());
- }),
- ]),
Section::make('Statistieken')
->description('Overzicht van recente alerts')
->icon('heroicon-o-chart-bar')
@@ -740,15 +358,7 @@ final class AlertSettings extends Page implements HasForms
->label('')
->content(fn () => $this->getStatsHtml()),
]),
- Section::make('Update Geschiedenis')
- ->description('Overzicht van alle updates')
- ->icon('heroicon-o-clock')
- ->schema([
- Placeholder::make('update_history')
- ->label('')
- ->content(fn () => $this->getUpdateHistoryHtml())
- ->columnSpanFull(),
- ]),
+
])
->statePath('data');
}
@@ -858,228 +468,11 @@ final class AlertSettings extends Page implements HasForms
$this->saveSettings(['alert_ddos_enabled', 'alert_ddos_threshold', 'alert_ddos_auto_block']);
}
- public function getUpdateHistoryHtml(): HtmlString
- {
- $historyService = app(UpdateHistoryService::class);
-
- return new HtmlString($historyService->getHtml());
- }
-
public function saveErrors(): void
{
$this->saveSettings(['alert_errors_enabled', 'alert_error_threshold', 'alert_min_severity']);
}
- public function saveEmulatorUpdate(): void
- {
- $this->saveSettings(['emulator_github_url', 'emulator_source_repo', 'emulator_jar_direct_url', 'emulator_jar_path', 'emulator_source_path', 'emulator_service_name', 'emulator_github_branch', 'emulator_database_host', 'emulator_database_port', 'emulator_database_name', 'emulator_database_username', 'emulator_database_password']);
- Cache::forget('emulator_latest_version');
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- Notification::make()
- ->success()
- ->title(__('Saved'))
- ->body(__('Emulator update settings have been saved.'))
- ->send();
- }
-
- public function saveAutoUpdate(): void
- {
- $this->saveSettings(['auto_update_enabled', 'auto_update_schedule', 'auto_update_days']);
-
- Notification::make()
- ->success()
- ->title(__('Saved'))
- ->body(__('Automatic update schedule has been saved.'))
- ->send();
- }
-
- public function runFullUpdate(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- $updateService = new EmulatorUpdateService;
-
- if (! $updateService->isConfigured()) {
- Notification::make()
- ->warning()
- ->title(__('Not configured'))
- ->body(__('Please enter a GitHub URL and service name first.'))
- ->send();
-
- return;
- }
-
- Notification::make()
- ->info()
- ->title('Emulator Updaten...')
- ->body(__('Downloading, installing and restarting emulator...'))
- ->send();
-
- $result = $updateService->updateEmulator();
-
- if ($result['success']) {
- Notification::make()
- ->success()
- ->title('Update Succes!')
- ->body($result['message'])
- ->send();
-
- $alertService = app(AlertService::class);
- $alertService->send(
- AlertType::EMULATOR_ONLINE,
- "Emulator succesvol geรผpdatet naar v{$result['version']}",
- ['version' => $result['version'], 'jar' => $result['jar'] ?? 'unknown'],
- );
- } else {
- Notification::make()
- ->danger()
- ->title(__('Update Failed'))
- ->body($result['error'] ?? 'Er is iets misgegaan')
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function checkEmulatorUpdates(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- $updateService = new EmulatorUpdateService;
-
- if (! $updateService->isConfigured()) {
- Notification::make()
- ->warning()
- ->title(__('Not configured'))
- ->body(__('Please enter a GitHub URL first and save.'))
- ->send();
-
- return;
- }
-
- $this->fillForm();
- $check = $updateService->checkForUpdates();
-
- if (isset($check['error'])) {
- Notification::make()
- ->danger()
- ->title(__('Error'))
- ->body($check['error'])
- ->send();
-
- return;
- }
-
- if ($check['update_available']) {
- Notification::make()
- ->success()
- ->title('Update Beschikbaar!')
- ->body("Versie {$check['latest_version']} is beschikbaar (huidig: {$check['current_version']})")
- ->send();
- } else {
- Notification::make()
- ->info()
- ->title('Up-to-date')
- ->body("Emulator is al up-to-date: v{$check['latest_version']}")
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function runEmulatorUpdate(): void
- {
- $this->downloadEmulatorJar();
- }
-
- public function isUpdateAvailable(): bool
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
- $updateService = new EmulatorUpdateService;
- $check = $updateService->checkForUpdates();
-
- $hasUpdate = $check['update_available'] ?? false;
- $hasUrl = ($check['jar_url'] ?? null) !== null;
- $isSourceBuild = ($check['type'] ?? '') === 'source_build';
-
- return $hasUpdate && ($hasUrl || $isSourceBuild);
- }
-
- public function getInstalledJarHtml(): HtmlString
- {
- $updateService = new EmulatorUpdateService;
- $jarFiles = $updateService->getInstalledJarInfo();
-
- if ($jarFiles !== []) {
- $html = '';
- foreach ($jarFiles as $jar) {
- $name = e($jar['name']);
- $size = e($jar['size']);
- $html .= "
๐ฆ {$size} {$name}
";
- }
- $html .= '
';
-
- return new HtmlString($html);
- }
-
- $jar = $updateService->getInstalledJar();
- if ($jar) {
- return new HtmlString("๐ฆ {$jar}");
- }
-
- return new HtmlString('Geen .jar gevonden');
- }
-
- public function getEmulatorVersionHtml(): HtmlString
- {
- $updateService = new EmulatorUpdateService;
- $version = $updateService->getInstalledVersion();
-
- return new HtmlString("v{$version}");
- }
-
- public function getUpdateStatusHtml(): HtmlString
- {
- $updateService = new EmulatorUpdateService;
- $check = $updateService->checkForUpdates();
-
- if (! $updateService->isConfigured()) {
- return new HtmlString('Configureer een GitHub URL');
- }
-
- if (isset($check['error'])) {
- return new HtmlString('' . __('Error') . ': ' . $check['error'] . '');
- }
-
- if ($check['type'] === 'manual') {
- return new HtmlString('โ ๏ธ Geen releases - handmatige download nodig');
- }
-
- if ($check['update_available']) {
- $jarInfo = '';
- if ($check['jar_size'] ?? null) {
- $jarInfo = " ({$check['jar_size']})";
- }
-
- $sourceBuild = ($check['type'] ?? '') === 'source_build';
- $sourceText = $sourceBuild ? ' (build vanaf source)' : '';
-
- return new HtmlString("โ
Update beschikbaar: v{$check['latest_version']}{$jarInfo}{$sourceText}");
- }
-
- $rateLimitedWarning = '';
- if ($check['type'] === 'direct_url' && ($check['gitHub_rate_limited'] ?? false)) {
- $rateLimitedWarning = ' (โ ๏ธ GitHub API rate limited - kan updates met zelfde versie niet detecteren)';
- }
-
- return new HtmlString('โ
Up-to-date (v' . $check['latest_version'] . ')' . $rateLimitedWarning);
- }
-
private function saveSettings(array $keys): void
{
foreach ($keys as $key) {
@@ -1097,256 +490,20 @@ final class AlertSettings extends Page implements HasForms
->send();
}
- public function resetUpdateDate(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
- $updateService = new EmulatorUpdateService;
- $updateService->resetInstalledDate();
-
- Notification::make()
- ->success()
- ->title('Reset Voltooid')
- ->body(__('Update date reset. The next check will detect a new update.'))
- ->send();
-
- $this->fillForm();
- }
-
- public function forceUpdateCheck(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- $updateService = new EmulatorUpdateService;
-
- // Clear all cached update data
- $updateService->resetInstalledDate();
-
- // Clear direct URL cached data
- $directUrl = setting('emulator_jar_direct_url');
- if ($directUrl) {
- WebsiteSetting::where('key', 'emulator_direct_url_info_' . md5((string) $directUrl))->delete();
- }
-
- // Re-check
- $check = $updateService->checkForUpdates();
-
- if ($check['update_available'] ?? false) {
- Notification::make()
- ->success()
- ->title('Update Gevonden!')
- ->body('Er is een nieuwe emulator versie: v' . ($check['latest_version'] ?? '?'))
- ->send();
- } else {
- $message = 'Geen update gevonden (v' . ($check['latest_version'] ?? setting('emulator_version', '?')) . ')';
- if ($check['type'] === 'direct_url' && ($check['gitHub_rate_limited'] ?? false)) {
- $message .= ' - GitHub API rate limited';
- }
- Notification::make()
- ->info()
- ->title('Check Result')
- ->body($message)
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function switchEmulatorBranch(): void
- {
- $updateService = new EmulatorUpdateService;
- $targetBranch = setting('emulator_github_branch', 'main');
-
- // Clear all caches
- DB::table('website_settings')
- ->where('key', 'emulator_github_branch')
- ->update(['value' => $targetBranch]);
-
- Cache::forget('website_settings');
- Cache::flush();
- SettingsService::clearCache();
- $updateService->resetInstalledDate();
-
- $directUrl = setting('emulator_jar_direct_url');
- if ($directUrl) {
- WebsiteSetting::where('key', 'emulator_direct_url_info_' . md5((string) $directUrl))->delete();
- }
-
- // Check and install from selected branch
- $check = $updateService->checkForUpdates();
-
- if (($check['update_available'] ?? false) && in_array($check['type'] ?? '', ['direct_url', 'github_folder'])) {
- $result = $updateService->performUpdate($check);
-
- if ($result['success'] ?? false) {
- Notification::make()
- ->success()
- ->title("๐ Switched to {$targetBranch}!")
- ->body('Emulator is bijgewerkt')
- ->send();
- } else {
- Notification::make()
- ->danger()
- ->title('Update mislukt')
- ->body($result['error'] ?? 'Onbekende fout')
- ->send();
- }
- } else {
- // Force set installed branch even if no update
- setting('emulator_installed_branch', $targetBranch);
- SettingsService::clearCache();
- Notification::make()
- ->info()
- ->title("Branch naar {$targetBranch} gezet")
- ->body('Geen nieuwe update beschikbaar')
- ->send();
- }
-
- $this->fillForm();
- }
-
public function clearAllLogs(): void
{
- $updateService = new EmulatorUpdateService;
- $result = $updateService->clearAllLogs();
AlertLog::truncate();
Cache::flush();
Notification::make()
->success()
->title('๐๏ธ Alle Logs Geleegd!')
- ->body($result['message'] . ': ' . implode(', ', $result['cleared']))
+ ->body('Alle logs zijn gewist.')
->send();
$this->fillForm();
}
- public function getDebugStatusHtml(): HtmlString
- {
- $updateService = new EmulatorUpdateService;
- $debug = $updateService->debugStatus();
-
- $html = '';
-
- $html .= '';
-
- // JAR Files Section
- $html .= '
';
- $html .= '
JAR Bestanden
';
- if (! empty($debug['jar_files'])) {
- foreach ($debug['jar_files'] as $jar) {
- $html .= '
';
- $html .= '
๐ฆ
';
- $html .= '
';
- $html .= '
' . e($jar['name']) . '
';
- $html .= '
';
- $html .= '
' . e($jar['size']) . '
';
- $html .= '
';
- }
- } else {
- $html .= '
Geen JAR gevonden
';
- }
- $html .= '
' . e($debug['jar_path'] ?? '') . '
';
- $html .= '
';
-
- // Stats Grid
- $installedOk = ! empty($debug['installed_date_formatted']);
- $commitOk = ! empty($debug['source_commit']);
- $dateOk = ! empty($debug['source_date_formatted']);
- $allOk = $installedOk && $commitOk && $dateOk;
-
- $version = e($debug['emulator_version'] ?: 'Onbekend');
- $commit = $debug['source_commit'] ? substr(e($debug['source_commit']), 0, 8) : 'Onbekend';
- $commitColor = $commitOk ? 'success' : 'neutral';
- $dateColor = $dateOk ? 'success' : 'neutral';
-
- $html .= '
';
- $html .= '
';
- $html .= '
';
- $html .= '
' . $version . '
';
- $html .= '
Versie
';
- $html .= '
';
- $html .= '
';
- $html .= '
' . ($allOk ? '100%' : '!') . '
';
- $html .= '
Tracking
';
- $html .= '
';
- $html .= '
';
- $html .= '
' . ($installedOk ? substr((string) $debug['installed_date_formatted'], 0, 10) : 'โ') . '
';
- $html .= '
Geรฏnstalleerd
';
- $html .= '
';
- $html .= '
';
- $html .= '
' . ($dateOk ? substr((string) $debug['source_date_formatted'], 0, 10) : 'โ') . '
';
- $html .= '
Source Datum
';
- $html .= '
';
- $html .= '
';
-
- // Commit Info
- $html .= '
';
- $html .= '
';
- $html .= '
Git Commit
';
- $html .= '
';
- $html .= '
SHA
';
- $html .= '
' . $commit . '
';
- $html .= '
';
- $html .= '
';
- $html .= '
Geรฏnstalleerd
';
- $html .= '
' . ($debug['installed_date_formatted'] ?: 'Nooit') . '
';
- $html .= '
';
- $html .= '
';
-
- // Repos
- $html .= '
';
- $html .= '
';
- $html .= '
Repositories
';
- $html .= '
';
- $html .= '
JAR Repo
';
- $repo = e($debug['github_repo'] ?: 'Niet geconfigureerd');
- $html .= '
' . $repo . '
';
- $html .= '
';
- $html .= '
';
- $html .= '
Source Repo
';
- $sourceRepo = e($debug['source_repo'] ?: 'Niet geconfigureerd');
- $html .= '
' . (strlen($sourceRepo) > 25 ? substr($sourceRepo, 0, 22) . 'โฆ' : $sourceRepo) . '
';
- $html .= '
';
- $html .= '
';
- $html .= '
Branch
';
- $branch = e($debug['github_branch'] ?: 'main');
- $html .= '
' . $branch . '
';
- $html .= '
';
- $html .= '
';
-
- $html .= '
';
-
- return new HtmlString($html);
- }
-
public function getOnlineUsersHtml(): HtmlString
{
try {
@@ -1771,608 +928,6 @@ final class AlertSettings extends Page implements HasForms
->send();
}
- public function checkSqlUpdates(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
- $updateService = new EmulatorUpdateService;
- $result = $updateService->checkForSqlUpdates();
-
- if ($result['has_updates'] ?? false) {
- Notification::make()
- ->warning()
- ->title('SQL Updates Beschikbaar!')
- ->body($result['message'])
- ->send();
- } else {
- Notification::make()
- ->info()
- ->title('Geen SQL Updates')
- ->body($result['message'] ?? 'Alle SQL updates zijn al toegepast')
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function fixAll(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- $fixService = new SystemFixService;
- $results = $fixService->checkAndFixAll();
-
- $errors = [];
- $fixed = [];
- $ok = [];
-
- foreach ($results as $result) {
- if ($result['status'] === 'error') {
- $errors[] = $result['item'] . ': ' . $result['message'];
- } elseif ($result['status'] === 'fixed') {
- $fixed[] = $result['item'];
- } else {
- $ok[] = $result['item'];
- }
- }
-
- if ($errors !== []) {
- Notification::make()
- ->danger()
- ->title('Systeem Fix Fouten')
- ->body(implode("\n", $errors))
- ->send();
- } elseif ($fixed !== []) {
- Notification::make()
- ->success()
- ->title('Systeem Gefixt!')
- ->body(count($fixed) . ' items automatisch gerepareerd: ' . implode(', ', $fixed))
- ->send();
- } else {
- Notification::make()
- ->info()
- ->title('Systeem OK')
- ->body('Alle checks geslaagd - geen reparaties nodig')
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function runSqlUpdates(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
- $updateService = new EmulatorUpdateService;
-
- Notification::make()
- ->info()
- ->title('SQL Updates Uitvoeren...')
- ->body('Database updates worden toegepast.')
- ->send();
-
- $result = $updateService->runSqlUpdates();
-
- if ($result['success']) {
- Notification::make()
- ->success()
- ->title('SQL Updates Succes!')
- ->body($result['message'])
- ->send();
-
- if ($result['sql_updated'] ?? false) {
- $alertService = app(AlertService::class);
- $alertService->send(
- AlertType::EMULATOR_ONLINE,
- 'SQL database updates succesvol uitgevoerd',
- ['files' => implode(', ', $result['files_run'] ?? [])],
- );
- }
- } else {
- Notification::make()
- ->danger()
- ->title(__('SQL Update Failed'))
- ->body($result['error'] ?? 'Er is iets misgegaan')
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function getSqlStatusHtml(): HtmlString
- {
- Cache::forget('website_settings');
- $updateService = new EmulatorUpdateService;
- $result = $updateService->checkForSqlUpdates();
-
- if ($result['has_updates'] ?? false) {
- return new HtmlString('โ ๏ธ ' . $result['message'] . '');
- }
-
- return new HtmlString('โ
' . ($result['message'] ?? 'Up-to-date') . '');
- }
-
- public function getNextAutoUpdateHtml(): HtmlString
- {
- $enabled = $this->getSettingBool('auto_update_enabled');
-
- if (! $enabled) {
- return new HtmlString('Automatische updates uitgeschakeld');
- }
-
- $scheduleTime = $this->getSetting('auto_update_schedule', '03:00');
- $scheduleDays = $this->getSetting('auto_update_days', '0,6');
- $allowedDays = array_map(intval(...), explode(',', $scheduleDays));
-
- $dayNames = ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'];
- $days = implode(', ', array_map(fn ($d) => $dayNames[$d] ?? $d, $allowedDays));
-
- $now = now();
- $today = $now->dayOfWeek;
- Carbon::parse($scheduleTime, $now->timezone);
-
- $nextRun = $now->copy();
- $daysUntil = 0;
-
- for ($i = 0; $i <= 7; $i++) {
- $checkDay = ($today + $i) % 7;
- if (in_array($checkDay, $allowedDays)) {
- $daysUntil = $i;
- break;
- }
- }
-
- $nextRun->addDays($daysUntil)->setTimeFromTimeString($scheduleTime);
-
- return new HtmlString('๐
' . $nextRun->format('d M Y, H:i') . ' (' . $days . ')');
- }
-
- public function getSqlLastUpdateHtml(): HtmlString
- {
- $updateService = new EmulatorUpdateService;
- $lastUpdate = $updateService->getLastSqlUpdate();
-
- if ($lastUpdate) {
- return new HtmlString('' . Carbon::parse($lastUpdate)->format('d M Y, H:i') . '');
- }
-
- return new HtmlString('Nog nooit uitgevoerd');
- }
-
- public function getNitroAutoUpdateNextHtml(): HtmlString
- {
- $enabled = $this->getSettingBool('nitro_auto_update_enabled');
-
- if (! $enabled) {
- return new HtmlString('Automatische Nitro updates uitgeschakeld');
- }
-
- $scheduleTime = $this->getSetting('nitro_auto_update_schedule', '03:00');
- $scheduleDays = $this->getSetting('nitro_auto_update_days', '0,6');
- $allowedDays = array_map(intval(...), explode(',', $scheduleDays));
-
- $dayNames = ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'];
- $days = implode(', ', array_map(fn ($d) => $dayNames[$d] ?? $d, $allowedDays));
-
- $now = now();
- $today = $now->dayOfWeek;
- Carbon::parse($scheduleTime, $now->timezone);
-
- $nextRun = $now->copy();
- $daysUntil = 0;
-
- for ($i = 0; $i <= 7; $i++) {
- $checkDay = ($today + $i) % 7;
- if (in_array($checkDay, $allowedDays)) {
- $daysUntil = $i;
- break;
- }
- }
-
- $nextRun->addDays($daysUntil)->setTimeFromTimeString($scheduleTime);
-
- return new HtmlString('๐
' . $nextRun->format('d M Y, H:i') . ' (' . $days . ')');
- }
-
- public function saveNitro(): void
- {
- $this->saveSettings(['nitro_github_url', 'nitro_client_path', 'nitro_renderer_path', 'nitro_build_path', 'nitro_webroot', 'nitro_auto_update_enabled', 'nitro_auto_update_schedule', 'nitro_auto_update_days', 'nitro_github_branch']);
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- Notification::make()
- ->success()
- ->title(__('Saved'))
- ->body('Nitro instellingen zijn opgeslagen.')
- ->send();
- }
-
- public function switchNitroBranch(): void
- {
- $targetBranch = setting('nitro_github_branch', 'main');
-
- // Set branch in DB
- DB::table('website_settings')
- ->where('key', 'nitro_github_branch')
- ->update(['value' => $targetBranch]);
-
- Cache::forget('website_settings');
- Cache::flush();
- SettingsService::clearCache();
-
- // Run update in background to prevent gateway timeout
- $logFile = '/tmp/nitro-switch-' . date('Ymd-His') . '.log';
- $artisan = base_path('artisan');
- $php = config('app.php_binary', '/usr/bin/php');
-
- Process::timeout(5)->run(
- "nohup {$php} {$artisan} app:switch-nitro-branch --branch=" . escapeshellarg((string) $targetBranch) . " > {$logFile} 2>&1 &",
- );
-
- Notification::make()
- ->success()
- ->title("๐ Switching to {$targetBranch}...")
- ->body('Build draait op de achtergrond. Resultaat verschijnt vanzelf.')
- ->send();
- }
-
- public function checkNitroUpdates(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
-
- // Update last checked time
- $settings = app(SettingsService::class);
- $settings->set('nitro_last_checked', now()->toIso8601String());
-
- $nitroService = new NitroUpdateService;
- $result = $nitroService->checkForUpdates();
-
- if ($result['has_updates'] ?? false) {
- $messages = [];
- if ($result['client_update'] ?? false) {
- $messages[] = 'Client update beschikbaar';
- }
- if ($result['renderer_update'] ?? false) {
- $messages[] = 'Renderer update beschikbaar';
- }
-
- Notification::make()
- ->warning()
- ->title('Nitro Updates Beschikbaar!')
- ->body(implode(', ', $messages))
- ->send();
- } else {
- Notification::make()
- ->info()
- ->title('Nitro Up-to-date')
- ->body('Client en renderer zijn up-to-date.')
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function buildNitro(): void
- {
- Cache::forget('website_settings');
- SettingsService::clearCache();
- $nitroService = new NitroUpdateService;
-
- Notification::make()
- ->info()
- ->title('Nitro Build & Deploy...')
- ->body('Client wordt gebouwd en gedeployed.')
- ->send();
-
- $result = $nitroService->updateNitro();
-
- if ($result['success']) {
- Notification::make()
- ->success()
- ->title('Nitro Update Succes!')
- ->body($result['message'] ?? 'Client succesvol geรผpdatet en gedeployed.')
- ->send();
- } else {
- Notification::make()
- ->danger()
- ->title(__('Nitro Update Failed'))
- ->body($result['error'] ?? $result['message'] ?? 'Er is iets misgegaan')
- ->send();
- }
-
- $this->fillForm();
- }
-
- public function getNitroStatusHtml(): HtmlString
- {
- Cache::forget('nitro_status');
-
- $nitroService = new NitroUpdateService;
- $status = $nitroService->getStatus();
-
- // Get update info from GitHub
- $updateCheck = $nitroService->checkForUpdates();
-
- $clientCommit = $status['client_commit'] ?? 'N/A';
- $rendererCommit = $status['renderer_commit'] ?? 'N/A';
- $latestClient = $updateCheck['client_commit'] ?? $clientCommit;
- $latestRenderer = $updateCheck['renderer_commit'] ?? $rendererCommit;
-
- $clientUpdate = $clientCommit !== $latestClient;
- $rendererUpdate = $rendererCommit !== $latestRenderer;
- $hasUpdates = $clientUpdate || $rendererUpdate;
-
- $this->getNitroTranslations();
-
- // Calculate health score with fallback checks
- $checkDirShell = function (string $path): bool {
- $result = Process::timeout(3)->run('test -d ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
-
- return trim($result->output()) === 'yes';
- };
- $checkFileShell = function (string $path): bool {
- $result = Process::timeout(3)->run('test -f ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
-
- return trim($result->output()) === 'yes';
- };
-
- $buildPath = $status['build_path'] ?? '/var/www/atomcms/nitro-client/dist';
- $webroot = $status['webroot'] ?? '/var/www/Client';
- $clientPath = $status['client_path'] ?? '/var/www/nitro-client';
- $rendererPath = $status['renderer_path'] ?? '/var/www/nitro-renderer';
-
- $checks = [
- // With fallbacks - check status or use shell fallback
- ($status['client_installed'] ?? false) || $checkDirShell($clientPath . '/.git'),
- ($status['renderer_installed'] ?? false) || $checkDirShell($rendererPath . '/.git'),
- ($status['build_exists'] ?? false) || $checkDirShell($buildPath),
- ($status['deployed'] ?? false) || $checkFileShell($webroot . '/renderer-config.json'),
- ($status['symlink_valid'] ?? false) || $checkDirShell($webroot . '/gamedata') || $checkDirShell('/var/www/Gamedata'),
- ($status['client_node_modules'] ?? false) || $checkDirShell($clientPath . '/node_modules'),
- ($status['renderer_node_modules'] ?? false) || $checkDirShell($rendererPath . '/node_modules'),
- ($status['renderer_config_valid'] ?? false) || $checkFileShell($webroot . '/renderer-config.json'),
- ($status['ui_config_valid'] ?? false) || $checkFileShell($webroot . '/ui-config.json'),
- ($status['nitro_config_valid'] ?? false) || $checkFileShell($webroot . '/UITexts.json'),
- ($status['has_index_js'] ?? false) || $checkFileShell($buildPath . '/index.js'),
- ($status['has_renderer_js'] ?? false) || $checkFileShell($buildPath . '/renderer.js'),
- ($status['has_vendor_js'] ?? false) || $checkFileShell($buildPath . '/vendor.js'),
- ($status['has_css_file'] ?? false) || Process::timeout(3)->run('find ' . escapeshellarg($buildPath) . " -name '*.css' -type f 2>/dev/null | head -1")->output() !== '',
- ($status['vite_config_valid'] ?? false) || $checkFileShell($clientPath . '/vite.config.js') || $checkFileShell($clientPath . '/vite.config.mjs'),
- ($status['has_uitexts_json'] ?? false) || $checkFileShell($webroot . '/UITexts.json'),
- ($status['has_renderer_example'] ?? false) || $checkFileShell($buildPath . '/renderer-config.example') || $checkFileShell($webroot . '/renderer-config.example'),
- ($status['has_uiconfig_example'] ?? false) || $checkFileShell($buildPath . '/ui-config.example') || $checkFileShell($webroot . '/ui-config.example'),
- ($status['has_image_assets'] ?? false) || $checkDirShell($webroot . '/assets') || $checkDirShell($buildPath . '/assets'),
- ($status['has_favicon'] ?? false) || $checkFileShell($webroot . '/favicon.ico') || $checkFileShell($buildPath . '/favicon.ico'),
- ($status['assets_writable'] ?? false) || Process::timeout(3)->run('test -w ' . escapeshellarg($webroot) . ' && echo "yes" || echo "no"')->output() === "yes\n",
- ($status['has_renderer_dist'] ?? false) || $checkDirShell($rendererPath . '/node_modules'),
- ($status['has_renderer_index'] ?? false) || $checkDirShell($rendererPath . '/packages'),
- ($status['webroot_exists'] ?? false) || $checkDirShell($webroot),
- ($status['webroot_writable'] ?? false) || Process::timeout(3)->run('test -w ' . escapeshellarg($webroot) . ' && echo "yes" || echo "no"')->output() === "yes\n",
- ($status['nginx_config_valid'] ?? false) || Process::timeout(3)->run('test -f /etc/nginx/sites-enabled/cms.conf -o -f /etc/nginx/sites-enabled/atom.conf -o -f /etc/nginx/conf.d/atom.conf && echo "yes" || echo "no"')->output() === "yes\n",
- // open_basedir check with direct fallback
- ($status['open_basedir_fixed'] ?? false) || (in_array(ini_get('open_basedir'), ['', '0'], true) || ini_get('open_basedir') === false),
- // Connection checks - use direct fallback
- ($status['websocket_accessible'] ?? false) || ($status['websocket_accessible'] ?? true),
- ($status['emulator_connected'] ?? false) || ($status['emulator_connected'] ?? true),
- ];
-
- $validChecks = count($checks);
- $passedChecks = count(array_filter($checks));
- $healthScore = $validChecks > 0 ? round(($passedChecks / $validChecks) * 100) : 100;
-
- // Force 100% if we have the key files
- if ($healthScore < 100) {
- $hasKeyFiles = $checkFileShell($webroot . '/renderer-config.json') &&
- $checkFileShell($webroot . '/ui-config.json') &&
- $checkFileShell($webroot . '/UITexts.json');
- if ($hasKeyFiles) {
- $healthScore = 100;
- }
- }
-
- $healthColor = match (true) {
- $healthScore >= 100 => 'success',
- $healthScore >= 75 => 'warning',
- default => 'danger',
- };
-
- $healthLabel = match (true) {
- $healthScore >= 100 => 'Alles OK',
- $healthScore >= 75 => 'Waarschuwing',
- default => 'Kritiek',
- };
-
- $html = '';
-
- $html .= '';
-
- // Header with score
- $html .= '';
-
- // Health bar
- $html .= '
';
-
- // Version Info Section
- $html .= '
๐ Versie Informatie
';
- $html .= '
';
-
- // Current Version
- $html .= '
';
- $html .= '๐ฎ Client (lokaal)';
- $html .= '' . ($clientCommit !== 'N/A' ? substr((string) $clientCommit, 0, 7) : 'Niet geรฏnstalleerd');
- if ($clientUpdate) {
- $html .= ' UPDATE';
- }
- $html .= '
';
-
- // Renderer Version
- $html .= '
';
- $html .= '๐จ Renderer (lokaal)';
- $html .= '' . ($rendererCommit !== 'N/A' ? substr((string) $rendererCommit, 0, 7) : 'Niet geรฏnstalleerd');
- if ($rendererUpdate) {
- $html .= ' UPDATE';
- }
- $html .= '
';
-
- // Latest from GitHub - Client
- $html .= '
';
- $html .= '๐ Client (GitHub)';
- $html .= '' . ($latestClient !== 'N/A' ? substr((string) $latestClient, 0, 7) : 'N/A') . '
';
-
- // Latest from GitHub - Renderer
- $html .= '
';
- $html .= '๐ Renderer (GitHub)';
- $html .= '' . ($latestRenderer !== 'N/A' ? substr((string) $latestRenderer, 0, 7) : 'N/A') . '
';
-
- $html .= '
';
-
- // Connection Section
- $wsIcon = ($status['websocket_accessible'] ?? false) ? 'โ
' : 'โ';
- $wsLabel = ($status['websocket_accessible'] ?? false) ? 'Online' : 'Offline';
- $emuIcon = ($status['emulator_connected'] ?? false) ? 'โ
' : 'โ';
- $emuLabel = ($status['emulator_connected'] ?? false) ? 'Online' : 'Offline';
- $socketUrl = $status['socket_url'] ?? 'N/A';
-
- $html .= '
๐ Verbinding
';
- $html .= '
' . $wsIcon . '
WebSocket
' . $wsLabel . '
';
- $html .= '
' . $emuIcon . '
Emulator
' . $emuLabel . '
';
- $html .= '
๐
Socket URL
' . htmlspecialchars($socketUrl) . '
';
- $html .= '
';
-
- // System Section
- $buildIcon = ($status['build_exists'] ?? false) ? 'โ
' : 'โ';
- $webrootIcon = ($status['webroot_exists'] ?? false) ? 'โ
' : 'โ';
- $deployedIcon = ($status['deployed'] ?? false) ? 'โ
' : 'โ';
- $clientDepsIcon = ($status['client_node_modules'] ?? false) ? 'โ
' : 'โ';
- $symlinkIcon = ($status['symlink_valid'] ?? false) ? 'โ
' : 'โ';
- $writableIcon = ($status['webroot_writable'] ?? false) ? 'โ
' : 'โ';
-
- $html .= '
โ๏ธ Systeem
';
- $html .= '
';
- $html .= '
' . $webrootIcon . '
Webroot
';
- $html .= '
' . $deployedIcon . '
Gedeployed
';
- $html .= '
' . $clientDepsIcon . '
Client deps
';
- $html .= '
' . $symlinkIcon . '
Symlink
';
- $html .= '
' . $writableIcon . '
Writable
';
- $html .= '
';
-
- // Server Config Section
- $nginxValid = ($status['nginx_config_valid'] ?? false) ? 'โ
' : 'โ';
-
- // Multiple fallback checks for open_basedir
- $openBasedirFixed = false;
-
- // Check 1: ini_get directly
- $openBaseDirValue = ini_get('open_basedir');
- if (in_array($openBaseDirValue, ['', '0', false], true)) {
- $openBasedirFixed = true;
- }
-
- // Check 2: from status
- if (! $openBasedirFixed && ($status['open_basedir_fixed'] ?? false) === true) {
- $openBasedirFixed = true;
- }
-
- // Check 3: shell command check if PHP says it's restricted
- if (! $openBasedirFixed) {
- $testResult = Process::timeout(3)->run('php -r "echo ini_get(\'open_basedir\');"');
- $shellBasedir = trim($testResult->output() ?? '');
- if ($shellBasedir === '' || $shellBasedir === '0') {
- $openBasedirFixed = true;
- }
- }
-
- // Check 4: check if PHP can access the paths it needs
- if (! $openBasedirFixed) {
- $canAccessClient = @is_dir('/var/www/nitro-client') || @is_dir('/var/www/atomcms/nitro-client');
- $canAccessRenderer = @is_dir('/var/www/nitro-renderer') || @is_dir('/var/www/atomcms/nitro-renderer');
- $canAccessWebroot = @is_dir('/var/www/Client');
- if ($canAccessClient && $canAccessRenderer && $canAccessWebroot) {
- $openBasedirFixed = true;
- }
- }
-
- $openBasedir = $openBasedirFixed ? 'โ
' : 'โ';
-
- $webrootFileCount = $status['webroot_file_count'] ?? 0;
-
- $html .= '
๐ฅ๏ธ Server Config
';
- $html .= '
' . $nginxValid . '
nginx.conf
';
- $html .= '
' . $openBasedir . '
open_basedir
';
- $html .= '
๐
Files
' . $webrootFileCount . '
';
- $html .= '
';
-
- // Storage Section
- $buildSize = $status['build_size'] ?? 0;
- $webrootSize = $status['webroot_size'] ?? 0;
- $buildSizeStr = $this->formatBytes($buildSize);
- $webrootSizeStr = $this->formatBytes($webrootSize);
-
- $html .= '
๐พ Opslag
';
- $html .= '
๐ฆ
Build
' . $buildSizeStr . '
';
- $html .= '
๐
Webroot
' . $webrootSizeStr . '
';
- $html .= '
';
-
- // Footer
- $lastChecked = empty($status['last_checked']) ? 'Nooit' : Carbon::parse($status['last_checked'])->diffForHumans();
-
- $html .= '';
-
- $html .= '
';
-
- return new HtmlString($html);
- }
-
private function formatBytes(int $bytes): string
{
if ($bytes >= 1073741824) {
@@ -2386,466 +941,11 @@ final class AlertSettings extends Page implements HasForms
return $bytes . ' B';
}
- private function getNitroTranslations(): array
- {
- return [
- 'title' => __('nitro.title'),
- 'healthy' => __('nitro.healthy'),
- 'warning' => __('nitro.warning'),
- 'critical' => __('nitro.critical'),
- 'installed' => __('nitro.installed'),
- 'missing' => __('nitro.missing'),
- 'available' => __('nitro.available'),
- 'latest' => __('nitro.latest'),
- 'client' => __('nitro.client'),
- 'renderer' => __('nitro.renderer'),
- 'status' => __('nitro.status'),
- 'version' => __('nitro.version'),
- 'remote' => __('nitro.remote'),
- 'system' => __('nitro.system'),
- 'build' => __('nitro.build'),
- 'webroot' => __('nitro.webroot'),
- 'deployed' => __('nitro.deployed'),
- 'dependencies' => __('nitro.dependencies'),
- 'symlink' => __('nitro.symlink'),
- 'never' => __('nitro.never'),
- 'off' => __('nitro.off'),
- 'checked' => __('nitro.checked'),
- 'auto' => __('nitro.auto'),
- 'connection' => 'Verbinding',
- 'build_files' => 'Build Bestanden',
- 'config_files' => 'Config Bestanden',
- 'storage' => 'Opslag',
- ];
- }
-
- public function getNitroScheduleHtml(): HtmlString
- {
- $enabled = setting('nitro_auto_update_enabled', false);
- $time = setting('nitro_auto_update_schedule', '04:00');
- $days = setting('nitro_auto_update_days', '0,6');
-
- if (! $enabled) {
- return new HtmlString('Automatische Nitro updates uitgeschakeld');
- }
-
- $allowedDays = array_map(intval(...), explode(',', $days));
- $dayNames = ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'];
- $daysText = implode(', ', array_map(fn ($d) => $dayNames[$d] ?? $d, $allowedDays));
-
- return new HtmlString('๐
' . $time . ' (' . $daysText . ')');
- }
-
- public function saveNitroUrl(): void
- {
- $settings = app(SettingsService::class);
- $siteUrl = $this->data['nitro_site_url'] ?? setting('nitro_site_url', $this->getCurrentSiteUrl());
- $autoUpdate = $this->data['nitro_auto_update_configs'] ?? false;
-
- $settings->set('nitro_site_url', $siteUrl);
- $settings->set('nitro_auto_update_configs', $autoUpdate ? '1' : '0');
-
- Notification::make()
- ->success()
- ->title(__('Saved'))
- ->body('Nitro site URL is opgeslagen: ' . $siteUrl)
- ->send();
-
- $this->fillForm();
- }
-
- public function generateNitroConfigs(): void
- {
- $siteUrl = setting('nitro_site_url', $this->getCurrentSiteUrl());
-
- if (empty($siteUrl) || ! filter_var($siteUrl, FILTER_VALIDATE_URL)) {
- Notification::make()
- ->danger()
- ->title('Ongeldige URL')
- ->body('Voer een geldige URL in (bijv. https://epicnabbo.nl)')
- ->send();
-
- return;
- }
-
- // Use Artisan command to generate configs
- $exitCode = Artisan::call('app:generate-nitro-configs', [
- '--site-url' => $siteUrl,
- ]);
-
- // Update last checked time
- $settings = app(SettingsService::class);
- $settings->set('nitro_last_checked', now()->toIso8601String());
-
- if ($exitCode === 0) {
- Notification::make()
- ->success()
- ->title('Configs Gegeneerd!')
- ->body('renderer-config.json, ui-config.json en UITexts.json zijn bijgewerkt voor ' . $siteUrl)
- ->send();
- } else {
- Notification::make()
- ->danger()
- ->title('Config generatie mislukt')
- ->body('Er is een fout opgetreden bij het genereren van de configs.')
- ->send();
- }
-
- $this->fillForm();
- }
-
private function getCurrentSiteUrl(): string
{
return setting('site_url', config('app.url', 'https://epicnabbo.nl'));
}
- private function parseRepoFromUrl(string $url): ?string
- {
- if (preg_match('/github\.com\/([^\/]+\/[^\/\?#]+)/', $url, $matches)) {
- return rtrim($matches[1], '/');
- }
-
- return null;
- }
-
- private function fetchBranches(string $repo): array
- {
- $cacheKey = 'git_branches_' . md5($repo);
-
- return Cache::remember($cacheKey, 600, function () use ($repo) {
- $token = config('services.github.token', '');
- $headers = ['User-Agent' => 'atomcms'];
- if ($token) {
- $headers['Authorization'] = 'Bearer ' . $token;
- }
-
- $branches = [];
- try {
- $response = Http::withHeaders($headers)
- ->timeout(10)
- ->get("https://api.github.com/repos/{$repo}/branches?per_page=100");
-
- if ($response->successful()) {
- foreach ($response->json() as $branch) {
- $name = $branch['name'] ?? '';
- $branches[strtolower($name)] = $name;
- }
- }
-
- if ($branches === []) {
- $result = Process::timeout(10)->run("git ls-remote --heads https://github.com/{$repo}.git 2>/dev/null | awk '{print \$2}' | sed 's|refs/heads/||'");
- if ($result->successful() && ! in_array(trim($result->output()), ['', '0'], true)) {
- foreach (array_filter(explode("\n", trim($result->output()))) as $name) {
- $branches[strtolower($name)] = $name;
- }
- }
- }
- } catch (\Exception) {
- }
-
- return $branches;
- });
- }
-
- public function getNitroConfigStatusHtml(): HtmlString
- {
- $siteUrl = setting('nitro_site_url', $this->getCurrentSiteUrl());
- $autoUpdate = setting('nitro_auto_update_configs', false);
- $generatedAt = setting('nitro_config_generated_at');
-
- $nitroService = new NitroUpdateService;
- $webroot = $nitroService->getStatus()['webroot'] ?? '/var/www/Client';
- $mtime = null;
- foreach (['renderer-config.json', 'ui-config.json', 'UITexts.json'] as $file) {
- $path = $webroot . '/' . $file;
- if (is_file($path) && ($t = filemtime($path)) > ($mtime ?? 0)) {
- $mtime = $t;
- }
- }
- if (! $generatedAt && $mtime) {
- $generatedAt = date('c', $mtime);
- }
-
- $html = '';
-
- $html .= '';
-
- // Stats Grid
- $generatedCount = 0;
- $nitroService = new NitroUpdateService;
- $webroot = $nitroService->getStatus()['webroot'] ?? '/var/www/Client';
- if (is_file($webroot . '/renderer-config.json')) {
- $generatedCount++;
- }
- if (is_file($webroot . '/ui-config.json')) {
- $generatedCount++;
- }
- if (is_file($webroot . '/UITexts.json')) {
- $generatedCount++;
- }
-
- $allGenerated = $generatedCount === 3;
- $html .= '
';
- $html .= '
';
- $html .= '
' . $generatedCount . '/3
';
- $html .= '
Configs
';
- $html .= '
';
- $html .= '
';
- $html .= '
' . ($autoUpdate ? 'AAN' : 'UIT') . '
';
- $html .= '
Auto-update
';
- $html .= '
';
- $html .= '
';
-
- // Site URL
- $html .= '
';
- $html .= '
';
- $html .= '
Website
';
- $html .= '
';
- $html .= '
Site URL
';
- $html .= '
';
- if ($siteUrl) {
- $display = strlen((string) $siteUrl) > 28
- ? '' . e($siteUrl) . ''
- : e($siteUrl);
- $html .= '' . $display . '';
- } else {
- $html .= 'Niet ingesteld';
- }
- $html .= '
';
- $html .= '
';
- $html .= '
';
-
- // Config URLs (cached for 5 minutes)
- $rendererConfig = Cache::remember('nitro_renderer_config', 300, function () use ($webroot) {
- if (is_file($webroot . '/renderer-config.json')) {
- return @json_decode(file_get_contents($webroot . '/renderer-config.json'), true) ?: [];
- }
-
- return [];
- });
- $uiConfig = Cache::remember('nitro_ui_config', 300, function () use ($webroot) {
- if (is_file($webroot . '/ui-config.json')) {
- return @json_decode(file_get_contents($webroot . '/ui-config.json'), true) ?: [];
- }
-
- return [];
- });
-
- $html .= '
';
- $html .= '
';
- $html .= '
' . e(__('Config URLs')) . '
';
-
- $configUrls = [
- [__('Socket URL'), $rendererConfig['socket.url'] ?? null],
- [__('Asset URL'), $rendererConfig['asset.url'] ?? null],
- [__('Images URL'), $rendererConfig['images.url'] ?? null],
- [__('Camera URL'), $uiConfig['camera.url'] ?? null],
- [__('Thumbnails URL'), $uiConfig['thumbnails.url'] ?? null],
- [__('Group Homepage'), $uiConfig['group.homepage.url'] ?? null],
- [__('Habbopages URL'), $uiConfig['habbopages.url'] ?? null],
- ];
-
- foreach ($configUrls as [$label, $url]) {
- if (! $url) {
- continue;
- }
- $html .= '
';
- $html .= '
' . e($label) . '
';
- $html .= '
';
- $display = strlen((string) $url) > 28
- ? '' . e($url) . ''
- : e($url);
- $html .= '' . $display . '';
- $html .= '
';
- $html .= '
';
- }
- $html .= '
';
-
- // Generation Info
- $html .= '
';
- $html .= '
';
- $html .= '
Config Generatie
';
- $html .= '
';
- $html .= '
Laatst gegenereerd
';
- $html .= '
';
- if ($generatedAt) {
- $timeAgo = Carbon::parse($generatedAt)->diffForHumans();
- $fullDate = Carbon::parse($generatedAt)->format('d M Y, H:i');
- $html .= '' . e($timeAgo) . '';
- } else {
- $html .= 'Nooit';
- }
- $html .= '
';
- $html .= '
';
- $html .= '
';
- $html .= '
Auto-update na deploy
';
- $html .= '
';
- $html .= '' . ($autoUpdate ? 'Ja' : 'Nee') . '';
- $html .= '
';
- $html .= '
';
- $html .= '
';
-
- $html .= '
';
-
- return new HtmlString($html);
- }
-
- public function getNitroConfigPreviewHtml(): HtmlString
- {
- $nitroService = new NitroUpdateService;
- $webroot = $nitroService->getStatus()['webroot'] ?? '/var/www/Client';
-
- $rendererExists = is_file($webroot . '/renderer-config.json');
- $uiExists = is_file($webroot . '/ui-config.json');
- $uitextsExists = is_file($webroot . '/UITexts.json');
-
- $rendererConfig = [];
- $uiConfig = [];
-
- if ($rendererExists) {
- $content = @file_get_contents($webroot . '/renderer-config.json');
- if ($content) {
- $rendererConfig = @json_decode($content, true) ?: [];
- }
- }
-
- if ($uiExists) {
- $content = @file_get_contents($webroot . '/ui-config.json');
- if ($content) {
- $uiConfig = @json_decode($content, true) ?: [];
- }
- }
-
- $html = '';
-
- $html .= '';
-
- // Config Files
- $html .= '
';
- $html .= '
Config Bestanden
';
- $configs = [
- 'renderer-config.json' => $rendererExists,
- 'ui-config.json' => $uiExists,
- 'UITexts.json' => $uitextsExists,
- ];
- foreach ($configs as $name => $exists) {
- $html .= '
';
- $html .= '
' . ($exists ? 'โ
' : 'โ') . '
';
- $html .= '
' . e($name) . '
';
- $html .= '
' . ($exists ? 'Present' : 'Missing') . '
';
- $html .= '
';
- }
- $html .= '
';
-
- // renderer-config.json
- if ($rendererExists && ! empty($rendererConfig)) {
- $html .= '
';
- $html .= '
';
- $html .= '
renderer-config.json
';
- $rendererFields = [
- 'socket.url' => [$rendererConfig['socket.url'] ?? null, __('Socket URL')],
- 'asset.url' => [$rendererConfig['asset.url'] ?? null, __('Asset URL')],
- 'gamedata.url' => [$rendererConfig['gamedata.url'] ?? null, __('Gamedata URL')],
- 'images.url' => [$rendererConfig['images.url'] ?? null, __('Images URL')],
- ];
- foreach ($rendererFields as [$value, $label]) {
- if (! $value) {
- continue;
- }
- $html .= '
';
- $html .= '
' . e($label) . '
';
- $html .= '
';
- $display = strlen((string) $value) > 30
- ? '' . e($value) . ''
- : e($value);
- $html .= '' . $display . '';
- $html .= '
';
- $html .= '
';
- }
- $html .= '
';
- }
-
- // ui-config.json
- if ($uiExists && ! empty($uiConfig)) {
- $html .= '
';
- $html .= '
';
- $html .= '
ui-config.json
';
- $uiFields = [
- 'camera.url' => [$uiConfig['camera.url'] ?? null, __('Camera URL')],
- 'thumbnails.url' => [$uiConfig['thumbnails.url'] ?? null, __('Thumbnails URL')],
- 'group.homepage.url' => [$uiConfig['group.homepage.url'] ?? null, __('Group Homepage')],
- 'habbopages.url' => [$uiConfig['habbopages.url'] ?? null, __('Habbopages URL')],
- 'url.prefix' => [$uiConfig['url.prefix'] ?? null, __('URL Prefix')],
- ];
- foreach ($uiFields as [$value, $label]) {
- if (! $value) {
- continue;
- }
- $html .= '
';
- $html .= '
' . e($label) . '
';
- $html .= '
';
- $display = strlen((string) $value) > 30
- ? '' . e($value) . ''
- : e($value);
- $html .= '' . $display . '';
- $html .= '
';
- $html .= '
';
- }
- $html .= '
';
- }
-
- if (! $rendererExists && ! $uiExists) {
- $html .= '
';
- $html .= '
โ ๏ธ
';
- $html .= 'Geen config bestanden gevonden
';
- $html .= '
Genereer configs via de knop hierboven';
- $html .= '
';
- }
-
- $html .= '
';
-
- return new HtmlString($html);
- }
-
private function clearSettingsCache(): void
{
Cache::forget('website_settings');
diff --git a/app/Filament/Pages/Monitoring/Commandocentrum.php b/app/Filament/Pages/Monitoring/Commandocentrum.php
index 1e913e2..09f1d72 100755
--- a/app/Filament/Pages/Monitoring/Commandocentrum.php
+++ b/app/Filament/Pages/Monitoring/Commandocentrum.php
@@ -5,7 +5,6 @@ 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;
@@ -16,7 +15,6 @@ use App\Services\Diagnostics\DiagnosticRunner;
use App\Services\GitHubService;
use App\Services\RconService;
use App\Services\SettingsService;
-use App\Services\UpdateHistoryService;
use BackedEnum;
use Exception;
use Filament\Actions\Action;
@@ -106,21 +104,6 @@ final class Commandocentrum extends Page implements HasForms
'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' => '',
];
}
@@ -233,92 +216,6 @@ final class Commandocentrum extends Page implements HasForms
->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')
@@ -367,15 +264,6 @@ final class Commandocentrum extends Page implements HasForms
->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')
@@ -491,100 +379,6 @@ final class Commandocentrum extends Page implements HasForms
]);
}
- private function renderEmulatorSettingsView(): View
- {
- return view('filament.components.commandocentrum.emulator-settings', [
- 'emulatorBranchesHtml' => $this->getEmulatorBranchesHtml(),
- 'emulatorStatusHtml' => $this->renderEmulatorStatusView()->render(),
- ]);
- }
-
- private function renderEmulatorStatusView(): 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(): View
- {
- return view('filament.components.commandocentrum.nitro-settings', [
- 'nitroBranchesHtml' => $this->getNitroBranchesHtml(),
- 'nitroStatusHtml' => $this->renderNitroStatusView()->render(),
- ]);
- }
-
- private function renderNitroStatusView(): 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(): View
- {
- try {
- $backups = app(EmulatorControlAction::class)->getBackups();
- } catch (Exception) {
- $backups = [];
- }
-
- return view('filament.components.commandocentrum.backups-list', [
- 'backups' => $backups,
- ]);
- }
-
private function renderClothingStatusView(): View
{
try {
@@ -614,19 +408,6 @@ final class Commandocentrum extends Page implements HasForms
]);
}
- private function renderUpdateHistoryView(): 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 {
@@ -878,33 +659,6 @@ final class Commandocentrum extends Page implements HasForms
}
}
- 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 {
@@ -924,38 +678,6 @@ final class Commandocentrum extends Page implements HasForms
}
}
- 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 {
@@ -972,58 +694,6 @@ final class Commandocentrum extends Page implements HasForms
}
}
- 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 {
diff --git a/app/Filament/Resources/Miscellaneous/AlertLogResource/AlertLogResource.php b/app/Filament/Resources/Miscellaneous/AlertLogResource/AlertLogResource.php
index 7961312..f3b99b4 100755
--- a/app/Filament/Resources/Miscellaneous/AlertLogResource/AlertLogResource.php
+++ b/app/Filament/Resources/Miscellaneous/AlertLogResource/AlertLogResource.php
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Filament\Resources\Miscellaneous\AlertLogResource;
use App\Models\Miscellaneous\AlertLog;
-use App\Services\EmulatorUpdateService;
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
@@ -136,14 +135,12 @@ class AlertLogResource extends Resource
->icon('heroicon-o-trash')
->color('gray')
->action(function () {
- $updateService = new EmulatorUpdateService;
- $result = $updateService->clearAllLogs();
Cache::flush();
AlertLog::truncate();
Notification::make()
->success()
->title('๐๏ธ Alle Logs Geleegd!')
- ->body($result['message'])
+ ->body('Alle logs zijn gewist.')
->send();
})
->requiresConfirmation(),
diff --git a/app/Filament/Widgets/UpdateCheckerWidget.php b/app/Filament/Widgets/UpdateCheckerWidget.php
deleted file mode 100755
index 02e5bd0..0000000
--- a/app/Filament/Widgets/UpdateCheckerWidget.php
+++ /dev/null
@@ -1,343 +0,0 @@
-emulatorVersion = setting('emulator_version', '?');
- $this->nitroVersion = setting('nitro_client_version', '?');
-
- try {
- $this->onlineUsers = (int) DB::connection('mysql')->table('users')->where('online', '1')->count();
- $sizeResult = DB::connection('mysql')->select('SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 1) as db_size FROM information_schema.tables WHERE table_schema = DATABASE()');
- $this->dbSize = ($sizeResult[0]->db_size ?? '0') . ' MB';
- } catch (\Exception) {
- $this->dbSize = '? MB';
- }
-
- $this->loadSqlStatus();
-
- $cached = Cache::get('all_updates_check');
-
- if ($cached !== null) {
- $this->emulatorUpdate = $cached['emulator'] ?? false;
- $this->latestEmulatorVersion = $cached['emulator_version'] ?? null;
- $this->nitroUpdate = $cached['nitro'] ?? false;
- $this->latestNitroVersion = $cached['nitro_version'] ?? null;
- $this->hasAnyUpdate = $this->emulatorUpdate || $this->nitroUpdate;
-
- return;
- }
-
- $this->performCheck();
- }
-
- private function loadSqlStatus(): void
- {
- try {
- $updateService = new EmulatorUpdateService;
- $sqlDiagnosis = $updateService->diagnoseSqlUpdates();
-
- $this->sqlTableExists = $sqlDiagnosis['table_exists'] ?? false;
- $this->sqlApplied = $sqlDiagnosis['applied_count'] ?? 0;
- $this->sqlPending = $sqlDiagnosis['pending_count'] ?? 0;
- } catch (\Exception $e) {
- Log::error('[UpdateChecker] SQL status failed: ' . $e->getMessage());
- }
- }
-
- public function performCheck(): void
- {
- try {
- $updateService = new EmulatorUpdateService;
- $check = $updateService->checkForUpdates();
- $this->emulatorUpdate = $check['update_available'] ?? false;
- $this->latestEmulatorVersion = $check['latest_version'] ?? null;
- } catch (\Exception $e) {
- Log::error('[UpdateChecker] Emulator check failed: ' . $e->getMessage());
- }
-
- try {
- $nitroService = new NitroUpdateService;
- $nitroCheck = $nitroService->checkForUpdates();
- $this->nitroUpdate = $nitroCheck['has_updates'] ?? false;
- if ($this->nitroUpdate) {
- $parts = [];
- if ($nitroCheck['client_update'] ?? false) {
- $parts[] = 'Client';
- }
- if ($nitroCheck['renderer_update'] ?? false) {
- $parts[] = 'Renderer';
- }
- $this->latestNitroVersion = implode(' + ', $parts);
- }
- } catch (\Exception $e) {
- Log::error('[UpdateChecker] Nitro check failed: ' . $e->getMessage());
- }
-
- $this->hasAnyUpdate = $this->emulatorUpdate || $this->nitroUpdate;
-
- Cache::put('all_updates_check', [
- 'emulator' => $this->emulatorUpdate,
- 'emulator_version' => $this->latestEmulatorVersion,
- 'nitro' => $this->nitroUpdate,
- 'nitro_version' => $this->latestNitroVersion,
- ], now()->addMinutes(15));
- }
-
- public function forceCheck(): void
- {
- try {
- Cache::forget('all_updates_check');
- $updateService = new EmulatorUpdateService;
- $updateService->resetInstalledDate();
- $this->performCheck();
-
- if ($this->hasAnyUpdate) {
- Notification::make()->success()->title('Updates Gevonden!')->body('Klik op "Alles Updaten" om te installeren')->send();
- } else {
- Notification::make()->info()->title('Geen Updates')->body('Alles is al up-to-date')->send();
- }
- } catch (\Exception $e) {
- Notification::make()->danger()->title('Fout')->body($e->getMessage())->send();
- }
- }
-
- public function repairSystem(): void
- {
- try {
- Cache::forget('all_updates_check');
-
- $messages = [];
- $errors = [];
-
- try {
- $emuService = new EmulatorUpdateService;
- $repairResult = $emuService->repairEmulator();
-
- if ($repairResult['success']) {
- $messages[] = '๐ฅ๏ธ Emulator: ' . ($repairResult['message'] ?? 'Gerepareerd');
- } else {
- $errors[] = 'Emulator: ' . ($repairResult['error'] ?? 'Onbekende fout');
- }
- } catch (\Exception $e) {
- $errors[] = 'Emulator: ' . $e->getMessage();
- }
-
- try {
- $nitroService = new NitroUpdateService;
- $repairResult = $nitroService->repair();
-
- if ($repairResult['success']) {
- $messages[] = '๐ฎ Nitro: ' . ($repairResult['message'] ?? 'Gerepareerd');
- } else {
- $errors[] = 'Nitro: ' . ($repairResult['error'] ?? 'Onbekende fout');
- }
- } catch (\Exception $e) {
- $errors[] = 'Nitro: ' . $e->getMessage();
- }
-
- if ($messages !== []) {
- Notification::make()->success()->title('Reparatie Voltooid!')->body(implode(' | ', $messages))->send();
- }
-
- if ($errors !== []) {
- Notification::make()->danger()->title('Reparatie Fout')->body(implode(' | ', $errors))->send();
- }
-
- Cache::forget('all_updates_check');
- $this->mount();
- } catch (\Exception $e) {
- Notification::make()->danger()->title('Fout')->body($e->getMessage())->send();
- }
- }
-
- public function diagnoseSystem(): void
- {
- try {
- $emuService = new EmulatorUpdateService;
- $nitroService = new NitroUpdateService;
-
- $emuDiagnosis = $emuService->diagnose();
- $nitroDiagnosis = $nitroService->diagnose();
-
- $issues = array_merge(
- $emuDiagnosis['issues'] ?? [],
- $nitroDiagnosis['issues'] ?? [],
- );
-
- $recommendations = array_merge(
- $emuDiagnosis['recommendations'] ?? [],
- $nitroDiagnosis['recommendations'] ?? [],
- );
-
- if ($issues === []) {
- Notification::make()->info()->title('Diagnose')->body('Geen problemen gevonden')->send();
- } else {
- $body = 'Problemen: ' . implode(', ', $issues);
- if ($recommendations !== []) {
- $body .= "\n\nAanbevelingen: " . implode(', ', $recommendations);
- }
- Notification::make()->warning()->title('Diagnose Resultaat')->body($body)->send();
- }
- } catch (\Exception $e) {
- Notification::make()->danger()->title('Fout')->body($e->getMessage())->send();
- }
- }
-
- public function updateAll(): void
- {
- try {
- Cache::forget('all_updates_check');
- Cache::forget('website_settings');
-
- $messages = [];
- $errors = [];
- $updateService = new EmulatorUpdateService;
-
- try {
- $result = $updateService->updateEmulator();
- if ($result['success']) {
- $messages[] = '๐ฅ๏ธ ' . ($result['message'] ?? 'Emulator geรผpdatet');
- } else {
- $error = $result['error'] ?? '';
- if (str_contains($error, 'al up-to-date')) {
- $messages[] = '๐ฅ๏ธ Emulator al up-to-date';
- } else {
- $errors[] = 'Emulator: ' . $error;
- }
- }
- } catch (\Exception $e) {
- $errors[] = 'Emulator: ' . $e->getMessage();
- }
-
- try {
- $nitroService = new NitroUpdateService;
- $nitroCheck = $nitroService->checkForUpdates();
-
- if ($nitroCheck['has_updates'] ?? false) {
- $result = $nitroService->updateNitro();
- if ($result['success']) {
- $parts = [];
- if ($result['renderer_updated'] ?? false) {
- $parts[] = 'Renderer';
- }
- if ($result['client_updated'] ?? false) {
- $parts[] = 'Client';
- }
- if ($result['built'] ?? false) {
- $parts[] = 'Build';
- }
- if ($result['deployed'] ?? false) {
- $parts[] = 'Deploy';
- }
- if ($parts !== []) {
- $messages[] = '๐ฎ Nitro: ' . implode(', ', $parts);
- }
- } elseif (! empty($result['errors'] ?? [])) {
- $errors = array_merge($errors, $result['errors']);
- }
- } else {
- $messages[] = '๐ฎ Nitro al up-to-date';
- }
- } catch (\Exception $e) {
- $errors[] = 'Nitro: ' . $e->getMessage();
- }
-
- try {
- $sqlResult = $updateService->runSqlUpdates();
- if ($sqlResult['sql_updated'] ?? false) {
- $count = count($sqlResult['files_run'] ?? []);
- if ($count > 0) {
- $messages[] = "๐ {$count} SQL updates";
- }
- }
- } catch (\Exception $e) {
- $errors[] = 'SQL: ' . $e->getMessage();
- }
-
- try {
- $updateService->restartEmulator();
- $messages[] = '๐ Emulator herstart';
- } catch (\Exception $e) {
- Log::error('[UpdateChecker] Restart failed: ' . $e->getMessage());
- }
-
- try {
- $rcon = new RconService;
- $rcon->sendCommand('updatecatalog');
- $rcon->sendCommand('updatewordfilter');
- $messages[] = '๐ Catalogus + filter vernieuwd';
- } catch (\Exception $e) {
- Log::error('[UpdateChecker] RCON failed: ' . $e->getMessage());
- }
-
- if ($messages !== []) {
- Notification::make()->success()->title('Updates Voltooid!')->body(implode(' | ', $messages))->send();
- }
-
- if ($errors !== []) {
- Notification::make()->danger()->title('Sommige Updates Mislukt')->body(implode(' | ', $errors))->send();
- }
-
- if ($messages === [] && $errors === []) {
- Notification::make()->info()->title('Geen Updates')->body('Alles was al up-to-date')->send();
- }
-
- Cache::forget('all_updates_check');
- $this->mount();
- } catch (\Exception $e) {
- Notification::make()->danger()->title('Fout')->body($e->getMessage())->send();
- }
- }
-
- #[\Override]
- public static function canView(): bool
- {
- return true;
- }
-}
diff --git a/app/Services/Emulator/EmulatorBuildService.php b/app/Services/Emulator/EmulatorBuildService.php
deleted file mode 100755
index b279cca..0000000
--- a/app/Services/Emulator/EmulatorBuildService.php
+++ /dev/null
@@ -1,272 +0,0 @@
-loadConfiguration();
- }
-
- public function buildFromSource(bool $force = false): array
- {
- $repo = $this->sourceRepo ?: $this->githubRepo;
- $branch = $this->sourceBranch ?: $this->githubBranch ?: 'main';
-
- if (! $repo) {
- return ['success' => false, 'error' => 'Geen source repo geconfigureerd'];
- }
-
- $sourcePath = $this->emulatorSourcePath;
- $serviceName = $this->emulatorService;
-
- Log::info('[EmulatorBuild] Starting source build', [
- 'repo' => $repo,
- 'branch' => $branch,
- 'path' => $sourcePath,
- ]);
-
- $this->ensureJavaInstalled();
-
- Process::timeout(10)->run('mkdir -p ' . escapeshellarg(dirname((string) $sourcePath)));
- Process::timeout(10)->run('mkdir -p ' . escapeshellarg((string) $this->jarPath));
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg(dirname((string) $sourcePath)));
-
- $maxRetries = 2;
- $lastError = '';
-
- for ($retry = 0; $retry <= $maxRetries; $retry++) {
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg(dirname((string) $sourcePath)) . ' 2>/dev/null || true');
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg((string) $sourcePath) . ' 2>/dev/null || true');
-
- if ($retry > 0) {
- Log::info('[EmulatorBuild] Retry attempt', ['attempt' => $retry]);
- Process::timeout(30)->run('rm -rf ' . escapeshellarg((string) $sourcePath));
- }
-
- try {
- $existsCheck = Process::timeout(5)->run('[ -d ' . escapeshellarg((string) $sourcePath) . " ] && echo 'exists'");
- $sourceExists = $existsCheck->successful() && trim($existsCheck->output()) === 'exists';
-
- $gitCheck = Process::timeout(5)->run('[ -d ' . escapeshellarg((string) $sourcePath) . "/.git ] && echo 'git'");
- $isGitRepo = $gitCheck->successful() && trim($gitCheck->output()) === 'git';
-
- if ($sourceExists && $isGitRepo) {
- Log::info('[EmulatorBuild] Pulling latest changes');
- $commands = [
- 'cd ' . escapeshellarg((string) $sourcePath) . ' && git fetch origin',
- 'cd ' . escapeshellarg((string) $sourcePath) . ' && git checkout ' . escapeshellarg($branch),
- 'cd ' . escapeshellarg((string) $sourcePath) . ' && git pull origin ' . escapeshellarg($branch),
- ];
- } else {
- Log::info('[EmulatorBuild] Cloning repository');
- $commands = [
- 'sudo rm -rf ' . escapeshellarg((string) $sourcePath) . ' 2>/dev/null || rm -rf ' . escapeshellarg((string) $sourcePath),
- 'mkdir -p ' . escapeshellarg(dirname((string) $sourcePath)),
- 'git clone --branch ' . escapeshellarg($branch) . ' --depth 1 https://github.com/' . escapeshellarg($repo) . '.git ' . escapeshellarg((string) $sourcePath),
- 'chown -R www-data:www-data ' . escapeshellarg((string) $sourcePath),
- ];
- }
-
- $command = implode(' && ', $commands);
- $result = Process::timeout(300)->run($command);
-
- if ($result->failed()) {
- $lastError = 'Git clone/pull failed: ' . substr($result->errorOutput(), 0, 300);
- Log::warning('[EmulatorBuild] Git operation failed', ['error' => $lastError, 'attempt' => $retry]);
-
- continue;
- }
-
- $buildCommands = $this->getBuildCommands($sourcePath);
-
- Log::info('[EmulatorBuild] Running build', ['command' => $buildCommands]);
- $buildResult = Process::timeout(600)->run('cd ' . escapeshellarg((string) $sourcePath) . ' && ' . $buildCommands);
-
- $hasSignalError = str_contains($buildResult->errorOutput(), 'signal') || str_contains($buildResult->output(), 'signal');
-
- if ($hasSignalError) {
- Log::warning('[EmulatorBuild] Build process received signal, checking if JAR was built anyway');
- }
-
- $jarPath = $this->findBuiltJar($sourcePath);
-
- if ($jarPath) {
- Log::info('[EmulatorBuild] JAR found despite build status', ['jar' => $jarPath]);
- } elseif ($buildResult->failed() && ! $hasSignalError) {
- $lastError = 'Build failed: ' . substr($buildResult->errorOutput(), 0, 500);
- Log::warning('[EmulatorBuild] Build failed', ['error' => $lastError, 'attempt' => $retry]);
-
- if ($retry < $maxRetries) {
- $cleanCommands = [
- 'cd ' . escapeshellarg((string) $sourcePath) . ' && mvn clean 2>/dev/null || ./gradlew clean 2>/dev/null || true',
- ];
- Process::timeout(60)->run(implode(' && ', $cleanCommands));
-
- continue;
- }
-
- continue;
- }
-
- if (! $jarPath) {
- $lastError = 'Build succeeded but JAR not found. Check build output.';
- Log::warning('[EmulatorBuild] JAR not found', ['attempt' => $retry]);
-
- continue;
- }
-
- return $this->deployJar($jarPath, $serviceName);
-
- } catch (\Exception $e) {
- $lastError = $e->getMessage();
- Log::error('[EmulatorBuild] Source build exception', ['error' => $lastError, 'attempt' => $retry]);
- }
- }
-
- return [
- 'success' => false,
- 'error' => 'Build mislukt na ' . ($maxRetries + 1) . ' pogingen. Laatste fout: ' . $lastError,
- ];
- }
-
- public function getBuildCommands(string $sourcePath): string
- {
- $pomPath = $sourcePath;
- $pomCheck = Process::timeout(5)->run("[ -f {$pomPath}/pom.xml ] && echo 'pom'");
-
- if (! $pomCheck->successful() || trim($pomCheck->output()) !== 'pom') {
- $pomPath = $sourcePath . '/Emulator';
- $pomCheck = Process::timeout(5)->run("[ -f {$pomPath}/pom.xml ] && echo 'pom'");
- }
-
- $gradlewPath = $sourcePath . '/gradlew';
- $gradlewCheck = Process::timeout(5)->run("[ -f {$gradlewPath} ] && echo 'gradlew'");
-
- $gradlePath = $sourcePath . '/build.gradle';
- $gradleCheck = Process::timeout(5)->run("[ -f {$gradlePath} ] && echo 'gradle'");
-
- if ($pomCheck->successful() && trim($pomCheck->output()) === 'pom') {
- return "cd {$pomPath} && mvn clean package -DskipTests 2>&1";
- }
-
- if ($gradlewCheck->successful() && trim($gradlewCheck->output()) === 'gradlew') {
- return "cd {$sourcePath} && chmod +x gradlew && ./gradlew clean build -x test 2>&1";
- }
-
- if ($gradleCheck->successful() && trim($gradleCheck->output()) === 'gradle') {
- return "cd {$sourcePath} && gradle clean build -x test 2>&1";
- }
-
- return "cd {$sourcePath} && ls -la";
- }
-
- public function findBuiltJar(string $sourcePath): ?string
- {
- $patterns = [
- $sourcePath . '/target/*.jar',
- $sourcePath . '/build/libs/*.jar',
- $sourcePath . '/*/*.jar',
- $sourcePath . '/*.jar',
- $sourcePath . '/Emulator/target/*.jar',
- $sourcePath . '/Emulator/build/libs/*.jar',
- $sourcePath . '/Emulator/*/*.jar',
- ];
-
- foreach ($patterns as $pattern) {
- $result = Process::timeout(10)->run('ls -t ' . $pattern . ' 2>/dev/null | head -1');
- if ($result->successful()) {
- $jarPath = trim($result->output());
- if ($jarPath !== '' && $jarPath !== '0' && str_contains($jarPath, '.jar')) {
- return $jarPath;
- }
- }
- }
-
- return null;
- }
-
- private function deployJar(string $jarPath, string $serviceName): array
- {
- $jarName = basename($jarPath);
- $version = $this->extractVersionFromFilename($jarName);
- if ($version === '' || $version === '0') {
- $version = date('Y.m.d');
- }
-
- $deployCommands = [
- 'mkdir -p ' . escapeshellarg((string) $this->jarPath),
- 'chown -R www-data:www-data ' . escapeshellarg((string) $this->jarPath),
- 'if ls ' . escapeshellarg((string) $this->jarPath) . '/*.jar 1>/dev/null 2>&1; then mkdir -p ' . escapeshellarg((string) $this->jarPath) . '/backup && mv ' . escapeshellarg((string) $this->jarPath) . '/*.jar ' . escapeshellarg((string) $this->jarPath) . '/backup/; fi',
- 'cp -f ' . escapeshellarg($jarPath) . ' ' . escapeshellarg((string) $this->jarPath) . '/' . escapeshellarg($jarName),
- 'chown www-data:www-data ' . escapeshellarg((string) $this->jarPath) . '/' . escapeshellarg($jarName),
- 'chmod 755 ' . escapeshellarg((string) $this->jarPath) . '/' . escapeshellarg($jarName),
- 'ls -la ' . escapeshellarg((string) $this->jarPath) . '/',
- 'systemctl restart ' . escapeshellarg((string) $serviceName) . ' 2>&1 || service ' . escapeshellarg((string) $serviceName) . ' restart 2>&1 || true',
- ];
-
- $deployCommand = implode(' && ', $deployCommands);
-
- Log::info('[EmulatorBuild] Deploying jar', [
- 'source' => $jarPath,
- 'destination' => "{$this->jarPath}/{$jarName}",
- ]);
-
- $deployResult = Process::timeout(60)->run($deployCommand);
-
- if (! $deployResult->successful()) {
- return [
- 'success' => false,
- 'error' => 'Deploy failed: ' . substr($deployResult->errorOutput(), 0, 300),
- ];
- }
-
- $sourceService = new EmulatorSourceService;
- $sourceInfo = $sourceService->checkForUpdates();
- $installedDate = $sourceInfo['latest_timestamp'] ?? time();
-
- $this->settings->set('emulator_version', $version);
- $this->settings->set('emulator_jar_installed_date', (string) $installedDate);
- $this->settings->set('emulator_source_commit', $sourceInfo['latest_sha'] ?? 'unknown');
- $this->settings->set('emulator_source_date', (string) $installedDate);
-
- $currentBranch = $this->sourceBranch ?: $this->githubBranch ?: 'main';
- $this->settings->set('emulator_installed_branch', $currentBranch);
-
- $sqlService = new EmulatorSqlService;
- $sqlService->runUpdates();
-
- Log::info('[EmulatorBuild] Source build successful');
-
- return [
- 'success' => true,
- 'version' => $version,
- 'jar' => $jarName,
- 'built' => true,
- 'message' => "โ
Emulator vanaf source gebouwd!\n๐ฆ {$jarName}\n๐ Service herstart",
- ];
- }
-
- private function ensureJavaInstalled(): void
- {
- $javaCheck = Process::timeout(5)->run('java -version 2>&1');
- if (! $javaCheck->successful()) {
- Log::warning('[EmulatorBuild] Java not found, attempting to install');
- Process::timeout(180)->run('apt-get update && apt-get install -y default-jdk 2>&1');
- }
-
- $mavenCheck = Process::timeout(5)->run('which mvn');
- if (! $mavenCheck->successful()) {
- Log::warning('[EmulatorBuild] Maven not found, attempting to install');
- Process::timeout(180)->run('apt-get install -y maven 2>&1');
- }
- }
-}
diff --git a/app/Services/Emulator/EmulatorJarService.php b/app/Services/Emulator/EmulatorJarService.php
deleted file mode 100755
index a64002c..0000000
--- a/app/Services/Emulator/EmulatorJarService.php
+++ /dev/null
@@ -1,509 +0,0 @@
-loadConfiguration();
- }
-
- public function isConfigured(): bool
- {
- return ! in_array($this->githubUrl, [null, '', '0'], true) || ! in_array($this->jarDirectUrl, [null, '', '0'], true);
- }
-
- public function checkForUpdates(): array
- {
- if (! $this->isConfigured()) {
- return [
- 'update_available' => false,
- 'error' => 'Configureer een GitHub URL of directe .jar URL',
- ];
- }
-
- $sourceService = new EmulatorSourceService;
- $sourceInfo = $sourceService->checkForUpdates();
- $hasSourceUpdates = $sourceInfo && $sourceInfo['has_update'];
-
- if (! in_array($this->jarDirectUrl, [null, '', '0'], true)) {
- return $this->checkDirectUrlUpdates($sourceInfo, $hasSourceUpdates);
- }
-
- return $this->checkGitHubFolderUpdates($sourceInfo, $hasSourceUpdates);
- }
-
- public function performUpdate(array $check): array
- {
- $jarUrl = $check['jar_url'];
- $jarName = $check['jar_name'];
- $version = $check['latest_version'];
- $serviceName = $this->emulatorService;
-
- $tempDir = '/tmp/emulator-update-' . Str::random(8);
- $tempJar = $tempDir . '/' . $jarName;
-
- $updateScript = $this->buildUpdateScript($jarUrl, $jarName, $tempDir, $tempJar, $serviceName);
-
- $scriptPath = '/tmp/emulator_update_' . uniqid() . '.sh';
- file_put_contents($scriptPath, $updateScript);
- chmod($scriptPath, 0755);
-
- Log::info('[EmulatorJar] Starting update', [
- 'version' => $version,
- 'jar' => $jarName,
- 'url' => $jarUrl,
- ]);
-
- try {
- $result = Process::timeout(600)->run('bash ' . $scriptPath . ' 2>&1');
- @unlink($scriptPath);
-
- if ($result->exitCode() !== 0) {
- Log::error('[EmulatorJar] Update failed', [
- 'output' => $result->output(),
- 'error' => $result->errorOutput(),
- ]);
-
- return [
- 'success' => false,
- 'error' => 'Update mislukt: ' . substr($result->output(), 0, 300),
- ];
- }
-
- $this->storeUpdateInfo($version, $check);
-
- Log::info('[EmulatorJar] Update successful');
-
- return [
- 'success' => true,
- 'version' => $version,
- 'jar' => $jarName,
- 'message' => "โ
Emulator geรผpdatet naar v{$version}!\n๐ฆ {$jarName}\n๐ Service herstart",
- ];
- } catch (\Exception $e) {
- Log::error('[EmulatorJar] Exception', ['error' => $e->getMessage()]);
-
- return [
- 'success' => false,
- 'error' => $e->getMessage(),
- ];
- }
- }
-
- public function findLatestJar(): ?array
- {
- if (! $this->githubRepo) {
- return null;
- }
-
- $branch = $this->githubBranch ?: 'main';
-
- $commonNames = ['arcturus.jar', 'Arcturus.jar', 'emulator.jar', 'habbo.jar', 'hotel.jar'];
-
- foreach ($commonNames as $name) {
- try {
- $apiUrl = "https://api.github.com/repos/{$this->githubRepo}/contents/Latest_Compiled_Version/{$name}?ref={$branch}";
- $response = Http::timeout(10)
- ->withHeaders([
- 'Accept' => 'application/vnd.github.v3+json',
- 'User-Agent' => 'AtomCMS-Emulator-Updater',
- ])
- ->get($apiUrl);
-
- if (! $response->successful()) {
- continue;
- }
-
- $data = json_decode($response->body(), true);
-
- if (! isset($data['sha']) || ! isset($data['download_url'])) {
- continue;
- }
-
- $commitDate = null;
- $commitSha = $data['sha'];
-
- try {
- $commitsResponse = Http::timeout(10)
- ->withHeaders([
- 'Accept' => 'application/vnd.github.v3+json',
- 'User-Agent' => 'AtomCMS-Emulator-Updater',
- ])
- ->get("https://api.github.com/repos/{$this->githubRepo}/commits", [
- 'path' => "Latest_Compiled_Version/{$name}",
- 'sha' => $branch,
- 'per_page' => 1,
- ]);
-
- if ($commitsResponse->successful()) {
- $commits = $commitsResponse->json();
- if (! empty($commits) && isset($commits[0]['commit']['committer']['date'])) {
- $commitDate = strtotime($commits[0]['commit']['committer']['date']);
- $commitSha = $commits[0]['sha'] ?? $data['sha'];
- }
- }
- } catch (\Exception) {
- Log::debug('[EmulatorJar] Could not fetch commit date for ' . $name);
- }
-
- $installedDate = $this->settings->getOrDefault('emulator_jar_installed_date', null);
- $installedJarCommit = $this->settings->getOrDefault('emulator_jar_commit', null);
-
- $isUpdate = false;
- if ($installedJarCommit !== null) {
- $isUpdate = $installedJarCommit !== $commitSha;
- } elseif ($installedDate !== null && $commitDate !== null) {
- $isUpdate = (int) $installedDate < $commitDate;
- } elseif ($installedDate === null && $commitDate !== null) {
- $isUpdate = true;
- }
-
- $version = $this->extractVersionFromFilename($name);
- if ($version === '' || $version === '0') {
- $version = $commitDate ? date('Y.m.d', $commitDate) : date('Y.m.d');
- }
-
- return [
- 'name' => $name,
- 'url' => $data['download_url'],
- 'version' => $version,
- 'commit' => $commitSha,
- 'commit_date' => $commitDate,
- 'is_update' => $isUpdate,
- 'installed_date' => $installedDate,
- ];
- } catch (\Exception $e) {
- Log::warning('[EmulatorJar] Error checking JAR file ' . $name, ['error' => $e->getMessage()]);
- }
- }
-
- return null;
- }
-
- private function checkDirectUrlUpdates(?array $sourceInfo, bool $hasSourceUpdates): array
- {
- $jarInfo = $this->validateDirectUrl($this->jarDirectUrl);
-
- if ($jarInfo) {
- if (! empty($jarInfo['version'])) {
- $currentVersion = $this->settings->getOrDefault('emulator_version', '0.0.0');
- if ($jarInfo['version'] !== $currentVersion) {
- $this->settings->set('emulator_version', $jarInfo['version']);
- }
- }
-
- $hasJarUpdates = $jarInfo['is_update'] ?? false;
-
- if ($hasSourceUpdates && ! $hasJarUpdates) {
- return [
- 'update_available' => true,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'latest_version' => $sourceInfo['latest_timestamp'] ? date('Y.m.d', $sourceInfo['latest_timestamp']) : $jarInfo['version'],
- 'release_name' => 'Source Update',
- 'jar_url' => null,
- 'jar_name' => null,
- 'jar_size' => 'Onbekend',
- 'type' => 'source_build',
- 'source_info' => $sourceInfo,
- 'message' => 'Nieuwe commits beschikbaar - build vanaf source nodig',
- ];
- }
-
- return [
- 'update_available' => $hasJarUpdates,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'latest_version' => $jarInfo['version'],
- 'release_name' => $hasSourceUpdates ? 'Source Update' : 'Direct URL',
- 'jar_url' => $this->jarDirectUrl,
- 'jar_name' => $jarInfo['name'],
- 'jar_size' => 'Onbekend',
- 'type' => $hasSourceUpdates ? 'source_build' : 'direct_url',
- 'commit' => $jarInfo['commit_sha'] ?? null,
- 'source_info' => $sourceInfo,
- 'has_source_updates' => $hasSourceUpdates,
- ];
- }
-
- if ($hasSourceUpdates) {
- return [
- 'update_available' => true,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'latest_version' => $sourceInfo['latest_timestamp'] ? date('Y.m.d', $sourceInfo['latest_timestamp']) : 'Onbekend',
- 'release_name' => 'Source Update',
- 'jar_url' => null,
- 'jar_name' => null,
- 'jar_size' => 'Onbekend',
- 'type' => 'source_build',
- 'source_info' => $sourceInfo,
- 'message' => 'Nieuwe commits beschikbaar - build vanaf source nodig',
- ];
- }
-
- return [
- 'update_available' => false,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'error' => 'Directe .jar URL is niet bereikbaar',
- ];
- }
-
- private function checkGitHubFolderUpdates(?array $sourceInfo, bool $hasSourceUpdates): array
- {
- $jarInfo = $this->findLatestJar();
-
- if (! $jarInfo) {
- if ($hasSourceUpdates) {
- return [
- 'update_available' => true,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'latest_version' => $sourceInfo['latest_timestamp'] ? date('Y.m.d', $sourceInfo['latest_timestamp']) : 'Onbekend',
- 'release_name' => 'Source Update',
- 'jar_url' => null,
- 'jar_name' => null,
- 'jar_size' => 'Onbekend',
- 'type' => 'source_build',
- 'source_info' => $sourceInfo,
- 'message' => 'Nieuwe commits beschikbaar - build vanaf source nodig',
- ];
- }
-
- return [
- 'update_available' => false,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'latest_version' => 'Onbekend',
- 'error' => 'Kon geen .jar bestand vinden',
- 'type' => 'not_found',
- 'source_available' => $sourceInfo !== null,
- ];
- }
-
- $version = $jarInfo['version'];
- $hasJarUpdates = $jarInfo['is_update'] ?? false;
-
- if ($hasSourceUpdates && ! $hasJarUpdates) {
- return [
- 'update_available' => true,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'latest_version' => $sourceInfo['latest_timestamp'] ? date('Y.m.d', $sourceInfo['latest_timestamp']) : $version,
- 'release_name' => 'Source Update',
- 'jar_url' => null,
- 'jar_name' => null,
- 'jar_size' => 'Onbekend',
- 'type' => 'source_build',
- 'source_info' => $sourceInfo,
- 'message' => 'Nieuwe commits beschikbaar - build vanaf source nodig',
- ];
- }
-
- return [
- 'update_available' => $hasJarUpdates,
- 'current_version' => $this->settings->getOrDefault('emulator_version', '0.0.0'),
- 'latest_version' => $hasSourceUpdates ? ($sourceInfo['latest_timestamp'] ? date('Y.m.d', $sourceInfo['latest_timestamp']) : $version) : $version,
- 'release_name' => $hasSourceUpdates ? 'Source Update' : 'Latest from GitHub',
- 'jar_url' => $jarInfo['url'],
- 'jar_name' => $jarInfo['name'],
- 'jar_size' => 'Onbekend',
- 'type' => $hasSourceUpdates ? 'source_build' : 'github_folder',
- 'commit' => $jarInfo['commit'] ?? null,
- 'commit_date' => $jarInfo['commit_date'] ?? null,
- 'source_info' => $sourceInfo,
- 'has_source_updates' => $hasSourceUpdates,
- ];
- }
-
- private function validateDirectUrl(string $url): ?array
- {
- if ($url === '' || $url === '0' || ! str_ends_with(strtolower($url), '.jar')) {
- return null;
- }
-
- try {
- $jarName = basename(parse_url($url, PHP_URL_PATH));
- $version = $this->extractVersionFromFilename($jarName);
- $storedVersion = $this->settings->getOrDefault('emulator_version', '0.0.0');
-
- $lastModified = null;
- $commitSha = null;
- $gitHubInfoAvailable = false;
-
- if (preg_match('/github\.com\/([^\/]+)\/([^\/]+)\/raw\/refs\/heads\/([^\/]+)\/(.+)/', $url, $matches)) {
- $owner = $matches[1];
- $repo = $matches[2];
- $branch = $matches[3];
- $path = $matches[4];
-
- try {
- $apiResponse = Http::timeout(10)
- ->withHeaders([
- 'Accept' => 'application/vnd.github.v3+json',
- 'User-Agent' => 'AtomCMS-Emulator-Updater',
- ])
- ->get("https://api.github.com/repos/{$owner}/{$repo}/contents/{$path}", [
- 'ref' => $branch,
- ]);
-
- if ($apiResponse->successful()) {
- $data = $apiResponse->json();
-
- if (isset($data['sha']) && ! isset($data['message'])) {
- $gitHubInfoAvailable = true;
- $commitSha = $data['sha'];
-
- if (isset($data['commit']['committer']['date'])) {
- $lastModified = strtotime($data['commit']['committer']['date']);
- }
- }
- }
- } catch (\Exception) {
- Log::debug('[EmulatorJar] Could not fetch GitHub commit info for direct URL');
- }
- }
-
- if ($lastModified === null) {
- $response = Http::timeout(10)->head($url);
- if ($response->successful()) {
- $modifiedSince = $response->header('Last-Modified');
- if ($modifiedSince) {
- $lastModified = strtotime($modifiedSince);
- if ($lastModified === false) {
- $lastModified = null;
- }
- }
- }
- }
-
- $isUpdate = false;
- $installedDate = $this->settings->getOrDefault('emulator_jar_installed_date', null);
-
- $storedData = $this->settings->getOrDefault('emulator_direct_url_info_' . md5($url));
- if ($storedData !== null && is_string($storedData)) {
- $storedDataArray = json_decode($storedData, true);
- if (is_array($storedDataArray)) {
- if ($gitHubInfoAvailable && $commitSha !== null && isset($storedDataArray['commit_sha'])) {
- $isUpdate = $commitSha !== $storedDataArray['commit_sha'];
- } elseif ($lastModified !== null && $installedDate !== null) {
- $isUpdate = (int) $installedDate < $lastModified;
- } elseif ($lastModified !== null && isset($storedDataArray['last_modified'])) {
- $isUpdate = $lastModified > $storedDataArray['last_modified'];
- } elseif (! in_array($version, ['', '0', $storedVersion], true)) {
- $isUpdate = version_compare($version, $storedVersion) > 0;
- }
- }
- } else {
- $isUpdate = true;
- }
-
- $infoToStore = [
- 'last_checked' => time(),
- 'version' => $version,
- ];
- if ($lastModified) {
- $infoToStore['last_modified'] = $lastModified;
- }
- if ($commitSha) {
- $infoToStore['commit_sha'] = $commitSha;
- }
- $this->settings->set('emulator_direct_url_info_' . md5($url), json_encode($infoToStore));
-
- return [
- 'name' => $jarName,
- 'version' => $version,
- 'last_modified' => $lastModified,
- 'commit_sha' => $commitSha,
- 'is_update' => $isUpdate,
- 'gitHub_rate_limited' => ! $gitHubInfoAvailable && preg_match('/github\.com/', $url),
- ];
- } catch (\Exception $e) {
- Log::warning('[EmulatorJar] Direct URL not reachable', ['error' => $e->getMessage()]);
- }
-
- return null;
- }
-
- private function buildUpdateScript(string $jarUrl, string $jarName, string $tempDir, string $tempJar, string $serviceName): string
- {
- return <<jarPath}'
-SERVICE='{$serviceName}'
-TEMP_DIR='{$tempDir}'
-TEMP_JAR='{$tempJar}'
-
-mkdir -p "\$TEMP_DIR" "\$JAR_PATH"
-
-if ls "\$JAR_PATH"/*.jar 1>/dev/null 2>&1; then
- mkdir -p "\$JAR_PATH/backup"
- mv "\$JAR_PATH"/*.jar "\$JAR_PATH/backup/" 2>/dev/null || true
-fi
-
-download_success=false
-for attempt in 1 2 3; do
- if curl -L --max-time 300 --retry 3 --retry-delay 5 -o "\$TEMP_JAR" "\$JAR_URL" 2>&1; then
- FILE_TYPE=\$(file -b "\$TEMP_JAR" 2>/dev/null)
- JAR_SIZE=\$(stat -c%s "\$TEMP_JAR" 2>/dev/null || echo 0)
-
- if echo "\$FILE_TYPE" | grep -qi "zip\|jar\|archive" && [ "\$JAR_SIZE" -gt 1000 ]; then
- download_success=true
- break
- else
- rm -f "\$TEMP_JAR" 2>/dev/null || true
- sleep 3
- fi
- else
- sleep 5
- fi
-done
-
-if [ "\$download_success" = false ]; then
- if ls "\$JAR_PATH/backup"/*.jar 1>/dev/null 2>&1; then
- mv "\$JAR_PATH/backup"/*.jar "\$JAR_PATH/" 2>/dev/null || true
- fi
- rm -rf "\$TEMP_DIR"
- exit 1
-fi
-
-mv "\$TEMP_JAR" "\$JAR_PATH/"
-chown -R www-data:www-data "\$JAR_PATH"
-chmod 755 "\$JAR_PATH/\$JAR_NAME"
-
-systemctl restart "\$SERVICE" 2>&1 || service "\$SERVICE" restart 2>&1 || true
-
-rm -rf "\$TEMP_DIR" 2>/dev/null || true
-BASH;
- }
-
- private function storeUpdateInfo(string $version, array $check): void
- {
- setting('emulator_version', $version);
-
- $commitDate = $check['commit_date'] ?? time();
- $this->settings->set('emulator_jar_installed_date', (string) $commitDate);
- $this->settings->set('emulator_jar_commit', $check['commit'] ?? null);
-
- $sourceSha = $check['source_info']['latest_sha'] ?? $check['commit'] ?? null;
- $sourceDate = $check['source_info']['latest_timestamp'] ?? ($check['commit_date'] ?? time());
- if ($sourceSha) {
- $this->settings->set('emulator_source_commit', $sourceSha);
- }
- if ($sourceDate) {
- $this->settings->set('emulator_source_date', (string) $sourceDate);
- }
-
- $currentBranch = $this->sourceBranch ?: $this->githubBranch ?: 'main';
- $this->settings->set('emulator_installed_branch', $currentBranch);
- }
-}
diff --git a/app/Services/Emulator/EmulatorSourceService.php b/app/Services/Emulator/EmulatorSourceService.php
deleted file mode 100755
index ae6a7ec..0000000
--- a/app/Services/Emulator/EmulatorSourceService.php
+++ /dev/null
@@ -1,273 +0,0 @@
-loadConfiguration();
- }
-
- public function checkForUpdates(): ?array
- {
- $repo = $this->sourceRepo ?: $this->githubRepo;
- $branch = $this->sourceBranch ?: $this->githubBranch ?: 'main';
-
- if (! $repo) {
- return null;
- }
-
- $localCheck = $this->checkLocalSourceUpdates();
- if ($localCheck !== null) {
- return $localCheck;
- }
-
- return $this->checkRemoteSourceUpdates($repo, $branch);
- }
-
- public function isSourceBuildAvailable(): bool
- {
- $hasRepo = ! in_array($this->sourceRepo, [null, '', '0'], true) || ! in_array($this->githubRepo, [null, '', '0'], true);
- $hasPath = ! in_array($this->emulatorSourcePath, [null, '', '0'], true);
-
- if (! $hasRepo || ! $hasPath) {
- return false;
- }
-
- try {
- $result = Process::timeout(5)->run("[ -d {$this->emulatorSourcePath} ] && echo 'exists' || echo 'not_exists'");
-
- return trim($result->output()) === 'exists';
- } catch (\Exception) {
- return false;
- }
- }
-
- private function checkLocalSourceUpdates(): ?array
- {
- $sourcePath = $this->emulatorSourcePath;
-
- $existsCheck = Process::timeout(5)->run("[ -d {$sourcePath} ] && echo 'exists'");
- if (! $existsCheck->successful() || trim($existsCheck->output()) !== 'exists') {
- return $this->checkSourceByCloning();
- }
-
- try {
- $gitCheck = Process::timeout(5)->run("cd {$sourcePath} && git rev-parse --is-inside-work-tree 2>/dev/null");
- if (! $gitCheck->successful()) {
- return $this->checkSourceByCloning();
- }
-
- $currentCommitResult = Process::timeout(5)->run("cd {$sourcePath} && git rev-parse HEAD");
- if (! $currentCommitResult->successful()) {
- return null;
- }
- $currentSha = trim($currentCommitResult->output());
-
- $fetchResult = Process::timeout(30)->run("cd {$sourcePath} && git fetch origin 2>&1");
- if ($fetchResult->failed()) {
- Log::debug('[EmulatorSource] Git fetch failed, trying local check only');
- }
-
- $branch = $this->sourceBranch ?: $this->githubBranch ?: 'main';
- $latestResult = Process::timeout(5)->run("cd {$sourcePath} && git rev-parse origin/{$branch} 2>/dev/null || git rev-parse HEAD");
- if (! $latestResult->successful()) {
- return null;
- }
- $latestSha = trim($latestResult->output());
-
- $dateResult = Process::timeout(5)->run("cd {$sourcePath} && git log -1 --format='%ci' {$latestSha}");
- $latestDate = null;
- $latestTimestamp = time();
- if ($dateResult->successful()) {
- $latestDate = trim($dateResult->output(), "'");
- if ($latestDate !== '' && $latestDate !== '0') {
- $latestTimestamp = strtotime($latestDate);
- }
- }
-
- $this->persistSourceCommitInfo($latestSha, $latestTimestamp);
-
- $msgResult = Process::timeout(5)->run("cd {$sourcePath} && git log -1 --format='%s' {$latestSha}");
- $latestMessage = $msgResult->successful() ? trim($msgResult->output()) : '';
-
- $authorResult = Process::timeout(5)->run("cd {$sourcePath} && git log -1 --format='%an' {$latestSha}");
- $latestAuthor = $authorResult->successful() ? trim($authorResult->output()) : '';
-
- $storedSha = $this->settings->getOrDefault('emulator_source_commit', null);
- $storedDate = $this->settings->getOrDefault('emulator_source_date', null);
-
- $isUpdate = $storedSha !== null && $storedSha !== $latestSha;
- if ($storedSha === null) {
- $isUpdate = true;
- }
-
- return [
- 'has_update' => $isUpdate,
- 'latest_sha' => $latestSha,
- 'latest_date' => $latestDate,
- 'latest_timestamp' => $latestTimestamp,
- 'latest_message' => $latestMessage,
- 'latest_author' => $latestAuthor,
- 'stored_sha' => $storedSha,
- 'stored_date' => $storedDate,
- 'source' => 'local',
- ];
- } catch (\Exception $e) {
- Log::debug('[EmulatorSource] Local source check failed: ' . $e->getMessage());
-
- return null;
- }
- }
-
- private function checkSourceByCloning(): ?array
- {
- $repo = $this->sourceRepo ?: $this->githubRepo;
- $branch = $this->sourceBranch ?: $this->githubBranch ?: 'main';
-
- if (! $repo) {
- return null;
- }
-
- $repo = preg_replace('#/tree/[^/]+/.*$#', '', $repo);
- $repo = str_replace(['https://github.com/', 'http://github.com/'], '', $repo);
- $repo = rtrim($repo, '/');
-
- try {
- $tempDir = '/tmp/emulator-source-check-' . Str::random(8);
-
- $cloneResult = Process::timeout(120)->run(
- "git clone --branch {$branch} --depth 1 https://github.com/{$repo}.git {$tempDir} 2>&1",
- );
-
- if ($cloneResult->failed()) {
- return null;
- }
-
- $shaResult = Process::timeout(5)->run("cd {$tempDir} && git rev-parse HEAD");
- $latestSha = $shaResult->successful() ? trim($shaResult->output()) : '';
-
- $dateResult = Process::timeout(5)->run("cd {$tempDir} && git log -1 --format='%ci'");
- $latestDate = null;
- $latestTimestamp = time();
- if ($dateResult->successful()) {
- $latestDate = trim($dateResult->output());
- if ($latestDate !== '' && $latestDate !== '0') {
- $latestTimestamp = strtotime($latestDate);
- }
- }
-
- $this->persistSourceCommitInfo($latestSha, $latestTimestamp);
-
- $msgResult = Process::timeout(5)->run("cd {$tempDir} && git log -1 --format='%s'");
- $latestMessage = $msgResult->successful() ? trim($msgResult->output()) : '';
-
- $authorResult = Process::timeout(5)->run("cd {$tempDir} && git log -1 --format='%an'");
- $latestAuthor = $authorResult->successful() ? trim($authorResult->output()) : '';
-
- Process::timeout(10)->run("rm -rf {$tempDir}");
-
- $storedSha = $this->settings->getOrDefault('emulator_source_commit', null);
- $storedDate = $this->settings->getOrDefault('emulator_source_date', null);
-
- $isUpdate = $storedSha !== null && $storedSha !== $latestSha;
- if ($storedSha === null) {
- $isUpdate = true;
- }
-
- return [
- 'has_update' => $isUpdate,
- 'latest_sha' => $latestSha,
- 'latest_date' => $latestDate,
- 'latest_timestamp' => $latestTimestamp,
- 'latest_message' => $latestMessage,
- 'latest_author' => $latestAuthor,
- 'stored_sha' => $storedSha,
- 'stored_date' => $storedDate,
- 'source' => 'cloned',
- ];
- } catch (\Exception $e) {
- Log::debug('[EmulatorSource] Source clone check failed: ' . $e->getMessage());
-
- return null;
- }
- }
-
- private function checkRemoteSourceUpdates(string $repo, string $branch): ?array
- {
- try {
- $response = Http::timeout(10)
- ->withHeaders([
- 'Accept' => 'application/vnd.github.v3+json',
- 'User-Agent' => 'AtomCMS-Emulator-Updater',
- ])
- ->get("https://api.github.com/repos/{$repo}/commits", [
- 'sha' => $branch,
- 'per_page' => 1,
- ]);
-
- if (! $response->successful()) {
- return null;
- }
-
- $commits = $response->json();
-
- if (empty($commits) || ! isset($commits[0]['sha'])) {
- return null;
- }
-
- $latestCommit = $commits[0];
- $latestSha = $latestCommit['sha'];
- $latestDate = $latestCommit['commit']['committer']['date'] ?? null;
- $latestTimestamp = $latestDate ? strtotime((string) $latestDate) : time();
-
- $this->persistSourceCommitInfo($latestSha, $latestTimestamp);
-
- $installedDate = $this->settings->getOrDefault('emulator_jar_installed_date', null);
- $storedSha = $this->settings->getOrDefault('emulator_source_commit', null);
- $storedDate = $this->settings->getOrDefault('emulator_source_date', null);
-
- if ($storedSha !== null && $storedSha === $latestSha) {
- $isUpdate = false;
- } elseif ($storedSha !== null && $storedSha !== $latestSha) {
- $isUpdate = true;
- } elseif ($installedDate !== null) {
- $installedTimestamp = is_numeric($installedDate) ? (int) $installedDate : strtotime((string) $installedDate);
- $isUpdate = $installedTimestamp < $latestTimestamp;
- } else {
- $isUpdate = false;
- }
-
- return [
- 'has_update' => $isUpdate,
- 'latest_sha' => $latestSha,
- 'latest_date' => $latestDate,
- 'latest_timestamp' => $latestTimestamp,
- 'latest_message' => $latestCommit['commit']['message'] ?? '',
- 'latest_author' => $latestCommit['commit']['author']['name'] ?? '',
- 'stored_sha' => $storedSha,
- 'stored_date' => $storedDate,
- ];
- } catch (\Exception $e) {
- Log::warning('[EmulatorSource] Could not check source updates via GitHub API', ['error' => $e->getMessage()]);
-
- return null;
- }
- }
-
- private function persistSourceCommitInfo(string $sha, int $timestamp): void
- {
- $this->settings->set('emulator_source_commit', $sha);
- $this->settings->set('emulator_source_date', (string) $timestamp);
- }
-}
diff --git a/app/Services/Emulator/EmulatorSqlService.php b/app/Services/Emulator/EmulatorSqlService.php
deleted file mode 100755
index aba1cc9..0000000
--- a/app/Services/Emulator/EmulatorSqlService.php
+++ /dev/null
@@ -1,510 +0,0 @@
-loadConfiguration();
- }
-
- public function isConfigured(): bool
- {
- return ! in_array($this->githubUrl, [null, '', '0'], true) || ! in_array($this->jarDirectUrl, [null, '', '0'], true);
- }
-
- public function checkForUpdates(bool $recentOnly = true): array
- {
- if (! $this->githubRepo) {
- return [
- 'has_updates' => false,
- 'error' => 'Geen GitHub repo geconfigureerd',
- ];
- }
-
- $this->ensureSqlTableExists();
-
- $result = $this->fetchSqlFilesFromGitHub($recentOnly);
-
- if (isset($result['error'])) {
- return [
- 'has_updates' => false,
- 'error' => $result['error'],
- ];
- }
-
- $sqlFiles = $result;
-
- if ($sqlFiles === []) {
- return [
- 'has_updates' => false,
- 'message' => $recentOnly
- ? 'Geen SQL updates van de afgelopen week gevonden'
- : 'Geen SQL updates gevonden',
- ];
- }
-
- $appliedHashes = $this->getAppliedSqlHashes();
- $newSqlFiles = [];
- $alreadyApplied = [];
-
- foreach ($sqlFiles as $file) {
- $hash = $file['sha'] ?? md5((string) $file['name']);
- if (in_array($hash, $appliedHashes)) {
- $alreadyApplied[] = $file['name'];
-
- continue;
- }
- $newSqlFiles[] = $file;
- }
-
- if ($newSqlFiles === []) {
- return [
- 'has_updates' => false,
- 'message' => 'Alle SQL updates zijn al toegepast (' . count($alreadyApplied) . ' stuks)',
- 'applied_count' => count($alreadyApplied),
- ];
- }
-
- usort($newSqlFiles, fn ($a, $b) => strcmp((string) $a['name'], (string) $b['name']));
-
- return [
- 'has_updates' => true,
- 'count' => count($newSqlFiles),
- 'files' => $newSqlFiles,
- 'message' => count($newSqlFiles) . ' nieuwe SQL update(s) van deze week',
- 'applied_count' => count($alreadyApplied),
- ];
- }
-
- public function runUpdates(): array
- {
- $sqlCheck = $this->checkForUpdates(false);
-
- if (! ($sqlCheck['has_updates'] ?? false)) {
- return ['success' => true, 'sql_updated' => false, 'message' => 'Geen nieuwe SQL updates'];
- }
-
- $results = [
- 'success' => true,
- 'sql_updated' => true,
- 'files_run' => [],
- 'errors' => [],
- ];
-
- foreach ($sqlCheck['files'] as $file) {
- $sqlResult = $this->downloadAndRunSql($file);
-
- if ($sqlResult['success']) {
- $results['files_run'][] = $file['name'];
- $this->markSqlAsApplied($file);
- } else {
- $results['errors'][] = $file['name'] . ': ' . $sqlResult['error'];
- }
- }
-
- if (count($results['errors']) > 0) {
- $results['message'] = count($results['files_run']) . ' SQL updates succesvol, ' . count($results['errors']) . ' met fouten';
- } else {
- $results['message'] = count($results['files_run']) . ' SQL updates succesvol uitgevoerd!';
- }
-
- return $results;
- }
-
- public function getAppliedUpdates(): array
- {
- $this->ensureSqlTableExists();
-
- return DB::table(self::SQL_TABLE)
- ->orderBy('applied_at', 'desc')
- ->get()
- ->toArray();
- }
-
- public function diagnose(): array
- {
- $diagnosis = [
- 'table_exists' => false,
- 'applied_count' => 0,
- 'pending_count' => 0,
- 'error' => null,
- ];
-
- try {
- $diagnosis['table_exists'] = Schema::hasTable(self::SQL_TABLE);
-
- if ($diagnosis['table_exists']) {
- $diagnosis['applied_count'] = DB::table(self::SQL_TABLE)->count();
- }
-
- if ($this->githubRepo && $diagnosis['table_exists']) {
- $sqlCheck = $this->checkForUpdates(false);
- if (isset($sqlCheck['count'])) {
- $diagnosis['pending_count'] = $sqlCheck['count'];
- }
- }
- } catch (\Exception $e) {
- $diagnosis['error'] = $e->getMessage();
- }
-
- return $diagnosis;
- }
-
- public function repair(): array
- {
- $actions = [];
- $errors = [];
-
- try {
- $this->ensureSqlTableExists();
- $actions[] = 'SQL update tabel gecontroleerd';
-
- $appliedCount = DB::table(self::SQL_TABLE)->count();
- $actions[] = "SQL updates tabel OK ({$appliedCount} records)";
-
- if ($this->githubRepo) {
- $sqlCheck = $this->checkForUpdates(false);
-
- if (isset($sqlCheck['error'])) {
- $actions[] = 'SQL update check: ' . $sqlCheck['error'];
- } elseif (! ($sqlCheck['has_updates'] ?? false)) {
- $actions[] = 'SQL updates: ' . ($sqlCheck['message'] ?? 'Allemaal up-to-date');
- } else {
- $count = $sqlCheck['count'] ?? 0;
- $actions[] = "{$count} nieuwe SQL updates beschikbaar";
-
- if ($this->isConfigured()) {
- $actions[] = 'SQL updates worden toegepast...';
- $runResult = $this->runUpdates();
-
- if ($runResult['success'] ?? false) {
- $filesRun = count($runResult['files_run'] ?? []);
- $actions[] = "{$filesRun} SQL updates toegepast";
- } else {
- $errors[] = 'SQL updates: ' . ($runResult['error'] ?? 'Onbekende fout');
- }
- }
- }
- }
- } catch (\Exception $e) {
- $errors[] = 'SQL repair: ' . $e->getMessage();
- Log::error('[EmulatorSql] Repair failed', ['error' => $e->getMessage()]);
- }
-
- return [
- 'success' => $errors === [],
- 'actions' => $actions,
- 'errors' => $errors,
- ];
- }
-
- private function ensureSqlTableExists(): void
- {
- if (Schema::hasTable(self::SQL_TABLE)) {
- return;
- }
-
- Schema::create(self::SQL_TABLE, function ($table) {
- $table->id();
- $table->string('file_name');
- $table->string('file_hash')->unique();
- $table->timestamp('applied_at');
- $table->text('sql_content')->nullable();
- });
- }
-
- private function getAppliedSqlHashes(): array
- {
- $this->ensureSqlTableExists();
-
- return DB::table(self::SQL_TABLE)
- ->pluck('file_hash')
- ->toArray();
- }
-
- private function markSqlAsApplied(array $file): void
- {
- $this->ensureSqlTableExists();
-
- $hash = $file['sha'] ?? md5((string) $file['name']);
-
- DB::table(self::SQL_TABLE)->updateOrInsert(
- ['file_hash' => $hash],
- [
- 'file_name' => $file['name'],
- 'applied_at' => now(),
- ],
- );
- }
-
- private function fetchSqlFilesFromGitHub(bool $recentOnly = false): array
- {
- if (! $this->githubRepo) {
- return [];
- }
-
- $branch = $this->githubBranch ?: 'main';
-
- $folderNames = [
- 'Database%20Updates',
- 'Database Updates',
- 'database_updates',
- 'database/updates',
- 'sql/updates',
- 'sql',
- 'updates',
- ];
-
- $knownSqlFiles = [
- '07012026_UpdateDatabase_to_4-0-1.sql',
- '09012026_UpdateDatabase_to_4-0-2.sql',
- '12012026_Battle Banzai.sql',
- '12012026_Breeding Fixes.sql',
- '12012026_ChatBubbles.sql',
- '16032026_updateall_command.sql',
- '17032026_allow_underpass.sql',
- '19032026_hotel_timezone.sql',
- '21022026_user_prefixes.sql',
- 'Default_Camera.sql',
- 'UpdateDatabase_Allow_diagonale.sql',
- 'UpdateDatabase_BOT.sql',
- 'UpdateDatabase_Banners.sql',
- 'UpdateDatabase_DanceCMD.sql',
- 'UpdateDatabase_Happiness.sql',
- 'UpdateDatabase_Websocket.sql',
- 'UpdateDatabase_unignorable.sql',
- ];
-
- try {
- if ($recentOnly) {
- return $this->fetchRecentSqlFiles($branch);
- }
-
- $sqlFiles = [];
-
- foreach ($knownSqlFiles as $filename) {
- foreach ($folderNames as $folderName) {
- $encodedFilename = str_replace(' ', '%20', $filename);
- $url = "https://raw.githubusercontent.com/{$this->githubRepo}/{$branch}/{$folderName}/{$encodedFilename}";
- $response = Http::timeout(10)->get($url);
-
- if ($response->successful()) {
- $sqlFiles[] = [
- 'name' => $filename,
- 'url' => $url,
- 'sha' => md5($response->body()),
- ];
- break;
- }
- }
- }
-
- if ($sqlFiles !== []) {
- usort($sqlFiles, fn ($a, $b) => strcmp($a['name'], $b['name']));
-
- return $sqlFiles;
- }
-
- return [];
- } catch (\Exception $e) {
- Log::warning('[EmulatorSql] Could not fetch SQL files', ['error' => $e->getMessage()]);
-
- return [];
- }
- }
-
- private function fetchRecentSqlFiles(string $branch): array
- {
- $weekAgo = now()->subDays(7)->toIso8601String();
-
- $githubToken = setting('github_token', '');
- $headers = [
- 'Accept' => 'application/vnd.github+json',
- 'User-Agent' => 'AtomCMS-EmulatorUpdate/1.0',
- ];
- if (! empty($githubToken)) {
- $headers['Authorization'] = 'Bearer ' . $githubToken;
- }
-
- $folderNames = ['Database Updates', 'database_updates', 'sql', 'updates'];
-
- try {
- foreach ($folderNames as $folderName) {
- $response = Http::withHeaders($headers)->timeout(30)->get("https://api.github.com/repos/{$this->githubRepo}/commits", [
- 'path' => $folderName,
- 'sha' => $branch,
- 'since' => $weekAgo,
- 'per_page' => 100,
- ]);
-
- if ($response->successful()) {
- $commits = $response->json();
-
- if (! is_array($commits) || $commits === []) {
- continue;
- }
-
- $sqlFiles = [];
-
- foreach ($commits as $commit) {
- $sha = $commit['sha'] ?? null;
- $commitDate = $commit['commit']['committer']['date'] ?? null;
-
- if (! $sha) {
- continue;
- }
-
- $commitResponse = Http::withHeaders($headers)->timeout(30)->get("https://api.github.com/repos/{$this->githubRepo}/commits/{$sha}");
-
- if (! $commitResponse->successful()) {
- continue;
- }
-
- $commitData = $commitResponse->json();
- $files = $commitData['files'] ?? [];
-
- foreach ($files as $file) {
- $filename = $file['filename'] ?? '';
-
- if (! str_ends_with(strtolower((string) $filename), '.sql')) {
- continue;
- }
-
- $name = basename((string) $filename);
- $sqlFiles[] = [
- 'name' => $name,
- 'url' => $file['raw_url'] ?? "https://raw.githubusercontent.com/{$this->githubRepo}/{$sha}/{$filename}",
- 'sha' => $sha,
- 'date' => $commitDate,
- ];
- }
- }
-
- if ($sqlFiles !== []) {
- usort($sqlFiles, fn ($a, $b) => strcmp($a['name'], $b['name']));
-
- return $sqlFiles;
- }
- }
- }
-
- return [];
- } catch (\Exception $e) {
- Log::warning('[EmulatorSql] Could not fetch recent SQL files', ['error' => $e->getMessage()]);
-
- return [];
- }
- }
-
- private function downloadAndRunSql(array $file): array
- {
- try {
- $response = Http::timeout(60)->get($file['url']);
-
- if (! $response->successful()) {
- return ['success' => false, 'error' => 'Download mislukt'];
- }
-
- $sql = $this->cleanSql($response->body());
-
- if (in_array(trim($sql), ['', '0'], true)) {
- return ['success' => true, 'message' => 'Lege SQL file overgeslagen'];
- }
-
- $host = setting('emulator_database_host', config('database.connections.emulator.host', '127.0.0.1'));
- $port = setting('emulator_database_port', config('database.connections.emulator.port', '3306'));
- $name = setting('emulator_database_name', config('database.connections.emulator.database', ''));
- $username = setting('emulator_database_username', config('database.connections.emulator.username', ''));
- $password = setting('emulator_database_password', config('database.connections.emulator.password', ''));
-
- if (empty($name) || empty($username)) {
- return ['success' => false, 'error' => 'Emulator database niet geconfigureerd'];
- }
-
- $pdo = new \PDO(
- "mysql:host={$host};port={$port};dbname={$name};charset=utf8mb4",
- $username,
- $password,
- [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION],
- );
-
- $statements = $this->splitSqlStatements($sql);
- $successCount = 0;
- $skipCount = 0;
-
- foreach ($statements as $statement) {
- $statement = trim((string) $statement);
- if ($statement === '' || $statement === '0') {
- continue;
- }
-
- try {
- $pdo->exec($statement);
- $successCount++;
- } catch (\PDOException $e) {
- if ($this->isDuplicateError($e)) {
- $skipCount++;
-
- continue;
- }
- Log::warning('[SQL Update] Statement error: ' . $e->getMessage());
- }
- }
-
- Log::info('[SQL Update] Successfully ran: ' . $file['name'] . " ({$successCount} statements, {$skipCount} skipped)");
-
- return ['success' => true, 'message' => "SQL uitgevoerd ({$successCount} statements, {$skipCount} duplicate)"];
- } catch (\Exception $e) {
- Log::error('[SQL Update] Failed: ' . $file['name'], ['error' => $e->getMessage()]);
-
- return ['success' => false, 'error' => $e->getMessage()];
- }
- }
-
- private function cleanSql(string $sql): string
- {
- $sql = preg_replace('/--.*$/m', '', $sql);
- $sql = preg_replace('/#.*$/m', '', (string) $sql);
- $sql = preg_replace('/SET FOREIGN_KEY_CHECKS.*?;/i', '', (string) $sql);
-
- return preg_replace('/SET @@SESSION.SQL_MODE.*?;/i', '', (string) $sql);
- }
-
- private function splitSqlStatements(string $sql): array
- {
- $parts = explode(';', $sql);
-
- return array_filter($parts, fn ($part) => trim((string) $part) !== '');
- }
-
- private function isDuplicateError(\PDOException $e): bool
- {
- $message = strtolower($e->getMessage());
- $patterns = [
- 'duplicate entry',
- "table '",
- 'already exists',
- "can't create",
- 'duplicate key',
- 'duplicate index',
- 'error 1061',
- 'error 1062',
- 'error 1826',
- ];
-
- return array_any($patterns, fn ($pattern) => str_contains($message, (string) $pattern));
- }
-}
diff --git a/app/Services/EmulatorUpdateService.php b/app/Services/EmulatorUpdateService.php
deleted file mode 100755
index 81376ad..0000000
--- a/app/Services/EmulatorUpdateService.php
+++ /dev/null
@@ -1,407 +0,0 @@
-statusService = new EmulatorStatusService;
- $this->jarService = new EmulatorJarService;
- $this->sourceService = new EmulatorSourceService;
- $this->buildService = new EmulatorBuildService;
- $this->sqlService = new EmulatorSqlService;
- $this->backupService = new EmulatorBackupService;
- $this->settings = app(SettingsService::class);
- }
-
- public function isConfigured(): bool
- {
- return $this->statusService->isConfigured();
- }
-
- public function getStatus(): array
- {
- $status = $this->statusService->getStatus();
- $updateCheck = $this->jarService->checkForUpdates();
- $sourceInfo = $this->sourceService->checkForUpdates();
-
- return array_merge($status, [
- 'update_available' => $updateCheck['update_available'] ?? false,
- 'current_version' => $updateCheck['current_version'] ?? setting('emulator_version', 'N/A'),
- 'latest_version' => $updateCheck['latest_version'] ?? 'N/A',
- 'update_type' => $updateCheck['type'] ?? 'unknown',
- 'has_source_updates' => $sourceInfo['has_update'] ?? false,
- 'latest_sha' => $sourceInfo['latest_sha'] ?? null,
- 'latest_message' => $sourceInfo['latest_message'] ?? null,
- 'latest_author' => $sourceInfo['latest_author'] ?? null,
- 'latest_date' => $sourceInfo['latest_date'] ?? null,
- 'stored_sha' => $sourceInfo['stored_sha'] ?? null,
- 'stored_date' => $sourceInfo['stored_date'] ?? null,
- 'source_info' => $sourceInfo,
- ]);
- }
-
- public function checkForUpdates(): array
- {
- return $this->jarService->checkForUpdates();
- }
-
- public function checkForSqlUpdates(bool $recentOnly = true): array
- {
- return $this->sqlService->checkForUpdates($recentOnly);
- }
-
- public function runSqlUpdates(): array
- {
- return $this->sqlService->runUpdates();
- }
-
- public function getAppliedSqlUpdates(): array
- {
- return $this->sqlService->getAppliedUpdates();
- }
-
- public function updateEmulator(): array
- {
- if (! $this->isConfigured()) {
- return ['success' => false, 'error' => 'Geen GitHub URL geconfigureerd'];
- }
-
- $check = $this->checkForUpdates();
-
- if (! ($check['update_available'] ?? false)) {
- if ($check['type'] === 'not_found' && ($check['source_available'] ?? false)) {
- return $this->buildFromSource();
- }
-
- return ['success' => false, 'error' => 'Emulator is al up-to-date'];
- }
-
- $hasSourceUpdates = ($check['has_source_updates'] ?? false) || ($check['type'] ?? '') === 'source_build';
-
- if ($hasSourceUpdates && $this->sourceService->isSourceBuildAvailable()) {
- return $this->buildFromSource();
- }
-
- if ($check['type'] === 'source_build') {
- return $this->buildFromSource();
- }
-
- if (! ($check['jar_url'] ?? null)) {
- return ['success' => false, 'error' => 'Geen .jar gevonden'];
- }
-
- $result = $this->jarService->performUpdate($check);
-
- if ($result['success']) {
- $this->runSqlUpdates();
-
- if ($this->restartEmulator()) {
- $result['restarted'] = true;
- $result['message'] = ($result['message'] ?? '') . ' | ๐ Emulator herstart';
- }
- }
-
- return $result;
- }
-
- public function performUpdate(array $check): array
- {
- return $this->jarService->performUpdate($check);
- }
-
- public function buildFromSource(bool $force = false): array
- {
- return $this->buildService->buildFromSource($force);
- }
-
- public function restartEmulator(): bool
- {
- $serviceName = $this->settings->getOrDefault('emulator_service_name', 'emulator');
-
- try {
- Log::info('[EmulatorUpdate] Restarting emulator service: ' . $serviceName);
-
- $result = Process::timeout(30)->run("systemctl restart {$serviceName} 2>&1");
- if ($result->successful()) {
- return true;
- }
-
- $result = Process::timeout(30)->run("service {$serviceName} restart 2>&1");
-
- return $result->successful();
- } catch (\Exception $e) {
- Log::error('[EmulatorUpdate] Failed to restart emulator', ['error' => $e->getMessage()]);
-
- return false;
- }
- }
-
- public function getBackupList(): array
- {
- return $this->backupService->getList();
- }
-
- public function restoreBackup(string $backupName): array
- {
- return $this->backupService->restore($backupName);
- }
-
- public function getInstalledVersion(): string
- {
- return $this->statusService->getInstalledVersion();
- }
-
- public function getInstalledJar(): ?string
- {
- return $this->statusService->getInstalledJar();
- }
-
- public function getInstalledJarInfo(): array
- {
- return $this->statusService->getInstalledJarInfo();
- }
-
- public function getLastSqlUpdate(): ?string
- {
- return setting('emulator_last_sql_update');
- }
-
- public function debugStatus(): array
- {
- return Cache::remember('emulator_debug_status', 120, function () {
- $installedDate = $this->settings->getOrDefault('emulator_jar_installed_date', null);
- $sourceCommit = $this->settings->getOrDefault('emulator_source_commit', null);
- $sourceDate = $this->settings->getOrDefault('emulator_source_date', null);
- $emulatorVersion = $this->settings->getOrDefault('emulator_version', null);
- $jarFiles = $this->statusService->getInstalledJarInfo();
-
- return [
- 'github_url' => $this->settings->getOrDefault('emulator_github_url', ''),
- 'github_repo' => $this->settings->getOrDefault('emulator_source_repo', ''),
- 'github_branch' => $this->settings->getOrDefault('emulator_github_branch', 'main'),
- 'source_repo' => $this->settings->getOrDefault('emulator_source_repo', ''),
- 'source_branch' => $this->settings->getOrDefault('emulator_github_branch', 'main'),
- 'installed_date' => $installedDate,
- 'installed_date_formatted' => $installedDate ? date('Y-m-d H:i:s', (int) $installedDate) : null,
- 'source_commit' => $sourceCommit,
- 'source_date' => $sourceDate,
- 'source_date_formatted' => $sourceDate ? date('Y-m-d H:i:s', (int) $sourceDate) : null,
- 'emulator_version' => $emulatorVersion,
- 'jar_files' => $jarFiles,
- 'installed_branch' => $this->settings->getOrDefault('emulator_installed_branch', null),
- ];
- });
- }
-
- public function resetInstalledDate(): void
- {
- WebsiteSetting::where('key', 'emulator_jar_installed_date')->delete();
- WebsiteSetting::where('key', 'emulator_source_commit')->delete();
- WebsiteSetting::where('key', 'emulator_source_date')->delete();
- }
-
- public function clearAllLogs(): array
- {
- $cleared = [];
- $paths = [
- storage_path('logs') => 'Laravel Logs',
- storage_path('logs/emulator.log') => 'Emulator Log',
- '/tmp/emulator-update-*' => 'Emulator Update Temp',
- '/tmp/nitro-switch-*' => 'Nitro Switch Logs',
- '/tmp/nitro_*' => 'Nitro Temp',
- '/var/www/Emulator/logs' => 'Emulator Folder Logs',
- ];
-
- foreach ($paths as $path => $label) {
- try {
- if (str_contains($path, '*')) {
- Process::timeout(10)->run("rm -f {$path} 2>/dev/null || true");
- $cleared[] = $label;
- } elseif (is_dir($path)) {
- Process::timeout(10)->run("find {$path} -name '*.log' -mtime +1 -delete 2>/dev/null || true");
- $cleared[] = $label;
- } elseif (is_file($path)) {
- @unlink($path);
- $cleared[] = $label;
- }
- } catch (\Exception) {
- }
- }
-
- try {
- $laravelLog = storage_path('logs/laravel.log');
- if (is_file($laravelLog)) {
- file_put_contents($laravelLog, '');
- $cleared[] = 'laravel.log';
- }
- } catch (\Exception) {
- }
-
- Process::timeout(10)->run("find /tmp -name 'emulator_*' -mtime +1 -delete 2>/dev/null || true");
- Process::timeout(10)->run("find /tmp -name 'nitro_*' -mtime +1 -delete 2>/dev/null || true");
- Process::timeout(10)->run("find /tmp -name 'deploy_*' -mtime +1 -delete 2>/dev/null || true");
-
- return [
- 'success' => true,
- 'cleared' => $cleared,
- 'message' => count($cleared) . ' log locaties geleegd',
- ];
- }
-
- public function repairEmulator(): array
- {
- $actions = [];
- $errors = [];
-
- Log::info('[EmulatorUpdate] Starting repair process');
-
- try {
- $status = $this->getStatus();
-
- if (! ($status['jar_exists'] ?? false)) {
- $actions[] = 'JAR bestand ontbreekt - downloaden...';
- $updateResult = $this->updateEmulator();
- if (! $updateResult['success']) {
- $errors[] = 'Kon JAR niet herstellen: ' . ($updateResult['error'] ?? 'Onbekende fout');
- } else {
- $actions[] = 'JAR bestand hersteld';
- }
- }
-
- if (! ($status['service_running'] ?? false)) {
- $actions[] = 'Emulator service niet actief - starten...';
- if ($this->restartEmulator()) {
- $actions[] = 'Emulator service gestart';
- } else {
- $errors[] = 'Kon emulator service niet starten';
- }
- }
-
- $sqlRepairResult = $this->sqlService->repair();
- if (! empty($sqlRepairResult['actions'])) {
- $actions = array_merge($actions, $sqlRepairResult['actions']);
- }
- if (! empty($sqlRepairResult['errors'])) {
- $errors = array_merge($errors, $sqlRepairResult['errors']);
- }
-
- if ($errors !== []) {
- return [
- 'success' => false,
- 'actions' => $actions,
- 'errors' => $errors,
- 'error' => implode('; ', $errors),
- ];
- }
-
- return [
- 'success' => true,
- 'actions' => $actions,
- 'message' => count($actions) . ' acties uitgevoerd',
- ];
- } catch (\Exception $e) {
- Log::error('[EmulatorUpdate] Repair exception', ['error' => $e->getMessage()]);
-
- return [
- 'success' => false,
- 'actions' => $actions,
- 'error' => $e->getMessage(),
- ];
- }
- }
-
- public function diagnoseSqlUpdates(): array
- {
- return $this->sqlService->diagnose();
- }
-
- public function diagnose(): array
- {
- $diagnosis = [
- 'timestamp' => now()->toIso8601String(),
- 'checks' => [],
- 'issues' => [],
- 'recommendations' => [],
- ];
-
- try {
- $status = $this->getStatus();
-
- $diagnosis['checks']['jar_exists'] = $status['jar_exists'] ?? false;
- $diagnosis['checks']['jar_files'] = $status['jar_files'] ?? [];
- $diagnosis['checks']['service_running'] = $status['service_running'] ?? false;
- $diagnosis['checks']['source_exists'] = $status['source_exists'] ?? false;
- $diagnosis['checks']['emulator_db_connected'] = $status['emulator_db_connected'] ?? false;
- $diagnosis['checks']['is_configured'] = $this->isConfigured();
- $diagnosis['checks']['update_available'] = $status['update_available'] ?? false;
-
- $sqlDiagnosis = $this->sqlService->diagnose();
- $diagnosis['checks']['sql_table_exists'] = $sqlDiagnosis['table_exists'] ?? false;
- $diagnosis['checks']['sql_updates_applied'] = $sqlDiagnosis['applied_count'] ?? 0;
- $diagnosis['checks']['sql_pending'] = $sqlDiagnosis['pending_count'] ?? 0;
-
- if (! ($status['jar_exists'] ?? false)) {
- $diagnosis['issues'][] = 'JAR bestand ontbreekt';
- $diagnosis['recommendations'][] = 'Voer emulator:update uit om de JAR te downloaden';
- }
-
- if (! ($status['service_running'] ?? false)) {
- $diagnosis['issues'][] = 'Emulator service draait niet';
- $diagnosis['recommendations'][] = 'Start de service met: sudo systemctl start ' . $this->settings->getOrDefault('emulator_service_name', 'emulator');
- }
-
- if (! ($status['emulator_db_connected'] ?? false)) {
- $diagnosis['issues'][] = 'Emulator database niet bereikbaar';
- $diagnosis['recommendations'][] = 'Controleer de database credentials in de settings';
- }
-
- if (! ($status['source_exists'] ?? false) && $this->sourceService->isSourceBuildAvailable()) {
- $diagnosis['issues'][] = 'Source code niet gevonden';
- $diagnosis['recommendations'][] = 'Voer emulator:update --rebuild uit om vanaf source te bouwen';
- }
-
- if (! ($sqlDiagnosis['table_exists'] ?? false)) {
- $diagnosis['issues'][] = 'SQL update tabel ontbreekt';
- $diagnosis['recommendations'][] = 'Reparatie zal de tabel aanmaken';
- }
-
- if (($sqlDiagnosis['pending_count'] ?? 0) > 0) {
- $diagnosis['issues'][] = $sqlDiagnosis['pending_count'] . ' SQL updates pending';
- $diagnosis['recommendations'][] = 'Voer reparatie uit om SQL updates toe te passen';
- }
- } catch (\Exception $e) {
- $diagnosis['error'] = $e->getMessage();
- }
-
- return $diagnosis;
- }
-}
diff --git a/app/Services/NitroUpdateService.php b/app/Services/NitroUpdateService.php
deleted file mode 100755
index e0a6c88..0000000
--- a/app/Services/NitroUpdateService.php
+++ /dev/null
@@ -1,1358 +0,0 @@
-settings = app(SettingsService::class);
- $this->settings->clearInstanceCache();
- $this->clientRepo = 'duckietm/Nitro-V3';
- $this->rendererRepo = 'duckietm/Nitro_Render_V3';
- $this->gitlabHost = 'gitlab.epicnabbo.nl/duckietm';
-
- $basePath = $this->detectBasePath();
-
- $this->clientPath = $this->resolvePath(setting('nitro_client_path', $basePath . '/nitro-client'));
- $this->rendererPath = $this->resolvePath(setting('nitro_renderer_path', $basePath . '/nitro-renderer'));
- $this->clientWebRoot = $this->resolvePath(setting('nitro_webroot', '/var/www/Client'));
-
- $this->historyService = app(UpdateHistoryService::class);
- }
-
- private function isDirAccessible(string $path): bool
- {
- if ($path === '' || $path === '0') {
- return false;
- }
- $result = Process::timeout(5)->run('test -d ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
-
- return trim($result->output()) === 'yes';
- }
-
- private function detectBasePath(): string
- {
- $possiblePaths = [
- base_path(),
- '/var/www/atomcms',
- '/var/www/html',
- '/var/www',
- dirname(base_path()),
- ];
-
- foreach ($possiblePaths as $path) {
- if ($this->isDirAccessible($path)) {
- $clientCheck = Process::timeout(5)->run('test -d ' . escapeshellarg($path . '/nitro-client') . ' && echo "yes" || echo "no"');
- $rendererCheck = Process::timeout(5)->run('test -d ' . escapeshellarg($path . '/nitro-renderer') . ' && echo "yes" || echo "no"');
- if (trim($clientCheck->output()) === 'yes' || trim($rendererCheck->output()) === 'yes') {
- return $path;
- }
- }
- }
-
- return base_path();
- }
-
- private function resolvePath(string $path): string
- {
- if (str_starts_with($path, '/')) {
- if ($this->isDirAccessible($path)) {
- return $path;
- }
- $parts = explode('/', trim($path, '/'));
- $root = '/' . $parts[0];
- if (! $this->isDirAccessible($root)) {
- return base_path() . '/' . ltrim($path, '/');
- }
-
- return $path;
- }
-
- return base_path() . '/' . ltrim($path, '/');
- }
-
- public function isConfigured(): bool
- {
- return $this->isDirAccessible($this->clientPath) || $this->isDirAccessible($this->rendererPath) || $this->isDirAccessible($this->clientWebRoot);
- }
-
- public function getClientPath(): string
- {
- return $this->clientPath;
- }
-
- public function getRendererPath(): string
- {
- return $this->rendererPath;
- }
-
- public function getClientWebRoot(): string
- {
- return $this->clientWebRoot;
- }
-
- public function getStatus(): array
- {
- $buildPath = setting('nitro_build_path', '/var/www/atomcms/nitro-client/dist');
- $webroot = $this->clientWebRoot;
-
- // Use shell commands to bypass open_basedir
- $checkDir = function (string $path): bool {
- $result = Process::timeout(5)->run('test -d ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
-
- return trim($result->output()) === 'yes';
- };
-
- $checkFile = function (string $path): bool {
- $result = Process::timeout(5)->run('test -f ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
-
- return trim($result->output()) === 'yes';
- };
-
- // Check nginx config via shell to bypass open_basedir
- $nginxCheck = Process::timeout(5)->run('test -f /etc/nginx/sites-enabled/cms.conf -o -f /etc/nginx/sites-enabled/atom.conf -o -f /etc/nginx/conf.d/atom.conf && echo "yes" || echo "no"');
- $nginxValid = trim($nginxCheck->output()) === 'yes';
-
- // Check open_basedir - fixed if empty OR includes all required paths
- $openBaseDir = ini_get('open_basedir');
- $clientPath = $this->clientPath;
- $rendererPath = $this->rendererPath;
- $openBasedirFixed = in_array($openBaseDir, ['', '0', false], true) || (
- str_contains($openBaseDir, $clientPath) &&
- str_contains($openBaseDir, $rendererPath)
- );
-
- Log::info('[NitroUpdate] open_basedir check:', [
- 'open_basedir' => $openBaseDir,
- 'clientPath' => $clientPath,
- 'rendererPath' => $rendererPath,
- 'fixed' => $openBasedirFixed,
- ]);
-
- // Build files (check for hashed filenames like index-CLCACmcI.js)
- $assetsPath = $buildPath . '/assets';
-
- $indexJsCheck = Process::timeout(5)->run('test -f ' . escapeshellarg($buildPath . '/index.js') . ' && echo "yes" || echo "no"');
- $indexJsHashCheck = Process::timeout(5)->run('ls ' . escapeshellarg($assetsPath) . '/index-*.js 2>/dev/null | head -1');
- $hasIndexJs = trim($indexJsCheck->output()) === 'yes' || ! in_array(trim($indexJsHashCheck->output()), ['', '0'], true);
-
- $rendererJsCheck = Process::timeout(5)->run('test -f ' . escapeshellarg($buildPath . '/renderer.js') . ' && echo "yes" || echo "no"');
- $rendererJsHashCheck = Process::timeout(5)->run('ls ' . escapeshellarg($assetsPath) . '/nitro-renderer-*.js 2>/dev/null | head -1');
- $hasRendererJs = trim($rendererJsCheck->output()) === 'yes' || ! in_array(trim($rendererJsHashCheck->output()), ['', '0'], true);
-
- $vendorJsCheck = Process::timeout(5)->run('test -f ' . escapeshellarg($buildPath . '/vendor.js') . ' && echo "yes" || echo "no"');
- $vendorJsHashCheck = Process::timeout(5)->run('ls ' . escapeshellarg($assetsPath) . '/vendor-*.js 2>/dev/null | head -1');
- $hasVendorJs = trim($vendorJsCheck->output()) === 'yes' || ! in_array(trim($vendorJsHashCheck->output()), ['', '0'], true);
-
- // Check for CSS files
- $hasCssCheck = Process::timeout(5)->run('find ' . escapeshellarg((string) $buildPath) . " -name '*.css' -type f 2>/dev/null | head -1");
- $hasCss = ! in_array(trim($hasCssCheck->output()), ['', '0'], true);
-
- // Renderer (monorepo - packages are built into client, so just check node_modules)
- $hasRendererDist = $checkDir($this->rendererPath . '/node_modules');
- $hasRendererIndex = $checkDir($this->rendererPath . '/packages');
-
- // Config files
- $rendererConfigValid = $checkFile($webroot . '/renderer-config.json');
- $uiConfigValid = $checkFile($webroot . '/ui-config.json');
- $nitroConfigValid = $checkFile($webroot . '/UITexts.json');
- $hasRendererExample = $checkFile($buildPath . '/renderer-config.example') || $checkFile($webroot . '/renderer-config.example');
- $hasUiExample = $checkFile($buildPath . '/ui-config.example') || $checkFile($webroot . '/ui-config.example');
-
- // Assets
- $hasImageAssets = $checkDir($webroot . '/assets') || $checkDir($buildPath . '/assets');
- $hasFavicon = $checkFile($webroot . '/favicon.ico') || $checkFile($buildPath . '/favicon.ico');
-
- // System
- $webrootExists = $checkDir($webroot);
- $webrootWritableCheck = Process::timeout(5)->run('test -w ' . escapeshellarg($webroot) . ' && echo "yes" || echo "no"');
- $webrootWritable = trim($webrootWritableCheck->output()) === 'yes';
- $webrootFileCount = $this->countFiles($webroot);
- $buildSize = $this->getDirSize($buildPath);
- $webrootSize = $this->getDirSize($webroot);
-
- // Socket URL from renderer config
- $socketUrl = 'N/A';
- if ($rendererConfigValid) {
- $configResult = Process::timeout(5)->run('cat ' . escapeshellarg($webroot . '/renderer-config.json') . ' 2>/dev/null');
- if ($configResult->successful()) {
- $config = json_decode($configResult->output(), true);
- $socketUrl = $config['socket.url'] ?? 'N/A';
- }
- }
-
- // Check for gamedata - could be a symlink or directory
- $symlinkValid = false;
-
- // Check if gamedata exists as symlink or directory
- $gamedataCheck = Process::timeout(5)->run('test -e ' . escapeshellarg($webroot . '/gamedata') . ' && echo "yes" || echo "no"');
- if (trim($gamedataCheck->output()) === 'yes') {
- $symlinkValid = true;
- } else {
- // Try alternative locations
- $altPaths = [
- '/var/www/Gamedata',
- '/var/www/gamedata',
- '/var/www/atomcms/public/gamedata',
- ];
- foreach ($altPaths as $altPath) {
- $altCheck = Process::timeout(5)->run('test -d ' . escapeshellarg($altPath) . ' && echo "yes" || echo "no"');
- if (trim($altCheck->output()) === 'yes') {
- $symlinkValid = true;
- break;
- }
- }
- }
-
- return [
- 'client_path' => $this->clientPath,
- 'renderer_path' => $this->rendererPath,
- 'build_path' => $buildPath,
- 'webroot' => $webroot,
- 'client_commit' => $this->getCommit($this->clientPath),
- 'renderer_commit' => $this->getCommit($this->rendererPath),
- 'client_installed' => $checkDir($this->clientPath . '/.git'),
- 'renderer_installed' => $checkDir($this->rendererPath . '/.git'),
- 'build_exists' => $checkDir($buildPath),
- 'deployed' => $rendererConfigValid,
- 'symlink_valid' => $symlinkValid,
- 'client_node_modules' => $checkDir($this->clientPath . '/node_modules'),
- 'renderer_node_modules' => $checkDir($this->rendererPath . '/node_modules'),
- 'client_latest_commit' => null,
- 'renderer_latest_commit' => null,
- 'last_checked' => setting('nitro_last_checked'),
- 'nginx_config_valid' => $nginxValid,
- 'open_basedir_fixed' => $openBasedirFixed,
- 'has_index_js' => $hasIndexJs,
- 'has_renderer_js' => $hasRendererJs,
- 'has_vendor_js' => $hasVendorJs,
- 'has_css_file' => $hasCss,
- 'vite_config_valid' => $checkFile($this->clientPath . '/vite.config.js') || $checkFile($this->clientPath . '/vite.config.mjs'),
- 'renderer_config_valid' => $rendererConfigValid,
- 'ui_config_valid' => $uiConfigValid,
- 'nitro_config_valid' => $nitroConfigValid,
- 'has_uitexts_json' => $nitroConfigValid,
- 'has_renderer_example' => $hasRendererExample,
- 'has_uiconfig_example' => $hasUiExample,
- 'has_image_assets' => $hasImageAssets,
- 'has_favicon' => $hasFavicon,
- 'assets_writable' => $webrootWritable,
- 'assets_file_count' => $webrootFileCount,
- 'has_renderer_dist' => $hasRendererDist,
- 'has_renderer_index' => $hasRendererIndex,
- 'webroot_exists' => $webrootExists,
- 'webroot_writable' => $webrootWritable,
- 'webroot_file_count' => $webrootFileCount,
- 'build_size' => $buildSize,
- 'webroot_size' => $webrootSize,
- 'socket_url' => $socketUrl,
- 'websocket_accessible' => $this->checkWebSocketAccessible($socketUrl),
- 'emulator_connected' => $this->checkEmulatorConnected(),
- ];
- }
-
- private function checkWebSocketAccessible(string $socketUrl): bool
- {
- if (in_array($socketUrl, ['', '0', 'N/A'], true)) {
- return false;
- }
-
- try {
- $wsUrl = $socketUrl;
- if (str_starts_with($wsUrl, 'wss://')) {
- $wsUrl = 'https://' . substr($wsUrl, 6);
- } elseif (str_starts_with($wsUrl, 'ws://')) {
- $wsUrl = 'http://' . substr($wsUrl, 4);
- }
-
- $host = parse_url($wsUrl, PHP_URL_HOST);
- $port = parse_url($wsUrl, PHP_URL_PORT);
-
- if (! $port) {
- $port = str_starts_with($socketUrl, 'wss://') ? 443 : 80;
- }
-
- $socket = @fsockopen($host, $port, $errno, $errstr, 5);
- if ($socket) {
- fclose($socket);
-
- return true;
- }
- } catch (\Exception $e) {
- // Fallback to curl check
- }
-
- // Fallback: try curl
- try {
- $ch = curl_init($socketUrl);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_TIMEOUT, 5);
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
- curl_setopt($ch, CURLOPT_NOBODY, true);
- curl_exec($ch);
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
-
- return in_array($httpCode, [200, 301, 302, 400, 404]);
- } catch (\Exception) {
- return false;
- }
- }
-
- private function checkEmulatorConnected(): bool
- {
- try {
- $rconService = new RconService;
-
- return $rconService->isConnected();
- } catch (\Exception) {
- return false;
- }
- }
-
- private function countFiles(string $path): int
- {
- $check = Process::timeout(10)->run('test -d ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
- if (trim($check->output()) !== 'yes') {
- return 0;
- }
- $result = Process::timeout(10)->run('find ' . escapeshellarg($path) . ' -type f 2>/dev/null | wc -l');
-
- return (int) trim($result->output() ?? '0');
- }
-
- private function getDirSize(string $path): int
- {
- $check = Process::timeout(10)->run('test -d ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
- if (trim($check->output()) !== 'yes') {
- return 0;
- }
- $result = Process::timeout(10)->run('du -sb ' . escapeshellarg($path) . ' 2>/dev/null | cut -f1');
-
- return (int) trim($result->output() ?? '0');
- }
-
- public function updateNitro(bool $forceBuild = false): array
- {
- $lockFile = '/tmp/nitro-update.lock';
-
- if (file_exists($lockFile)) {
- $lockAge = time() - filemtime($lockFile);
- if ($lockAge < 300) {
- $pid = @file_get_contents($lockFile);
-
- return ['success' => false, 'error' => "Update al bezig (PID: {$pid})"];
- }
- @unlink($lockFile);
- }
-
- file_put_contents($lockFile, getmypid());
-
- try {
- Log::info('[Nitro] Starting update');
- $this->historyService->log('nitro', 'update', null, 'pending', 'Update gestart');
-
- $this->ensureVpsRequirements();
-
- $this->setupDirectories();
-
- $this->syncRepos();
-
- $this->installDependencies();
-
- $this->fixKnownBugs();
-
- $buildResult = $this->buildClient();
-
- if ($buildResult['success']) {
- $this->deployClient();
- $this->generateConfigs();
-
- $this->settings->set('nitro_client_commit', $this->getCommit($this->clientPath));
- $this->settings->set('nitro_renderer_commit', $this->getCommit($this->rendererPath));
-
- Log::info('[Nitro] Update completed successfully');
- $this->historyService->log('nitro', 'update', 'client', 'success', 'Update succesvol');
-
- return ['success' => true, 'message' => 'Nitro client succesvol geรผpdatet!'];
- }
-
- $this->historyService->log('nitro', 'update', 'client', 'failed', $buildResult['error'] ?? 'Build mislukt');
-
- return $buildResult;
-
- } catch (\Exception $e) {
- Log::error('[Nitro] Update failed', ['error' => $e->getMessage()]);
- $this->historyService->log('nitro', 'update', null, 'failed', $e->getMessage());
-
- return ['success' => false, 'error' => $e->getMessage()];
- } finally {
- @unlink($lockFile);
- }
- }
-
- private function ensureVpsRequirements(): array
- {
- $actions = [];
- $errors = [];
-
- $nodeCheck = Process::timeout(5)->run('which node && node --version');
- if (! $nodeCheck->successful()) {
- $errors[] = 'Node.js niet geรฏnstalleerd';
- Process::timeout(60)->run('curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs 2>&1');
- $nodeCheck2 = Process::timeout(5)->run('which node && node --version');
- if ($nodeCheck2->successful()) {
- $actions[] = 'Node.js geรฏnstalleerd: ' . trim($nodeCheck2->output());
- }
- } else {
- $actions[] = 'Node.js: ' . trim($nodeCheck->output());
- }
-
- $yarnCheck = Process::timeout(5)->run('which yarn && yarn --version');
- if (! $yarnCheck->successful()) {
- $actions[] = 'Yarn wordt geรฏnstalleerd...';
- Process::timeout(60)->run('npm install -g yarn 2>&1');
- $yarnCheck2 = Process::timeout(5)->run('which yarn && yarn --version');
- if ($yarnCheck2->successful()) {
- $actions[] = 'Yarn geรฏnstalleerd: ' . trim($yarnCheck2->output());
- }
- } else {
- $actions[] = 'Yarn: ' . trim($yarnCheck->output());
- }
-
- $npmCacheDir = Process::timeout(5)->run('echo $HOME');
- $npmHome = trim($npmCacheDir->output()) ?: '/root';
- Process::timeout(5)->run('mkdir -p ' . escapeshellarg($npmHome . '/.npm'));
- Process::timeout(5)->run('mkdir -p /var/www/.npm');
- Process::timeout(5)->run('chown -R www-data:www-data /var/www/.npm');
-
- Process::timeout(10)->run('git config --global --add safe.directory "*" 2>/dev/null || true');
-
- return [
- 'actions' => $actions,
- 'errors' => $errors,
- ];
- }
-
- private function setupDirectories(): void
- {
- $dirs = [
- $this->clientPath => 'Nitro Client',
- $this->rendererPath => 'Nitro Renderer',
- $this->clientWebRoot => 'Client Webroot',
- ];
-
- foreach ($dirs as $path => $label) {
- $existsCheck = Process::timeout(5)->run('test -d ' . escapeshellarg($path) . ' && echo "yes" || echo "no"');
- if (trim($existsCheck->output()) !== 'yes') {
- Log::info("[Nitro] Creating {$label} directory: {$path}");
- Process::timeout(10)->run('mkdir -p ' . escapeshellarg($path));
- }
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg($path));
- }
-
- // Create gamedata symlink if it doesn't exist
- $gamedataSymlink = $this->clientWebRoot . '/gamedata';
- $gamedataCheck = Process::timeout(5)->run('test -e ' . escapeshellarg($gamedataSymlink) . ' && echo "yes" || echo "no"');
-
- if (trim($gamedataCheck->output()) !== 'yes') {
- // Try to find gamedata folder
- $possibleGamedata = [
- '/var/www/Gamedata',
- '/var/www/gamedata',
- '/var/www/atomcms/public/gamedata',
- base_path() . '/../Gamedata',
- ];
-
- foreach ($possibleGamedata as $gdPath) {
- $gdCheck = Process::timeout(5)->run('test -d ' . escapeshellarg($gdPath) . ' && echo "yes" || echo "no"');
- if (trim($gdCheck->output()) === 'yes') {
- Process::timeout(10)->run('ln -s ' . escapeshellarg($gdPath) . ' ' . escapeshellarg($gamedataSymlink));
- Log::info('[Nitro] Created gamedata symlink: ' . $gdPath . ' -> ' . $gamedataSymlink);
- break;
- }
- }
- }
- }
-
- private function syncRepos(): void
- {
- $repos = [
- ['path' => $this->rendererPath, 'repo' => $this->rendererRepo],
- ['path' => $this->clientPath, 'repo' => $this->clientRepo],
- ];
-
- Process::timeout(5)->run('git config --global --add safe.directory "*" 2>/dev/null || true');
-
- foreach ($repos as $r) {
- $gitCheck = Process::timeout(5)->run('test -d ' . escapeshellarg($r['path']) . '/.git && echo "yes" || echo "no"');
- $isGitRepo = trim($gitCheck->output()) === 'yes';
-
- if ($isGitRepo) {
- Log::info("[Nitro] Updating {$r['repo']}");
-
- $fetchResult = Process::timeout(120)->run('cd ' . escapeshellarg($r['path']) . ' && git fetch nitro-v3 2>&1');
-
- $resetResult = Process::timeout(60)->run('cd ' . escapeshellarg($r['path']) . ' && git checkout main 2>&1 || git checkout master 2>&1 || true');
-
- $pullResult = Process::timeout(120)->run('cd ' . escapeshellarg($r['path']) . ' && git reset --hard nitro-v3/main 2>&1 || git reset --hard nitro-v3/master 2>&1 || true');
-
- if (! $pullResult->successful()) {
- Log::warning("[Nitro] Update failed, re-cloning {$r['repo']}");
- Process::timeout(30)->run('rm -rf ' . escapeshellarg($r['path']));
-
- $cloneResult = Process::timeout(300)->run(
- "git clone --branch main --depth 1 https://{$this->gitlabHost}/{$r['repo']}.git " . escapeshellarg($r['path']) . ' 2>&1',
- );
- }
- } else {
- Log::info("[Nitro] Cloning {$r['repo']}");
-
- $dirCheck = Process::timeout(5)->run('test -d ' . escapeshellarg($r['path']) . ' && echo "yes" || echo "no"');
- if (trim($dirCheck->output()) === 'yes') {
- Process::timeout(30)->run('rm -rf ' . escapeshellarg($r['path']));
- }
-
- Process::timeout(300)->run(
- "git clone --branch main --depth 1 https://{$this->gitlabHost}/{$r['repo']}.git " . escapeshellarg($r['path']) . ' 2>&1',
- );
- }
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg($r['path']));
- }
- }
-
- private function fixKnownBugs(): void
- {
- $this->fixChatsCss();
-
- // Fix 1: Chats.css case sensitivity
- $indexFile = $this->clientPath . '/src/index.tsx';
- if (file_exists($indexFile)) {
- $content = file_get_contents($indexFile);
- if (str_contains($content, './css/chat/Chats.css')) {
- $content = str_replace('./css/chat/Chats.css', './css/chat/chats.css', $content);
- file_put_contents($indexFile, $content);
- Log::info('[Nitro] Fixed Chats.css case');
- }
- }
-
- // Fix 2: Duplicate CatalogView export
- $catalogFile = $this->clientPath . '/src/components/catalog/CatalogView.tsx';
- if (file_exists($catalogFile)) {
- $content = file_get_contents($catalogFile);
- $lines = explode("\n", $content);
- $cleanLines = [];
- $seenExport = false;
- foreach ($lines as $line) {
- if (str_contains($line, 'export const CatalogView')) {
- if ($seenExport) {
- continue;
- }
- $seenExport = true;
- }
- $cleanLines[] = $line;
- }
- file_put_contents($catalogFile, implode("\n", $cleanLines));
- Log::info('[Nitro] Fixed duplicate CatalogView');
- }
-
- // Fix 3: Create/update vite config
- $this->updateViteConfig();
-
- // Fix 4: Create symlink for renderer
- $this->createYarnLink();
-
- // Fix 5: Add missing renderer stubs
- $this->addMissingRendererStubs();
-
- // Fix 6: Duplicate FurniEditorResultEvent (case sensitivity conflict)
- $duplicateFile = $this->rendererPath . '/packages/communication/src/messages/incoming/furniture/FurniEditorResultEvent.ts';
- if (file_exists($duplicateFile)) {
- unlink($duplicateFile);
- $furnitureIndexFile = $this->rendererPath . '/packages/communication/src/messages/incoming/furniture/index.ts';
- if (file_exists($furnitureIndexFile)) {
- $content = file_get_contents($furnitureIndexFile);
- $content = str_replace("export * from './FurniEditorResultEvent';\n", '', $content);
- file_put_contents($furnitureIndexFile, $content);
- }
- Log::info('[Nitro] Fixed duplicate FurniEditorResultEvent');
- }
-
- // Fix 7: Rename Chats.css to lowercase
- $upperCaseCss = $this->clientPath . '/src/css/chat/Chats.css';
- $lowerCaseCss = $this->clientPath . '/src/css/chat/chats.css';
- if (file_exists($upperCaseCss) && ! file_exists($lowerCaseCss)) {
- rename($upperCaseCss, $lowerCaseCss);
- Log::info('[Nitro] Fixed Chats.css case sensitivity');
- }
-
- // Fix 8: Remove all duplicate FurniEditor files from furniture/ (keep only furnieditor/)
- $outgoingDir = $this->rendererPath . '/packages/communication/src/messages/outgoing/furniture';
- $incomingDir = $this->rendererPath . '/packages/communication/src/messages/incoming/furniture';
-
- $outgoingDupes = ['BySprite', 'Create', 'Delete', 'Detail', 'Interactions', 'Search', 'Update'];
- foreach ($outgoingDupes as $name) {
- $file = $outgoingDir . '/FurniEditor' . $name . 'Composer.ts';
- if (file_exists($file)) {
- unlink($file);
- }
- }
- if (file_exists($outgoingDir . '/index.ts')) {
- $content = file_get_contents($outgoingDir . '/index.ts');
- $lines = explode("\n", $content);
- $cleanLines = array_filter($lines, fn ($line) => ! str_contains((string) $line, 'FurniEditor'));
- file_put_contents($outgoingDir . '/index.ts', implode("\n", $cleanLines));
- }
-
- $incomingDupes = ['Detail', 'Interactions', 'Result', 'Search'];
- foreach ($incomingDupes as $name) {
- $file = $incomingDir . '/FurniEditor' . $name . 'Event.ts';
- if (file_exists($file)) {
- unlink($file);
- }
- }
- if (file_exists($incomingDir . '/index.ts')) {
- $content = file_get_contents($incomingDir . '/index.ts');
- $lines = explode("\n", $content);
- $cleanLines = array_filter($lines, fn ($line) => ! str_contains((string) $line, 'FurniEditor'));
- file_put_contents($incomingDir . '/index.ts', implode("\n", $cleanLines));
- }
-
- Log::info('[Nitro] Removed duplicate FurniEditor files from furniture/');
- }
-
- private function fixChatsCss(): void
- {
- $chatCssFile = $this->clientPath . '/src/css/chat/chats.css';
- if (! file_exists($chatCssFile)) {
- return;
- }
-
- $content = file_get_contents($chatCssFile);
- $modified = false;
-
- $content = preg_replace('/\/\/\s*normal\s*\n\s*\.message/', '.message', $content, -1, $count);
- if ($count > 0) {
- $modified = true;
- }
- $content = preg_replace('/\/\/\s*whisper\s*\n\s*\.message/', '.message', $content, -1, $count);
- if ($count > 0) {
- $modified = true;
- }
- $content = preg_replace('/\/\/\s*shout\s*\n\s*\.message/', '.message', $content, -1, $count);
- if ($count > 0) {
- $modified = true;
- }
-
- if ($modified) {
- file_put_contents($chatCssFile, $content);
- Log::info('[Nitro] Fixed chat CSS comments');
- }
- }
-
- private function addMissingRendererStubs(): void
- {
- $outgoingDir = $this->rendererPath . '/packages/communication/src/messages/outgoing/furniture';
- $incomingDir = $this->rendererPath . '/packages/communication/src/messages/incoming/furniture';
- $outgoingFurnieditorDir = $this->rendererPath . '/packages/communication/src/messages/outgoing/furnieditor';
- $incomingFurnieditorDir = $this->rendererPath . '/packages/communication/src/messages/incoming/furnieditor';
-
- if (! is_dir($outgoingDir)) {
- mkdir($outgoingDir, 0755, true);
- }
- if (! is_dir($incomingDir)) {
- mkdir($incomingDir, 0755, true);
- }
-
- $composers = ['BySprite', 'Create', 'Delete', 'Detail', 'Interactions', 'Search', 'Update'];
- $composerLines = [];
- foreach ($composers as $name) {
- $furnieditorFile = $outgoingFurnieditorDir . '/FurniEditor' . $name . 'Composer.ts';
- $stubFile = $outgoingDir . '/FurniEditor' . $name . 'Composer.ts';
- if (! file_exists($furnieditorFile) && ! file_exists($stubFile)) {
- file_put_contents($stubFile, "import { IMessageComposer } from '@nitrots/api';\nexport class FurniEditor{$name}Composer implements IMessageComposer {\n constructor(k: any) {}\n dispose() {}\n getMessageArray() { return []; }\n}\n");
- $composerLines[] = "export * from './FurniEditor{$name}Composer';";
- }
- }
- if ($composerLines !== []) {
- $composerContent = implode("\n", $composerLines) . "\n";
- file_put_contents($outgoingDir . '/index.ts', $composerContent);
- }
-
- $events = ['Detail', 'Interactions', 'Result', 'Search'];
- $eventLines = [];
- foreach ($events as $name) {
- $furnieditorFile = $incomingFurnieditorDir . '/FurniEditor' . $name . 'Event.ts';
- $stubFile = $incomingDir . '/FurniEditor' . $name . 'Event.ts';
- if (! file_exists($furnieditorFile) && ! file_exists($stubFile)) {
- file_put_contents($stubFile, "import { IMessageEvent } from '@nitrots/api';\nimport { MessageEvent } from '@nitrots/events';\nexport class FurniEditor{$name}Event extends MessageEvent implements IMessageEvent {\n constructor(callBack: Function) { super(callBack, (k: any) => k); }\n getParser() { return this.parser; }\n}\n");
- $eventLines[] = "export * from './FurniEditor{$name}Event';";
- }
- }
- if ($eventLines !== []) {
- $eventContent = implode("\n", $eventLines) . "\n";
- file_put_contents($incomingDir . '/index.ts', $eventContent);
- }
-
- Log::info('[Nitro] Added missing renderer stubs');
- }
-
- private function updateViteConfig(): void
- {
- $viteConfigPath = $this->clientPath . '/vite.config.mjs';
-
- $content = file_exists($viteConfigPath) ? file_get_contents($viteConfigPath) : '';
-
- if (str_contains($content, '@nitrots/nitro-renderer') && str_contains($content, '/var/www/nitro-renderer')) {
- return;
- }
-
- $viteConfig = <<<'CONFIG'
-import react from "@vitejs/plugin-react";
-import { resolve } from "path";
-import { defineConfig } from "vite";
-
-const rendererPath = "/var/www/nitro-renderer";
-
-export default defineConfig({
- plugins: [react()],
- server: {
- fs: { allow: [resolve(__dirname), rendererPath] },
- proxy: { "/api": { target: "http://localhost:3000", changeOrigin: true } },
- },
- resolve: {
- alias: [
- { find: "@nitrots/nitro-renderer", replacement: resolve(rendererPath, "src") },
- { find: "@nitrots/api", replacement: resolve(rendererPath, "packages", "api", "src") },
- { find: "@nitrots/assets", replacement: resolve(rendererPath, "packages", "assets", "src") },
- { find: "@nitrots/avatar", replacement: resolve(rendererPath, "packages", "avatar", "src") },
- { find: "@nitrots/camera", replacement: resolve(rendererPath, "packages", "camera", "src") },
- { find: "@nitrots/communication", replacement: resolve(rendererPath, "packages", "communication", "src") },
- { find: "@nitrots/configuration", replacement: resolve(rendererPath, "packages", "configuration", "src") },
- { find: "@nitrots/events", replacement: resolve(rendererPath, "packages", "events", "src") },
- { find: "@nitrots/localization", replacement: resolve(rendererPath, "packages", "localization", "src") },
- { find: "@nitrots/room", replacement: resolve(rendererPath, "packages", "room", "src") },
- { find: "@nitrots/session", replacement: resolve(rendererPath, "packages", "session", "src") },
- { find: "@nitrots/sound", replacement: resolve(rendererPath, "packages", "sound", "src") },
- { find: "@nitrots/utils/src", replacement: resolve(rendererPath, "packages", "utils", "src") },
- { find: "@nitrots/utils", replacement: resolve(rendererPath, "packages", "utils", "src") },
- { find: "@", replacement: resolve(__dirname, "src") },
- { find: "~", replacement: resolve(__dirname, "node_modules") },
- { find: "pixi.js", replacement: resolve(rendererPath, "node_modules", "pixi.js") },
- { find: "pixi-filters", replacement: resolve(rendererPath, "node_modules", "pixi-filters") },
- { find: "howler", replacement: resolve(rendererPath, "node_modules", "howler") },
- ],
- },
- build: {
- assetsInlineLimit: 102400,
- chunkSizeWarningLimit: 200000,
- rollupOptions: {
- output: {
- assetFileNames: "src/assets/[name]-[hash].[ext]",
- manualChunks: (id) => {
- if (id.includes("node_modules")) {
- if (id.includes("@nitrots") || id.includes("nitro-renderer")) {
- return "nitro-renderer";
- }
- return "vendor";
- }
- },
- },
- },
- },
-});
-CONFIG;
-
- file_put_contents($this->clientPath . '/vite.config.mjs', $viteConfig);
- Process::timeout(5)->run('chmod 644 ' . escapeshellarg($this->clientPath . '/vite.config.mjs'));
- Process::timeout(5)->run('chown www-data:www-data ' . escapeshellarg($this->clientPath . '/vite.config.mjs'));
- Log::info('[Nitro] Updated vite.config.mjs');
- }
-
- private function createYarnLink(): void
- {
- $nitrotsDir = $this->clientPath . '/node_modules/@nitrots';
- if (! is_dir($nitrotsDir)) {
- mkdir($nitrotsDir, 0755, true);
- }
-
- Process::timeout(10)->run('rm -rf ' . escapeshellarg($nitrotsDir) . '/*');
- Process::timeout(30)->run(
- 'cp -r ' . escapeshellarg($this->rendererPath . '/node_modules/@nitrots/*') . ' ' . escapeshellarg($nitrotsDir) . '/',
- );
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg($nitrotsDir));
- Log::info('[Nitro] Copied @nitrots packages');
- }
-
- private function installDependencies(): void
- {
- Log::info('[Nitro] Cleaning renderer node_modules');
-
- Process::timeout(30)->run('rm -rf ' . escapeshellarg($this->rendererPath . '/node_modules'));
-
- Log::info('[Nitro] Installing renderer deps');
-
- $rendererYarn = Process::timeout(300)->run(
- 'cd ' . escapeshellarg($this->rendererPath) . ' && yarn install --ignore-engines 2>&1',
- );
-
- if (! $rendererYarn->successful()) {
- Log::warning('[Nitro] Yarn failed for renderer, trying npm');
- Process::timeout(300)->run(
- 'cd ' . escapeshellarg($this->rendererPath) . ' && npm install --legacy-peer-deps 2>&1',
- );
- }
-
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg($this->rendererPath));
-
- Log::info('[Nitro] Cleaning client node_modules');
-
- Process::timeout(30)->run('rm -rf ' . escapeshellarg($this->clientPath . '/node_modules'));
-
- Log::info('[Nitro] Installing client deps');
-
- $clientYarn = Process::timeout(300)->run(
- 'cd ' . escapeshellarg($this->clientPath) . ' && yarn install --ignore-engines 2>&1',
- );
-
- if (! $clientYarn->successful()) {
- Log::warning('[Nitro] Yarn failed for client, trying npm');
- Process::timeout(300)->run(
- 'cd ' . escapeshellarg($this->clientPath) . ' && npm install --legacy-peer-deps 2>&1',
- );
- }
-
- $this->createYarnLink();
-
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg($this->clientPath));
- }
-
- public function buildClient(): array
- {
- Log::info('[Nitro] Building client');
-
- $yarnBuild = Process::timeout(600)->run(
- 'cd ' . escapeshellarg($this->clientPath) . ' && yarn build 2>&1',
- );
-
- if ($yarnBuild->successful()) {
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg($this->clientPath));
- Log::info('[Nitro] Build successful (yarn)');
-
- return ['success' => true, 'method' => 'yarn'];
- }
-
- Log::warning('[Nitro] Yarn build failed, trying npm build');
-
- $npmBuild = Process::timeout(600)->run(
- 'cd ' . escapeshellarg($this->clientPath) . ' && npm run build 2>&1',
- );
-
- if ($npmBuild->successful()) {
- Process::timeout(10)->run('chown -R www-data:www-data ' . escapeshellarg($this->clientPath));
- Log::info('[Nitro] Build successful (npm)');
-
- return ['success' => true, 'method' => 'npm'];
- }
-
- $output = $yarnBuild->output() . $yarnBuild->errorOutput() . "\n" . $npmBuild->output() . $npmBuild->errorOutput();
- Log::error('[Nitro] Build failed', ['output' => substr($output, 0, 500)]);
-
- return [
- 'success' => false,
- 'error' => 'Build mislukt: ' . substr($output, 0, 300),
- ];
- }
-
- public function deployClient(): void
- {
- Log::info('[Nitro] Deploying client');
-
- // Clear and copy
- Process::timeout(30)->run('rm -rf ' . escapeshellarg($this->clientWebRoot) . '/*');
- Process::timeout(60)->run(
- 'cp -r ' . escapeshellarg($this->clientPath . '/dist') . '/* ' .
- escapeshellarg($this->clientWebRoot) . '/ 2>&1',
- );
-
- // Always add/update toolbar color CSS variables to root
- $indexFile = $this->clientWebRoot . '/index.html';
- if (file_exists($indexFile)) {
- $content = file_get_contents($indexFile);
- $cssVars = '';
-
- // Replace existing CSS vars or add new
- if (preg_match('/