You've already forked Atomcms-edit
510 lines
20 KiB
PHP
Executable File
510 lines
20 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Emulator;
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Process;
|
|
use Illuminate\Support\Str;
|
|
|
|
class EmulatorJarService
|
|
{
|
|
use EmulatorConfiguration;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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 <<<BASH
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
JAR_URL='{$jarUrl}'
|
|
JAR_NAME='{$jarName}'
|
|
JAR_PATH='{$this->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);
|
|
}
|
|
}
|