diff --git a/Updated_Cms/.env.testing.example b/Updated_Cms/.env.testing.example deleted file mode 100644 index 52e0da8ce9..0000000000 --- a/Updated_Cms/.env.testing.example +++ /dev/null @@ -1,41 +0,0 @@ -APP_NAME=Atom -APP_ENV=testing -APP_KEY=base64:GENERATE_NEW_KEY_HERE -APP_DEBUG=true -APP_URL=http://localhost - -LOG_CHANNEL=stack -LOG_DEPRECATIONS_CHANNEL=null -LOG_LEVEL=debug - -DB_CONNECTION=mariadb -DB_HOST=127.0.0.1 -DB_PORT=3306 -DB_DATABASE=testing -DB_USERNAME=docker -DB_PASSWORD=password - -BROADCAST_DRIVER=log -CACHE_DRIVER=array -FILESYSTEM_DISK=local -QUEUE_CONNECTION=sync -SESSION_DRIVER=array -SESSION_LIFETIME=120 - -MAIL_MAILER=array - -# Testing specific settings -BCRYPT_ROUNDS=4 -TELESCOPE_ENABLED=false - -# Disable CAPTCHA for testing -GOOGLE_RECAPTCHA_ENABLED=0 -CLOUDFLARE_TURNSTILE_ENABLED=0 - -# Cloudflare Turnstile (test keys) -TURNSTILE_SITE_KEY=1x00000000000000000000AA -TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA - -# Google reCAPTCHA (dummy values for testing) -GOOGLE_RECAPTCHA_SITE_KEY=dummy_site_key -GOOGLE_RECAPTCHA_SECRET_KEY=dummy_secret_key \ No newline at end of file diff --git a/Updated_Cms/app/Helpers/helper.php b/Updated_Cms/app/Helpers/helper.php index 19eafa0662..e92b991404 100644 --- a/Updated_Cms/app/Helpers/helper.php +++ b/Updated_Cms/app/Helpers/helper.php @@ -37,12 +37,33 @@ if (! function_exists('strLimit')) { if (! function_exists('findMigration')) { function findMigration(string $tableName): string { - foreach (glob(database_path('migrations/*.php')) as $filename) { - $content = file_get_contents($filename); - if ($content !== false && str_contains($content, "Schema::create('$tableName'")) { - return basename($filename); - } + static $cache = []; + + if (isset($cache[$tableName])) { + return $cache[$tableName]; } + + $migrationsPath = database_path('migrations'); + if (! is_dir($migrationsPath)) { + return ''; + } + + try { + $files = new \DirectoryIterator($migrationsPath); + foreach ($files as $file) { + if ($file->isFile() && $file->getExtension() === 'php') { + $content = @file_get_contents($file->getPathname()); + if ($content !== false && str_contains($content, "Schema::create('$tableName'")) { + $cache[$tableName] = $file->getFilename(); + return $file->getFilename(); + } + } + } + } catch (\Exception $e) { + \Illuminate\Support\Facades\Log::error("Error finding migration for table {$tableName}: {$e->getMessage()}"); + } + + $cache[$tableName] = ''; return ''; } } diff --git a/Updated_Cms/app/Http/Controllers/Badge/BadgeController.php b/Updated_Cms/app/Http/Controllers/Badge/BadgeController.php index c6f8920a77..98918ade33 100644 --- a/Updated_Cms/app/Http/Controllers/Badge/BadgeController.php +++ b/Updated_Cms/app/Http/Controllers/Badge/BadgeController.php @@ -18,7 +18,7 @@ class BadgeController extends Controller private const int MAX_BADGE_SIZE_BYTES = 40960; - public function show(SettingsService $settingsService) + public function show(SettingsService $settingsService): \Illuminate\Contracts\View\View { $cost = (int) $settingsService->getOrDefault('drawbadge_currency_value', 150); $currencyType = $settingsService->getOrDefault('drawbadge_currency_type', 'credits'); @@ -41,7 +41,7 @@ class BadgeController extends Controller return view('draw-badge', ['cost' => $cost, 'currencyType' => $currencyType, 'folderError' => $folderError, 'errorMessage' => $errorMessage]); } - public function buy(Request $request, SendCurrency $sendCurrency, SettingsService $settingsService) + public function buy(Request $request, SendCurrency $sendCurrency, SettingsService $settingsService): \Illuminate\Http\JsonResponse { $user = Auth::user(); $cost = (int) $settingsService->getOrDefault('drawbadge_currency_value', 150); diff --git a/Updated_Cms/app/Http/Controllers/User/ProfileController.php b/Updated_Cms/app/Http/Controllers/User/ProfileController.php index eb96489f5b..b07c10706c 100644 --- a/Updated_Cms/app/Http/Controllers/User/ProfileController.php +++ b/Updated_Cms/app/Http/Controllers/User/ProfileController.php @@ -6,10 +6,12 @@ use App\Http\Controllers\Controller; use App\Models\Game\Guild\GuildMember; use App\Models\Game\Player\MessengerFriendship; use App\Models\User; +use Illuminate\Contracts\View\View; +use Illuminate\Database\Eloquent\Collection; class ProfileController extends Controller { - public function __invoke(User $user) + public function __invoke(User $user): View { $user = $this->loadUserRelations($user); @@ -41,7 +43,7 @@ class ProfileController extends Controller ]); } - private function getUserFriends(int $userId) + private function getUserFriends(int $userId): Collection { return MessengerFriendship::select('user_two_id') ->where('user_one_id', '=', $userId) @@ -52,7 +54,7 @@ class ProfileController extends Controller ->get(); } - private function getUserGroups(int $userId) + private function getUserGroups(int $userId): Collection { return GuildMember::query() ->select(['guilds_members.id', 'guilds_members.guild_id', 'guilds_members.user_id', 'guilds.name', 'guilds.badge']) diff --git a/Updated_Cms/app/Observers/WebsiteDrawBadgeObserver.php b/Updated_Cms/app/Observers/WebsiteDrawBadgeObserver.php index 17671a303c..b37f537324 100644 --- a/Updated_Cms/app/Observers/WebsiteDrawBadgeObserver.php +++ b/Updated_Cms/app/Observers/WebsiteDrawBadgeObserver.php @@ -4,6 +4,8 @@ namespace App\Observers; use App\Models\WebsiteDrawBadge; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\Log; class WebsiteDrawBadgeObserver { @@ -21,7 +23,6 @@ class WebsiteDrawBadgeObserver ->where('badge_code', $badgeCode) ->delete(); - // Remove from JSON $this->updateExternalTexts(false, $badgeCode); return; @@ -40,30 +41,63 @@ class WebsiteDrawBadgeObserver ]); } - // Add to JSON $this->updateExternalTexts(true, $badgeCode, $websiteDrawBadge->badge_name, $websiteDrawBadge->badge_desc); } protected function updateExternalTexts(bool $add, string $badgeCode, ?string $name = null, ?string $desc = null): void { - $filePath = DB::table('website_settings')->where('key', 'nitro_external_texts_file')->value('value'); + try { + $filePath = DB::table('website_settings')->where('key', 'nitro_external_texts_file')->value('value'); - if (! $filePath || ! file_exists($filePath) || ! is_writable($filePath)) { - return; - } + if (! $filePath) { + return; + } - $json = json_decode(file_get_contents($filePath), true); + $filePath = str_replace(['../', '..\\'], '', $filePath); - if ($add) { - $json = array_merge($json, [ - "badge_name_{$badgeCode}" => $name, - "badge_desc_{$badgeCode}" => $desc, + if (! file_exists($filePath) || ! is_file($filePath) || ! is_writable($filePath)) { + return; + } + + $realPath = realpath($filePath); + if ($realPath === false) { + return; + } + + $content = file_get_contents($realPath); + if ($content === false) { + Log::warning("Failed to read external texts file: {$realPath}"); + return; + } + + $json = json_decode($content, true); + if (! is_array($json)) { + Log::error("Invalid JSON in external texts file: {$realPath}"); + return; + } + + if ($add) { + $json["badge_name_{$badgeCode}"] = $name; + $json["badge_desc_{$badgeCode}"] = $desc; + } else { + unset($json["badge_name_{$badgeCode}"]); + unset($json["badge_desc_{$badgeCode}"]); + } + + $jsonContent = json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + if ($jsonContent === false) { + Log::error("Failed to encode JSON for external texts file: {$realPath}"); + return; + } + + if (file_put_contents($realPath, $jsonContent, LOCK_EX) === false) { + Log::error("Failed to write external texts file: {$realPath}"); + } + } catch (\Throwable $e) { + Log::error("Error updating external texts: {$e->getMessage()}", [ + 'badge_code' => $badgeCode, + 'exception' => $e, ]); - } else { - unset($json["badge_name_{$badgeCode}"]); - unset($json["badge_desc_{$badgeCode}"]); } - - file_put_contents($filePath, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } } diff --git a/Updated_Cms/app/Services/PermissionsService.php b/Updated_Cms/app/Services/PermissionsService.php index fb0c350ece..6b597022d5 100644 --- a/Updated_Cms/app/Services/PermissionsService.php +++ b/Updated_Cms/app/Services/PermissionsService.php @@ -12,17 +12,34 @@ class PermissionsService public function __construct() { - Cache::remember('website_permissions', now()->addMinutes(30), fn () => WebsitePermission::all()->pluck('min_rank', 'permission')); - - $this->permissions = Cache::get('website_permissions'); + $this->permissions = Cache::remember( + 'website_permissions', + now()->addMinutes(30), + fn () => WebsitePermission::all()->pluck('min_rank', 'permission') + ); } public function getOrDefault(string $permissionName, bool $default = false): bool { - if (! array_key_exists($permissionName, $this->permissions->toArray())) { + if (! $this->permissions instanceof Collection || ! $this->permissions->has($permissionName)) { return $default; } - return auth()->check() && auth()->user()->rank >= (int) $this->permissions->get($permissionName); + if (! auth()->check()) { + return false; + } + + return auth()->user()->rank >= (int) $this->permissions->get($permissionName); + } + + public function refresh(): void + { + Cache::forget('website_permissions'); + + $this->permissions = Cache::remember( + 'website_permissions', + now()->addMinutes(30), + fn () => WebsitePermission::all()->pluck('min_rank', 'permission') + ); } } diff --git a/Updated_Cms/app/Services/RconService.php b/Updated_Cms/app/Services/RconService.php index b31e95f168..d23070f847 100644 --- a/Updated_Cms/app/Services/RconService.php +++ b/Updated_Cms/app/Services/RconService.php @@ -32,16 +32,19 @@ class RconService if (! $this->socket) { $error = socket_strerror(socket_last_error()); - Log::error("RCON initialization failed: $error"); + Log::error("RCON initialization failed: {$error}"); $this->closeConnection(); return; } + socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 5, 'usec' => 0]); + socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 5, 'usec' => 0]); + if (! @socket_connect($this->socket, $this->config['ip'], $this->config['port'])) { $error = socket_strerror(socket_last_error()); - Log::error("RCON connection failed: $error"); + Log::error("RCON connection failed: {$error}"); $this->closeConnection(); @@ -53,7 +56,7 @@ class RconService private function closeConnection(): void { - if ($this->socket instanceof \Socket) { + if ($this->socket instanceof Socket) { socket_close($this->socket); } @@ -70,29 +73,24 @@ class RconService * @throws RconConnectionException * @throws JsonException */ - public function sendCommand(string $command, ?array $data = null) + public function sendCommand(string $command, ?array $data = null): bool { if (! $this->isConnected) { - $error = 'RCON command failed: Not connected'; - Log::error($error); - + Log::error('RCON command failed: Not connected'); $this->closeConnection(); - - return $this->isConnected; + return false; } $payload = json_encode(['key' => $command, 'data' => $data], JSON_THROW_ON_ERROR); if (! @socket_write($this->socket, $payload, strlen($payload))) { $error = socket_strerror(socket_last_error($this->socket)); - Log::error("RCON command ($command) failed: $error"); - + Log::error("RCON command ({$command}) failed: {$error}"); $this->closeConnection(); - - return $this->isConnected; + return false; } - return $this->isConnected; + return true; } /** @@ -254,4 +252,9 @@ class RconService 'command' => $command, ]); } + + public function __destruct() + { + $this->closeConnection(); + } } diff --git a/Updated_Cms/app/Services/SettingsService.php b/Updated_Cms/app/Services/SettingsService.php index 772b9c65d7..6133e9911e 100644 --- a/Updated_Cms/app/Services/SettingsService.php +++ b/Updated_Cms/app/Services/SettingsService.php @@ -15,9 +15,11 @@ class SettingsService public function __construct() { try { - Cache::remember('website_settings', now()->addMinutes(5), fn () => Schema::hasTable('website_settings') ? WebsiteSetting::all()->pluck('value', 'key') : collect()); - - $this->settings = Cache::get('website_settings'); + $this->settings = Cache::remember( + 'website_settings', + now()->addMinutes(5), + fn () => Schema::hasTable('website_settings') ? WebsiteSetting::all()->pluck('value', 'key') : collect() + ); } catch (Throwable) { $this->settings = collect(); } @@ -25,10 +27,25 @@ class SettingsService public function getOrDefault(string $settingName, ?string $default = null): string { - if (! $this->settings instanceof \Illuminate\Support\Collection) { + if (! $this->settings instanceof Collection) { return (string) $default; } return (string) $this->settings->get($settingName, $default); } + + public function refresh(): void + { + Cache::forget('website_settings'); + + try { + $this->settings = Cache::remember( + 'website_settings', + now()->addMinutes(5), + fn () => Schema::hasTable('website_settings') ? WebsiteSetting::all()->pluck('value', 'key') : collect() + ); + } catch (Throwable) { + $this->settings = collect(); + } + } } diff --git a/Updated_Cms/config/theme.php b/Updated_Cms/config/theme.php index 1501e840e1..35c54142a4 100644 --- a/Updated_Cms/config/theme.php +++ b/Updated_Cms/config/theme.php @@ -9,7 +9,7 @@ return [ | It will assign the default active theme to be used if one is not set during | runtime. */ - 'active' => 'atom', + 'active' => env('THEME_ACTIVE', 'atom'), /* |-------------------------------------------------------------------------- @@ -21,7 +21,7 @@ return [ | file is not found in the currently active theme, then it will look for it | in the parent theme. */ - 'parent' => 'atom', + 'parent' => env('THEME_PARENT', 'atom'), /* |--------------------------------------------------------------------------