You've already forked Atomcms-edit
fix(security): eliminate remaining critical vulnerabilities
- SystemFixService: removed ALL shell_exec/sudo calls (30+ instances), replaced with safe PHP alternatives (mkdir, chmod, disk_total_space, Artisan calls) - InstallationController: added ALLOWED_SETTINGS whitelist to prevent arbitrary settings manipulation via request data - ExceptionHandler: removed dangerous npm run build execution and hardcoded chown/chmod paths from auto-recovery - AuthController: fixed user enumeration timing attack by running Hash::make() even when user doesn't exist (constant-time comparison) - DDoSDetectionCommand: added IP validation (FILTER_VALIDATE_IP) before blocking to prevent iptables manipulation with spoofed/malicious IPs - trackRequest: now validates IP before storing in cache
This commit is contained in:
@@ -53,41 +53,12 @@ class DDoSDetectionCommand extends Command
|
||||
$data = Cache::get(self::CACHE_KEY_DDOS_TRACKING, []);
|
||||
|
||||
if (empty($data)) {
|
||||
$data = $this->fetchApacheTrafficData();
|
||||
$data = $this->getManualTrackingData();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function fetchApacheTrafficData(): array
|
||||
{
|
||||
$this->warn('Gebruik handmatige IP tracking (geen server logs toegang).');
|
||||
|
||||
return $this->getManualTrackingData();
|
||||
}
|
||||
|
||||
private function tailFile(string $path, int $lines = 100): array
|
||||
{
|
||||
if (! file_exists($path)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$file = new \SplFileObject($path, 'r');
|
||||
$file->seek(PHP_INT_MAX);
|
||||
$totalLines = $file->key() + 1;
|
||||
|
||||
$startLine = max(0, $totalLines - $lines);
|
||||
$result = [];
|
||||
|
||||
$file->seek($startLine);
|
||||
while (! $file->eof()) {
|
||||
$result[] = rtrim($file->current(), "\r\n");
|
||||
$file->next();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getManualTrackingData(): array
|
||||
{
|
||||
$tracking = Cache::get('manual_ip_tracking', []);
|
||||
@@ -141,6 +112,10 @@ class DDoSDetectionCommand extends Command
|
||||
$newBlocks = [];
|
||||
|
||||
foreach ($suspiciousIps as $ip => $details) {
|
||||
if (! $this->isValidIp($ip)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->error("VERDACHTE IP GEDETECTEERD: {$ip}");
|
||||
$this->table(
|
||||
['Metric', 'Value'],
|
||||
@@ -177,8 +152,19 @@ class DDoSDetectionCommand extends Command
|
||||
Cache::put(self::CACHE_KEY_BLOCKED_IPS, $blockedIps, 3600);
|
||||
}
|
||||
|
||||
private function isValidIp(string $ip): bool
|
||||
{
|
||||
return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
|
||||
}
|
||||
|
||||
private function blockIp(string $ip): void
|
||||
{
|
||||
if (! $this->isValidIp($ip)) {
|
||||
Log::warning("Attempted to block invalid IP: {$ip}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$escapedIp = escapeshellarg($ip);
|
||||
exec("iptables -A INPUT -s {$escapedIp} -j DROP 2>/dev/null");
|
||||
@@ -192,6 +178,10 @@ class DDoSDetectionCommand extends Command
|
||||
|
||||
public static function trackRequest(string $ip): void
|
||||
{
|
||||
if (! filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tracking = Cache::get('manual_ip_tracking', []);
|
||||
|
||||
if (! isset($tracking[$ip])) {
|
||||
@@ -201,7 +191,7 @@ class DDoSDetectionCommand extends Command
|
||||
$tracking[$ip]['requests'][] = time();
|
||||
$tracking[$ip]['count']++;
|
||||
|
||||
Cache::put('manual_ip_tracking', $tracking, self::TRACKING_DURATION_SECONDS);
|
||||
Cache::put('manual_ip_tracking', $tracking, 300);
|
||||
}
|
||||
|
||||
public static function getBlockedIps(): array
|
||||
@@ -211,6 +201,10 @@ class DDoSDetectionCommand extends Command
|
||||
|
||||
public static function clearBlockedIp(string $ip): void
|
||||
{
|
||||
if (! filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$blocked = Cache::get(self::CACHE_KEY_BLOCKED_IPS, []);
|
||||
$blocked = array_filter($blocked, fn ($blockedIp) => $blockedIp !== $ip);
|
||||
Cache::put(self::CACHE_KEY_BLOCKED_IPS, array_values($blocked), 3600);
|
||||
|
||||
@@ -12,7 +12,6 @@ use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Throwable;
|
||||
|
||||
@@ -101,10 +100,6 @@ class Handler extends ExceptionHandler
|
||||
Artisan::call('config:cache');
|
||||
Artisan::call('view:cache');
|
||||
|
||||
if (str_contains($exceptionClass, 'ViteManifestNotFoundException') || str_contains($message, 'Vite manifest')) {
|
||||
$this->rebuildViteManifest();
|
||||
}
|
||||
|
||||
if (function_exists('opcache_reset')) {
|
||||
@opcache_reset();
|
||||
}
|
||||
@@ -122,31 +117,6 @@ class Handler extends ExceptionHandler
|
||||
}
|
||||
}
|
||||
|
||||
private function rebuildViteManifest(): void
|
||||
{
|
||||
$manifestPath = public_path('build/manifest.json');
|
||||
|
||||
if (! file_exists($manifestPath)) {
|
||||
Log::warning('Vite manifest missing, attempting rebuild');
|
||||
|
||||
$result = Process::timeout(120)->run('npm run build');
|
||||
|
||||
if ($result->successful()) {
|
||||
Log::info('Vite manifest rebuilt successfully');
|
||||
|
||||
if (file_exists('/var/www/atomcms/public/build')) {
|
||||
Process::run('chown -R www-data:www-data /var/www/atomcms/public/build');
|
||||
Process::run('chmod -R 775 /var/www/atomcms/public/build');
|
||||
}
|
||||
} else {
|
||||
Log::error('Vite manifest rebuild failed', [
|
||||
'output' => $result->output(),
|
||||
'error' => $result->errorOutput(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handleExceptionAlert(Throwable $e): void
|
||||
{
|
||||
if (! $this->shouldAlertException($e)) {
|
||||
|
||||
@@ -26,7 +26,11 @@ class AuthController extends Controller
|
||||
->orWhere('mail', $username)
|
||||
->first();
|
||||
|
||||
if (! $user || ! Hash::check($request->input('password'), $user->password)) {
|
||||
$credentialsValid = $user && Hash::check($request->input('password'), $user->password);
|
||||
|
||||
if (! $credentialsValid) {
|
||||
Hash::make($request->input('password'));
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => ['The provided credentials are incorrect.'],
|
||||
]);
|
||||
|
||||
@@ -16,6 +16,13 @@ use Illuminate\View\View;
|
||||
|
||||
class InstallationController extends Controller
|
||||
{
|
||||
private const array ALLOWED_SETTINGS = [
|
||||
'hotel_name', 'hotel_url', 'emulator_database_host', 'emulator_database_port',
|
||||
'emulator_database_name', 'emulator_database_username', 'emulator_database_password',
|
||||
'theme', 'start_credits', 'start_pixels', 'start_diamonds',
|
||||
'social_login_google_enabled', 'social_login_discord_enabled', 'social_login_github_enabled',
|
||||
];
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
return view('installation.index');
|
||||
@@ -77,11 +84,9 @@ class InstallationController extends Controller
|
||||
|
||||
public function completeInstallation(): RedirectResponse
|
||||
{
|
||||
// Clear all caches before marking as complete
|
||||
Cache::forget('website_permissions');
|
||||
Cache::forget('website_settings');
|
||||
|
||||
// Mark installation as complete
|
||||
WebsiteInstallation::latest()->first()->update([
|
||||
'completed' => true,
|
||||
]);
|
||||
@@ -91,13 +96,17 @@ class InstallationController extends Controller
|
||||
|
||||
private function updateSettings(Request $request): void
|
||||
{
|
||||
foreach ($request->except('_token') as $key => $value) {
|
||||
WebsiteSetting::where('key', '=', $key)->update([
|
||||
'value' => $value ?? '',
|
||||
$data = $request->except(['_token', '_method']);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (! in_array($key, self::ALLOWED_SETTINGS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WebsiteSetting::where('key', $key)->update([
|
||||
'value' => is_array($value) ? json_encode($value) : (string) $value,
|
||||
]);
|
||||
}
|
||||
|
||||
// Cache will be automatically cleared by WebsiteSetting model events
|
||||
}
|
||||
|
||||
private function getSettingsForStep(int $step): Collection
|
||||
@@ -109,7 +118,7 @@ class InstallationController extends Controller
|
||||
2 => $settingsData[1] ?? [],
|
||||
3 => $settingsData[2] ?? [],
|
||||
4 => $settingsData[3] ?? [],
|
||||
5 => [], // Completion step has no settings
|
||||
5 => [],
|
||||
default => throw new Exception('Step does not exist'),
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class SystemFixService
|
||||
{
|
||||
private array $results = [];
|
||||
@@ -10,55 +12,16 @@ class SystemFixService
|
||||
{
|
||||
$this->results = [];
|
||||
|
||||
// 1. Check PHP extensions
|
||||
$this->checkAndFixPhpExtensions();
|
||||
|
||||
// 2. Check Git
|
||||
$this->checkAndFixGit();
|
||||
|
||||
// 3. Check Maven
|
||||
$this->checkAndFixMaven();
|
||||
|
||||
// 4. Check Node.js
|
||||
$this->checkAndFixNode();
|
||||
|
||||
// 5. Check MySQL connection
|
||||
$this->checkPhpExtensions();
|
||||
$this->checkDatabase();
|
||||
|
||||
// 6. Fix permissions
|
||||
$this->fixPermissions();
|
||||
|
||||
// 7. Fix open_basedir
|
||||
$this->fixOpenBaseDir();
|
||||
|
||||
// 8. Configure emulator database from .env
|
||||
$this->configureEmulatorDatabase();
|
||||
|
||||
// 9. Create missing directories
|
||||
$this->createMissingDirectories();
|
||||
|
||||
// 10. Fix storage permissions
|
||||
$this->fixStoragePermissions();
|
||||
|
||||
// 11. Check disk space
|
||||
$this->checkDiskSpace();
|
||||
|
||||
// 12. Clear old caches
|
||||
$this->clearOldCaches();
|
||||
|
||||
// 13. Fix git safe directories
|
||||
$this->fixGitSafeDirectories();
|
||||
|
||||
// 14. Create .m2 directory for Maven
|
||||
$this->fixMavenDirectory();
|
||||
|
||||
// 15. Check and install Java (needed for Maven)
|
||||
$this->checkAndFixJava();
|
||||
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
private function checkAndFixPhpExtensions(): void
|
||||
private function checkPhpExtensions(): void
|
||||
{
|
||||
$requiredExtensions = ['pdo_mysql', 'curl', 'json', 'mbstring', 'xml', 'zip', 'bcmath'];
|
||||
$missing = [];
|
||||
@@ -72,94 +35,7 @@ class SystemFixService
|
||||
if ($missing === []) {
|
||||
$this->results[] = ['item' => 'PHP Extensions', 'status' => 'ok', 'message' => 'Alle vereiste extensies geïnstalleerd'];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'PHP Extensions', 'status' => 'warning', 'message' => 'Ontbrekend: ' . implode(', ', $missing)];
|
||||
}
|
||||
}
|
||||
|
||||
private function checkAndFixGit(): void
|
||||
{
|
||||
$result = shell_exec('which git 2>/dev/null || echo ""');
|
||||
if (! in_array(trim($result ?? ''), ['', '0'], true)) {
|
||||
$this->results[] = ['item' => 'Git', 'status' => 'ok', 'message' => trim($result)];
|
||||
} else {
|
||||
// Install Git
|
||||
shell_exec('sudo apt-get update && sudo apt-get install -y git 2>&1');
|
||||
$result = shell_exec('which git 2>/dev/null || echo ""');
|
||||
if (! in_array(trim($result ?? ''), ['', '0'], true)) {
|
||||
$this->results[] = ['item' => 'Git', 'status' => 'fixed', 'message' => 'Automatisch geïnstalleerd'];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Git', 'status' => 'error', 'message' => 'Installatie mislukt'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function checkAndFixMaven(): void
|
||||
{
|
||||
$result = shell_exec('which mvn 2>/dev/null || echo ""');
|
||||
if (! in_array(trim($result ?? ''), ['', '0'], true)) {
|
||||
$this->results[] = ['item' => 'Maven', 'status' => 'ok', 'message' => trim($result)];
|
||||
} else {
|
||||
// Install Maven
|
||||
shell_exec('sudo apt-get update && sudo apt-get install -y maven 2>&1');
|
||||
// Fix Maven repository permissions
|
||||
shell_exec('sudo mkdir -p /var/www/.m2/repository && sudo chown -R www-data:www-data /var/www/.m2');
|
||||
$result = shell_exec('which mvn 2>/dev/null || echo ""');
|
||||
if (! in_array(trim($result ?? ''), ['', '0'], true)) {
|
||||
$this->results[] = ['item' => 'Maven', 'status' => 'fixed', 'message' => 'Automatisch geïnstalleerd'];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Maven', 'status' => 'error', 'message' => 'Installatie mislukt'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function checkAndFixNode(): void
|
||||
{
|
||||
// Use shell command to check for node (bypasses open_basedir)
|
||||
$nodeResult = shell_exec('which node 2>/dev/null || echo ""');
|
||||
$nodePath = trim($nodeResult ?? '');
|
||||
|
||||
if ($nodePath !== '' && $nodePath !== '0') {
|
||||
$version = shell_exec("{$nodePath} --version 2>/dev/null || echo ''");
|
||||
$versionNum = trim($version ?? '');
|
||||
|
||||
// Check if version is 24.x
|
||||
if (str_starts_with($versionNum, 'v24')) {
|
||||
$this->results[] = ['item' => 'Node.js', 'status' => 'ok', 'message' => $versionNum];
|
||||
} else {
|
||||
// Upgrade to Node.js 24
|
||||
$this->installNode24();
|
||||
}
|
||||
} else {
|
||||
// Install Node.js 24
|
||||
$this->installNode24();
|
||||
}
|
||||
}
|
||||
|
||||
private function installNode24(): void
|
||||
{
|
||||
// Install Node.js 24 via NodeSource
|
||||
$commands = [
|
||||
'curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash - 2>&1',
|
||||
'sudo apt-get install -y nodejs 2>&1',
|
||||
];
|
||||
|
||||
$success = true;
|
||||
foreach ($commands as $cmd) {
|
||||
$result = shell_exec($cmd);
|
||||
if (str_contains($result ?? '', 'E:')) {
|
||||
$success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify installation
|
||||
$version = shell_exec('node --version 2>/dev/null || echo ""');
|
||||
$versionNum = trim($version ?? '');
|
||||
|
||||
if ($versionNum !== '' && $versionNum !== '0' && str_starts_with($versionNum, 'v24')) {
|
||||
$this->results[] = ['item' => 'Node.js', 'status' => 'fixed', 'message' => "Geïnstalleerd: {$versionNum}"];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Node.js', 'status' => 'error', 'message' => 'Installatie mislukt'];
|
||||
$this->results[] = ['item' => 'PHP Extensions', 'status' => 'warning', 'message' => 'Ontbrekend: ' . implode(', ', $missing) . ' — installeer via: sudo apt install php-' . implode(' php-', $missing)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,16 +55,13 @@ class SystemFixService
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate inputs
|
||||
$validationErrors = $this->validateDatabaseInputs($host, $port, $name, $username);
|
||||
if ($validationErrors !== []) {
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => implode(', ', $validationErrors)];
|
||||
$this->autoFixEmulatorDatabase();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Try connection
|
||||
try {
|
||||
$pdo = new \PDO(
|
||||
"mysql:host={$host};port={$port};charset=utf8mb4",
|
||||
@@ -197,27 +70,14 @@ class SystemFixService
|
||||
[\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_TIMEOUT => 5],
|
||||
);
|
||||
|
||||
// Test with database
|
||||
$pdo->exec("USE `{$name}`");
|
||||
$pdo->query('SELECT 1');
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'ok', 'message' => "Verbonden met {$name}"];
|
||||
} catch (\PDOException $e) {
|
||||
$errorMsg = $e->getMessage();
|
||||
if (str_contains($errorMsg, 'Unknown database')) {
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => "Database '{$name}' bestaat niet"];
|
||||
$this->autoFixEmulatorDatabase();
|
||||
} elseif (str_contains($errorMsg, 'Access denied')) {
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => 'Verkeerde gebruikersnaam/wachtwoord'];
|
||||
$this->autoFixEmulatorDatabase();
|
||||
} elseif (str_contains($errorMsg, 'Connection refused')) {
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => "Kan niet verbinden met {$host}:{$port}"];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => $errorMsg];
|
||||
}
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => $e->getMessage()];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->results[] = ['item' => 'Emulator Database', 'status' => 'error', 'message' => $e->getMessage()];
|
||||
$this->autoFixEmulatorDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,32 +85,28 @@ class SystemFixService
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
// Validate host
|
||||
if ($host === '' || $host === '0') {
|
||||
$errors[] = 'Host is leeg';
|
||||
} elseif (! filter_var($host, FILTER_VALIDATE_IP) && ! preg_match('/^[a-zA-Z0-9.-]+$/', $host)) {
|
||||
$errors[] = 'Ongeldige host: ' . $host;
|
||||
$errors[] = 'Ongeldige host';
|
||||
}
|
||||
|
||||
// Validate port
|
||||
if ($port === '' || $port === '0') {
|
||||
$errors[] = 'Port is leeg';
|
||||
} elseif (! is_numeric($port) || $port < 1 || $port > 65535) {
|
||||
$errors[] = 'Ongeldige port: ' . $port;
|
||||
$errors[] = 'Ongeldige port';
|
||||
}
|
||||
|
||||
// Validate database name
|
||||
if ($name === '' || $name === '0') {
|
||||
$errors[] = 'Database naam is leeg';
|
||||
} elseif (! preg_match('/^\w+$/', $name)) {
|
||||
$errors[] = 'Ongeldige database naam: ' . $name;
|
||||
$errors[] = 'Ongeldige database naam';
|
||||
}
|
||||
|
||||
// Validate username
|
||||
if ($username === '' || $username === '0') {
|
||||
$errors[] = 'Gebruikersnaam is leeg';
|
||||
} elseif (! preg_match('/^\w+$/', $username)) {
|
||||
$errors[] = 'Ongeldige gebruikersnaam: ' . $username;
|
||||
$errors[] = 'Ongeldige gebruikersnaam';
|
||||
}
|
||||
|
||||
return $errors;
|
||||
@@ -258,7 +114,6 @@ class SystemFixService
|
||||
|
||||
private function autoFixEmulatorDatabase(): void
|
||||
{
|
||||
// Try to get correct values from .env / config
|
||||
$host = config('database.connections.mysql.host', '127.0.0.1');
|
||||
$port = config('database.connections.mysql.port', '3306');
|
||||
$name = config('database.connections.mysql.database', '');
|
||||
@@ -266,18 +121,9 @@ class SystemFixService
|
||||
$password = config('database.connections.mysql.password', '');
|
||||
|
||||
if (empty($name) || empty($username)) {
|
||||
// Try to detect from MySQL process list or common names
|
||||
$detected = $this->detectDatabaseFromMysql();
|
||||
if ($detected) {
|
||||
$name = $detected['name'];
|
||||
$host = $detected['host'] ?? $host;
|
||||
$username = $detected['username'] ?? $username;
|
||||
$password = $detected['password'] ?? $password;
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Emulator Database Fix', 'status' => 'warning', 'message' => 'Kon geen geldige database vinden'];
|
||||
$this->results[] = ['item' => 'Emulator Database Fix', 'status' => 'warning', 'message' => 'Kon geen geldige database vinden'];
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = app(SettingsService::class);
|
||||
@@ -290,307 +136,64 @@ class SystemFixService
|
||||
$this->results[] = ['item' => 'Emulator Database Fix', 'status' => 'fixed', 'message' => "Automatisch gefixt: {$name}@{$host}"];
|
||||
}
|
||||
|
||||
private function detectDatabaseFromMysql(): ?array
|
||||
{
|
||||
// Common database names for hotel emulators
|
||||
$commonNames = ['habbo', 'hotel', 'retro', 'emulator', 'game', 'server', 'arcturus', 'fusion'];
|
||||
|
||||
// Get CMS database credentials that work
|
||||
$cmsHost = config('database.connections.mysql.host', '127.0.0.1');
|
||||
$cmsPort = config('database.connections.mysql.port', '3306');
|
||||
$cmsUser = config('database.connections.mysql.username', '');
|
||||
$cmsPass = config('database.connections.mysql.password', '');
|
||||
$cmsDb = config('database.connections.mysql.database', '');
|
||||
|
||||
if (empty($cmsUser)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = new \PDO(
|
||||
"mysql:host={$cmsHost};port={$cmsPort};charset=utf8mb4",
|
||||
$cmsUser,
|
||||
$cmsPass,
|
||||
[\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_TIMEOUT => 5],
|
||||
);
|
||||
|
||||
// Get all databases the user can access
|
||||
$stmt = $pdo->query('SHOW DATABASES');
|
||||
$databases = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
|
||||
// Check for common names
|
||||
foreach ($commonNames as $name) {
|
||||
if (in_array($name, $databases)) {
|
||||
return [
|
||||
'name' => $name,
|
||||
'host' => $cmsHost,
|
||||
'username' => $cmsUser,
|
||||
'password' => $cmsPass,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// If CMS database exists and works, use that
|
||||
if (! empty($cmsDb) && in_array($cmsDb, $databases)) {
|
||||
return [
|
||||
'name' => $cmsDb,
|
||||
'host' => $cmsHost,
|
||||
'username' => $cmsUser,
|
||||
'password' => $cmsPass,
|
||||
];
|
||||
}
|
||||
|
||||
// Return first database that's not system db
|
||||
foreach ($databases as $db) {
|
||||
if (! in_array($db, ['information_schema', 'performance_schema', 'mysql', 'sys'])) {
|
||||
return [
|
||||
'name' => $db,
|
||||
'host' => $cmsHost,
|
||||
'username' => $cmsUser,
|
||||
'password' => $cmsPass,
|
||||
];
|
||||
}
|
||||
}
|
||||
} catch (\Exception) {
|
||||
// Could not detect
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function fixPermissions(): void
|
||||
{
|
||||
$directories = [
|
||||
'/var/www/emulator-source',
|
||||
'/var/www/nitro-client',
|
||||
'/var/www/nitro-renderer',
|
||||
'/var/www/Client',
|
||||
'/var/www/atomcms/storage',
|
||||
'/var/www/atomcms/bootstrap/cache',
|
||||
'/var/www/atomcms/app',
|
||||
storage_path(),
|
||||
storage_path('logs'),
|
||||
storage_path('framework'),
|
||||
storage_path('framework/cache'),
|
||||
storage_path('framework/sessions'),
|
||||
storage_path('framework/views'),
|
||||
bootstrap_path('cache'),
|
||||
];
|
||||
|
||||
$fixed = false;
|
||||
foreach ($directories as $dir) {
|
||||
if (is_dir($dir)) {
|
||||
// Check if already owned by www-data
|
||||
$owner = shell_exec("stat -c '%U' {$dir} 2>/dev/null || echo ''");
|
||||
if (trim($owner ?? '') !== 'www-data') {
|
||||
shell_exec("sudo chown -R www-data:www-data {$dir} 2>/dev/null || true");
|
||||
$fixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always fix app directory permissions (new files might have wrong owner)
|
||||
shell_exec("sudo find /var/www/atomcms/app -type f -name '*.php' ! -user www-data -exec chown www-data:www-data {} \\; 2>/dev/null || true");
|
||||
|
||||
$this->results[] = ['item' => 'Permissions', 'status' => $fixed ? 'fixed' : 'ok', 'message' => $fixed ? 'Directories eigendom gefixt' : 'Al correct geconfigureerd'];
|
||||
}
|
||||
|
||||
private function fixOpenBaseDir(): void
|
||||
{
|
||||
$openBaseDir = ini_get('open_basedir');
|
||||
|
||||
if (in_array($openBaseDir, ['', '0', false], true)) {
|
||||
$this->results[] = ['item' => 'open_basedir', 'status' => 'ok', 'message' => 'Geen restrictie'];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all required paths are in open_basedir
|
||||
$requiredPaths = ['/var/www', '/tmp', '/root'];
|
||||
$missing = [];
|
||||
|
||||
foreach ($requiredPaths as $path) {
|
||||
if (! str_contains($openBaseDir, $path)) {
|
||||
$missing[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if ($missing === []) {
|
||||
$this->results[] = ['item' => 'open_basedir', 'status' => 'ok', 'message' => 'Alle paden toegestaan'];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'open_basedir', 'status' => 'warning', 'message' => 'Ontbrekende paden: ' . implode(', ', $missing)];
|
||||
}
|
||||
}
|
||||
|
||||
private function configureEmulatorDatabase(): void
|
||||
{
|
||||
// Only configure if not already set
|
||||
$currentHost = setting('emulator_database_host', '');
|
||||
if (! empty($currentHost)) {
|
||||
$this->results[] = ['item' => 'Emulator Database Config', 'status' => 'ok', 'message' => 'Al geconfigureerd'];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get from .env
|
||||
$host = config('database.connections.mysql.host', '127.0.0.1');
|
||||
$port = config('database.connections.mysql.port', '3306');
|
||||
$name = config('database.connections.mysql.database', '');
|
||||
$username = config('database.connections.mysql.username', '');
|
||||
$password = config('database.connections.mysql.password', '');
|
||||
|
||||
if (empty($name) || empty($username)) {
|
||||
$this->results[] = ['item' => 'Emulator Database Config', 'status' => 'warning', 'message' => 'Geen database gevonden in .env'];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = app(SettingsService::class);
|
||||
$settings->set('emulator_database_host', $host);
|
||||
$settings->set('emulator_database_port', $port);
|
||||
$settings->set('emulator_database_name', $name);
|
||||
$settings->set('emulator_database_username', $username);
|
||||
$settings->set('emulator_database_password', $password);
|
||||
|
||||
$this->results[] = ['item' => 'Emulator Database Config', 'status' => 'fixed', 'message' => "Automatisch geconfigureerd: {$name}@{$host}"];
|
||||
}
|
||||
|
||||
private function createMissingDirectories(): void
|
||||
{
|
||||
$directories = [
|
||||
'/var/www/emulator-source',
|
||||
'/var/www/nitro-client',
|
||||
'/var/www/nitro-client/dist',
|
||||
'/var/www/nitro-renderer',
|
||||
'/var/www/Client',
|
||||
'/var/www/Client/gamedata',
|
||||
'/var/www/Gamedata',
|
||||
'/root/emulator',
|
||||
];
|
||||
|
||||
$created = [];
|
||||
foreach ($directories as $dir) {
|
||||
if (! is_dir($dir)) {
|
||||
shell_exec("sudo mkdir -p {$dir} 2>/dev/null && sudo chown www-data:www-data {$dir} 2>/dev/null");
|
||||
$created[] = basename($dir);
|
||||
}
|
||||
}
|
||||
|
||||
if ($created !== []) {
|
||||
$this->results[] = ['item' => 'Directories', 'status' => 'fixed', 'message' => 'Aangemaakt: ' . implode(', ', $created)];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Directories', 'status' => 'ok', 'message' => 'Alle directories bestaan'];
|
||||
}
|
||||
}
|
||||
|
||||
private function fixStoragePermissions(): void
|
||||
{
|
||||
$storageDirs = [
|
||||
'/var/www/atomcms/storage',
|
||||
'/var/www/atomcms/storage/logs',
|
||||
'/var/www/atomcms/storage/framework',
|
||||
'/var/www/atomcms/storage/framework/cache',
|
||||
'/var/www/atomcms/storage/framework/sessions',
|
||||
'/var/www/atomcms/storage/framework/views',
|
||||
'/var/www/atomcms/bootstrap/cache',
|
||||
];
|
||||
|
||||
$fixed = false;
|
||||
foreach ($storageDirs as $dir) {
|
||||
if (is_dir($dir)) {
|
||||
$owner = shell_exec("stat -c '%U' {$dir} 2>/dev/null || echo ''");
|
||||
if (trim($owner ?? '') !== 'www-data') {
|
||||
shell_exec("sudo chown -R www-data:www-data {$dir} 2>/dev/null");
|
||||
$fixed = true;
|
||||
}
|
||||
} else {
|
||||
shell_exec("sudo mkdir -p {$dir} && sudo chown www-data:www-data {$dir} 2>/dev/null");
|
||||
@mkdir($dir, 0775, true);
|
||||
$fixed = true;
|
||||
}
|
||||
|
||||
if (is_writable($dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@chmod($dir, 0775);
|
||||
$fixed = true;
|
||||
}
|
||||
|
||||
// Make storage writable
|
||||
shell_exec('sudo chmod -R 775 /var/www/atomcms/storage 2>/dev/null');
|
||||
shell_exec('sudo chmod -R 775 /var/www/atomcms/bootstrap/cache 2>/dev/null');
|
||||
|
||||
$this->results[] = ['item' => 'Storage Permissions', 'status' => $fixed ? 'fixed' : 'ok', 'message' => $fixed ? 'Gefixt' : 'Al correct'];
|
||||
$this->results[] = ['item' => 'Permissions', 'status' => $fixed ? 'fixed' : 'ok', 'message' => $fixed ? 'Permissions gefixt' : 'Al correct geconfigureerd'];
|
||||
}
|
||||
|
||||
private function checkDiskSpace(): void
|
||||
{
|
||||
$result = shell_exec("df -h /var/www 2>/dev/null | tail -1 | awk '{print $5}'");
|
||||
$usage = trim($result ?? '0%');
|
||||
$usageNum = (int) str_replace('%', '', $usage);
|
||||
$total = disk_total_space('/');
|
||||
$free = disk_free_space('/');
|
||||
|
||||
if ($usageNum >= 90) {
|
||||
$this->results[] = ['item' => 'Disk Space', 'status' => 'error', 'message' => "Disk {$usage} vol!"];
|
||||
} elseif ($usageNum >= 75) {
|
||||
$this->results[] = ['item' => 'Disk Space', 'status' => 'warning', 'message' => "Disk {$usage} gebruikt"];
|
||||
if ($total === false || $free === false) {
|
||||
$this->results[] = ['item' => 'Disk Space', 'status' => 'warning', 'message' => 'Kon schijfruimte niet bepalen'];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$usagePercent = (int) ((($total - $free) / $total) * 100);
|
||||
|
||||
if ($usagePercent >= 90) {
|
||||
$this->results[] = ['item' => 'Disk Space', 'status' => 'error', 'message' => "Schijf {$usagePercent}% vol!"];
|
||||
} elseif ($usagePercent >= 75) {
|
||||
$this->results[] = ['item' => 'Disk Space', 'status' => 'warning', 'message' => "Schijf {$usagePercent}% gebruikt"];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Disk Space', 'status' => 'ok', 'message' => "Disk {$usage} gebruikt"];
|
||||
$this->results[] = ['item' => 'Disk Space', 'status' => 'ok', 'message' => "Schijf {$usagePercent}% gebruikt"];
|
||||
}
|
||||
}
|
||||
|
||||
private function clearOldCaches(): void
|
||||
{
|
||||
// Clear old temp files
|
||||
shell_exec("find /tmp -name 'emulator-update-*' -mtime +1 -exec rm -rf {} \\; 2>/dev/null");
|
||||
shell_exec("find /tmp -name 'nitro_*' -mtime +1 -exec rm -rf {} \\; 2>/dev/null");
|
||||
shell_exec("find /tmp -name '*.lock' -mtime +1 -delete 2>/dev/null");
|
||||
Artisan::call('cache:clear');
|
||||
Artisan::call('view:clear');
|
||||
Artisan::call('config:clear');
|
||||
|
||||
// Clear Laravel caches
|
||||
shell_exec('cd /var/www/atomcms && php artisan cache:clear 2>/dev/null');
|
||||
shell_exec('cd /var/www/atomcms && php artisan view:clear 2>/dev/null');
|
||||
|
||||
$this->results[] = ['item' => 'Cache Cleanup', 'status' => 'fixed', 'message' => 'Oude caches opgeruimd'];
|
||||
}
|
||||
|
||||
private function fixGitSafeDirectories(): void
|
||||
{
|
||||
$directories = [
|
||||
'/var/www/atomcms',
|
||||
'/var/www/emulator-source',
|
||||
'/var/www/emulator-source/Emulator',
|
||||
'/var/www/nitro-client',
|
||||
'/var/www/nitro-renderer',
|
||||
'/var/www/Client',
|
||||
'/root',
|
||||
'/root/emulator',
|
||||
'/var/www',
|
||||
];
|
||||
|
||||
foreach ($directories as $dir) {
|
||||
shell_exec("git config --global --add safe.directory {$dir} 2>/dev/null || true");
|
||||
}
|
||||
|
||||
$this->results[] = ['item' => 'Git Safe Directories', 'status' => 'fixed', 'message' => 'Alle directories toegevoegd'];
|
||||
}
|
||||
|
||||
private function fixMavenDirectory(): void
|
||||
{
|
||||
$mavenDir = '/var/www/.m2';
|
||||
if (! is_dir($mavenDir)) {
|
||||
shell_exec("sudo mkdir -p {$mavenDir}/repository && sudo chown -R www-data:www-data {$mavenDir}");
|
||||
$this->results[] = ['item' => 'Maven Directory', 'status' => 'fixed', 'message' => 'Aangemaakt'];
|
||||
} else {
|
||||
$owner = shell_exec("stat -c '%U' {$mavenDir} 2>/dev/null || echo ''");
|
||||
if (trim($owner ?? '') !== 'www-data') {
|
||||
shell_exec("sudo chown -R www-data:www-data {$mavenDir}");
|
||||
$this->results[] = ['item' => 'Maven Directory', 'status' => 'fixed', 'message' => 'Permissions gefixt'];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Maven Directory', 'status' => 'ok', 'message' => 'Al correct'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function checkAndFixJava(): void
|
||||
{
|
||||
$result = shell_exec('which java 2>/dev/null || echo ""');
|
||||
if (! in_array(trim($result ?? ''), ['', '0'], true)) {
|
||||
$version = shell_exec('java -version 2>&1 | head -1');
|
||||
$this->results[] = ['item' => 'Java', 'status' => 'ok', 'message' => trim($version ?? '')];
|
||||
} else {
|
||||
// Install Java
|
||||
shell_exec('sudo apt-get update && sudo apt-get install -y default-jdk 2>&1');
|
||||
$result = shell_exec('which java 2>/dev/null || echo ""');
|
||||
if (! in_array(trim($result ?? ''), ['', '0'], true)) {
|
||||
$this->results[] = ['item' => 'Java', 'status' => 'fixed', 'message' => 'Automatisch geïnstalleerd'];
|
||||
} else {
|
||||
$this->results[] = ['item' => 'Java', 'status' => 'error', 'message' => 'Installatie mislukt'];
|
||||
}
|
||||
}
|
||||
$this->results[] = ['item' => 'Cache Cleanup', 'status' => 'fixed', 'message' => 'Caches opgeruimd'];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user