*/ private array $checks = []; private int $errors = 0; private bool $skipMigrations = false; private bool $skipDuplicates = false; private bool $skipBuild = false; private bool $fixEnv = false; private bool $setup = false; private int $warnings = 0; private string $platform = 'auto'; private string $webUser = 'www-data'; private string $webGroup = 'www-data'; private string $phpIniPath = ''; /** @var array */ private array $missingSeeders = []; public function handle(): int { if (! $this->option('skip-env')) { try { $envContent = file_get_contents(base_path('.env')); if ($envContent !== false && ! str_contains($envContent, "\n") && strlen($envContent) > 500) { $this->warn('⚠️ .env file appears to be on a single line. This will cause errors.'); $this->warn(' Please ensure .env has proper line breaks between each key-value pair.'); } } catch (\Exception) { // Ignore } } $lang = $this->option('lang'); if (is_string($lang) && in_array($lang, ['en', 'nl'])) { App::setLocale($lang); } $this->skipMigrations = (bool) $this->option('skip-migrations'); $this->skipDuplicates = (bool) $this->option('skip-duplicates'); $this->skipBuild = (bool) $this->option('skip-build'); $this->fixEnv = (bool) $this->option('fix-env'); $this->setup = (bool) $this->option('setup'); if ($this->fixEnv) { $this->fixEnvFile(); return 0; } if ($this->setup) { return $this->runFullSetup(); } $platformOption = $this->option('platform'); $this->platform = $platformOption !== 'auto' && is_string($platformOption) ? $platformOption : $this->detectPlatform(); $this->detectWebUserContext(); $this->phpIniPath = php_ini_loaded_file() ?: 'Not found'; $this->info('╔════════════════════════════════════════════════════════════╗'); $this->info('║ ATOM CMS MASTER REPAIR COMMAND ║'); $this->info('╚════════════════════════════════════════════════════════════╝'); $this->info(' Platform: ' . strtoupper($this->platform)); if ($this->isWindows()) { $this->info(' Stack : ' . ($this->isXampp() ? 'XAMPP' : ($this->isWamp() ? 'WAMP' : ($this->isNginx() ? 'NGINX' : 'IIS/Native')))); } else { $this->info(' Stack : ' . ($this->isNginx() ? 'NGINX' : 'APACHE')); } $this->info(' User : ' . $this->webUser . ' / ' . $this->webGroup); $this->info(' PHP : ' . PHP_VERSION); $this->newLine(); $this->runDiagnosticRoutine(); $this->displaySummary(); if ($this->option('fix') || $this->option('interactive')) { return $this->runFixWizard(); } return $this->errors > 0 ? 1 : 0; } /** * @return array */ private function discoverAllSeeders(): array { $seedersPath = database_path('seeders'); $seeders = []; if (! File::exists($seedersPath)) { return $seeders; } $files = File::files($seedersPath); foreach ($files as $file) { $filename = $file->getFilenameWithoutExtension(); if ($filename === 'DatabaseSeeder' || str_ends_with($filename, 'Base')) { continue; } $className = "Database\\Seeders\\{$filename}"; if (! class_exists($className)) { continue; } $reflection = new \ReflectionClass($className); if (! $reflection->isInstantiable()) { continue; } $seeders[$filename] = [ 'class' => $className, 'table' => $this->detectSeederTable($className), ]; } return $seeders; } private function detectSeederTable(string $className): string { $seederName = str_replace('Seeder', '', class_basename($className)); $tableMappings = [ 'WebsiteSettings' => 'website_settings', 'WebsiteLanguage' => 'website_languages', 'WebsitePermission' => 'permissions', 'WebsiteWordfilter' => 'website_wordfilter', 'WebsiteMaintenanceTasks' => 'website_maintenance_tasks', 'WebsiteArticle' => 'website_articles', 'WebsiteTeam' => 'website_teams', 'WebsiteShopCategories' => 'website_shop_categories', 'WebsiteShopArticle' => 'website_shop_articles', 'WebsiteRuleCategory' => 'website_rule_categories', 'WebsiteRule' => 'website_rules', 'WebsiteHelperCenterCategory' => 'website_help_center_categories', 'WebsiteRareValuesCategory' => 'website_rare_value_categories', 'HousekeepingPermission' => 'website_housekeeping_permissions', 'RadioSettings' => 'radio_settings', 'RadioSongVote' => 'radio_song_votes', 'RadioContest' => 'radio_contests', 'RadioSongRequest' => 'radio_song_requests', 'RadioGiveaway' => 'radio_giveaways', 'RadioListenerPoint' => 'radio_listener_points', 'RadioTest' => 'radio_tests', 'BaseSettings' => 'base_settings', 'WebsiteArticleFeature' => 'website_shop_article_features', ]; if (isset($tableMappings[$seederName])) { return $tableMappings[$seederName]; } $modelName = str_replace('Seeder', '', class_basename($className)); $modelClass = 'App\\Models\\' . $modelName; if (class_exists($modelClass)) { /** @var Model $model */ $model = new $modelClass; return $model->getTable(); } $snakeCase = strtolower((string) preg_replace('/(?> */ private function discoverAllTables(): array { $migrationsPath = database_path('migrations'); $tables = []; if (! File::exists($migrationsPath)) { return $tables; } $files = File::files($migrationsPath); $validMethods = ['bigInteger', 'binary', 'boolean', 'char', 'date', 'datetime', 'decimal', 'double', 'enum', 'float', 'foreignId', 'foreignIdFor', 'foreignUlid', 'foreignUuid', 'geometry', 'geometryCollection', 'id', 'integer', 'json', 'jsonb', 'lineString', 'longText', 'macAddress', 'mediumInteger', 'mediumText', 'morphs', 'nullableMorphs', 'nullableUuidMorphs', 'nullableTimestamps', 'point', 'polygon', 'rememberToken', 'set', 'smallInteger', 'softDeletes', 'softDeletesTz', 'string', 'text', 'time', 'timestamp', 'timestamps', 'timestampsTz', 'tinyInteger', 'tinyText', 'unsignedBigInteger', 'unsignedInteger', 'unsignedMediumInteger', 'unsignedSmallInteger', 'unsignedTinyInteger', 'uuid', 'year']; $specialMethods = [ 'morphs' => ['_id', '_type'], 'nullableMorphs' => ['_id', '_type'], 'nullableUuidMorphs' => ['_id', '_type'], 'timestamps' => ['created_at', 'updated_at'], 'nullableTimestamps' => ['created_at', 'updated_at'], 'softDeletes' => ['deleted_at'], 'softDeletesTz' => ['deleted_at'], 'rememberToken' => ['remember_token'], ]; foreach ($files as $file) { $content = File::get($file->getPathname()); if (preg_match('/Schema::create\([\'"](\w+)[\'"]/', $content, $matches)) { $tableName = $matches[1]; $tables[$tableName] = []; preg_match_all('/\$table->(\w+)\([\'"]([^\'"]+)[\'"]\)/', $content, $matches2); foreach ($matches2[1] as $index => $method) { if (in_array($method, $validMethods)) { $columnName = $matches2[2][$index]; if (! in_array($columnName, $tables[$tableName])) { $tables[$tableName][] = $columnName; } } elseif (isset($specialMethods[$method])) { foreach ($specialMethods[$method] as $col) { if (! in_array($col, $tables[$tableName])) { $tables[$tableName][] = $col; } } } } preg_match_all('/\$table->(\w+)\(([^\)]+)\)/', $content, $matches3); foreach ($matches3[1] as $index => $method) { if (isset($specialMethods[$method])) { $paramName = trim($matches3[2][$index], "'\""); foreach ($specialMethods[$method] as $suffix) { $col = $paramName . $suffix; if (! in_array($col, $tables[$tableName])) { $tables[$tableName][] = $col; } } } } if (preg_match_all('/\$table->id\(\)/', $content) && ! in_array('id', $tables[$tableName])) { $tables[$tableName][] = 'id'; } } if (preg_match_all('/Schema::table\([\'"](\w+)[\'"]/', $content, $tableMatches)) { foreach ($tableMatches[1] as $tableName) { if (! isset($tables[$tableName])) { $tables[$tableName] = []; } preg_match_all('/\$table->(\w+)\([\'"]([^\'"]+)[\'"]\)/', $content, $matches2); foreach ($matches2[1] as $index => $method) { if (in_array($method, $validMethods)) { $columnName = $matches2[2][$index]; if (! in_array($columnName, $tables[$tableName])) { $tables[$tableName][] = $columnName; } } elseif (isset($specialMethods[$method])) { foreach ($specialMethods[$method] as $col) { if (! in_array($col, $tables[$tableName])) { $tables[$tableName][] = $col; } } } } if (preg_match_all('/\$table->id\(\)/', $content) && ! in_array('id', $tables[$tableName])) { $tables[$tableName][] = 'id'; } } } } $coreTables = [ 'users' => ['id', 'username', 'mail', 'password', 'rank', 'look', 'motto'], ]; return array_merge($coreTables, $tables); } private function checkAllDatabaseTables(): void { try { $discoveredTables = array_keys($this->discoverAllTables()); $dbTables = DB::connection()->select('SHOW TABLES'); $dbTables = array_map(function ($t) { if (is_array($t)) { return reset($t); } if (is_object($t)) { return (array) $t; } }, $dbTables); // Flatten if objects were converted to arrays $flatTables = []; foreach ($dbTables as $t) { $flatTables[] = is_array($t) ? reset($t) : $t; } $dbTables = array_filter($flatTables); $missingTables = array_diff($discoveredTables, $dbTables); $optionalTables = [ 'users_session_logs', 'radio_settings', 'radio_djs', 'radio_requests', 'radio_song_votes', 'radio_contests', 'radio_giveaways', 'radio_listener_points', 'radio_song_requests', 'radio_ranks', 'radio_shouts', 'radio_schedules', 'radio_banners', 'radio_history', 'radio_applications', ]; foreach ($missingTables as $table) { if (in_array($table, $optionalTables)) { continue; } $this->addCheck("Missing Table: {$table}", '⚠️', 'Will be auto-created'); } } catch (\Exception $e) { $this->addCheck('Database Tables Check', '⚠️', 'Could not check: ' . $e->getMessage()); } } private function runDiagnosticRoutine(): void { $this->comment('--- [1/7] Environment & Security ---'); $this->checkEnvFile(); $this->checkAppKey(); $this->checkDebugMode(); $this->checkComposerSecurity(); $this->checkRequiredFiles(); $this->checkStorageSymlink(); $discoveredTables = $this->discoverAllTables(); $discoveredSeeders = $this->discoverAllSeeders(); $this->comment("\n--- [2/7] Database ---"); $this->checkDatabaseConnection(); $this->checkDeepDatabaseSchema($discoveredTables); $this->checkMigrationsStatus(); $this->checkSeedersStatus($discoveredSeeders); $this->checkAllDatabaseTables(); $this->checkRequiredSettingsData(); $this->checkRadioTables(); $this->checkAdminUser(); $this->comment("\n--- [3/7] PHP Stack ---"); $this->checkPHPExtensions(); $this->checkPHPConfiguration(); $this->checkForPhpUpdates(); $this->checkCacheOptimization(); $this->checkSessionConfiguration(); $this->checkFilamentPages(); $this->comment("\n--- [4/7] Web Server ---"); $this->checkWebServerConfiguration(); $this->checkSSLCertificates(); $this->comment("\n--- [5/7] System ---"); $this->checkFilePermissions(); $this->checkFirewallPorts(); $this->comment("\n--- [6/7] Assets ---"); $this->checkAssetsStatus(); $this->checkRedisConnection(); $this->checkCronJobsStatus(); $this->checkQueueWorkersStatus(); $this->checkSupervisorConfig(); $this->checkFrontendManifest(); $this->comment("\n--- [7/7] HTTP Errors ---"); $this->checkHttpErrors(); } private function runFixWizard(): int { $auto = (bool) $this->option('auto'); $this->info("\n" . str_repeat('=', 65)); $this->info(' INTERACTIVE MASTER FIX WIZARD '); $this->info(str_repeat('=', 65)); $fixed = 0; if ($auto) { $this->info('🔧 Running in AUTO-FIX mode - fixing EVERYTHING...'); return $this->fixEverything($auto); } // Security Audit Fix if ($this->hasError('Security Audit')) { $confirm = $auto ? true : $this->confirm('🛡️ Security vulnerabilities found in dependencies. Run update?', true); if ($confirm === true) { $this->info('Running composer update...'); $result = $this->runSystemCommandWithRetry('composer update', 2, 1000); if ($result['success']) { $this->info('✅ Dependencies updated.'); $fixed++; } else { $this->error('❌ Update failed: ' . ($result['output'] ?: 'Unknown error')); } } } // SSL/HTTPS Fix if ($this->hasError('SSL Status') || $this->hasError('Mixed HTTPS')) { $confirm = $auto ? true : $this->confirm('🔒 SSL/HTTPS issues found. Fix automatically?', true); if ($confirm === true) { $this->fixHttpsConfiguration(); $this->info('✅ HTTPS configuration fixed.'); $fixed++; } } if ($this->hasError('Environment File') || $this->hasError('Application Key')) { $confirm = $auto ? true : $this->confirm('⚠️ Fix critical environment issues?', true); if ($confirm === true) { if (! File::exists(base_path('.env'))) { File::copy(base_path('.env.example'), base_path('.env')); } $this->call('key:generate', ['--force' => true]); $this->info('✅ Environment initialized.'); $fixed++; } } if ($this->hasError('Table:') || $this->hasError('Column:') || $this->hasError('Missing Table:')) { $confirm = $auto ? true : $this->confirm('💾 Create database backup before applying fixes?', true); if ($confirm === true) { if ($this->confirm('💾 Backup database?', true) === true) { $this->backupDatabase(); } $this->info('🗄️ Running migrations...'); if ($this->skipMigrations) { $this->info('⏭️ Migrations skipped (--skip-migrations)'); } else { if ($this->isWindows()) { $this->addMysqlToPath(); } if ($this->skipDuplicates) { $this->runMigrationsWithSkip(); } else { $this->call('migrate', ['--force' => true]); } $this->info('✅ Migrations complete.'); } $fixed++; } } if ($this->missingSeeders !== []) { $confirm = $auto ? true : $this->confirm('🌱 ' . count($this->missingSeeders) . ' seeders missing data. Run seeders?', true); if ($confirm) { $this->info('🌱 Running ALL seeders...'); $this->call('db:seed', ['--force' => true]); $this->info('✅ All seeders complete.'); $fixed++; } } if ($this->hasError('Admin User')) { $confirm = $auto ? true : $this->confirm('👤 No Admin found. Create one?', true); if ($confirm) { if ($auto) { // Auto-create admin with default credentials $this->createAdminUser('Admin', 'admin@atom.local', 'admin123'); } else { $username = (string) $this->ask('Enter Admin Username', 'Admin'); $email = (string) $this->ask('Enter Admin Email', 'admin@example.com'); $password = (string) $this->secret('Enter Admin Password'); $this->createAdminUser($username, $email, $password); } $fixed++; } } if ($this->isApache() && ($this->hasError('Apache Rewrite') || $this->hasError('Index.php'))) { $confirm = $auto ? true : $this->confirm('🌐 Repair Apache configuration?', true); if ($confirm) { $this->repairApache(); $this->info('✅ Apache rules fixed.'); $fixed++; } } elseif ($this->isIIS() && $this->hasError('IIS Routing')) { $confirm = $auto ? true : $this->confirm('🌐 Create web.config for IIS?', true); if ($confirm) { $this->repairIIS(); $this->info('✅ IIS config created.'); $fixed++; } } elseif ($this->isNginx()) { $confirm = $auto ? true : $this->confirm('🌐 Generate Nginx config template?', true); if ($confirm) { $this->repairNginx(); $fixed++; } } if ($this->isLinux() && ($this->hasError('Writable:') || $this->hasError('Permissions:'))) { $confirm = $auto ? true : $this->confirm('🔐 Fix Linux file permissions?', true); if ($confirm) { $this->repairPermissions(); $this->info('✅ Permissions applied.'); $fixed++; } } if ($this->hasWarning('PHP.ini:')) { $confirm = $auto ? true : $this->confirm('⚙️ Tune PHP performance limits?', true); if ($confirm && $this->repairPHPConfig()) { $this->info('✅ PHP.ini optimized.'); $fixed++; } } if ($this->hasError('PHP Ext:') && $this->isLinux()) { $confirm = $auto ? true : $this->confirm('📦 Install missing extensions?', true); if ($confirm) { $count = $this->repairPHPExtensions(); $this->info("✅ Installed {$count} extensions."); $fixed += $count; } } if ($this->hasError('Storage Link')) { $confirm = $auto ? true : $this->confirm('🔗 Fix storage symlink?', true); if ($confirm) { if (File::exists(public_path('storage'))) { @unlink(public_path('storage')); } $this->call('storage:link'); $this->info('✅ Storage restored.'); $fixed++; } } if ($this->hasError('Frontend Assets') || $this->hasError('Vite Manifest')) { $confirm = $auto ? true : $this->confirm('🎨 Build frontend assets?', true); if ($confirm) { $this->buildFrontendAssets(); $fixed++; } } if ($this->isLinux() && $this->hasWarning('Supervisor')) { $confirm = $auto ? true : $this->confirm('🐘 Generate Supervisor config?', true); if ($confirm) { $this->generateSupervisorConfig(); $fixed++; } } if ($this->hasError('Redis') && $this->isLinux()) { $confirm = $auto ? true : $this->confirm('🟦 Install Redis Server?', true); if ($confirm) { $this->installRedisIfNeeded($fixed); } } // Known common errors - auto fix $this->fixKnownCommonErrors(); // Fix HTTP errors $this->fixHttpErrors($auto); // Final verification - re-check everything $this->newLine(); $this->info('🔍 Running final verification...'); $this->newLine(); $this->errors = 0; $this->warnings = 0; $this->checks = []; $this->runDiagnosticRoutine(); $this->newLine(); $this->info('════════════════════════════════════════════════════════════'); $this->info(' FINAL VERIFICATION '); $this->info('════════════════════════════════════════════════════════════'); $this->displaySummary(); if ($this->errors === 0 && $this->warnings === 0) { $this->info('🎉 CMS is 100% healthy!'); } elseif ($this->errors === 0) { $this->warn('⚠️ CMS is operational with ' . $this->warnings . ' warnings'); } else { $this->error('❌ CMS has ' . $this->errors . ' remaining issues'); $this->info('Run atom:check --auto again to attempt fixes'); } // Final optimization if ($this->confirm('🚀 Run final optimizations (config:cache, routes:cache)?', false)) { $this->call('config:cache'); $this->call('route:cache'); $this->info('✅ Optimization complete.'); $fixed++; } $this->newLine(); $this->info("🎉 Wizard complete. Fixed: {$fixed} items."); return 0; } // --- CHECKS --- private function checkComposerSecurity(): void { if (file_exists(base_path('composer.lock'))) { $result = $this->runSystemCommand('composer audit'); if ($result['success']) { $this->addCheck('Security Audit', '✅', 'No vulnerabilities found'); } else { $count = 0; foreach (explode("\n", (string) $result['output']) as $line) { if (str_contains($line, 'Package:')) { $count++; } } $msg = $count > 0 ? "{$count} Vulnerabilities found!" : 'Audit failed / Issues found'; $this->addCheck('Security Audit', '❌', $msg); $this->errors++; } } else { $this->addCheck('Security Audit', '⚠️', 'composer.lock missing'); } } private function checkEnvFile(): void { $ok = File::exists(base_path('.env')); $this->addCheck('Environment File', $ok ? '✅' : '❌', $ok ? 'OK' : 'Missing'); } private function checkAppKey(): void { $key = config('app.key'); $this->addCheck('Application Key', empty($key) ? '❌' : '✅', empty($key) ? 'Missing' : 'Set'); } private function checkDebugMode(): void { $debug = config('app.debug'); $this->addCheck('Debug Mode', $debug ? '⚠️' : '✅', $debug ? 'Enabled' : 'Disabled'); } private function checkDatabaseConnection(): void { try { DB::connection()->getPdo(); $this->addCheck('Database Connection', '✅', 'Connected'); } catch (\Exception) { $this->addCheck('Database Connection', '❌', 'Failed'); } } private function checkDeepDatabaseSchema(array $discoveredTables = []): void { $tablesToCheck = $discoveredTables; $coreTablesFromSql = ['users', 'rooms', 'items', 'bans', 'chatlogs', 'photos', 'hotel_users']; $skipColumnCheck = [ 'users_session_logs', 'personal_access_tokens', 'website_ip_whitelist', 'website_ip_blacklist', 'website_permissions', 'taggables', 'cache', 'radio_ranks', 'radio_contests', 'radio_giveaways', 'website_blocked_countries', 'website_shop_articles', 'website_shop_article_features', ]; foreach ($tablesToCheck as $table => $columns) { if (! Schema::hasTable($table)) { if (! in_array($table, $skipColumnCheck)) { $this->addCheck("Table: {$table}", '❌', 'Table Missing'); } continue; } if (empty($columns)) { $this->addCheck("Table: {$table}", '✅', 'Exists'); continue; } if (in_array($table, $coreTablesFromSql)) { $this->addCheck("Table: {$table}", '✅', 'Exists (SQL)'); continue; } if (in_array($table, $skipColumnCheck)) { $this->addCheck("Table: {$table}", '✅', 'OK (skipped)'); continue; } $missingCols = []; foreach ($columns as $col) { if (! Schema::hasColumn($table, $col)) { $missingCols[] = $col; } } if ($missingCols !== []) { $this->addCheck("Column: {$table}", '❌', 'Missing cols: ' . implode(', ', $missingCols)); $this->errors++; } else { $this->addCheck("Table: {$table}", '✅', 'OK'); } } } private function checkRequiredSettingsData(): void { if (! Schema::hasTable('website_settings')) { return; } $requiredKeys = ['hotel_name', 'rcon_ip', 'rcon_port']; foreach ($requiredKeys as $key) { $exists = DB::table('website_settings')->where('key', $key)->exists(); $this->addCheck("Setting Data: {$key}", $exists ? '✅' : '⚠️', $exists ? 'Found' : 'Missing Row'); if (! $exists) { $this->warnings++; } } } private function checkMigrationsStatus(): void { try { if (! Schema::hasTable('migrations')) { $this->addCheck('Migrations Table', '❌', 'Missing'); return; } $ran = DB::table('migrations')->pluck('migration')->toArray(); $files = File::files(base_path('database/migrations')); $pending = 0; foreach ($files as $f) { $name = str_replace('.php', '', $f->getFilename()); if (! in_array($name, $ran)) { $pending++; } } $this->addCheck('Migrations Status', $pending === 0 ? '✅' : '❌', $pending === 0 ? 'Up to date' : "{$pending} pending"); if ($pending > 0) { $this->errors++; } } catch (\Exception $e) { $this->addCheck('Migrations Status', '❌', 'Failed: ' . $e->getMessage()); $this->errors++; } } private function checkSeedersStatus(array $discoveredSeeders = []): void { if ($discoveredSeeders === []) { $discoveredSeeders = $this->discoverAllSeeders(); } $missingSeeders = []; $optionalTables = [ 'radio_settings', 'radio_song_votes', 'radio_contests', 'radio_song_requests', 'radio_giveaways', 'radio_listener_points', 'radio_tests', 'testings', ]; foreach ($discoveredSeeders as $seeder => $config) { $table = $config['table']; if (empty($table)) { continue; } if (! Schema::hasTable($table)) { if (in_array($table, $optionalTables)) { $this->addCheck("Seeder: {$seeder}", '✅', 'Optional (skip)'); } else { $this->addCheck("Seeder: {$seeder}", '❌', "Table {$table} missing"); $missingSeeders[] = $config['class']; $this->errors++; } continue; } $count = DB::table($table)->count(); if ($count > 0) { if ($table === 'website_articles') { $this->addCheck("Seeder: {$seeder}", '✅', 'Er is al nieuws gevonden, ga verder'); } else { $this->addCheck("Seeder: {$seeder}", '✅', "{$count} rows"); } } elseif (in_array($table, $optionalTables)) { $this->addCheck("Seeder: {$seeder}", '✅', 'Optional (skip)'); } else { $this->addCheck("Seeder: {$seeder}", '⚠️', 'No data'); $this->warnings++; $missingSeeders[] = $config['class']; } } $this->missingSeeders = $missingSeeders; } private function checkRequiredTables(): void { $tables = ['users', 'website_settings', 'permissions', 'articles', 'radio_settings', 'categories', 'rooms', 'photos', 'hotel_users', 'bans', 'items']; foreach ($tables as $t) { if (! Schema::hasTable($t)) { $this->addCheck("Table: {$t}", '❌', 'Missing'); } } } private function checkRequiredSettings(): void { if (! Schema::hasTable('website_settings')) { return; } $keys = ['hotel_name', 'hotel_url', 'rcon_ip', 'recaptcha_site_key']; foreach ($keys as $k) { $exists = DB::table('website_settings')->where('key', $k)->exists(); $this->addCheck("Setting: {$k}", $exists ? '✅' : '⚠️', $exists ? 'Set' : 'Missing'); } } private function checkRadioTables(): void { $tables = ['radio_song_votes', 'radio_contests', 'radio_giveaways', 'radio_listener_points', 'radio_song_requests', 'radio_ranks', 'radio_shouts', 'radio_schedules', 'radio_banners', 'radio_history', 'radio_applications']; foreach ($tables as $t) { if (! Schema::hasTable($t)) { $this->addCheck("Table: {$t}", '⚠️', 'Missing'); } } } private function checkAdminUser(): void { if (! Schema::hasTable('users')) { return; } try { $hasAdmin = DB::table('users')->where('rank', '>=', 7)->exists(); $this->addCheck('Admin User', $hasAdmin ? '✅' : '❌', $hasAdmin ? 'Found' : 'Missing'); if (! $hasAdmin) { $this->errors++; } } catch (\Exception) { $this->addCheck('Admin User', '❌', 'Check Failed'); } } private function checkPHPExtensions(): void { $exts = ['curl', 'mbstring', 'pdo_mysql', 'xml', 'bcmath', 'openssl', 'gd', 'zip', 'intl', 'redis', 'fileinfo']; foreach ($exts as $e) { $ok = extension_loaded($e); $this->addCheck("PHP Ext: {$e}", $ok ? '✅' : '❌', $ok ? 'OK' : 'Missing'); } } private function checkPHPConfiguration(): void { $limits = ['memory_limit' => 256, 'upload_max_filesize' => 32, 'post_max_size' => 32]; foreach ($limits as $key => $target) { $val = ini_get($key); $ok = ((int) $val >= $target || $val == -1); $this->addCheck("PHP.ini: {$key}", $ok ? '✅' : '⚠️', "Val: {$val}"); } } private function checkForPhpUpdates(): void { $this->addCheck('PHP Version', '✅', PHP_VERSION); } private function checkCacheOptimization(): void { $ok = File::exists(base_path('bootstrap/cache/config.php')); $this->addCheck('Config Cache', $ok ? '✅' : '⚠️', $ok ? 'Optimized' : 'Not cached'); } private function checkSessionConfiguration(): void { $driver = config('session.driver'); $this->addCheck('Session Driver', $driver !== 'file' ? '✅' : '⚠️', $driver); } private function checkFilamentPages(): void { $files = [ 'app/Filament/Pages/Monitoring/AlertSettings.php' => 'AlertSettings page', 'resources/views/filament/pages/monitoring/alert-settings.blade.php' => 'AlertSettings view', 'app/Filament/Widgets/UpdateCheckerWidget.php' => 'UpdateCheckerWidget', 'resources/views/filament/widgets/update-checker.blade.php' => 'UpdateChecker view', 'app/Services/EmulatorUpdateService.php' => 'EmulatorUpdateService', 'app/Services/NitroUpdateService.php' => 'NitroUpdateService', 'app/Services/RconService.php' => 'RconService', ]; $missing = []; foreach ($files as $path => $label) { if (! file_exists(base_path($path))) { $missing[] = $label; } } if ($missing === []) { $this->addCheck('Filament Pages & Widgets', '✅', 'All files present'); } else { $this->addCheck('Filament Pages & Widgets', '❌', 'Missing: ' . implode(', ', $missing)); $this->errors++; } // Check emulator service $serviceName = setting('emulator_service_name', 'emulator'); $result = $this->runSystemCommand("systemctl is-active {$serviceName} 2>/dev/null || echo 'inactive'"); if (trim((string) $result['output']) === 'active') { $this->addCheck('Emulator Service', '✅', $serviceName . ' is running'); } else { $this->addCheck('Emulator Service', '⚠️', $serviceName . ' is not running'); $this->warnings++; } // Check emulator JAR $jarPath = setting('emulator_jar_path', '/var/www/Emulator'); $jarFiles = glob("{$jarPath}/*.jar"); if ($jarFiles !== [] && $jarFiles !== false) { $this->addCheck('Emulator JAR', '✅', basename($jarFiles[0])); } else { $this->addCheck('Emulator JAR', '⚠️', 'No JAR found in ' . $jarPath); $this->warnings++; } } private function checkWebServerConfiguration(): void { // Detect Cloudflare $this->checkCloudflare(); // Detect NPM (Nginx Proxy Manager) $this->checkNpmProxy(); if ($this->isApache()) { $path = public_path('.htaccess'); $ok = File::exists($path) && str_contains(File::get($path), 'RewriteEngine On'); $this->addCheck('Apache Rewrite', $ok ? '✅' : '⚠️', $ok ? 'OK' : 'Check .htaccess'); } if ($this->isIIS()) { $ok = File::exists(public_path('web.config')); $this->addCheck('IIS Routing', $ok ? '✅' : '❌', $ok ? 'OK' : 'Missing web.config'); } if ($this->isNginx()) { $this->addCheck('Nginx Config', '⚠️', 'Check try_files manually'); } } private function checkCloudflare(): void { $headers = [ 'HTTP_CF_CONNECTING_IP', 'HTTP_CF_IPCOUNTRY', 'HTTP_CF_RAY', 'HTTP_CF_VISITOR', 'HTTP_CF_REQUEST_ID', ]; $detected = false; $foundHeaders = []; // Check HTTP headers (when running via web) foreach ($headers as $header) { if (isset($_SERVER[$header])) { $detected = true; $foundHeaders[] = $header; } } $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? ''; if (str_contains((string) $serverSoftware, 'cloudflare')) { $detected = true; $foundHeaders[] = 'SERVER: cloudflare'; } // Check via DNS if not detected via headers (CLI mode) if (! $detected) { $siteUrl = setting('site_url', config('app.url', '')); if (! empty($siteUrl)) { $domain = parse_url((string) $siteUrl, PHP_URL_HOST); if (! empty($domain)) { // Check nameservers $nsResult = $this->runSystemCommand("dig NS {$domain} +short 2>/dev/null | grep -i cloudflare"); if (! in_array(trim((string) $nsResult['output']), ['', '0'], true)) { $detected = true; $foundHeaders[] = 'NS: cloudflare'; } // Check if proxied (CF IP range) if (! $detected) { $ipResult = $this->runSystemCommand("dig {$domain} +short 2>/dev/null | head -1"); $ip = trim($ipResult['output'] ?? ''); if ($ip !== '' && $ip !== '0') { // Cloudflare IP ranges (simplified check) $cfRanges = ['104.16.', '104.17.', '104.18.', '104.19.', '104.20.', '104.21.', '104.22.', '104.23.', '104.24.', '104.25.', '104.26.', '104.27.', '104.28.', '104.29.', '104.30.', '104.31.', '172.64.', '172.65.', '172.66.', '172.67.', '172.68.', '172.69.', '172.70.', '172.71.', '103.21.244.', '103.22.200.', '103.31.4.', '141.101.64.', '108.162.192.', '190.93.240.', '188.114.96.', '197.234.240.', '198.41.128.', '162.158.', '131.0.72.']; foreach ($cfRanges as $range) { if (str_starts_with($ip, $range)) { $detected = true; $foundHeaders[] = 'IP: Cloudflare range'; break; } } } } } } } if ($detected) { $this->addCheck('Cloudflare', '✅', 'Active (' . count($foundHeaders) . ' headers)'); } else { $this->addCheck('Cloudflare', '⚠️', 'Not detected (CLI mode)'); } // Check custom port $this->checkCustomPort(); } private function checkCustomPort(): void { $port = (int) ($_SERVER['SERVER_PORT'] ?? 80); $host = (string) ($_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? ''); // Extract port from host header if (preg_match('/:(\d+)$/', $host, $matches)) { $port = (int) $matches[1]; } $customPorts = [8080, 3000, 81, 8000, 8888, 4433, 8443]; if ($port === 80 || $port === 443) { $this->addCheck('Server Port', '✅', 'Standard (' . $port . ')'); } elseif (in_array($port, $customPorts)) { $this->addCheck('Server Port', '⚠️', 'Custom port: ' . $port . ' (check proxy)'); } else { $this->addCheck('Server Port', '⚠️', 'Non-standard port: ' . $port); } // Check if behind proxy if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) || isset($_SERVER['HTTP_X_REAL_IP'])) { $this->addCheck('Behind Proxy', '✅', 'Detected (check trust proxies)'); } else { $this->addCheck('Behind Proxy', '✅', 'Not required'); } // Check WebSocket $this->checkWebSocket(); // Check Client Settings $this->checkClientSettings(); } private function checkWebSocket(): void { $wsPort = (int) config('habbo.websocket.port', env('WEBSOCKET_PORT', 3001)); $wsHost = (string) config('habbo.websocket.host', env('WEBSOCKET_HOST', '127.0.0.1')); $wsEnabled = (bool) config('habbo.websocket.enabled', env('WEBSOCKET_ENABLED', false)); if ($wsEnabled) { $this->addCheck('WebSocket', '✅', "Enabled ({$wsHost}:{$wsPort})"); // Check if port is open $socket = @fsockopen($wsHost, $wsPort, $errno, $errstr, 2); if ($socket) { $this->addCheck('WebSocket Port', '✅', "Port {$wsPort} is open"); fclose($socket); } else { $this->addCheck('WebSocket Port', '❌', "Port {$wsPort} is closed - check emulator"); } } else { $this->addCheck('WebSocket', '✅', 'Not required'); } // Check Nitro/Client WebSocket $nitroWsUrl = config('habbo.client.nitro_websocket_url'); if (! empty($nitroWsUrl) && is_string($nitroWsUrl)) { $this->addCheck('Nitro WS URL', '✅', $nitroWsUrl); } else { $this->addCheck('Nitro WS URL', '✅', 'Not required'); } } private function checkClientSettings(): void { // Check client SWF path $flashEnabled = (bool) config('habbo.client.flash_enabled', env('FLASH_CLIENT_ENABLED', false)); if ($flashEnabled) { $swfPath = config('habbo.flash.habbo_swf', 'Habbo.swf'); $swfBasePath = config('habbo.flash.swf_base_path'); if (! empty($swfBasePath) && is_string($swfBasePath)) { $this->addCheck('Flash SWF', '✅', $swfBasePath . '/' . $swfPath); } else { $this->addCheck('Flash SWF', '⚠️', 'SWF path not configured'); } } // Check Nitro client path $nitroPath = config('habbo.client.nitro_path', '/client/html5/nitro-client'); $nitroFullPath = public_path($nitroPath); if (is_dir($nitroFullPath)) { $this->addCheck('Nitro Client', '✅', $nitroPath . ' exists'); } else { $this->addCheck('Nitro Client', '⚠️', $nitroPath . ' not found'); } // Check emulator connection $emulatorIp = (string) config('habbo.flash.host', env('EMULATOR_IP', '127.0.0.1')); $emulatorPort = (int) config('habbo.flash.port', env('EMULATOR_PORT', 3000)); $socket = @fsockopen($emulatorIp, $emulatorPort, $errno, $errstr, 2); if ($socket) { $this->addCheck('Emulator', '✅', "{$emulatorIp}:{$emulatorPort} online"); fclose($socket); } else { $this->addCheck('Emulator', '❌', "{$emulatorIp}:{$emulatorPort} offline"); } // Check external texts/variables $externalTexts = config('habbo.flash.external_texts'); $externalVars = config('habbo.flash.external_variables'); if (! empty($externalTexts) && is_string($externalTexts)) { $this->addCheck('External Texts', '✅', 'Configured'); } else { $this->addCheck('External Texts', '✅', 'Not required'); } if (! empty($externalVars) && is_string($externalVars)) { $this->addCheck('External Variables', '✅', 'Configured'); } else { $this->addCheck('External Variables', '✅', 'Not required'); } // Check client URL in database try { $clientUrl = DB::table('website_settings')->where('key', 'client_url')->first(); if ($clientUrl && isset($clientUrl->value)) { $this->addCheck('Client URL Setting', '✅', (string) $clientUrl->value); } else { $this->addCheck('Client URL Setting', '⚠️', 'Not set in database'); } } catch (\Exception) { // Ignore } } private function checkNpmProxy(): void { $indicators = []; // Check for NPM default ports $host = (string) ($_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? ''); if (preg_match('/(:81|:443|:80$)/', $host) && str_contains($host, ':81')) { $indicators[] = 'Port 81 (NPM admin)'; } // Check SERVER headers for NPM $server = (string) ($_SERVER['SERVER_SOFTWARE'] ?? ''); if ((str_contains($server, 'nginx') || str_contains($server, 'NPM')) && (! str_contains($server, 'Apache') && ! str_contains($server, 'cloudflare'))) { $indicators[] = 'Nginx detected'; } // Check for NPM proxy headers $proxyHeaders = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED_PROTO', 'HTTP_X_REAL_IP', 'HTTP_X_NPM', ]; foreach ($proxyHeaders as $header) { if (isset($_SERVER[$header])) { $indicators[] = $header; } } // Check response headers if available if ($indicators !== []) { $this->addCheck('NPM Proxy', '✅', 'Detected - ' . implode(', ', $indicators)); } else { $this->addCheck('NPM Proxy', '✅', 'Not required'); } } private function checkSSLCertificates(): void { $appUrl = config('app.url'); $isHttps = str_starts_with((string) $appUrl, 'https'); $this->addCheck('SSL Status', $isHttps ? '✅' : '⚠️', $isHttps ? 'HTTPS' : 'HTTP'); if (! $isHttps) { $this->errors++; } if ($isHttps) { $this->checkMixedHttps($appUrl); } } private function checkMixedHttps(string $appUrl): void { $baseUrl = rtrim($appUrl, '/'); $issues = []; $httpUrl = str_replace('https://', 'http://', $baseUrl); $httpsCheckUrl = $baseUrl . '/'; $httpReachable = $this->checkUrlReachable($httpUrl); $httpsReachable = $this->checkUrlReachable($httpsCheckUrl); if ($httpReachable && $httpsReachable) { $issues[] = 'Both HTTP and HTTPS accessible - configure redirect'; } if ($httpReachable) { $redirectCheck = $this->checkHttpRedirect($httpUrl); if (! $redirectCheck) { $issues[] = 'HTTP is not redirecting to HTTPS'; } } $forceHttps = config('habbo.site.force_https'); if (! $forceHttps) { $issues[] = 'FORCE_HTTPS not enabled (set FORCE_HTTPS=true in .env)'; } if ($this->isWindows()) { $this->checkWindowsHttpsConfig($issues); } else { $this->checkLinuxHttpsConfig($issues); } if ($issues === []) { $this->addCheck('Mixed HTTPS', '✅', 'Secure configuration'); } else { foreach ($issues as $issue) { $this->addCheck('Mixed HTTPS', '⚠️', $issue); $this->errors++; } } } private function checkWindowsHttpsConfig(array &$issues): void { $envPath = base_path('.env'); if (File::exists($envPath)) { $envContent = File::get($envPath); if (preg_match('/^APP_URL=http:\/\//m', $envContent)) { $issues[] = 'Windows: APP_URL uses HTTP instead of HTTPS'; } if (! preg_match('/^FORCE_HTTPS=true/m', $envContent)) { $issues[] = 'Windows: FORCE_HTTPS not set in .env'; } } $this->checkIISConfig($issues); } private function checkLinuxHttpsConfig(array &$issues): void { $envPath = base_path('.env'); if (File::exists($envPath)) { $envContent = File::get($envPath); if (preg_match('/^APP_URL=http:\/\//m', $envContent)) { $issues[] = 'Linux: APP_URL uses HTTP instead of HTTPS'; } if (! preg_match('/^FORCE_HTTPS=true/m', $envContent)) { $issues[] = 'Linux: FORCE_HTTPS not set in .env'; } } if ($this->isNginx()) { $this->checkNginxSslConfig($issues); } elseif ($this->isApache()) { $this->checkApacheSslConfig($issues); } } private function checkIISConfig(array &$issues): void { $webConfig = base_path('web.config'); if (File::exists($webConfig)) { $content = File::get($webConfig); if (! str_contains($content, 'httpsRedirect') && ! str_contains($content, 'rewrite')) { $issues[] = 'IIS: No HTTPS redirect rules found in web.config'; } } else { $issues[] = 'IIS: web.config not found - create for HTTPS redirect'; } } private function checkNginxSslConfig(array &$issues): void { $domain = parse_url((string) config('app.url'), PHP_URL_HOST); $nginxPaths = [ '/etc/nginx/sites-available/' . $domain, '/etc/nginx/sites-enabled/' . $domain, '/etc/nginx/conf.d/' . $domain . '.conf', ]; $found = false; foreach ($nginxPaths as $path) { if (File::exists($path)) { $found = true; $content = File::get($path); $hasHttps = str_contains($content, 'listen 443') || str_contains($content, 'listen 443 ssl'); $hasRedirect = str_contains($content, 'return 301') || str_contains($content, 'return 302'); if (! $hasHttps) { $issues[] = 'Nginx: No SSL/TLS configuration found'; } if (! $hasRedirect) { $issues[] = 'Nginx: HTTP to HTTPS redirect not configured'; } break; } } if (! $found) { $issues[] = 'Nginx: Config file not found in standard locations'; } } private function checkApacheSslConfig(array &$issues): void { $htaccess = public_path('.htaccess'); if (File::exists($htaccess)) { $content = File::get($htaccess); if (! str_contains($content, 'HTTPS') && ! str_contains($content, 'httpsRedirect')) { $issues[] = 'Apache: No HTTPS redirect in .htaccess'; } } $apacheConfigs = glob('/etc/apache2/sites-*/*'); $foundSsl = false; foreach ($apacheConfigs as $config) { if (is_file($config) && is_readable($config)) { $content = File::get($config); if (str_contains($content, 'SSLEngine') || str_contains($content, '443')) { $foundSsl = true; break; } } } if (! $foundSsl) { $issues[] = 'Apache: No SSL virtual host found'; } } private function checkHttpRedirect(string $httpUrl): bool { try { $context = stream_context_create([ 'http' => [ 'method' => 'HEAD', 'timeout' => 5, 'follow_location' => 0, 'ignore_errors' => true, ], ]); $response = @file_get_contents($httpUrl, false, $context); foreach ($http_response_header as $header) { if (stripos($header, 'Location:') !== false || stripos($header, 'location:') !== false) { $location = trim(substr($header, strpos($header, ':') + 1)); return stripos($location, 'https://') !== false; } } return false; } catch (\Exception) { return false; } } private function checkUrlReachable(string $url): bool { if ($this->checkUrlWithCurl($url)) { return true; } if ($this->checkUrlWithFileGetContents($url)) { return true; } return $this->checkUrlWithSocket($url); } private function checkUrlWithCurl(string $url): bool { if (! function_exists('curl_init')) { return false; } try { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_NOBODY => true, CURLOPT_TIMEOUT => 5, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_FOLLOWLOCATION => false, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); return in_array($httpCode, [200, 301, 302, 303, 307, 308]); } catch (\Exception) { return false; } } private function checkUrlWithFileGetContents(string $url): bool { try { $context = stream_context_create([ 'http' => [ 'method' => 'HEAD', 'timeout' => 5, 'ignore_errors' => true, ], 'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false, ], ]); $response = @file_get_contents($url, false, $context); return $response !== false; } catch (\Exception) { return false; } } private function checkUrlWithSocket(string $url): bool { try { $parsed = parse_url($url); $host = $parsed['host'] ?? 'localhost'; $port = $parsed['port'] ?? (($parsed['scheme'] ?? 'http') === 'https' ? 443 : 80); $path = $parsed['path'] ?? '/'; $socket = @fsockopen( ($port === 443 ? 'ssl://' : '') . $host, $port, $errno, $errstr, 5, ); if ($socket) { $request = "HEAD {$path} HTTP/1.1\r\n"; $request .= "Host: {$host}\r\n"; $request .= "Connection: close\r\n\r\n"; fwrite($socket, $request); $response = fread($socket, 1024); fclose($socket); return stripos($response, 'HTTP/') !== false; } return false; } catch (\Exception) { return false; } } private function checkFilePermissions(): void { $paths = ['storage', 'bootstrap/cache', 'public/uploads']; foreach ($paths as $p) { $full = base_path($p); if (! is_dir($full)) { $this->addCheck("Path: {$p}", '❌', 'Missing'); // Create missing directories if ($this->confirm("Create missing directory: {$p}?", true)) { @mkdir($full, 0755, true); $this->info(" ✅ Created: {$p}"); } continue; } $writable = is_writable($full); $this->addCheck("Writable: {$p}", $writable ? '✅' : '❌', $writable ? 'Yes' : 'No'); } } private function checkFirewallPorts(): void { $ports = [80 => 'HTTP', 443 => 'HTTPS', 3306 => 'MySQL', 3000 => 'Nitro']; foreach (array_keys($ports) as $port) { $connection = @fsockopen('127.0.0.1', $port, $errno, $errstr, 0.05); if ($connection) { $this->addCheck("Port {$port}", '✅', 'Open'); fclose($connection); } else { $this->addCheck("Port {$port}", '⚠️', 'Closed'); } } } private function checkStorageSymlink(): void { $ok = File::exists(public_path('storage')); $this->addCheck('Storage Link', $ok ? '✅' : '❌', $ok ? 'OK' : 'Missing'); } private function checkAssetsStatus(): void { $ok = File::exists(public_path('assets/css/app.css')) || File::exists(public_path('build/assets')); $this->addCheck('Frontend Assets', $ok ? '✅' : '⚠️', $ok ? 'Built' : 'Not found'); } private function checkFrontendManifest(): void { $manifestPath = public_path('build/manifest.json'); if (File::exists($manifestPath)) { $this->addCheck('Vite Manifest', '✅', 'Found'); } else { $this->addCheck('Vite Manifest', '❌', 'Missing'); $this->errors++; } } private function checkHttpErrors(): void { // 400 Bad Request $this->check400Error(); // 401 Unauthorized $this->check401Error(); // 403 Forbidden $this->check403Error(); // 404 Not Found $this->check404Error(); // 419 Page Expired (CSRF) $this->check419Error(); // 429 Too Many Requests $this->check429Error(); // 500 Internal Server Error $this->check500Error(); // 502 Bad Gateway $this->check502Error(); // 503 Service Unavailable $this->check503Error(); // 504 Gateway Timeout $this->check504Error(); } private function check400Error(): void { $issues = []; // Check for cookie issues if (config('session.driver') === 'file') { $sessionPath = storage_path('framework/sessions'); if (! is_writable($sessionPath)) { $issues[] = 'Sessions directory not writable'; } } // Check for large POST data $postMax = ini_get('post_max_size'); $uploadMax = ini_get('upload_max_filesize'); if ((int) $postMax < 32 || (int) $uploadMax < 32) { $issues[] = 'POST/upload limits too low'; } if ($issues !== []) { $this->addCheck('HTTP 400 (Bad Request)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 400 (Bad Request)', '✅', 'OK'); } } private function check401Error(): void { $issues = []; // Check .env authentication if (config('app.env') === 'production' && empty(config('app.key'))) { $issues[] = 'APP_KEY missing'; } if ($issues !== []) { $this->addCheck('HTTP 401 (Unauthorized)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 401 (Unauthorized)', '✅', 'OK'); } } private function check403Error(): void { $issues = []; // Check public/index.php exists if (! File::exists(public_path('index.php'))) { $issues[] = 'index.php missing'; } // Check .htaccess (Apache) if ($this->isApache() && ! File::exists(public_path('.htaccess'))) { $issues[] = '.htaccess missing'; } // Check storage permissions $storagePath = storage_path(); if (! is_writable($storagePath)) { $issues[] = 'Storage not writable'; } // Check public permissions $publicPath = public_path(); if (! is_readable($publicPath)) { $issues[] = 'Public not readable'; } // Check CSRF token mismatch - common issue if (config('session.driver') !== 'file') { $sessionDriver = config('session.driver'); if ($sessionDriver === 'redis' && ! extension_loaded('redis')) { $issues[] = 'Redis extension missing'; } } if ($issues !== []) { $this->addCheck('HTTP 403 (Forbidden)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 403 (Forbidden)', '✅', 'OK'); } } private function check404Error(): void { $issues = []; // Check routes cache if (File::exists(base_path('bootstrap/cache/routes.php')) && config('cache.default') !== 'array') { // Routes might be cached but stale } // Check public directory if (! is_dir(public_path())) { $issues[] = 'Public directory missing'; } // Check storage symlink if (! File::exists(public_path('storage'))) { $issues[] = 'Storage symlink missing'; } // Check .htaccess for proper rewrite rules if ($this->isApache()) { $htaccess = public_path('.htaccess'); if (File::exists($htaccess)) { $content = File::get($htaccess); if (! str_contains($content, 'RewriteRule')) { $issues[] = 'Missing rewrite rules'; } } } // Check nginx config if ($this->isNginx()) { $nginxConfig = base_path('atom-nginx.conf'); if (! File::exists($nginxConfig)) { $issues[] = 'Nginx config not generated'; } } if ($issues !== []) { $this->addCheck('HTTP 404 (Not Found)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 404 (Not Found)', '✅', 'OK'); } } private function check419Error(): void { $issues = []; // CSRF token expired - usually session related if (config('session.lifetime') < 60) { $issues[] = 'Session lifetime too short'; } if (config('session.driver') === 'file') { $sessionPath = storage_path('framework/sessions'); if (! is_writable($sessionPath)) { $issues[] = 'Sessions not writable'; } } if ($issues !== []) { $this->addCheck('HTTP 419 (Page Expired)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 419 (Page Expired)', '✅', 'OK'); } } private function check429Error(): void { $issues = []; // Rate limiting issues if (config('cache.default') === 'file') { $issues[] = 'File cache for rate limiting (slow)'; } if ($issues !== []) { $this->addCheck('HTTP 429 (Too Many Requests)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 429 (Too Many Requests)', '✅', 'OK'); } } private function check500Error(): void { $issues = []; // Check PHP errors $errorLog = ini_get('error_log'); if (! in_array($errorLog, ['', '0', false], true) && File::exists($errorLog)) { $recentErrors = $this->getRecentErrors($errorLog, 10); if (count($recentErrors) > 5) { $issues[] = 'Many PHP errors in log'; } } // Check debug mode if (! config('app.debug')) { // Debug is off - 500 errors won't show details } // Check composer autoload if (! File::exists(base_path('vendor/autoload.php'))) { $issues[] = 'Composer dependencies not installed'; } // Check bootstrap/cache $bootstrapCache = base_path('bootstrap/cache'); if (! is_writable($bootstrapCache)) { $issues[] = 'Bootstrap cache not writable'; } // Check storage permissions if (! is_writable(storage_path())) { $issues[] = 'Storage not writable'; } // Check database connection try { DB::connection()->getPdo(); } catch (\Exception) { $issues[] = 'Database connection failed'; } if ($issues !== []) { $this->addCheck('HTTP 500 (Internal Error)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 500 (Internal Error)', '✅', 'OK'); } } private function check502Error(): void { $issues = []; $isWindows = DIRECTORY_SEPARATOR === '\\'; // Check PHP-FPM (Linux only) if (! $isWindows && ($this->isNginx() || $this->isApache())) { $phpFpm = shell_exec('systemctl status php*-fpm 2>/dev/null | head -1'); if (in_array($phpFpm, ['', '0', false, null], true)) { $issues[] = 'PHP-FPM may not be running'; } } // Check socket files if (! $isWindows) { $phpVersion = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; $socketPaths = [ "/var/run/php/php{$phpVersion}-fpm.sock", '/var/run/php-fpm.sock', "/run/php/php{$phpVersion}-fpm.sock", ]; $socketExists = array_any($socketPaths, fn ($path) => File::exists($path)); if (! $socketExists) { $issues[] = 'PHP-FPM socket not found'; } } else { // On Windows with nginx, check PHP-CGI $phpCgiRunning = shell_exec('tasklist 2>nul | findstr php-cgi'); if (in_array($phpCgiRunning, ['', '0', false, null], true)) { $issues[] = 'PHP-CGI may not be running'; } } if ($issues !== []) { $this->addCheck('HTTP 502 (Bad Gateway)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 502 (Bad Gateway)', '✅', 'OK'); } } private function check503Error(): void { $issues = []; // Check maintenance mode $maintenanceFile = storage_path('framework/maintenance.php'); if (File::exists($maintenanceFile)) { $issues[] = 'Maintenance mode enabled'; } // Check queue workers if (config('queue.default') !== 'sync') { $result = $this->runSystemCommand('pgrep -f "queue:work"'); if (in_array(trim((string) $result['output']), ['', '0'], true)) { $issues[] = 'Queue workers not running'; } } // Check database connection try { DB::connection()->getPdo(); } catch (\Exception) { $issues[] = 'Database unavailable'; } // Check Redis if (config('cache.default') === 'redis') { try { Redis::connection()->ping(); } catch (\Exception) { $issues[] = 'Redis unavailable'; } } if ($issues !== []) { $this->addCheck('HTTP 503 (Unavailable)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 503 (Unavailable)', '✅', 'OK'); } } private function check504Error(): void { $issues = []; // Check PHP execution time $maxExecutionTime = ini_get('max_execution_time'); if ($maxExecutionTime < 60 && $maxExecutionTime != 0) { $issues[] = 'Max execution time too low'; } // Check database query time try { DB::connection()->getPdo(); } catch (\Exception) { $issues[] = 'Database timeout'; } // Check Redis timeout if (config('cache.default') === 'redis') { try { Redis::connection()->ping(); } catch (\Exception) { $issues[] = 'Redis timeout'; } } // Check PHP-FPM timeout if ($this->isNginx()) { $nginxConfig = base_path('atom-nginx.conf'); if (File::exists($nginxConfig)) { $content = File::get($nginxConfig); if (! str_contains($content, 'fastcgi_read_timeout')) { $issues[] = 'Nginx fastcgi timeout not set'; } } } if ($issues !== []) { $this->addCheck('HTTP 504 (Gateway Timeout)', '⚠️', implode(', ', $issues)); } else { $this->addCheck('HTTP 504 (Gateway Timeout)', '✅', 'OK'); } } private function getRecentErrors(string $logFile, int $lines = 10): array { if (! File::exists($logFile)) { return []; } $content = File::get($logFile); $arr = explode("\n", $content); return array_slice($arr, -$lines); } private function checkRedisConnection(): void { if (! extension_loaded('redis')) { return; } $redisClass = '\Redis'; if (class_exists($redisClass)) { try { $redis = new $redisClass; $host = config('database.redis.default.host', '127.0.0.1'); $port = config('database.redis.default.port', 6379); $redis->connect($host, $port); if ($pass = config('database.redis.default.password')) { $redis->auth($pass); } if ($redis->ping()) { $this->addCheck('Redis', '✅', 'Connected'); } else { $this->addCheck('Redis', '❌', 'Ping Failed'); $this->errors++; } } catch (\Exception $e) { $this->addCheck('Redis', '❌', 'Failed: ' . $e->getMessage()); $this->errors++; } } } private function checkCronJobsStatus(): void { if ($this->isWindows()) { return; } $cronResult = $this->runSystemCommand('crontab -l'); $cron = $cronResult['output']; $ok = str_contains((string) $cron, 'schedule:run'); $this->addCheck('Laravel Cron', $ok ? '✅' : '⚠️', $ok ? 'Active' : 'Missing'); } private function checkQueueWorkersStatus(): void { if ($this->isWindows()) { $result = $this->runSystemCommand('tasklist /FI "IMAGENAME eq php.exe" /FO CSV'); $ok = array_filter(explode("\n", (string) $result['output']), fn ($line) => str_contains((string) $line, 'queue:work')) !== []; } else { $result = $this->runSystemCommand('pgrep -f "queue:work"'); $ok = ! in_array(trim((string) $result['output']), ['', '0'], true); } $this->addCheck('Queue Worker', $ok ? '✅' : '⚠️', $ok ? 'Running' : 'Stopped'); } private function checkSupervisorConfig(): void { if ($this->isLinux()) { $path = '/etc/supervisor/conf.d/atom-worker.conf'; $exists = File::exists($path) || File::exists('/etc/supervisor.d/atom.ini'); $this->addCheck('Supervisor', $exists ? '✅' : '⚠️', $exists ? 'Found' : 'Missing'); if (! $exists) { $this->warnings++; } } } private function checkRequiredFiles(): void { if (! File::exists(public_path('index.php'))) { $this->addCheck('public/index.php', '❌', 'Missing'); } } // --- REPAIRS --- private function createAdminUser(?string $username = null, ?string $email = null, ?string $password = null): void { if ($username === null) { $username = $this->ask('Enter Admin Username', 'Admin'); } if ($email === null) { $email = $this->ask('Enter Admin Email', 'admin@example.com'); } if ($password === null) { $password = $this->secret('Enter Admin Password'); } if (empty($password)) { $this->error('❌ Password is required.'); return; } try { $existingAdmin = DB::table('users')->where('username', $username)->orWhere('rank', '>=', 7)->first(); if ($existingAdmin) { $this->info("✅ Admin user already exists ({$existingAdmin->username})"); return; } DB::table('users')->insert([ 'username' => $username, 'mail' => $email, 'password' => Hash::make($password), 'rank' => 7, 'look' => 'hr-115-42.hd-190-1.ch-215-66.lg-270-66.sh-300-66', 'motto' => 'Atom CMS Admin', 'ip_register' => '127.0.0.1', 'ip_current' => '127.0.0.1', 'account_created' => time(), 'last_login' => time(), ]); $this->info("✅ Admin user '{$username}' created."); } catch (\Exception $e) { $this->error('❌ Failed: ' . $e->getMessage()); } } // --- REPAIRS --- private function buildFrontendAssets(): void { if ($this->skipBuild) { $this->info('⏭️ Asset building skipped'); return; } $isWindows = DIRECTORY_SEPARATOR === '\\'; if ($isWindows) { $this->info('⏭️ Asset building skipped on Windows'); return; } $manager = 'npm'; $cmd = 'npm install && npm run build'; if (File::exists(base_path('yarn.lock'))) { $manager = 'yarn'; $cmd = 'yarn && yarn build'; } elseif (File::exists(base_path('pnpm-lock.yaml'))) { $manager = 'pnpm'; $cmd = 'pnpm install && pnpm build'; } $this->info("🚀 Building assets with {$manager}..."); exec("{$cmd} 2>&1", $output, $exitCode); if ($exitCode === 0) { $this->info('✅ Assets built.'); } else { $this->warn('⚠️ Build failed - skipped'); } } private function generateSupervisorConfig(): void { if ($this->isWindows()) { $this->generateWindowsQueueService(); return; } $path = base_path('atom-worker.conf'); $user = $this->webUser; $root = base_path(); $content = "[program:atom-worker] process_name=%(program_name)s_%(process_num)02d command=php {$root}/artisan queue:work --sleep=3 --tries=3 --max-time=3600 autostart=true autorestart=true stopasgroup=true killasgroup=true user={$user} numprocs=2 redirect_stderr=true stdout_logfile={$root}/storage/logs/worker.log "; File::put($path, $content); $supervisorPath = '/etc/supervisor/conf.d/atom-worker.conf'; $isRoot = function_exists('posix_getuid') && posix_getuid() === 0; if ($this->isWindows()) { $this->info('⏭️ Supervisor config not needed on Windows'); return; } if ($isRoot) { @copy($path, $supervisorPath); $this->runSystemCommand('sudo supervisorctl reread', true); $this->runSystemCommand('sudo supervisorctl update', true); $this->runSystemCommand('sudo supervisorctl start atom-worker:*', true); $this->info("✅ Generated and installed 'atom-worker.conf' to {$supervisorPath}"); } else { @copy($path, $supervisorPath); $this->info("✅ Generated 'atom-worker.conf'. Installed to {$supervisorPath}"); $this->info(' Run: sudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl start atom-worker:*'); } } private function generateWindowsQueueService(): void { $root = base_path(); $batPath = base_path('queue-worker.bat'); $content = "@echo off cd /d \"{$root}\" :start php artisan queue:work --sleep=3 --tries=3 --max-time=3600 goto start "; File::put($batPath, $content); $this->info("✅ Generated 'queue-worker.bat'"); $this->info(' To run as Windows Service:'); $this->info(' 1. Download NSSM: https://nssm.cc/download'); $this->info(" 2. Run: nssm install AtomWorker \"{$batPath}\""); $this->info(' 3. Or use Task Scheduler: taskschd.msc'); } private function repairApache(): void { $path = public_path('.htaccess'); $rules = "\n \n Options -MultiViews -Indexes\n \n\n RewriteEngine On\n RewriteCond %{REQUEST_FILENAME} !-d\n RewriteCond %{REQUEST_FILENAME} !-f\n RewriteRule ^ index.php [L]\n"; File::put($path, $rules); if ($this->isLinux()) { exec('sudo a2enmod rewrite 2>/dev/null'); } } private function repairIIS(): void { $path = public_path('web.config'); $data = ''; File::put($path, $data); } private function repairNginx(): void { $phpVer = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; $template = 'server { listen 80; server_name example.com; root ' . public_path() . "; add_header X-Frame-Options \"SAMEORIGIN\"; add_header X-Content-Type-Options \"nosniff\"; index index.php; charset utf-8; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php{$phpVer}-fpm.sock; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } }"; File::put(base_path('atom-nginx.conf'), $template); $this->info('✅ Template created: atom-nginx.conf'); } private function repairPermissions(): void { $isWindows = DIRECTORY_SEPARATOR === '\\'; if ($isWindows) { $paths = [base_path('storage'), base_path('bootstrap/cache'), public_path('uploads')]; foreach ($paths as $path) { if (! file_exists($path)) { @mkdir($path, 0755, true); $this->info(" ✅ Created: {$path}"); continue; } if (is_dir($path)) { if ($this->isXampp() || $this->isWamp() || $this->isNginx()) { @chmod($path, 0777); $this->recursiveChmod($path, 0777); } else { @chmod($path, 0755); $this->recursiveChmod($path, 0755); } } } $this->info('✅ Windows permissions fixed'); return; } // Linux permissions $paths = [base_path('storage'), base_path('bootstrap/cache'), public_path('uploads')]; foreach ($paths as $path) { if (! file_exists($path)) { @mkdir($path, 0755, true); $this->info(" ✅ Created: {$path}"); continue; } if (is_dir($path)) { @exec("sudo chown -R {$this->webUser}:{$this->webGroup} {$path} 2>/dev/null"); @exec("find {$path} -type d -exec chmod 775 {} \; 2>/dev/null"); @exec("find {$path} -type f -exec chmod 664 {} \; 2>/dev/null"); } } $this->info('✅ Linux permissions fixed'); } private function recursiveChmod(string $dir, int $mode): void { if (! is_dir($dir)) { return; } $files = @array_diff(scandir($dir), ['.', '..']); if ($files === false) { return; } foreach ($files as $file) { $path = $dir . DIRECTORY_SEPARATOR . $file; if (is_dir($path)) { @chmod($path, $mode); $this->recursiveChmod($path, $mode); } else { @chmod($path, $mode); } } } private function repairLinuxPermissions(): void { $this->repairPermissions(); } private function fixKnownCommonErrors(): void { $this->info('🔧 Fixing known common errors...'); // Clear all Laravel caches $this->call('cache:clear'); $this->call('view:clear'); $this->call('route:clear'); $this->call('config:clear'); $this->call('event:clear'); $this->call('clear-compiled'); $this->info('✅ All caches cleared'); // Fix storage permissions $this->repairPermissions(); $this->info('✅ Storage permissions fixed'); // Fix bootstrap/cache permissions $cachePath = base_path('bootstrap/cache'); if (is_dir($cachePath)) { if ($this->isWindows()) { @chmod($cachePath, 0777); } else { @chmod($cachePath, 0755); exec("sudo chown -R {$this->webUser}:{$this->webGroup} {$cachePath} 2>/dev/null"); } } // Fix sessions table if needed if (Schema::hasTable('sessions')) { try { DB::statement('OPTIMIZE TABLE sessions'); $this->info('✅ Sessions table optimized'); } catch (\Exception) { // Ignore } } // Ensure storage directories exist $directories = [ storage_path('app'), storage_path('app/public'), storage_path('framework/cache'), storage_path('framework/cache/data'), storage_path('framework/sessions'), storage_path('framework/views'), storage_path('logs'), ]; foreach ($directories as $dir) { if (! is_dir($dir)) { @mkdir($dir, 0755, true); } } $this->info('✅ Storage directories ensured'); // Fix radio tables - always run to ensure they exist $this->createRadioTables(); // Fix .env if needed if (! File::exists(base_path('.env'))) { File::copy(base_path('.env.example'), base_path('.env')); $this->info('✅ .env file created from example'); } // Fix APP_KEY if missing if (empty(config('app.key'))) { $this->call('key:generate', ['--force' => true]); $this->info('✅ APP_KEY generated'); } // Fix database if using SQLite (common error) if (config('database.default') === 'sqlite') { $dbPath = database_path('database.sqlite'); if (! File::exists($dbPath)) { touch($dbPath); chmod($dbPath, 0755); $this->info('✅ SQLite database created'); } } // Check and fix common migration issues try { $this->call('migrate:status', ['--format' => 'compact']); } catch (\Exception) { $this->warn('⚠️ Migration status check failed'); } // Fix queue table if using database driver if (config('queue.default') === 'database') { if (! Schema::hasTable('jobs')) { $this->call('queue:table', ['--force' => true]); $this->info('✅ Queue jobs table created'); } if (! Schema::hasTable('failed_jobs')) { $this->call('queue:failed-table', ['--create' => true]); $this->info('✅ Failed jobs table created'); } } // Fix broadcasting if using Pusher if (config('broadcasting.default') === 'pusher' && empty(config('broadcasting.connections.pusher.key'))) { $this->warn('⚠️ Pusher credentials missing in config'); } // Fix Redis connection if configured but not connected if (config('cache.default') === 'redis') { try { Redis::connection()->ping(); } catch (\Exception) { $this->warn('⚠️ Redis not connected, falling back to file cache'); // Can't auto-switch, just warn } } $this->info('✅ Known common errors fixed'); } private function repairPHPConfig(): bool { if (! is_writable($this->phpIniPath)) { return false; } $c = File::get($this->phpIniPath); $c = preg_replace(['/memory_limit\s*=.*/', '/upload_max_filesize\s*=.*/', '/post_max_size\s*=.*/'], ['memory_limit=512M', 'upload_max_filesize=64M', 'post_max_size=64M'], $c); return (bool) File::put($this->phpIniPath, $c); } private function repairPHPExtensions(): int { $v = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; $missing = []; foreach (['gd', 'zip', 'redis', 'intl', 'bcmath', 'xml'] as $ext) { if (! extension_loaded($ext)) { $missing[] = $ext; } } if ($missing === []) { return 0; } if ($this->isWindows()) { $this->info('Installing PHP extensions on Windows...'); // Check for Chocolatey $chocoExists = shell_exec('where choco 2>nul'); if (! in_array($chocoExists, ['', '0', false, null], true)) { foreach ($missing as $ext) { $this->info("Installing PHP {$ext} via Chocolatey..."); // Try to install PHP extension via choco shell_exec("choco install php{$v}-{$ext} -y 2>nul"); } } else { $this->warn('Cannot auto-install extensions on Windows without Chocolatey'); $this->warn('Missing extensions: ' . implode(', ', $missing)); $this->warn('To install manually:'); $this->warn('1. Download PHP extensions from: https://windows.php.net/downloads/pecl/releases/'); $this->warn('2. Or enable in php.ini: extension=php_' . implode('.dll, extension=php_', $missing) . '.dll'); } return count($missing); } // Linux $pm = (shell_exec('which apt 2>/dev/null')) ? 'apt' : ((shell_exec('which yum 2>/dev/null')) ? 'yum' : 'pacman'); $isWindows = DIRECTORY_SEPARATOR === '\\'; foreach ($missing as $ext) { if ($isWindows) { $this->warn("⚠️ Cannot install {$ext} on Windows - install manually"); continue; } $pkg = ($pm === 'apt') ? "php{$v}-{$ext}" : "php-{$ext}"; $this->info("Installing {$pkg}..."); // Check if we can install without sudo (already root) $isRoot = function_exists('posix_getuid') && posix_getuid() === 0; $cmd = $isRoot ? "{$pm} install -y {$pkg}" : "sudo {$pm} install -y {$pkg}"; exec("{$cmd} 2>/dev/null", $output, $exitCode); if ($exitCode !== 0 && ! $isRoot) { $this->warn("⚠️ Cannot install {$pkg} - run manually: sudo {$pm} install -y {$pkg}"); } } return count($missing); } private function updatePhpVersion(): int { if ($this->isLinux()) { $this->info('Adding PHP repository...'); exec('sudo add-apt-repository -y ppa:ondrej/php && sudo apt-get update 2>/dev/null'); $this->warn("Repository added. Run 'sudo apt upgrade' to finish."); return 1; } return 0; } private function createRadioTables(): void { $radioTables = [ 'radio_song_votes', 'radio_contests', 'radio_giveaways', 'radio_listener_points', 'radio_song_requests', 'radio_ranks', 'radio_shouts', 'radio_schedules', 'radio_banners', 'radio_history', 'radio_applications', ]; $this->info('🔊 Checking radio tables...'); $missingTables = []; foreach ($radioTables as $table) { if (! Schema::hasTable($table)) { $missingTables[] = $table; } } if ($this->isWindows()) { $mysqlPath = $this->findMysql(); if ($mysqlPath) { $mysqlDir = dirname($mysqlPath); $currentPath = getenv('PATH') ?: ''; if (! str_contains($currentPath, $mysqlDir)) { putenv("PATH={$currentPath};{$mysqlDir}"); } } } if ($missingTables !== []) { $this->info('📦 Creating ' . count($missingTables) . ' missing radio tables...'); // Run all migrations if ($this->skipMigrations) { $this->info('⏭️ Migrations skipped (--skip-migrations)'); } else { if ($this->isWindows()) { $this->addMysqlToPath(); } if ($this->skipDuplicates) { $this->runMigrationsWithSkip(); } else { $this->call('migrate', ['--force' => true]); } } } // Run all radio seeders $radioSeeders = [ RadioSettingsSeeder::class, RadioContestSeeder::class, RadioGiveawaySeeder::class, RadioSongRequestSeeder::class, RadioSongVoteSeeder::class, RadioListenerPointSeeder::class, RadioTestSeeder::class, ]; foreach ($radioSeeders as $seeder) { if (class_exists($seeder)) { try { $this->call('db:seed', ['--class' => $seeder, '--force' => true]); } catch (\Exception) { // Ignore individual seeder failures } } } // Enable radio in settings try { DB::table('website_settings')->updateOrInsert( ['key' => 'radio_enabled'], ['value' => '1', 'comment' => 'Radio enabled (0=no, 1=yes)'], ); $this->info('✅ Radio enabled'); } catch (\Exception) { // Ignore } $this->info('✅ Radio tables and data created'); } private function installRedisIfNeeded(int &$f): void { if ($this->isLinux()) { $this->info('Installing Redis Server (Linux)...'); exec('sudo apt-get install -y redis-server 2>/dev/null'); exec('sudo systemctl enable redis-server && sudo systemctl start redis-server 2>/dev/null'); $f++; return; } if ($this->isWindows()) { $this->info('Installing Redis Server (Windows)...'); // Check if Redis is already installed $redisExists = shell_exec('where redis-server 2>nul') ?: shell_exec('where redis 2>nul'); if (in_array($redisExists, ['', '0', false, null], true)) { // Try to install via Chocolatey $chocoExists = shell_exec('where choco 2>nul'); if (! in_array($chocoExists, ['', '0', false, null], true)) { $this->info('Installing Redis via Chocolatey...'); shell_exec('choco install redis-64 -y 2>nul'); } else { $this->warn('Redis not installed. To install on Windows:'); $this->warn('1. Download Redis from: https://github.com/microsoftarchive/redis/releases'); $this->warn('2. Or install via Chocolatey: choco install redis-64'); $this->warn('3. Or use Memurai/Redis Windows: https://www.memurai.com/'); } } // Check if Redis is running $redisRunning = shell_exec('sc query Redis 2>nul') ?: shell_exec('sc query redis 2>nul'); if (in_array($redisRunning, ['', '0', false, null], true)) { $this->info('Starting Redis service...'); shell_exec('net start redis 2>nul') ?: shell_exec('sc start redis 2>nul'); } $f++; return; } } private function fixEverything(bool $auto = false): int { $fixed = 0; $errors = []; $interactive = ! $auto; $this->info(''); $this->info('════════════════════════════════════════════════════════════'); $this->info(' FIXING EVERYTHING IN ATOMCMS '); $this->info('════════════════════════════════════════════════════════════'); $this->info(''); // Step 1: Environment try { $this->info('[1/11] 🔧 Fixing Environment...'); $envExists = File::exists(base_path('.env')); $appKeySet = ! empty(config('app.key')); if ($envExists && $appKeySet && $interactive) { $this->warn(' ⚠️ Environment looks good already!'); if (! $this->confirm(' 💡 Weet je zeker dat je dit opnieuw wilt doen?', false)) { $this->info(' ⏭️ Overgeslagen'); goto step2; } } if (! $envExists) { File::copy(base_path('.env.example'), base_path('.env')); $this->info(' ✅ .env created'); } if (! $appKeySet) { $this->call('key:generate', ['--force' => true]); $this->info(' ✅ APP_KEY generated'); } if ($envExists && $appKeySet) { $this->info(' ✅ Environment was already good'); } $fixed++; } catch (\Exception $e) { $errors[] = 'Environment: ' . $e->getMessage(); $this->warn(' ⚠️ Environment error: ' . $e->getMessage()); } step2: // Step 2: Clear all caches try { $this->info('[2/11] 🗑️ Clearing all caches...'); if ($interactive && ! $this->confirm(' 💡 Caches legen? (kan helpen bij problemen)', true)) { $this->info(' ⏭️ Overgeslagen'); goto step3; } $this->call('cache:clear'); $this->call('view:clear'); $this->call('route:clear'); $this->call('config:clear'); $this->call('event:clear'); $this->call('clear-compiled'); $this->call('optimize:clear'); $this->info(' ✅ All caches cleared'); $fixed++; } catch (\Exception $e) { $errors[] = 'Cache clear: ' . $e->getMessage(); $this->warn(' ⚠️ Cache clear error: ' . $e->getMessage()); } step3: // Step 3: Fix permissions (with multiple fallbacks) try { $this->info('[3/11] 🔐 Fixing permissions...'); $storageOk = is_writable(storage_path()) && is_writable(public_path('uploads')); if ($storageOk && $interactive) { $this->warn(' ⚠️ Permissions zien er goed uit!'); if (! $this->confirm(' 💡 Weet je zeker dat je permissions opnieuw wilt fixen?', false)) { $this->info(' ⏭️ Overgeslagen'); goto step4; } } $permissionSuccess = false; // Attempt 1: Standard repair try { $this->repairPermissions(); $permissionSuccess = true; $this->info(' ✅ Permissions fixed'); } catch (\Exception) { $this->warn(' ⚠️ Standard repair failed, trying chmod...'); } // Attempt 2: Direct chmod if (! $permissionSuccess) { try { $dirs = [storage_path(), base_path('bootstrap/cache'), public_path('uploads')]; foreach ($dirs as $dir) { if (is_dir($dir)) { @chmod($dir, 0775); $this->runSystemCommand("chmod -R 775 {$dir} 2>/dev/null"); } } $permissionSuccess = true; $this->info(' ✅ Permissions fixed (chmod)'); } catch (\Exception) { $this->warn(' ⚠️ chmod failed, trying sudo...'); } } // Attempt 3: sudo chmod if (! $permissionSuccess && $this->isLinux()) { try { $this->runSystemCommand('sudo chmod -R 775 ' . storage_path() . ' 2>/dev/null'); $this->runSystemCommand('sudo chmod -R 775 ' . base_path('bootstrap/cache') . ' 2>/dev/null'); $this->runSystemCommand('sudo chmod -R 775 ' . public_path('uploads') . ' 2>/dev/null'); $permissionSuccess = true; $this->info(' ✅ Permissions fixed (sudo)'); } catch (\Exception) { $this->warn(' ⚠️ All permission attempts failed'); $errors[] = 'Permissions: All 3 attempts failed'; } } $fixed++; } catch (\Exception $e) { $errors[] = 'Permissions: ' . $e->getMessage(); $this->warn(' ⚠️ Permissions error: ' . $e->getMessage()); } step4: // Step 4: Run ALL migrations (with multiple fallbacks) try { $this->info('[4/11] 🗄️ Running ALL migrations...'); if ($interactive && ! $this->confirm(' 💡 Database migrations draaien?', true)) { $this->info(' ⏭️ Overgeslagen'); goto step5; } if ($this->skipMigrations) { $this->info(' ⏭️ Migrations skipped (--skip-migrations)'); } else { if ($this->isWindows()) { $this->addMysqlToPath(); } $migrationSuccess = false; // Attempt 1: Normal migration try { $this->call('migrate', ['--force' => true]); $migrationSuccess = true; $this->info(' ✅ Migrations complete'); } catch (\Exception) { $this->warn(' ⚠️ Normal migration failed, trying with --step...'); } // Attempt 2: Migration with --step (if normal failed) if (! $migrationSuccess) { try { $this->call('migrate', ['--force' => true, '--step' => true]); $migrationSuccess = true; $this->info(' ✅ Migrations complete (step mode)'); } catch (\Exception) { $this->warn(' ⚠️ Step migration failed, trying rollback + migrate...'); } } // Attempt 3: Rollback and migrate fresh (if still failed) if (! $migrationSuccess) { try { $this->call('migrate:rollback', ['--force' => true, '--step' => 1]); $this->call('migrate', ['--force' => true]); $migrationSuccess = true; $this->info(' ✅ Migrations complete (rollback + migrate)'); } catch (\Exception) { $this->warn(' ⚠️ Rollback failed, trying fresh...'); } } // Attempt 4: NEVER USE migrate:fresh - it deletes all data! // Only show warning if all attempts failed if (! $migrationSuccess) { $this->warn(' ⚠️ All migration attempts failed'); $this->warn(' 💡 Run manually: php artisan migrate --force'); $errors[] = 'Migrations: All attempts failed (manual fix needed)'; } } $fixed++; } catch (\Exception $e) { $errors[] = 'Migrations: ' . $e->getMessage(); $this->warn(' ⚠️ Migration error: ' . $e->getMessage()); } step5: // Step 5: Run ALL seeders (with multiple fallbacks) try { $this->info('[5/11] 🌱 Running ALL seeders...'); if ($interactive && ! $this->confirm(' 💡 Database seeders draaien?', true)) { $this->info(' ⏭️ Overgeslagen'); goto step6; } $seederSuccess = false; // Attempt 1: Normal seeder try { $this->call('db:seed', ['--force' => true]); $seederSuccess = true; $this->info(' ✅ All seeders complete'); } catch (\Exception $e1) { if (str_contains($e1->getMessage(), 'Duplicate entry') || str_contains($e1->getMessage(), 'already exists')) { $seederSuccess = true; $this->info(' ✅ Seeders already applied (duplicates ignored)'); } else { $this->warn(' ⚠️ Normal seeder failed, trying individual seeders...'); } } // Attempt 2: Run individual seeders (if normal failed) if (! $seederSuccess) { try { $seeders = $this->discoverAllSeeders(); $seededCount = 0; foreach ($seeders as $seeder) { try { $this->call('db:seed', ['--class' => $seeder, '--force' => true]); $seededCount++; } catch (\Exception) { // Skip individual seeder errors } } if ($seededCount > 0) { $seederSuccess = true; $this->info(" ✅ {$seededCount} seeders completed"); } } catch (\Exception) { $this->warn(' ⚠️ Individual seeders failed, trying with --class option...'); } } // Attempt 3: Try with class option if (! $seederSuccess) { try { $this->call('db:seed', ['--class' => 'DatabaseSeeder', '--force' => true]); $seederSuccess = true; $this->info(' ✅ Seeders complete (with class option)'); } catch (\Exception) { $this->warn(' ⚠️ All seeder attempts failed'); $errors[] = 'Seeders: All 3 attempts failed'; } } $fixed++; } catch (\Exception $e) { $errors[] = 'Seeders: ' . $e->getMessage(); $this->warn(' ⚠️ Seeder error: ' . $e->getMessage()); } step6: // Step 6: Fix storage (with multiple fallbacks) try { $this->info('[6/11] 📁 Fixing storage...'); $storageLinkExists = File::exists(public_path('storage')); if ($storageLinkExists && $interactive) { $this->warn(' ⚠️ Storage symlink bestaat al!'); if (! $this->confirm(' 💡 Weet je zeker dat je storage opnieuw wilt fixen?', false)) { $this->info(' ⏭️ Overgeslagen'); goto step6b; } } $storageSuccess = false; // Attempt 1: Laravel storage:link try { if (File::exists(public_path('storage'))) { @unlink(public_path('storage')); } $this->call('storage:link'); $storageSuccess = true; $this->info(' ✅ Storage symlink created'); } catch (\Exception) { $this->warn(' ⚠️ storage:link failed, trying manual symlink...'); } // Attempt 2: Manual symlink if (! $storageSuccess) { try { if (File::exists(public_path('storage'))) { @unlink(public_path('storage')); } symlink(storage_path('app/public'), public_path('storage')); $storageSuccess = true; $this->info(' ✅ Storage symlink created (manual)'); } catch (\Exception) { $this->warn(' ⚠️ Manual symlink failed, trying copy...'); } } // Attempt 3: Copy instead of symlink if (! $storageSuccess) { try { $this->runSystemCommand('cp -r ' . storage_path('app/public') . ' ' . public_path('storage')); $storageSuccess = true; $this->info(' ✅ Storage copied (no symlink)'); } catch (\Exception) { $this->warn(' ⚠️ All storage attempts failed'); $errors[] = 'Storage: All 3 attempts failed'; } } // Create all required directories $directories = [ storage_path('app'), storage_path('app/public'), storage_path('framework/cache'), storage_path('framework/cache/data'), storage_path('framework/sessions'), storage_path('framework/views'), storage_path('logs'), ]; foreach ($directories as $dir) { if (! is_dir($dir)) { @mkdir($dir, 0755, true); } } $this->info(' ✅ Storage directories created'); $fixed++; } catch (\Exception $e) { $errors[] = 'Storage: ' . $e->getMessage(); $this->warn(' ⚠️ Storage error: ' . $e->getMessage()); } // Step 5b: Supervisor config if ($this->isLinux()) { try { $this->info('[5b/11] 🐘 Generating Supervisor config...'); $supervisorPath = '/etc/supervisor/conf.d/atom-worker.conf'; $supervisorExists = File::exists($supervisorPath) || File::exists('/etc/supervisor.d/atom.ini'); if ($supervisorExists) { $this->info(' ✅ Supervisor config exists'); } else { if ($interactive && ! $this->confirm(' 💡 Supervisor config genereren?', true)) { $this->info(' ⏭️ Overgeslagen'); goto step6b; } $this->generateSupervisorConfig(); $this->info(' ✅ Supervisor config generated'); } $fixed++; } catch (\Exception $e) { $errors[] = 'Supervisor: ' . $e->getMessage(); $this->warn(' ⚠️ Supervisor error: ' . $e->getMessage()); } } step6b: // Step 6b: Fix Radio try { $this->info('[6c/12] 📻 Fixing Radio tables...'); if ($interactive && ! $this->confirm(' 💡 Radio tabellen fixen?', true)) { $this->info(' ⏭️ Overgeslagen'); goto step7; } $this->createRadioTables(); $this->info(' ✅ Radio tables fixed'); $fixed++; } catch (\Exception $e) { $errors[] = 'Radio: ' . $e->getMessage(); $this->warn(' ⚠️ Radio error: ' . $e->getMessage()); } step7: // Step 7: Create admin user if not exists try { $this->info('[7/12] 👤 Checking admin user...'); $hasAdmin = DB::table('users')->where('rank', '>=', 7)->exists(); if ($hasAdmin) { $this->warn(' ⚠️ Admin gebruiker bestaat al!'); if ($interactive && ! $this->confirm(' 💡 Weet je zeker dat je een nieuwe admin wilt aanmaken?', false)) { $this->info(' ⏭️ Overgeslagen'); goto step8; } } if (! $hasAdmin) { $this->createAdminUser('Admin', 'admin@atom.local', 'admin123'); $this->info(' ✅ Admin user created'); } else { $this->info(' ✅ Admin user exists (was al goed)'); } $fixed++; } catch (\Exception $e) { $errors[] = 'Admin user: ' . $e->getMessage(); $this->warn(' ⚠️ Admin error: ' . $e->getMessage()); $this->warn(' 💡 Create admin manually: php artisan make:admin'); } step8: // Step 8: Fix web server config try { $this->info('[8/12] 🌐 Fixing web server config...'); if ($interactive && ! $this->confirm(' 💡 Web server config opnieuw genereren?', false)) { $this->info(' ⏭️ Overgeslagen'); goto step9; } if ($this->isApache() || $this->isWamp()) { $this->repairApache(); $this->info(' ✅ Apache config fixed'); } elseif ($this->isIIS()) { $this->repairIIS(); $this->info(' ✅ IIS config fixed'); } elseif ($this->isNginx() || $this->isXampp()) { $this->repairNginx(); $this->info(' ✅ Nginx config generated'); } $fixed++; } catch (\Exception $e) { $errors[] = 'Web server: ' . $e->getMessage(); $this->warn(' ⚠️ Web server error: ' . $e->getMessage()); } step9: // Step 9: Fix PHP config try { $this->info('[9/12] ⚙️ Optimizing PHP config...'); if ($interactive && ! $this->confirm(' 💡 PHP config optimaliseren?', true)) { $this->info(' ⏭️ Overgeslagen'); goto step10; } $this->repairPHPConfig(); $this->info(' ✅ PHP config optimized'); $this->repairPHPExtensions(); $this->info(' ✅ PHP extensions checked'); $this->call('config:cache'); $this->info(' ✅ Config cache created'); $fixed++; } catch (\Exception $e) { $errors[] = 'PHP config: ' . $e->getMessage(); $this->warn(' ⚠️ PHP config error: ' . $e->getMessage()); } step10: // Step 10: Build assets (with multiple fallbacks) try { $this->info('[10/12] 🎨 Building assets...'); if ($interactive && ! $this->confirm(' 💡 Frontend assets opnieuw bouwen? (kan lang duren)', true)) { $this->info(' ⏭️ Overgeslagen'); goto step11; } $buildSuccess = false; // Attempt 1: buildFrontendAssets method try { $this->buildFrontendAssets(); $buildSuccess = true; $this->info(' ✅ Assets built'); } catch (\Exception) { $this->warn(' ⚠️ Standard build failed, trying npm run build...'); } // Attempt 2: npm run build if (! $buildSuccess) { try { $result = $this->runSystemCommand('cd ' . base_path() . ' && npm run build 2>&1'); if ($result['success'] || str_contains((string) $result['output'], 'built in')) { $buildSuccess = true; $this->info(' ✅ Assets built (npm run build)'); } } catch (\Exception) { $this->warn(' ⚠️ npm run build failed, trying npm run build:atom...'); } } // Attempt 3: npm run build:atom if (! $buildSuccess) { try { $result = $this->runSystemCommand('cd ' . base_path() . ' && npm run build:atom 2>&1'); if ($result['success'] || str_contains((string) $result['output'], 'built in')) { $buildSuccess = true; $this->info(' ✅ Assets built (npm run build:atom)'); } } catch (\Exception) { $this->warn(' ⚠️ All build attempts failed'); $errors[] = 'Assets: All 3 attempts failed'; } } $fixed++; } catch (\Exception $e) { $errors[] = 'Assets: ' . $e->getMessage(); $this->warn(' ⚠️ Assets error: ' . $e->getMessage()); } step11: // Step 11: Fix HTTP errors try { $this->info('[11/12] 🌐 Fixing HTTP errors...'); if ($interactive && ! $this->confirm(' 💡 HTTP errors controleren en fixen?', true)) { $this->info(' ⏭️ Overgeslagen'); goto stepFilament; } $this->fixHttpErrors($auto); $this->info(' ✅ HTTP errors checked'); $fixed++; } catch (\Exception $e) { $errors[] = 'HTTP errors: ' . $e->getMessage(); $this->warn(' ⚠️ HTTP error check: ' . $e->getMessage()); } stepFilament: // Step 12: Fix HTTPS / Mixed Content try { $this->info('[12/13] 🔒 Fixing HTTPS configuration...'); if ($interactive && ! $this->confirm(' 💡 HTTPS en mixed content fixen?', true)) { $this->info(' ⏭️ Overgeslagen'); goto finalOpt; } $this->fixHttpsConfiguration(); $this->info(' ✅ HTTPS configuration fixed'); $fixed++; } catch (\Exception $e) { $errors[] = 'HTTPS: ' . $e->getMessage(); $this->warn(' ⚠️ HTTPS error: ' . $e->getMessage()); } // Step 13: Fix Filament pages and widgets try { $this->info('[13/16] 📄 Fixing Filament pages & widgets...'); $this->fixFilamentPages(); $this->info(' ✅ Filament files checked'); $fixed++; } catch (\Exception $e) { $errors[] = 'Filament: ' . $e->getMessage(); $this->warn(' ⚠️ Filament error: ' . $e->getMessage()); } // Step 14: Fix emulator service (auto-detect + multiple fallbacks) try { $this->info('[14/16] 🖥️ Checking emulator service...'); // Auto-detect emulator service $serviceName = $this->detectEmulatorService(); $this->info(" 🔍 Detected service: {$serviceName}"); // Update setting if different if ($serviceName !== setting('emulator_service_name')) { WebsiteSetting::updateOrCreate( ['key' => 'emulator_service_name'], ['value' => $serviceName], ); $this->info(" ✅ Updated setting to: {$serviceName}"); } $result = $this->runSystemCommand("systemctl is-active {$serviceName} 2>/dev/null || echo 'inactive'"); if (trim((string) $result['output']) !== 'active') { $this->warn(" ⚠️ Emulator service '{$serviceName}' is not running"); if ($auto || $this->confirm(' 💡 Emulator service starten?', true)) { $serviceStarted = false; // Attempt 1: systemctl start try { $this->runSystemCommand("systemctl start {$serviceName} 2>/dev/null"); sleep(2); $check = $this->runSystemCommand("systemctl is-active {$serviceName} 2>/dev/null"); if (trim((string) $check['output']) === 'active') { $serviceStarted = true; $this->info(' ✅ Emulator service started (systemctl)'); } } catch (\Exception) { // Continue to next attempt } // Attempt 2: service start if (! $serviceStarted) { try { $this->runSystemCommand("service {$serviceName} start 2>/dev/null"); sleep(2); $serviceStarted = true; $this->info(' ✅ Emulator service started (service)'); } catch (\Exception) { // Continue to next attempt } } // Attempt 3: Direct java command if (! $serviceStarted) { try { $jarPath = setting('emulator_jar_path', '/var/www/Emulator'); $jarFiles = glob("{$jarPath}/*.jar"); if ($jarFiles !== [] && $jarFiles !== false) { $this->runSystemCommand("cd {$jarPath} && nohup java -jar " . basename($jarFiles[0]) . ' > /dev/null 2>&1 &'); sleep(3); $serviceStarted = true; $this->info(' ✅ Emulator started (direct java)'); } } catch (\Exception) { $this->warn(' ⚠️ All emulator start attempts failed'); $errors[] = 'Emulator: All start attempts failed'; } } } } else { $this->info(" ✅ Emulator service '{$serviceName}' is running"); } $fixed++; } catch (\Exception $e) { $errors[] = 'Emulator: ' . $e->getMessage(); $this->warn(' ⚠️ Emulator error: ' . $e->getMessage()); } // Step 15: Fix database tables try { $this->info('[15/16] 🗄️ Checking database tables...'); $requiredTables = ['users', 'permissions', 'website_settings', 'rooms', 'items', 'catalog_items']; $missing = []; foreach ($requiredTables as $table) { if (! Schema::hasTable($table)) { $missing[] = $table; } } if ($missing !== []) { $this->warn(' ⚠️ Missing tables: ' . implode(', ', $missing)); if ($auto || $this->confirm(' 💡 Database migrations draaien?', true)) { $this->call('migrate', ['--force' => true]); $this->info(' ✅ Migrations executed'); } } else { $this->info(' ✅ All required tables exist'); } $fixed++; } catch (\Exception $e) { $errors[] = 'Database tables: ' . $e->getMessage(); $this->warn(' ⚠️ Database error: ' . $e->getMessage()); } // Step 16: Fix file permissions for critical files try { $this->info('[16/16] 🔐 Fixing file permissions...'); $criticalDirs = [ storage_path(), base_path('bootstrap/cache'), public_path('uploads'), public_path('storage'), base_path('app/Filament'), base_path('app/Services'), base_path('resources/views/filament'), ]; foreach ($criticalDirs as $dir) { if (is_dir($dir)) { $this->runSystemCommand("chown -R {$this->webUser}:{$this->webGroup} {$dir} 2>/dev/null"); $this->runSystemCommand("chmod -R 775 {$dir} 2>/dev/null"); } } $this->info(' ✅ File permissions fixed'); $fixed++; } catch (\Exception $e) { $errors[] = 'Permissions: ' . $e->getMessage(); $this->warn(' ⚠️ Permission error: ' . $e->getMessage()); } // Step 17: Ensure critical database settings exist try { $this->info('[17/17] ⚙️ Ensuring critical settings...'); $requiredSettings = [ 'emulator_jar_path' => '/var/www/Emulator', 'emulator_service_name' => 'emulator', 'emulator_version' => '4.0.5', ]; foreach ($requiredSettings as $key => $default) { $current = setting($key); if (empty($current)) { WebsiteSetting::updateOrCreate( ['key' => $key], ['value' => $default], ); $this->info(" ✅ Set {$key} = {$default}"); } } $fixed++; } catch (\Exception $e) { $errors[] = 'Settings: ' . $e->getMessage(); $this->warn(' ⚠️ Settings error: ' . $e->getMessage()); } // Step 18: Auto-fix everything else that can go wrong try { $this->info('[18/18] 🔧 Ultimate auto-fix...'); // 1. Auto-create missing database tables $this->autoCreateMissingTables(); // 2. Auto-fix missing PHP extensions $this->autoFixPhpExtensions(); // 3. Auto-fix file permissions (all platforms) $this->autoFixAllPermissions(); // 4. Auto-fix configuration $this->autoFixConfiguration(); // 5. Auto-restart all services $this->autoRestartServices(); $this->info(' ✅ Ultimate auto-fix complete'); $fixed++; } catch (\Exception $e) { $errors[] = 'Ultimate fix: ' . $e->getMessage(); $this->warn(' ⚠️ Ultimate fix error: ' . $e->getMessage()); } finalOpt: // Final optimization try { $this->info(''); $this->info('🚀 Running final optimizations...'); $this->call('config:cache'); $this->call('route:cache'); $this->call('view:cache'); $this->call('event:cache'); $this->call('filament:cache-components'); $this->info(' ✅ Optimizations complete'); } catch (\Exception $e) { $errors[] = 'Optimization: ' . $e->getMessage(); $this->warn(' ⚠️ Optimization error: ' . $e->getMessage()); } // Summary $this->info(''); if ($errors === []) { $this->info('════════════════════════════════════════════════════════════'); $this->info("🎉 ATOMCMS IS 100% FIXED! ({$fixed}/17 steps completed)"); $this->info('════════════════════════════════════════════════════════════'); if (DIRECTORY_SEPARATOR === '\\') { $this->info(''); $this->info('📝 WINDOWS INSTALLATION STEPS:'); $this->info('-----------------------------------'); $this->info('1. Install dependencies:'); $this->info(' composer install'); $this->info(' npm install'); $this->info(''); $this->info('2. Set up database:'); $this->info(' php artisan migrate --seed'); $this->info(' php artisan key:generate'); $this->info(''); $this->info('3. Build assets:'); $this->info(' npm run build:atom'); $this->info(' (For dev: npm run dev:atom)'); $this->info(''); $this->info('4. IIS: Point to public folder'); $this->info(' Grant Full control to IUSR and IIS_IUSRS'); } } else { $this->warn('════════════════════════════════════════════════════════════'); $this->warn('⚠️ FIXED WITH ' . count($errors) . ' WARNINGS'); $this->warn('════════════════════════════════════════════════════════════'); foreach ($errors as $error) { $this->warn(' - ' . $error); } $this->info(''); $this->info('💡 Most issues are fixed. Restart your server for full effect.'); $this->info(' - Nginx/Apache: sudo systemctl restart nginx/apache'); $this->info(' - PHP-FPM: sudo systemctl restart php*-fpm'); $this->info(' - Queue: php artisan queue:work --daemon'); } $this->info(''); return 0; } private function fixHttpErrors(bool $auto = false): void { $this->info('🔧 Fixing HTTP errors...'); // Fix 400 - Ensure proper session config $this->fix400Errors($auto); // Fix 403 - Permissions $this->fix403Errors(); // Fix 404 - Routes and symlinks $this->fix404Errors(); // Fix 419 - Session issues $this->fix419Errors(); // Fix 500 - Common server errors $this->fix500Errors(); // Fix 502 - PHP-FPM $this->fix502Errors(); // Fix 503 - Maintenance and queue $this->fix503Errors(); // Fix 504 - Timeouts $this->fix504Errors(); $this->info('✅ HTTP errors fixed'); } private function fix400Errors(bool $auto): void { // Fix sessions directory $sessionPath = storage_path('framework/sessions'); if (! is_dir($sessionPath)) { @mkdir($sessionPath, 0755, true); } @chmod($sessionPath, 0755); // Fix POST limits if ($auto) { $this->repairPHPConfig(); } } private function fix403Errors(): void { // Fix permissions $this->repairPermissions(); // Fix .htaccess if ($this->isApache()) { $htaccess = public_path('.htaccess'); if (! File::exists($htaccess)) { $this->repairApache(); } } // Ensure public is readable @chmod(public_path(), 0755); } private function fix404Errors(): void { // Fix storage symlink if (! File::exists(public_path('storage'))) { if (File::exists(public_path('storage'))) { @unlink(public_path('storage')); } $this->call('storage:link'); } // Generate nginx config if needed if ($this->isNginx()) { $nginxConfig = base_path('atom-nginx.conf'); if (! File::exists($nginxConfig)) { $this->repairNginx(); } } // Clear routes cache $this->call('route:clear'); } private function fix419Errors(): void { // Clear sessions $sessionPath = storage_path('framework/sessions'); if (is_dir($sessionPath)) { $files = glob($sessionPath . '/*'); foreach ($files as $file) { if (is_file($file)) { @unlink($file); } } } // Clear cache $this->call('cache:clear'); // Clear session files $sessionPath = storage_path('framework/sessions'); if (is_dir($sessionPath)) { foreach (glob("$sessionPath/*") as $file) { if (is_file($file)) { @unlink($file); } } } } private function fix500Errors(): void { // Clear all caches $this->call('cache:clear'); $this->call('view:clear'); $this->call('route:clear'); $this->call('config:clear'); $this->call('clear-compiled'); // Fix permissions $this->repairPermissions(); // Ensure bootstrap/cache is writable $bootstrapCache = base_path('bootstrap/cache'); @chmod($bootstrapCache, 0755); // Fix database try { DB::connection()->getPdo(); } catch (\Exception) { // Can't fix database connection automatically } } private function fix502Errors(): void { $phpVersion = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; // Check if PHP-FPM is running $isWindows = DIRECTORY_SEPARATOR === '\\'; if ($isWindows) { $this->info(' ⚠️ PHP-FPM on Windows - check your PHP setup'); return; } // Try to restart PHP-FPM $this->info(' 🔄 Restarting PHP-FPM...'); // Try different service names $services = [ "php{$phpVersion}-fpm", 'php-fpm', "php{$phpVersion}-fpm.service", ]; $restarted = false; foreach ($services as $service) { exec("sudo systemctl restart {$service} 2>/dev/null", $out, $code); if ($code === 0) { $this->info(" ✅ Restarted PHP-FPM ({$service})"); $restarted = true; break; } } if (! $restarted) { // Try to start if not running exec('sudo systemctl start php-fpm 2>/dev/null'); exec("sudo systemctl start php{$phpVersion}-fpm 2>/dev/null"); $this->info(' ⚠️ Could not auto-restart PHP-FPM'); $this->info(' 💡 Try manually: sudo systemctl restart php-fpm'); } // Check socket permissions $socketPaths = [ "/var/run/php/php{$phpVersion}-fpm.sock", '/var/run/php-fpm.sock', "/run/php/php{$phpVersion}-fpm.sock", ]; foreach ($socketPaths as $path) { if (File::exists($path)) { @chmod($path, 0666); $this->info(" ✅ Fixed socket: {$path}"); } } } private function fix503Errors(): void { // Disable maintenance mode $maintenanceFile = storage_path('framework/maintenance.php'); if (File::exists($maintenanceFile)) { @unlink($maintenanceFile); } // Restart queue workers $isWindows = DIRECTORY_SEPARATOR === '\\'; if (! $isWindows) { @exec("pkill -f 'queue:work' 2>/dev/null"); $this->info('Queue workers stopped - restart with supervisor'); } else { $this->info('Queue workers managed manually on Windows'); } // Check database try { DB::connection()->getPdo(); } catch (\Exception) { $this->warn('Database unavailable'); } } private function fix504Errors(): void { // Increase PHP timeout $iniPath = php_ini_loaded_file(); if (! in_array($iniPath, ['', '0', false], true) && is_writable($iniPath)) { $content = File::get($iniPath); if (! str_contains($content, 'max_execution_time')) { $content .= "\nmax_execution_time = 300"; File::put($iniPath, $content); } } // Increase PHP-FPM timeout in nginx if ($this->isNginx()) { $nginxConfig = base_path('atom-nginx.conf'); if (File::exists($nginxConfig)) { $content = File::get($nginxConfig); if (! str_contains($content, 'fastcgi_read_timeout')) { $content = str_replace('location ~ \.php$ {', "location ~ \.php$ {\n fastcgi_read_timeout 300;", $content); File::put($nginxConfig, $content); } } } } private function backupDatabase(): void { $this->info('📦 Starting database backup...'); $filename = 'backup-' . date('Y-m-d-H-i-s') . '.sql'; $path = storage_path('app/backups'); if (! File::exists($path)) { File::makeDirectory($path, 0755, true); } $config = config('database.connections.mysql'); if ($this->isWindows()) { $this->addMysqlToPath(); } if (! $this->checkCommandExists('mysqldump')) { $this->error('❌ mysqldump is not installed or not in PATH'); return; } $passPart = empty($config['password']) ? '' : '--password=' . escapeshellarg((string) $config['password']); $command = sprintf('mysqldump --user=%s %s --host=%s --port=%s %s > %s/%s', escapeshellarg((string) $config['username']), $passPart, escapeshellarg((string) $config['host']), escapeshellarg((string) $config['port']), escapeshellarg((string) $config['database']), escapeshellarg($path), escapeshellarg($filename), ); $result = $this->runSystemCommand($command); if ($result['success']) { $this->info("✅ Backup created: {$filename}"); } else { $this->error('❌ Backup failed: ' . ($result['output'] ?: 'Unknown error')); } } // --- UTILS --- private function detectPlatform(): string { if (DIRECTORY_SEPARATOR === '\\') { return $this->detectWindowsStack(); } $soft = $_SERVER['SERVER_SOFTWARE'] ?? ''; if (str_contains((string) $soft, 'Nginx')) { return 'nginx'; } if (str_contains((string) $soft, 'Apache')) { return 'apache'; } if (str_contains((string) $soft, 'Microsoft-IIS')) { return 'iis'; } return 'linux'; } private function detectEmulatorService(): string { // Common emulator service names $possibleNames = [ 'emulator', 'arcturus', 'arcturus-emulator', 'arcturus-morningstar', 'arcturus-ms', 'nitro', 'hotel', 'habbo', 'retro', ]; // Check setting first $settingName = setting('emulator_service_name', ''); if (! empty($settingName)) { $result = $this->runSystemCommand("systemctl list-unit-files {$settingName}.service 2>/dev/null | grep -q {$settingName} && echo 'exists'"); if (trim((string) $result['output']) === 'exists') { return $settingName; } } // Check each possible name foreach ($possibleNames as $name) { $result = $this->runSystemCommand("systemctl list-unit-files {$name}.service 2>/dev/null | grep -q {$name} && echo 'exists'"); if (trim((string) $result['output']) === 'exists') { return $name; } } // Check for Java processes running emulator JARs $result = $this->runSystemCommand("ps aux | grep -i 'java.*jar' | grep -v grep | awk '{print $11}' | head -1"); if (! in_array(trim((string) $result['output']), ['', '0'], true)) { // Try to find service from running process $result2 = $this->runSystemCommand("systemctl list-units --type=service --state=running | grep -i 'emul\\|arct\\|nitro\\|hotel' | awk '{print $1}' | sed 's/.service//' | head -1"); if (! in_array(trim((string) $result2['output']), ['', '0'], true)) { return trim((string) $result2['output']); } } // Check all running services for Java-based services $result = $this->runSystemCommand('systemctl list-units --type=service --state=running 2>/dev/null'); $lines = explode("\n", $result['output'] ?? ''); foreach ($lines as $line) { if (str_contains(strtolower($line), 'java') || str_contains(strtolower($line), 'jar')) { preg_match('/(\S+)\.service/', $line, $matches); if (isset($matches[1]) && ($matches[1] !== '' && $matches[1] !== '0')) { return $matches[1]; } } } // Default fallback return 'emulator'; } private function detectWindowsStack(): string { $soft = $_SERVER['SERVER_SOFTWARE'] ?? ''; if (str_contains((string) $soft, 'Microsoft-IIS')) { return 'iis'; } if (str_contains((string) $soft, 'Apache')) { return 'wamp'; } if (str_contains((string) $soft, 'nginx')) { return 'nginx'; } $processList = shell_exec('tasklist 2>nul') ?: ''; if (str_contains($processList, 'apache')) { return 'wamp'; } if (str_contains($processList, 'nginx')) { return 'nginx'; } if (str_contains($processList, 'httpd')) { return 'wamp'; } return 'windows'; } private function isXampp(): bool { return $this->platform === 'xampp'; } private function isWamp(): bool { return $this->platform === 'wamp'; } private function autoCreateMissingTables(): void { $this->info(' 🔍 Checking for missing tables...'); // Run migrations to create missing tables try { $this->call('migrate', ['--force' => true]); $this->info(' ✅ Missing tables created'); } catch (\Exception) { // Try with skip duplicates try { $this->runMigrationsWithSkip(); $this->info(' ✅ Missing tables created (with skips)'); } catch (\Exception) { // Last resort: create essential tables manually $this->createEssentialTables(); } } } private function createEssentialTables(): void { $essentialTables = [ 'users' => "CREATE TABLE IF NOT EXISTS `users` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `rank` int(11) NOT NULL DEFAULT 1, `credits` int(11) NOT NULL DEFAULT 0, `pixels` int(11) NOT NULL DEFAULT 0, `points` int(11) NOT NULL DEFAULT 0, `online` enum('0','1','2') NOT NULL DEFAULT '0', `last_login` int(11) NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `users_username_unique` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", 'permissions' => "CREATE TABLE IF NOT EXISTS `permissions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `rank_name` varchar(255) NOT NULL, `rank_level` int(11) NOT NULL DEFAULT 1, `acc_placefurni` enum('0','1') NOT NULL DEFAULT '0', `acc_moverotate` enum('0','1') NOT NULL DEFAULT '0', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", 'website_settings' => 'CREATE TABLE IF NOT EXISTS `website_settings` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `key` varchar(255) NOT NULL, `value` text DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `website_settings_key_unique` (`key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci', ]; foreach ($essentialTables as $table => $sql) { try { DB::statement($sql); $this->info(" ✅ Created table: {$table}"); } catch (\Exception) { // Table might already exist } } } private function autoFixPhpExtensions(): void { $this->info(' 🔍 Checking PHP extensions...'); $required = ['curl', 'mbstring', 'pdo_mysql', 'xml', 'bcmath', 'openssl', 'gd', 'zip', 'intl', 'redis']; foreach ($required as $ext) { if (! extension_loaded($ext)) { $this->info(" ⚠️ Missing extension: {$ext}"); // Try to install on Linux if ($this->isLinux()) { $this->runSystemCommand("sudo apt-get install -y php-{$ext} 2>/dev/null"); } } } $this->info(' ✅ PHP extensions checked'); } private function autoFixAllPermissions(): void { $this->info(' 🔍 Fixing all permissions...'); $dirs = [ storage_path(), base_path('bootstrap/cache'), public_path('uploads'), public_path('storage'), ]; foreach ($dirs as $dir) { if (is_dir($dir)) { if ($this->isWindows()) { @chmod($dir, 0777); } else { $this->runSystemCommand("sudo chmod -R 775 {$dir} 2>/dev/null"); $this->runSystemCommand("sudo chown -R {$this->webUser}:{$this->webGroup} {$dir} 2>/dev/null"); } } } $this->info(' ✅ All permissions fixed'); } private function autoFixConfiguration(): void { $this->info(' 🔍 Checking configuration...'); // Ensure .env exists if (! File::exists(base_path('.env')) && File::exists(base_path('.env.example'))) { File::copy(base_path('.env.example'), base_path('.env')); $this->call('key:generate', ['--force' => true]); $this->info(' ✅ .env created'); } // Ensure APP_KEY is set if (empty(config('app.key'))) { $this->call('key:generate', ['--force' => true]); $this->info(' ✅ APP_KEY generated'); } $this->info(' ✅ Configuration checked'); } private function autoRestartServices(): void { $this->info(' 🔍 Restarting services...'); if ($this->isLinux()) { // Restart PHP-FPM $phpVersion = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; $this->runSystemCommand("sudo systemctl restart php{$phpVersion}-fpm 2>/dev/null"); // Restart web server if ($this->isNginx()) { $this->runSystemCommand('sudo systemctl restart nginx 2>/dev/null'); } elseif ($this->isApache()) { $this->runSystemCommand('sudo systemctl restart apache2 2>/dev/null'); } // Restart emulator $serviceName = setting('emulator_service_name', 'emulator'); $this->runSystemCommand("sudo systemctl restart {$serviceName} 2>/dev/null"); } $this->info(' ✅ Services restarted'); } private function detectWebUserContext(): string { if ($this->isWindows()) { if ($this->isXampp() || $this->isWamp()) { $this->webUser = get_current_user(); $this->webGroup = 'Administrators'; } else { $this->webUser = 'SYSTEM'; $this->webGroup = 'SYSTEM'; } return $this->webUser; } $candidates = ['www-data', 'nginx', 'apache', 'http', 'www']; $passwd = @file_get_contents('/etc/passwd') ?: ''; foreach ($candidates as $user) { if (str_contains($passwd, $user)) { $this->webUser = $user; $this->webGroup = $user; return $user; } } $whoami = $this->runSystemCommand('whoami', true); return $this->webUser = $whoami['success'] ? trim((string) $whoami['output']) : 'www-data'; } private function isWindows(): bool { return $this->platform === 'windows'; } private function isLinux(): bool { return ! $this->isWindows(); } private function isApache(): bool { return $this->platform === 'apache' || str_contains($_SERVER['SERVER_SOFTWARE'] ?? '', 'Apache'); } private function isIIS(): bool { return str_contains($_SERVER['SERVER_SOFTWARE'] ?? '', 'Microsoft-IIS') || $this->platform === 'iis'; } private function isNginx(): bool { return $this->platform === 'nginx' || str_contains($_SERVER['SERVER_SOFTWARE'] ?? '', 'Nginx'); } private function addCheck(string $n, string $s, string $m): void { $this->checks[] = [$n, $s, $m]; if ($s === '❌') { $this->errors++; } if ($s === '⚠️') { $this->warnings++; } } private function hasError(string $p): bool { return array_any($this->checks, fn ($c) => str_contains((string) $c[0], $p) && $c[1] === '❌'); } private function hasWarning(string $p): bool { return array_any($this->checks, fn ($c) => str_contains((string) $c[0], $p) && $c[1] === '⚠️'); } private function displaySummary(): void { $this->newLine(); // Recount errors from checks (not from internal increments) $realErrors = 0; $realWarnings = 0; foreach ($this->checks as $check) { if ($check[1] === '❌') { $realErrors++; } if ($check[1] === '⚠️') { $realWarnings++; } } $this->table(['Feature', 'Status', 'Message'], $this->checks); if ($realErrors > 0) { $this->error("\nFound {$realErrors} errors! Run with --fix."); } elseif ($realWarnings > 0) { $this->warn("\nAll OK! ({$realWarnings} warnings)"); } else { $this->info("\n🎉 Everything looks perfect!"); } } private function runSystemCommand(string $command, bool $silent = false): array { $output = []; $exitCode = 0; if ($this->isWindows()) { exec($command . ' 2>NUL', $output, $exitCode); } else { exec($command . ' 2>&1', $output, $exitCode); } $result = [ 'success' => $exitCode === 0, 'output' => implode("\n", $output), 'exit_code' => $exitCode, 'command' => $command, ]; if (! $silent && ! $result['success'] && $output !== []) { $this->warn("Command failed: {$command}"); $this->warn('Output: ' . $result['output']); } return $result; } private function runSystemCommandWithRetry(string $command, int $retries = 3, int $delayMs = 500): array { for ($i = 0; $i < $retries; $i++) { $result = $this->runSystemCommand($command, true); if ($result['success']) { return $result; } if ($i < $retries - 1) { usleep($delayMs * 1000); } } return $this->runSystemCommand($command, false); } private function checkCommandExists(string $command): bool { $which = $this->isWindows() ? 'where' : 'which'; $result = $this->runSystemCommand("{$which} {$command}", true); return $result['success'] && ! in_array(trim((string) $result['output']), ['', '0'], true); } private function findMysqldump(): ?string { if (! $this->isWindows()) { return null; } $possiblePaths = [ 'C:\xampp\mysql\bin\mysqldump.exe', 'C:\xampp\mysql\bin\mysqldump', 'D:\xampp\mysql\bin\mysqldump.exe', 'D:\xampp\mysql\bin\mysqldump', 'C:\nginx\mysql\bin\mysqldump.exe', 'C:\nginx\mysql\bin\mysqldump', 'D:\nginx\mysql\bin\mysqldump.exe', 'D:\nginx\mysql\bin\mysqldump', 'C:\phpmysql\mysqldump.exe', 'C:\mysql\bin\mysqldump.exe', 'C:\Program Files\MariaDB 11.8\bin\mysqldump.exe', 'C:\Program Files\MariaDB 11.8\bin\mariadb-dump.exe', 'C:\Program Files (x86)\MariaDB 11.8\bin\mysqldump.exe', getenv('USERPROFILE') . '\xampp\mysql\bin\mysqldump.exe', getenv('USERPROFILE') . '\xampp\mysql\bin\mysqldump', getenv('USERPROFILE') . '\nginx\mysql\bin\mysqldump.exe', 'C:\wamp\bin\mysql\mysql5.7.31\bin\mysqldump.exe', 'C:\wamp64\bin\mysql\mysql5.7.31\bin\mysqldump.exe', 'C:\laragon\bin\mysql\mysql-5.7.40-winx64\bin\mysqldump.exe', 'C:\laragon\bin\mysql\mysql-8.0.30-winx64\bin\mysqldump.exe', ]; foreach ($possiblePaths as $path) { if (file_exists($path)) { return $path; } } return null; } private function findMysql(): ?string { if (! $this->isWindows()) { return null; } $possiblePaths = [ 'C:\xampp\mysql\bin\mysql.exe', 'C:\xampp\mysql\bin\mysql', 'D:\xampp\mysql\bin\mysql.exe', 'D:\xampp\mysql\bin\mysql', 'C:\nginx\mysql\bin\mysql.exe', 'C:\nginx\mysql\bin\mysql', 'D:\nginx\mysql\bin\mysql.exe', 'D:\nginx\mysql\bin\mysql', 'C:\phpmysql\mysql.exe', 'C:\mysql\bin\mysql.exe', 'C:\Program Files\MariaDB 11.8\bin\mysql.exe', 'C:\Program Files\MariaDB 11.8\bin\mariadb.exe', 'C:\Program Files (x86)\MariaDB 11.8\bin\mysql.exe', 'C:\Program Files (x86)\MariaDB 11.8\bin\mariadb.exe', getenv('USERPROFILE') . '\xampp\mysql\bin\mysql.exe', getenv('USERPROFILE') . '\xampp\mysql\bin\mysql', getenv('USERPROFILE') . '\nginx\mysql\bin\mysql.exe', 'C:\wamp\bin\mysql\mysql5.7.31\bin\mysql.exe', 'C:\wamp64\bin\mysql\mysql5.7.31\bin\mysql.exe', 'C:\laragon\bin\mysql\mysql-5.7.40-winx64\bin\mysql.exe', 'C:\laragon\bin\mysql\mysql-8.0.30-winx64\bin\mysql.exe', ]; foreach ($possiblePaths as $path) { if (file_exists($path)) { return $path; } } return null; } private function addMysqlToPath(): bool { if (! $this->isWindows()) { return false; } $mysqlPath = dirname($this->findMysql() ?: $this->findMysqldump() ?: ''); if (in_array($mysqlPath, ['', '0', '.', '\\'], true)) { return false; } $currentPath = getenv('PATH') ?: ''; if (str_contains($currentPath, $mysqlPath)) { return true; } $newPath = $currentPath . ';' . $mysqlPath; putenv("PATH={$newPath}"); return true; } private function fixEnvFile(): void { $this->info('🔧 Fixing .env file...'); $envPath = base_path('.env'); if (! File::exists($envPath)) { $this->error('❌ .env file not found!'); return; } $currentContent = File::get($envPath); // Check if .env is on single line (broken) OR if user wants to regenerate $isBroken = ! str_contains($currentContent, "\n") && strlen($currentContent) > 500; $this->info('Current .env status: ' . ($isBroken ? 'BROKEN (single line)' : 'OK')); if ($isBroken || $this->confirm('Regenerate .env file with new settings?', true)) { if ($isBroken) { $this->warn('⚠️ .env file is on a single line - fixing...'); } // Ask for site URL $siteUrl = $this->ask('What is your site URL?', 'http://localhost'); // Ask for database credentials $dbHost = $this->ask('Database host?', '127.0.0.1'); $dbPort = $this->ask('Database port?', '3306'); $dbDatabase = $this->ask('Database name?', 'habbo'); $dbUsername = $this->ask('Database username?', 'root'); $dbPassword = $this->secret('Database password?'); // Ask about Redis $useRedis = $this->confirm('Use Redis for caching/queue?', false); // Ask about session driver $sessionDriver = $this->ask('Session driver? (database/file)', 'database'); // Generate the fixed .env $newContent = $this->generateFixedEnv($siteUrl, $dbHost, $dbPort, $dbDatabase, $dbUsername, $dbPassword, $sessionDriver, $useRedis); File::put($envPath, $newContent); $this->info('✅ .env file fixed!'); } else { $this->info('✅ .env file looks good!'); } // Clear config cache to reload .env $this->call('config:clear'); // Generate key if missing try { $key = config('app.key'); if (empty($key)) { $this->call('key:generate', ['--force' => true]); $this->info('✅ App key generated'); } } catch (\Exception) { $this->call('key:generate', ['--force' => true]); $this->info('✅ App key generated'); } } private function generateFixedEnv(string $siteUrl, string $dbHost, string $dbPort, string $dbDatabase, string $dbUsername, string $dbPassword, string $sessionDriver, bool $useRedis): string { $redisConfig = ''; $cacheDriver = 'file'; $broadcastDriver = 'log'; $queueConnection = 'sync'; if ($useRedis) { $redisConfig = ' REDIS_CLIENT=phpredis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 '; $cacheDriver = 'redis'; $broadcastDriver = 'redis'; $queueConnection = 'redis'; } return "APP_NAME=\"Atom CMS\" APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL={$siteUrl} LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST={$dbHost} DB_PORT={$dbPort} DB_DATABASE={$dbDatabase} DB_USERNAME={$dbUsername} DB_PASSWORD={$dbPassword} BROADCAST_DRIVER={$broadcastDriver} CACHE_DRIVER={$cacheDriver} FILESYSTEM_DISK=local QUEUE_CONNECTION={$queueConnection} SESSION_DRIVER={$sessionDriver} SESSION_LIFETIME=120 MEMCACHED_HOST=127.0.0.1 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_MAILER=smtp MAIL_HOST=mailpit MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS=\"hello@example.com\" MAIL_FROM_NAME=\"\${APP_NAME}\" AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_HOST= PUSHER_PORT=443 PUSHER_SCHEME=https PUSHER_APP_CLUSTER=mt1 VITE_PUSHER_APP_KEY=\"\${PUSHER_APP_KEY}\" VITE_PUSHER_HOST=\"\${PUSHER_HOST}\" VITE_PUSHER_PORT=\"\${PUSHER_PORT}\" VITE_PUSHER_SCHEME=\"\${PUSHER_SCHEME}\" VITE_PUSHER_APP_CLUSTER=\"\${PUSHER_APP_CLUSTER}\" RCON_HOST=127.0.0.1 RCON_PORT=3001 FINDRETROS_NAME= FINDRETROS_ENABLED=false GOOGLE_RECAPTCHA_SITE_KEY= GOOGLE_RECAPTCHA_SECRET_KEY= TURNSTILE_SITE_KEY= TURNSTILE_SECRET_KEY= RENAME_COLLIDING_TABLES=false FLASH_CLIENT_ENABLED=false EMULATOR_IP=127.0.0.1 EMULATOR_PORT=3000 SWF_BASE_PATH=client/flash HABBO_SWF=Habbo.swf PRODUCTION_FOLDER=gordon/PRODUCTION EXTERNAL_FURNIDATA=gamedata/furnidata.xml EXTERNAL_FIGUREMAP=gamedata/figuremap.xml EXTERNAL_FIGUREDATA=gamedata/figuredata.xml EXTERNAL_PRODUCTDATA=gamedata/productdata.txt EXTERNAL_TEXTS=gamedata/external_flash_texts.txt EXTERNAL_VARIABLES=gamedata/external_variables.txt EXTERNAL_OVERRIDE_TEXTS=gamedata/override/external_flash_override_texts.txt EXTERNAL_OVERRIDE_VARIABLES=gamedata/override/external_override_variables.txt CONVERT_PASSWORDS=false FORCE_HTTPS=false APP_LOCALE=en PASSWORD_RESET_TOKEN_TIME=15 PAYPAL_MODE='sandbox' PAYPAL_PAYMENT_ACTION='Order' PAYPAL_CURRENCY='USD' PAYPAL_NOTIFY_URL= PAYPAL_LOCALE='en_US' PAYPAL_VALIDATE_SSL=true PAYPAL_SANDBOX_CLIENT_ID= PAYPAL_SANDBOX_CLIENT_SECRET= PAYPAL_SANDBOX_APP_ID= PAYPAL_LIVE_CLIENT_ID= PAYPAL_LIVE_CLIENT_SECRET= PAYPAL_LIVE_APP_ID= FORTIFY_PREFIX= "; } private function getDefaultEnvTemplate(): string { return 'APP_NAME="Atom CMS" APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=habbo DB_USERNAME=root DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file FILESYSTEM_DISK=local QUEUE_CONNECTION=sync SESSION_DRIVER=database SESSION_LIFETIME=120 MEMCACHED_HOST=127.0.0.1 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_MAILER=smtp MAIL_HOST=mailpit MAIL_PORT=1025 RCON_HOST=127.0.0.1 RCON_PORT=3001 EMULATOR_IP=127.0.0.1 EMULATOR_PORT=3000 FLASH_CLIENT_ENABLED=false SWF_BASE_PATH=client/flash HABBO_SWF=Habbo.swf PRODUCTION_FOLDER=gordon/PRODUCTION APP_LOCALE=en FORCE_HTTPS=false TURNSTILE_SITE_KEY= TURNSTILE_SECRET_KEY= GOOGLE_RECAPTCHA_SITE_KEY= GOOGLE_RECAPTCHA_SECRET_KEY= PAYPAL_MODE= '; } private function runMigrationsWithSkip(): void { $ranMigrations = DB::table('migrations')->pluck('migration')->toArray(); $migrationFiles = File::files(base_path('database/migrations')); $pendingMigrations = []; foreach ($migrationFiles as $file) { $migrationName = str_replace('.php', '', $file->getFilename()); if (! in_array($migrationName, $ranMigrations)) { $pendingMigrations[] = $migrationName; } } if ($pendingMigrations === []) { $this->info('⏭️ No pending migrations (all already ran)'); } else { $this->info('📦 Running ' . count($pendingMigrations) . ' new migrations...'); $this->call('migrate', ['--force' => true]); } } private function runFullSetup(): int { $this->info('════════════════════════════════════════════════════════════'); $this->info('🚀 RUNNING FULL ATOM CMS SETUP'); $this->info('════════════════════════════════════════════════════════════'); $this->info(''); $fixed = 0; $errors = []; // Step 1: Fix .env if needed $this->info('[1/6] 📝 Checking .env file...'); try { $envContent = file_get_contents(base_path('.env')); if (! str_contains($envContent, "\n") && strlen($envContent) > 500) { $this->warn(' .env is broken - use --fix-env first!'); $errors[] = '.env file needs fixing (run: atom:check --fix-env)'; } else { $this->info(' ✅ .env looks good'); $fixed++; } } catch (\Exception $e) { $this->error(' ❌ .env error: ' . $e->getMessage()); $errors[] = '.env: ' . $e->getMessage(); } // Step 2: Clear caches $this->info('[2/6] 💨 Clearing caches...'); try { $this->call('cache:clear'); $this->call('config:clear'); $this->call('route:clear'); $this->call('view:clear'); $this->info(' ✅ Caches cleared'); $fixed++; } catch (\Exception $e) { $this->error(' ❌ Cache clear failed: ' . $e->getMessage()); $errors[] = 'Cache: ' . $e->getMessage(); } // Step 3: Run migrations $this->info('[3/6] 🗄️ Running migrations...'); try { $isWindows = DIRECTORY_SEPARATOR === '\\'; if ($isWindows) { $this->addMysqlToPath(); } $this->call('migrate', ['--force' => true]); $this->info(' ✅ Migrations complete'); $fixed++; } catch (\Exception $e) { $this->error(' ❌ Migration failed: ' . $e->getMessage()); $errors[] = 'Migrations: ' . $e->getMessage(); } // Step 4: Run seeders $this->info('[4/6] 🌱 Running seeders...'); try { $this->call('db:seed', ['--force' => true]); $this->info(' ✅ Seeders complete'); $fixed++; } catch (\Exception $e) { $this->warn(' ⚠️ Seeder warning: ' . $e->getMessage()); } // Step 5: Storage link $this->info('[5/6] 🔗 Setting up storage...'); try { if (File::exists(public_path('storage'))) { @unlink(public_path('storage')); } $this->call('storage:link'); $this->info(' ✅ Storage linked'); $fixed++; } catch (\Exception $e) { $this->error(' ❌ Storage link failed: ' . $e->getMessage()); $errors[] = 'Storage: ' . $e->getMessage(); } // Step 6: Build assets $this->info('[6/6] 🎨 Building assets...'); $isWindows = DIRECTORY_SEPARATOR === '\\'; try { if ($isWindows) { $this->info(' ⏭️ Skipped on Windows - run manually: npm install && npm run build'); } else { $manager = 'npm'; $cmd = 'npm install && npm run build'; if (File::exists(base_path('yarn.lock'))) { $manager = 'yarn'; $cmd = 'yarn && yarn build'; } elseif (File::exists(base_path('pnpm-lock.yaml'))) { $manager = 'pnpm'; $cmd = 'pnpm install && pnpm build'; } $this->info(" Building with {$manager}..."); exec("{$cmd} 2>&1", $output, $exitCode); if ($exitCode === 0) { $this->info(' ✅ Assets built'); $fixed++; } else { $this->warn(' ⚠️ Build failed - try manually'); } } } catch (\Exception $e) { $this->warn(' ⚠️ Build warning: ' . $e->getMessage()); } // Final optimizations $this->info(''); $this->info('🔧 Running optimizations...'); try { $this->call('config:cache'); $this->call('route:cache'); $this->call('view:cache'); $this->info(' ✅ Optimizations complete'); } catch (\Exception) { // Ignore } // Summary $this->info(''); $this->info('════════════════════════════════════════════════════════════'); if ($errors === []) { $this->info("🎉 SETUP COMPLETE! ({$fixed}/6 steps)"); $this->info('════════════════════════════════════════════════════════════'); $this->info(''); $this->info('📝 NEXT STEPS:'); $this->info(' 1. Start your server (nginx/apache/php artisan serve)'); $this->info(' 2. Visit your site URL'); $this->info(' 3. Login to Filament admin: /admin'); } else { $this->warn('⚠️ SETUP COMPLETED WITH ERRORS:'); $this->warn('════════════════════════════════════════════════════════════'); foreach ($errors as $error) { $this->warn(' - ' . $error); } $this->info(''); $this->info('💡 Try fixing errors and run again'); } return $errors === [] ? 0 : 1; } private function fixHttpsConfiguration(): void { $envPath = base_path('.env'); $envContent = File::get($envPath); $appUrl = config('app.url'); $isHttps = str_starts_with((string) $appUrl, 'https'); if (! $isHttps && str_starts_with((string) $appUrl, 'http://')) { $newAppUrl = str_replace('http://', 'https://', $appUrl); $envContent = preg_replace( '/^APP_URL=.*$/m', 'APP_URL=' . $newAppUrl, $envContent, ); File::put($envPath, $envContent); $this->info(' ✅ APP_URL updated to HTTPS'); } $forceHttpsPattern = '/^FORCE_HTTPS=.*$/m'; if (preg_match($forceHttpsPattern, (string) $envContent)) { $envContent = preg_replace('/^FORCE_HTTPS=.*$/m', 'FORCE_HTTPS=true', (string) $envContent); } else { $envContent .= "\nFORCE_HTTPS=true"; } File::put($envPath, $envContent); $this->info(' ✅ FORCE_HTTPS=true added to .env'); if ($this->isNginx()) { $this->fixNginxHttpsRedirect(); } $this->call('config:clear'); $this->info(' ✅ Configuration cleared'); } private function fixNginxHttpsRedirect(): void { $nginxConfigs = glob('/etc/nginx/sites-*/*'); $domain = parse_url((string) config('app.url'), PHP_URL_HOST); foreach ($nginxConfigs as $config) { if (is_file($config) && is_readable($config)) { $content = File::get($config); if (str_contains($content, $domain)) { $hasHttpsRedirect = str_contains($content, 'return 301 https://'); $hasHttpBlock = str_contains($content, 'listen 80'); if ($hasHttpBlock && ! $hasHttpsRedirect) { $this->info(' ⚠️ Found nginx config but no HTTP->HTTPS redirect'); $this->info(' 💡 Add to your nginx config for port 80:'); $this->info(' return 301 https://$server_name$request_uri;'); } return; } } } $this->info(' ⚠️ Could not find nginx config automatically'); $this->info(' 💡 Create /etc/nginx/sites-available/your-site with HTTP->HTTPS redirect'); } private function fixFilament(): void { $platform = $this->detectPlatform(); $this->info(' 🧹 Clearing Filament caches...'); // Clear all Laravel caches $this->call('cache:clear'); $this->call('config:clear'); $this->call('route:clear'); $this->call('view:clear'); $this->call('event:clear'); $this->call('optimize:clear'); $this->info(' 🎨 Publishing Filament assets...'); // Publish and install Filament try { $this->call('filament:install', ['--force' => true]); } catch (\Exception $e) { $this->warn(' ⚠️ Filament install warning: ' . $e->getMessage()); } // Platform-specific fixes if ($this->isLinux()) { $this->info(' 🐧 Applying Linux-specific fixes...'); // Fix permissions for storage $this->fixStoragePermissions(); // Clear OPcache if available if (function_exists('opcache_get_status') && opcache_get_status() !== false) { @opcache_reset(); $this->info(' ✅ OPcache cleared'); } } elseif ($this->isWindows()) { $this->info(' 🪟 Applying Windows-specific fixes...'); // On Windows, just ensure storage is writable $this->fixStoragePermissions(); } // Rebuild caches $this->info(' 💾 Rebuilding caches...'); $this->call('config:cache'); $this->call('route:cache'); $this->call('view:cache'); $this->call('event:cache'); $this->call('optimize'); // Web server specific fixes if ($platform === 'nginx') { $this->info(' 🌐 Fixing Nginx configuration...'); $this->fixNginxConfig(); } elseif (in_array($platform, ['apache', 'wamp', 'xampp'], true)) { $this->info(' 🌐 Fixing Apache configuration...'); $this->fixApacheConfig(); } $this->info(' ✅ Filament repair complete for ' . strtoupper($platform)); } private function fixStoragePermissions(): void { $directories = [ storage_path('app'), storage_path('app/public'), storage_path('framework/cache'), storage_path('framework/cache/data'), storage_path('framework/sessions'), storage_path('framework/views'), storage_path('logs'), ]; foreach ($directories as $dir) { if (is_dir($dir)) { @chmod($dir, 0755); } } } private function fixNginxConfig(): void { // Check if nginx config has proper fastcgi settings $nginxConf = '/etc/nginx/nginx.conf'; if (file_exists($nginxConf)) { $content = file_get_contents($nginxConf); if (! str_contains($content, 'fastcgi_buffer_size')) { $this->warn(' ⚠️ Consider adding fastcgi_buffer_size to nginx.conf for better performance'); } } } private function fixApacheConfig(): void { // Ensure .htaccess is properly configured $htaccess = public_path('.htaccess'); if (file_exists($htaccess)) { $content = file_get_contents($htaccess); // Check for required directives if (! str_contains($content, 'Options -MultiViews')) { $this->warn(' ⚠️ Consider adding "Options -MultiViews" to .htaccess'); } } } private function fixFilamentPages(): void { $this->info('🔧 Checking and fixing missing Filament pages, widgets and views...'); // Define ALL expected files: [source path in git, target local path] $files = [ // AlertSettings page [ 'git' => 'app/Filament/Pages/Monitoring/AlertSettings.php', 'local' => base_path('app/Filament/Pages/Monitoring/AlertSettings.php'), ], [ 'git' => 'resources/views/filament/pages/monitoring/alert-settings.blade.php', 'local' => base_path('resources/views/filament/pages/monitoring/alert-settings.blade.php'), ], // UpdateCheckerWidget [ 'git' => 'app/Filament/Widgets/UpdateCheckerWidget.php', 'local' => base_path('app/Filament/Widgets/UpdateCheckerWidget.php'), ], [ 'git' => 'resources/views/filament/widgets/update-checker.blade.php', 'local' => base_path('resources/views/filament/widgets/update-checker.blade.php'), ], // Services [ 'git' => 'app/Services/EmulatorUpdateService.php', 'local' => base_path('app/Services/EmulatorUpdateService.php'), ], [ 'git' => 'app/Services/NitroUpdateService.php', 'local' => base_path('app/Services/NitroUpdateService.php'), ], [ 'git' => 'app/Services/RconService.php', 'local' => base_path('app/Services/RconService.php'), ], [ 'git' => 'app/Services/AlertService.php', 'local' => base_path('app/Services/AlertService.php'), ], [ 'git' => 'app/Services/AlertSettings.php', 'local' => base_path('app/Services/AlertSettings.php'), ], ]; foreach ($files as $file) { if (! file_exists($file['local'])) { $this->info(" Looking for {$file['git']} in git history..."); $content = null; $result = $this->runSystemCommand("git show HEAD:{$file['git']}"); if ($result['success'] && ! empty($result['output'])) { $content = $result['output']; } else { $this->warn(" Could not find {$file['git']} in git HEAD. Skipping."); continue; } if ($content !== null) { $dir = dirname($file['local']); if (! is_dir($dir)) { mkdir($dir, 0755, true); $this->info(" Created directory: {$dir}"); } if (file_put_contents($file['local'], $content) !== false) { $this->info(" ✅ Restored {$file['local']}"); } else { $this->error(" ❌ Failed to write {$file['local']}"); } } } else { $this->info(" ✅ {$file['git']} exists"); } } // Check PHP syntax of all important files $this->info("\n🔍 Checking PHP syntax of critical files..."); $phpFiles = [ 'app/Filament/Pages/Monitoring/AlertSettings.php', 'app/Filament/Widgets/UpdateCheckerWidget.php', 'app/Services/EmulatorUpdateService.php', 'app/Services/NitroUpdateService.php', 'app/Services/RconService.php', 'app/Services/AlertService.php', 'app/Services/AlertSettings.php', ]; foreach ($phpFiles as $phpFile) { $fullPath = base_path($phpFile); if (file_exists($fullPath)) { $result = $this->runSystemCommand("php -l {$fullPath} 2>&1"); if (str_contains((string) $result['output'], 'Errors parsing')) { $this->error(" ❌ Syntax error in {$phpFile}: {$result['output']}"); } else { $this->info(" ✅ {$phpFile} syntax OK"); } } } // Check file permissions $this->info("\n🔍 Checking file permissions..."); $dirs = [ storage_path(), base_path('bootstrap/cache'), public_path('uploads'), public_path('storage'), ]; foreach ($dirs as $dir) { if (is_dir($dir)) { if (is_writable($dir)) { $this->info(" ✅ {$dir} is writable"); } else { $this->error(" ❌ {$dir} is NOT writable"); } } else { $this->warn(" ⚠️ {$dir} does not exist"); } } // Check database connection $this->info("\n🔍 Checking database connection..."); try { DB::connection()->getPdo(); $this->info(' ✅ Database connection OK'); } catch (\Exception $e) { $this->error(' ❌ Database connection failed: ' . $e->getMessage()); } // Check required tables $this->info("\n🔍 Checking required tables..."); $requiredTables = ['users', 'permissions', 'website_settings', 'rooms', 'items']; foreach ($requiredTables as $table) { if (Schema::hasTable($table)) { $count = DB::table($table)->count(); $this->info(" ✅ {$table} exists ({$count} rows)"); } else { $this->error(" ❌ {$table} does NOT exist"); } } // Check RCON connection $this->info("\n🔍 Checking RCON connection..."); try { $rcon = new RconService; if ($rcon->isConnected()) { $this->info(' ✅ RCON connection OK'); } else { $this->warn(' ⚠️ RCON not connected (emulator might be offline)'); } } catch (\Exception $e) { $this->warn(' ⚠️ RCON check failed: ' . $e->getMessage()); } // Check emulator service $this->info("\n🔍 Checking emulator service..."); $serviceName = setting('emulator_service_name', 'emulator'); $result = $this->runSystemCommand("systemctl is-active {$serviceName} 2>/dev/null || echo 'inactive'"); if (trim((string) $result['output']) === 'active') { $this->info(" ✅ Emulator service '{$serviceName}' is running"); } else { $this->warn(" ⚠️ Emulator service '{$serviceName}' is not running"); } // Check emulator JAR $this->info("\n🔍 Checking emulator JAR..."); $jarPath = setting('emulator_jar_path', '/var/www/Emulator'); $jarFiles = glob("{$jarPath}/*.jar"); if ($jarFiles !== [] && $jarFiles !== false) { $this->info(' ✅ Found JAR: ' . basename($jarFiles[0])); } else { $this->warn(" ⚠️ No JAR found in {$jarPath}"); } // Check Nitro client $this->info("\n🔍 Checking Nitro client..."); $nitroPath = setting('nitro_client_path', '/var/www/atomcms/nitro-client'); if (is_dir($nitroPath)) { $this->info(' ✅ Nitro client directory exists'); if (file_exists("{$nitroPath}/package.json")) { $this->info(' ✅ package.json exists'); } else { $this->warn(' ⚠️ package.json missing'); } } else { $this->warn(' ⚠️ Nitro client directory not found'); } // Check webroot $this->info("\n🔍 Checking webroot..."); $webroot = setting('nitro_webroot', '/var/www/Client'); if (is_dir($webroot)) { $this->info(" ✅ Webroot exists: {$webroot}"); $indexFile = "{$webroot}/index.html"; if (file_exists($indexFile)) { $this->info(' ✅ index.html exists'); } else { $this->warn(' ⚠️ index.html missing in webroot'); } } else { $this->warn(" ⚠️ Webroot not found: {$webroot}"); } $this->info("\n✅ All checks complete!"); } }