Files
Atomcms-edit/app/Services/EmulatorUpdateService.php
T
2026-05-23 19:05:37 +02:00

408 lines
15 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Miscellaneous\WebsiteSetting;
use App\Services\Emulator\EmulatorBackupService;
use App\Services\Emulator\EmulatorBuildService;
use App\Services\Emulator\EmulatorJarService;
use App\Services\Emulator\EmulatorSourceService;
use App\Services\Emulator\EmulatorSqlService;
use App\Services\Emulator\EmulatorStatusService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Process;
class EmulatorUpdateService
{
private readonly EmulatorStatusService $statusService;
private readonly EmulatorJarService $jarService;
private readonly EmulatorSourceService $sourceService;
private readonly EmulatorBuildService $buildService;
private readonly EmulatorSqlService $sqlService;
private readonly EmulatorBackupService $backupService;
private readonly SettingsService $settings;
public function __construct()
{
$this->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;
}
}