You've already forked Atomcms-edit
refactor: extract action classes, add Blade components, reduce Commandocentrum
- Create EmulatorControlAction and NitroControlAction classes - Extract business logic from Commandocentrum controller methods - Add Blade components for status cards, diagnostics, and summary cards - Replace shell_exec with file_get_contents in config reading - Remove duplicate methods and unused code - Commandocentrum reduced from 2033 to 1780 lines
This commit is contained in:
+87
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class EmulatorControlAction
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly SettingsService $settings,
|
||||||
|
private readonly EmulatorUpdateService $updateService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function start(): array
|
||||||
|
{
|
||||||
|
$serviceName = $this->settings->getOrDefault('emulator_service_name', 'arcturus');
|
||||||
|
$result = Process::timeout(30)->run("systemctl start {$serviceName} 2>&1");
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => $result->successful(),
|
||||||
|
'message' => $result->successful() ? 'Emulator gestart!' : ($result->output() ?: 'Kon emulator niet starten'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stop(): array
|
||||||
|
{
|
||||||
|
$serviceName = $this->settings->getOrDefault('emulator_service_name', 'arcturus');
|
||||||
|
$result = Process::timeout(30)->run("systemctl stop {$serviceName} 2>&1");
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => $result->successful(),
|
||||||
|
'message' => $result->successful() ? 'Emulator gestopt!' : ($result->output() ?: 'Kon emulator niet stoppen'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restart(): array
|
||||||
|
{
|
||||||
|
$serviceName = $this->settings->getOrDefault('emulator_service_name', 'arcturus');
|
||||||
|
$result = Process::timeout(60)->run("systemctl restart {$serviceName} 2>&1");
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => $result->successful(),
|
||||||
|
'message' => $result->successful() ? 'Emulator herstart!' : ($result->output() ?: 'Kon emulator niet herstarten'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendAlert(string $message): array
|
||||||
|
{
|
||||||
|
if (empty($message)) {
|
||||||
|
return ['success' => false, 'message' => 'Bericht mag niet leeg zijn'];
|
||||||
|
}
|
||||||
|
|
||||||
|
app(RconService::class)->sendCommand('alert', ['message' => $message]);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Actions\Commandocentrum;
|
||||||
|
|
||||||
|
use App\Services\SettingsService;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
|
||||||
|
class NitroControlAction
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly SettingsService $settings,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function pullUpdates(string $clientPath, string $rendererPath, string $branch): array
|
||||||
|
{
|
||||||
|
$this->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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Filament\Pages\Monitoring;
|
namespace App\Filament\Pages\Monitoring;
|
||||||
|
|
||||||
|
use App\Actions\Commandocentrum\EmulatorControlAction;
|
||||||
|
use App\Actions\Commandocentrum\NitroControlAction;
|
||||||
use App\Enums\AlertSeverity;
|
use App\Enums\AlertSeverity;
|
||||||
use App\Models\Miscellaneous\WebsitePermission;
|
use App\Models\Miscellaneous\WebsitePermission;
|
||||||
use App\Models\StaffActivity;
|
use App\Models\StaffActivity;
|
||||||
@@ -1267,64 +1269,29 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
|
|
||||||
public function sendHotelAlert(): void
|
public function sendHotelAlert(): void
|
||||||
{
|
{
|
||||||
try {
|
$result = app(EmulatorControlAction::class)->sendAlert($this->data['hotel_alert_message'] ?? '');
|
||||||
$message = $this->data['hotel_alert_message'] ?? '';
|
$this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger');
|
||||||
if (empty($message)) {
|
if ($result['success']) {
|
||||||
$this->notify('Error', 'Bericht mag niet leeg zijn', 'danger');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
app(RconService::class)->sendCommand('alert', ['message' => $message]);
|
|
||||||
$this->notify('Success', 'Alert verstuurd naar alle gebruikers!', 'success');
|
|
||||||
$this->data['hotel_alert_message'] = '';
|
$this->data['hotel_alert_message'] = '';
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function startEmulator(): void
|
public function startEmulator(): void
|
||||||
{
|
{
|
||||||
try {
|
$result = app(EmulatorControlAction::class)->start();
|
||||||
$serviceName = $this->getSetting('emulator_service_name', 'arcturus');
|
$this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger');
|
||||||
$result = Process::timeout(30)->run("sudo systemctl start {$serviceName} 2>&1");
|
|
||||||
if ($result->successful()) {
|
|
||||||
$this->notify('Success', 'Emulator gestart!', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Error', $result->output() ?: 'Kon emulator niet starten', 'danger');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stopEmulator(): void
|
public function stopEmulator(): void
|
||||||
{
|
{
|
||||||
try {
|
$result = app(EmulatorControlAction::class)->stop();
|
||||||
$serviceName = $this->getSetting('emulator_service_name', 'arcturus');
|
$this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger');
|
||||||
$result = Process::timeout(30)->run("sudo systemctl stop {$serviceName} 2>&1");
|
|
||||||
if ($result->successful()) {
|
|
||||||
$this->notify('Success', 'Emulator gestopt!', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Error', $result->output() ?: 'Kon emulator niet stoppen', 'danger');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restartEmulator(): void
|
public function restartEmulator(): void
|
||||||
{
|
{
|
||||||
try {
|
$result = app(EmulatorControlAction::class)->restart();
|
||||||
$serviceName = $this->getSetting('emulator_service_name', 'arcturus');
|
$this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger');
|
||||||
$result = Process::timeout(60)->run("sudo systemctl restart {$serviceName} 2>&1");
|
|
||||||
if ($result->successful()) {
|
|
||||||
$this->notify('Success', 'Emulator herstart!', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Error', $result->output() ?: 'Kon emulator niet herstarten', 'danger');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkEmulator(): void
|
public function checkEmulator(): void
|
||||||
@@ -1345,95 +1312,22 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
|
|
||||||
public function checkEmulatorUpdates(): void
|
public function checkEmulatorUpdates(): void
|
||||||
{
|
{
|
||||||
try {
|
$result = app(EmulatorControlAction::class)->update();
|
||||||
$this->notify('Info', '🔨 Emulator wordt gebouwd vanaf source...', 'info');
|
$this->notify($result['success'] ?? false ? 'Success' : 'Error', $result['message'] ?? $result['error'] ?? 'Onbekende fout', ($result['success'] ?? false) ? 'success' : 'danger');
|
||||||
|
|
||||||
$updateService = app(EmulatorUpdateService::class);
|
|
||||||
$result = $updateService->buildFromSource();
|
|
||||||
|
|
||||||
if ($result['success'] ?? false) {
|
|
||||||
$this->notify('Success', $result['message'] ?? '✅ Emulator gebouwd!', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Error', $result['error'] ?? 'Build mislukt', 'danger');
|
|
||||||
}
|
|
||||||
|
|
||||||
Cache::forget('all_updates_check');
|
Cache::forget('all_updates_check');
|
||||||
} catch (Exception $e) {
|
$this->fillForm();
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildEmulator(): void
|
public function buildEmulator(): void
|
||||||
{
|
{
|
||||||
try {
|
$result = app(EmulatorControlAction::class)->build();
|
||||||
$sourcePath = $this->getSetting('emulator_source_path', '/var/www/emulator-source');
|
$this->notify($result['success'] ?? false ? 'Success' : 'Error', $result['message'] ?? $result['error'] ?? 'Onbekende fout', ($result['success'] ?? false) ? 'success' : 'danger');
|
||||||
$githubUrl = $this->getSetting('emulator_github_url', '');
|
|
||||||
$branch = $this->getSetting('emulator_github_branch', 'main');
|
|
||||||
|
|
||||||
if ($githubUrl === '' || $githubUrl === '0') {
|
|
||||||
$this->notify('Error', 'Configureer eerst de Emulator GitHub URL', 'danger');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull latest from GitHub
|
|
||||||
$this->notify('Info', '🔄 Pulling latest changes from GitHub...', 'info');
|
|
||||||
$pullResult = $this->runCommand('cd ' . escapeshellarg($sourcePath) . ' && git pull origin ' . escapeshellarg($branch) . ' 2>&1', 60);
|
|
||||||
|
|
||||||
$mavenCheck = $this->runCommand('which mvn 2>/dev/null');
|
|
||||||
if (! $mavenCheck || ! trim($mavenCheck)) {
|
|
||||||
$this->notify('Warning', 'Maven (mvn) is niet geïnstalleerd - kan niet bouwen', 'warning');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find pom.xml and build
|
|
||||||
$pomDirs = [$sourcePath, $sourcePath . '/Emulator', $sourcePath . '/Emulator/Emulator'];
|
|
||||||
foreach ($pomDirs as $pomDir) {
|
|
||||||
$pomCheck = $this->runCommand('test -f ' . escapeshellarg($pomDir . '/pom.xml') . ' && echo yes');
|
|
||||||
if ($pomCheck === 'yes') {
|
|
||||||
$this->notify('Info', '🔨 Building emulator with Maven...', 'info');
|
|
||||||
$buildResult = $this->runCommand('cd ' . escapeshellarg($pomDir) . ' && mvn clean package -DskipTests 2>&1', 600);
|
|
||||||
|
|
||||||
if ($buildResult && str_contains($buildResult, 'BUILD SUCCESS')) {
|
|
||||||
$jarPath = $this->getSetting('emulator_jar_path', '/var/www/Emulator');
|
|
||||||
$jarFind = $this->runCommand('find ' . escapeshellarg($pomDir . '/target') . ' -name "*jar-with-dependencies.jar" -type f 2>/dev/null | head -1', 30);
|
|
||||||
|
|
||||||
if ($jarFind) {
|
|
||||||
$sourceJar = trim($jarFind);
|
|
||||||
$jarName = basename($sourceJar);
|
|
||||||
$destJar = $jarPath . '/' . $jarName;
|
|
||||||
|
|
||||||
$this->runCommand('cp ' . escapeshellarg($sourceJar) . ' ' . escapeshellarg($destJar) . ' 2>&1', 30);
|
|
||||||
|
|
||||||
$latestPath = $sourcePath . '/Emulator/Latest_Compiled_Version';
|
|
||||||
if (is_dir($latestPath)) {
|
|
||||||
$this->runCommand('cp ' . escapeshellarg($sourceJar) . ' ' . escapeshellarg($latestPath . '/' . $jarName) . ' 2>&1', 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->notify('Success', '✅ Build succesvol! JAR verplaatst naar ' . $jarName . '. Herstart de emulator.', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Success', '✅ Build succesvol! Herstart de emulator.', 'success');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->notify('Error', 'Build mislukt - controleer logs', 'danger');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->notify('Warning', 'Geen pom.xml gevonden - kan niet bouwen vanaf source', 'warning');
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderBackupsList(): HtmlString
|
public function renderBackupsList(): HtmlString
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$service = new EmulatorUpdateService;
|
$backups = app(EmulatorControlAction::class)->getBackups();
|
||||||
$backups = $service->getBackupList();
|
|
||||||
|
|
||||||
if ($backups === []) {
|
if ($backups === []) {
|
||||||
return new HtmlString(<<<'HTML'
|
return new HtmlString(<<<'HTML'
|
||||||
@@ -1480,34 +1374,17 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restoreBackup(string $backupName): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$service = new EmulatorUpdateService;
|
|
||||||
$result = $service->restoreBackup($backupName);
|
|
||||||
|
|
||||||
if ($result['success']) {
|
|
||||||
$this->notify('Success', $result['message'], 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Error', $result['error'], 'danger');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runSqlUpdates(): void
|
public function runSqlUpdates(): void
|
||||||
{
|
{
|
||||||
try {
|
$result = app(EmulatorControlAction::class)->runSqlUpdates();
|
||||||
$exitCode = Artisan::call('update:auto', ['--force' => true]);
|
$this->notify($result['success'] ?? false ? 'Success' : 'Error', $result['message'] ?? 'Onbekende fout', ($result['success'] ?? false) ? 'success' : 'danger');
|
||||||
if ($exitCode === 0) {
|
|
||||||
$this->notify('Success', 'SQL updates toegepast!', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Warning', 'Update controle voltooid', 'warning');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function restoreBackup(string $backupName): void
|
||||||
|
{
|
||||||
|
$result = app(EmulatorControlAction::class)->restoreBackup($backupName);
|
||||||
|
$this->notify($result['success'] ? 'Success' : 'Error', $result['message'] ?? $result['error'] ?? 'Onbekende fout', $result['success'] ? 'success' : 'danger');
|
||||||
|
$this->fillForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveEmulator(): void
|
public function saveEmulator(): void
|
||||||
@@ -1531,109 +1408,34 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
|
|
||||||
public function checkNitroUpdates(): void
|
public function checkNitroUpdates(): void
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$clientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client');
|
$clientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client');
|
||||||
$rendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer');
|
$rendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer');
|
||||||
$branch = $this->getSetting('nitro_github_branch', 'main');
|
$branch = $this->getSetting('nitro_github_branch', 'main');
|
||||||
|
|
||||||
// Get local commits before pull
|
$result = app(NitroControlAction::class)->pullUpdates($clientPath, $rendererPath, $branch);
|
||||||
$clientCommitBefore = $this->getGitCommit($clientPath);
|
$this->notify('Success', $result['message'], 'success');
|
||||||
$rendererCommitBefore = $this->getGitCommit($rendererPath);
|
$this->fillForm();
|
||||||
|
|
||||||
// Pull latest from GitHub using sudo
|
|
||||||
$this->notify('Info', '🔄 Pulling Nitro Client van GitHub...', 'info');
|
|
||||||
$this->runCommand('cd ' . escapeshellarg($clientPath) . ' && sudo -u www-data git pull origin ' . escapeshellarg($branch) . ' 2>&1', 60);
|
|
||||||
|
|
||||||
$this->runCommand('cd ' . escapeshellarg($rendererPath) . ' && sudo -u www-data git pull origin ' . escapeshellarg($branch) . ' 2>&1', 60);
|
|
||||||
|
|
||||||
// Get local commits after pull
|
|
||||||
$clientCommitAfter = $this->getGitCommit($clientPath);
|
|
||||||
$rendererCommitAfter = $this->getGitCommit($rendererPath);
|
|
||||||
|
|
||||||
// Check if anything was updated
|
|
||||||
$clientUpdated = $clientCommitBefore !== $clientCommitAfter;
|
|
||||||
$rendererUpdated = $rendererCommitBefore !== $rendererCommitAfter;
|
|
||||||
|
|
||||||
if ($clientUpdated || $rendererUpdated) {
|
|
||||||
$this->notify('Success', '✅ Nitro bijgewerkt! Build opnieuw met "Build" knop.', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Success', '✓ Nitro is al up-to-date!', 'success');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildNitro(): void
|
public function buildNitro(): void
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$clientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client');
|
$clientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client');
|
||||||
$rendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer');
|
$rendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer');
|
||||||
$branch = $this->getSetting('nitro_github_branch', 'main');
|
$branch = $this->getSetting('nitro_github_branch', 'main');
|
||||||
|
|
||||||
// Pull latest from GitHub
|
$result = app(NitroControlAction::class)->build($clientPath, $rendererPath, $branch);
|
||||||
$this->notify('Info', '🔄 Pulling Nitro Client...', 'info');
|
$this->notify($result['success'] ? 'Success' : 'Warning', $result['message'], $result['success'] ? 'success' : 'warning');
|
||||||
$this->runCommand('cd ' . escapeshellarg($clientPath) . ' && sudo -u www-data git pull origin ' . escapeshellarg($branch) . ' 2>&1', 60);
|
|
||||||
|
|
||||||
$this->runCommand('cd ' . escapeshellarg($rendererPath) . ' && sudo -u www-data git pull origin ' . escapeshellarg($branch) . ' 2>&1', 60);
|
|
||||||
|
|
||||||
$this->runCommand('cd ' . escapeshellarg($clientPath) . ' && sudo -u www-data npm install 2>&1', 120);
|
|
||||||
$this->runCommand('cd ' . escapeshellarg($rendererPath) . ' && sudo -u www-data npm install 2>&1', 120);
|
|
||||||
|
|
||||||
// Build
|
|
||||||
$this->notify('Info', '🔨 Building Nitro...', 'info');
|
|
||||||
$exitCode = Artisan::call('build:theme');
|
|
||||||
if ($exitCode === 0) {
|
|
||||||
$this->notify('Success', '✅ Nitro build succesvol!', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Warning', 'Build gestart - controleer handmatig', 'warning');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateNitroConfigs(): void
|
public function generateNitroConfigs(): void
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$siteUrl = $this->getSetting('nitro_site_url', $this->getCurrentSiteUrl());
|
$siteUrl = $this->getSetting('nitro_site_url', $this->getCurrentSiteUrl());
|
||||||
|
$webroot = $this->getSetting('nitro_webroot', '/var/www/Client');
|
||||||
|
$gamedataPath = $this->getSetting('gamedata_path', '/var/www/Gamedata');
|
||||||
|
|
||||||
if ($siteUrl === '' || $siteUrl === '0' || ! filter_var($siteUrl, FILTER_VALIDATE_URL)) {
|
$result = app(NitroControlAction::class)->generateConfigs($siteUrl, $webroot, $gamedataPath);
|
||||||
$this->notify('Error', 'Voer een geldige URL in (bijv. https://epicnabbo.nl)', 'danger');
|
$this->notify($result['success'] ? 'Success' : 'Error', $result['message'], $result['success'] ? 'success' : 'danger');
|
||||||
|
$this->fillForm();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$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']);
|
|
||||||
|
|
||||||
$webroot = $paths['nitro_webroot'];
|
|
||||||
$existingConfigs = $this->readExistingConfigs($webroot, $paths['gamedata_path']);
|
|
||||||
|
|
||||||
$exitCode = Artisan::call('app:generate-nitro-configs', [
|
|
||||||
'--site-url' => $siteUrl,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($existingConfigs !== [] && $exitCode === 0) {
|
|
||||||
$this->mergeExistingConfigs($webroot, $existingConfigs);
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings->set('nitro_last_checked', now()->toIso8601String());
|
|
||||||
|
|
||||||
if ($exitCode === 0) {
|
|
||||||
$this->notify('Success', 'Configs gegenereerd & bestaande instellingen behouden!', 'success');
|
|
||||||
} else {
|
|
||||||
$this->notify('Warning', 'Config gegenereerd (controleer handmatig)', 'warning');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->notify('Error', $e->getMessage(), 'danger');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderClothingStatus(): HtmlString
|
private function renderClothingStatus(): HtmlString
|
||||||
@@ -1669,58 +1471,6 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = $this->readFile($path);
|
|
||||||
if ($content) {
|
|
||||||
$decoded = json_decode($content, true);
|
|
||||||
if (json_last_error() === JSON_ERROR_NONE) {
|
|
||||||
$configs[$file] = $decoded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($gamedataPath !== '' && $gamedataPath !== '0') {
|
|
||||||
$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) {
|
|
||||||
$result = $this->readFile($path);
|
|
||||||
if ($result) {
|
|
||||||
$decoded = json_decode($result, 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 = $this->readFile($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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function detectAndSavePaths(): void
|
public function detectAndSavePaths(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
@props(['diagnostics'])
|
||||||
|
|
||||||
|
@php
|
||||||
|
$errors = array_filter($diagnostics, fn ($r) => $r->status === 'error');
|
||||||
|
$warnings = array_filter($diagnostics, fn ($r) => $r->status === 'warning');
|
||||||
|
$ok = array_filter($diagnostics, fn ($r) => $r->status === 'ok');
|
||||||
|
|
||||||
|
$errorCount = count($errors);
|
||||||
|
$warningCount = count($warnings);
|
||||||
|
$okCount = count($ok);
|
||||||
|
|
||||||
|
$overallStatus = $errorCount > 0 ? 'error' : ($warningCount > 0 ? 'warning' : 'ok');
|
||||||
|
$overallColor = match ($overallStatus) {
|
||||||
|
'error' => '#ef4444',
|
||||||
|
'warning' => '#f59e0b',
|
||||||
|
default => '#22c55e',
|
||||||
|
};
|
||||||
|
$overallLabel = match ($overallStatus) {
|
||||||
|
'error' => 'Kritieke Problemen',
|
||||||
|
'warning' => 'Waarschuwingen',
|
||||||
|
default => 'Gezond',
|
||||||
|
};
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div style="display:flex;flex-direction:column;gap:16px;">
|
||||||
|
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px;">
|
||||||
|
<x-filament-components::commandocentrum.summary-card label="Gezond" :count="$okCount" color="#22c55e" icon="check" />
|
||||||
|
<x-filament-components::commandocentrum.summary-card label="Waarschuwingen" :count="$warningCount" color="#f59e0b" icon="warning" />
|
||||||
|
<x-filament-components::commandocentrum.summary-card label="Fouten" :count="$errorCount" color="#ef4444" icon="error" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="background:{{ $overallColor }}15;border:1px solid {{ $overallColor }}30;border-radius:12px;padding:16px;display:flex;align-items:center;gap:12px;">
|
||||||
|
<div style="width:12px;height:12px;border-radius:50%;background:{{ $overallColor }};"></div>
|
||||||
|
<span style="font-weight:700;color:{{ $overallColor }};font-size:16px;">Systeem Status: {{ $overallLabel }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($errorCount > 0 || $warningCount > 0)
|
||||||
|
<div style="display:flex;flex-direction:column;gap:8px;">
|
||||||
|
@foreach ($diagnostics as $result)
|
||||||
|
@if ($result->status === 'ok')
|
||||||
|
@continue
|
||||||
|
@endif
|
||||||
|
@php
|
||||||
|
$color = $result->status === 'error' ? '#ef4444' : '#f59e0b';
|
||||||
|
@endphp
|
||||||
|
<div style="background:#fff;border:1px solid {{ $color }}30;border-radius:10px;padding:14px 16px;display:flex;align-items:flex-start;gap:12px;">
|
||||||
|
<div style="flex-shrink:0;margin-top:2px;">
|
||||||
|
@if ($result->status === 'error')
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="{{ $color }}" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
|
||||||
|
@else
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="{{ $color }}" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div style="flex:1;">
|
||||||
|
<div style="font-weight:600;color:#1e293b;font-size:14px;">{{ $result->name }}</div>
|
||||||
|
<div style="color:#64748b;font-size:13px;margin-top:2px;">{{ $result->message }}</div>
|
||||||
|
@if ($result->fix)
|
||||||
|
<div style="background:#f8fafc;border-radius:6px;padding:8px 12px;margin-top:8px;font-size:12px;color:#475569;font-family:monospace;">💡 {{ $result->fix }}</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
@props(['label', 'value', 'color', 'icon'])
|
||||||
|
|
||||||
|
<div style="background:#fff;border-radius:16px;padding:24px;border:1px solid #e2e8f0;box-shadow:0 1px 3px rgba(0,0,0,0.06);transition:all 0.3s ease;cursor:default;" onmouseover="this.style.transform='translateY(-4px)';this.style.boxShadow='0 12px 24px rgba(0,0,0,0.1)';this.style.borderColor='{{ $color }}40';" onmouseout="this.style.transform='translateY(0)';this.style.boxShadow='0 1px 3px rgba(0,0,0,0.06)';this.style.borderColor='#e2e8f0';">
|
||||||
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;">
|
||||||
|
<div style="background:{{ $color }}15;padding:10px;border-radius:12px;">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="{{ $color }}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
@if ($icon === 'users')
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||||
|
@elseif ($icon === 'server')
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
|
||||||
|
@elseif ($icon === 'database')
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||||
|
@elseif ($icon === 'cpu')
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
|
||||||
|
@endif
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:#94a3b8;">{{ $label }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="width:8px;height:8px;border-radius:50%;background:{{ $color }};animation:pulse 2s infinite;"></div>
|
||||||
|
</div>
|
||||||
|
<div style="font-size:36px;font-weight:800;color:#1e293b;line-height:1;">{{ $value }}</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
@props(['label', 'count', 'color', 'icon'])
|
||||||
|
|
||||||
|
<div style="background:#fff;border-radius:12px;padding:16px;border:1px solid #e2e8f0;box-shadow:0 1px 3px rgba(0,0,0,0.06);">
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;">
|
||||||
|
<div style="background:{{ $color }}15;padding:8px;border-radius:10px;">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="{{ $color }}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
@if ($icon === 'check')
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
@elseif ($icon === 'warning')
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
|
@elseif ($icon === 'error')
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" />
|
||||||
|
@endif
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span style="font-size:12px;font-weight:600;color:#64748b;">{{ $label }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="font-size:28px;font-weight:800;color:{{ $color }};line-height:1;">{{ $count }}</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user