Fix security, performance, and code quality issues across CMS

Security:
- Replace unescaped {!! !!} with Purify::clean() in 15+ Blade templates (XSS)
- Add rate limiting to register (3/hr), upload (10/min), SSE (6/min)
- Add max:5000 validation on article comments
- Remove duplicate exception handler callback

Hardcoded paths:
- Replace ~44 /var/www/ hardcoded paths with env() configs
- CatalogService (13), AutoDetectService (18), Commandocentrum (11), AppServiceProvider (2)

Performance:
- Add 10 missing database indexes (radio_song_requests, help_center_tickets, etc.)
- Replace Cache::flush() with targeted Cache::forget() in RadioSettings
- Cache getCachedCategories() in TicketController (N+1 fix)
- Remove redundant top-3 leaderboard query

Bug fixes:
- Fix undefined $enabled variable → $isOnline in radio index view
- Add getAvatarAttribute() accessor for non-existent avatar column
- Fix User::guilds() from wrong HasMany to HasManyThrough

Code quality:
- Replace file_get_contents with Http::timeout(10) in TraxService
- Remove commented Echo/Pusher boilerplate in bootstrap.js
- Remove TODO/FIXME comments from logo-generator templates
- Replace hardcoded Turnstile CDN URL with config()
- Restore QUEUE_CONNECTION=redis in .env.example files
This commit is contained in:
root
2026-06-29 18:28:19 +02:00
parent ef0aec4301
commit f29ba72591
30 changed files with 407 additions and 159 deletions
+70 -25
View File
@@ -60,6 +60,50 @@ class CatalogService
],
];
private ?array $furnitureDataCache = null;
private function gamedataPath(string $path = ''): string
{
$base = env('NITRO_GAMEDATA_DIR', '/var/www/Gamedata/config');
if ($path === '') {
return $base;
}
return $base . '/' . ltrim($path, '/');
}
private function storagePath(string $path = ''): string
{
if ($path === '') {
return storage_path();
}
return storage_path($path);
}
private function loadFurnitureData(): array
{
if ($this->furnitureDataCache !== null) {
return $this->furnitureDataCache;
}
$path = $this->gamedataPath('FurnitureData.json');
if (! file_exists($path)) {
$path = $this->gamedataPath('FurnitureData.json');
}
if (! file_exists($path)) {
$path = base_path('public/gamedata/config/FurnitureData.json');
}
$data = json_decode(file_get_contents($path), true);
$this->furnitureDataCache = $data['roomitemtypes']['furnitype'] ?? [];
return $this->furnitureDataCache;
}
public function getFurnitureData(): array
{
return $this->loadFurnitureData();
}
public function createCatalogPages(): array
{
$created = 0;
@@ -157,21 +201,6 @@ class CatalogService
return ['pages_created' => $created];
}
public function getFurnitureData(): array
{
$path = base_path('../Gamedata/config/FurnitureData.json');
if (! file_exists($path)) {
$path = '/var/www/Gamedata/config/FurnitureData.json';
}
if (! file_exists($path)) {
$path = base_path('public/gamedata/config/FurnitureData.json');
}
$data = json_decode(file_get_contents($path), true);
return $data['roomitemtypes']['furnitype'] ?? [];
}
private function createMainPage(string $name, int $order): int
{
$exists = DB::table('catalog_pages')->where('caption', $name)->first();
@@ -362,7 +391,7 @@ class CatalogService
// 1. Download FurnitureData.json
$furnitureUrl = $baseUrl . '/gamedata/config/FurnitureData.json';
$localPath = '/var/www/Gamedata/config/FurnitureData.json';
$localPath = $this->gamedataPath('FurnitureData.json');
try {
$content = @file_get_contents($furnitureUrl);
@@ -384,7 +413,7 @@ class CatalogService
try {
$content = @file_get_contents($productUrl);
if ($content && json_decode($content, true)) {
file_put_contents('/var/www/Gamedata/config/ProductData.json', $content);
file_put_contents($this->gamedataPath('ProductData.json'), $content);
$data = json_decode($content, true);
$results['product'] = count($data['productdata'] ?? []);
Log::info('[CatalogService] Downloaded ProductData.json');
@@ -407,7 +436,7 @@ class CatalogService
private function downloadIconsFromUrl(string $baseUrl): int
{
$downloaded = 0;
$iconsPath = '/var/www/Gamedata/icons';
$iconsPath = $this->gamedataPath('../icons');
$url = $baseUrl . '/gamedata/icons';
// Try to list files via HTTP (may not work)
@@ -456,7 +485,7 @@ class CatalogService
private function downloadCatalogImagesFromUrl(string $baseUrl): int
{
$downloaded = 0;
$catalogPath = '/var/www/Gamedata/catalogue';
$catalogPath = $this->gamedataPath('../catalogue');
$url = $baseUrl . '/gamedata/catalogue';
// Download common catalogue images
@@ -521,7 +550,7 @@ class CatalogService
}
// Save to file
$sqlPath = '/var/www/atomcms/storage/logs/catalog_update_' . date('Ymd_His') . '.sql';
$sqlPath = $this->storagePath('logs/catalog_update_' . date('Ymd_His') . '.sql');
file_put_contents($sqlPath, $sql);
return [
@@ -711,7 +740,7 @@ class CatalogService
// Step 1: Download FurnitureData.json
$furnitureUrl = rtrim($baseUrl, '/') . '/gamedata/config/FurnitureData.json';
$localFurniturePath = '/var/www/Gamedata/config/FurnitureData.json';
$localFurniturePath = $this->gamedataPath('FurnitureData.json');
$furnitureContent = @file_get_contents($furnitureUrl);
if ($furnitureContent && $furnitureData = json_decode($furnitureContent, true)) {
@@ -730,7 +759,7 @@ class CatalogService
$productUrl = rtrim($baseUrl, '/') . '/gamedata/config/ProductData.json';
$productContent = @file_get_contents($productUrl);
if ($productContent && json_decode($productContent, true)) {
file_put_contents('/var/www/Gamedata/config/ProductData.json', $productContent);
file_put_contents($this->gamedataPath('ProductData.json'), $productContent);
$results['product'] = 1;
}
@@ -877,7 +906,7 @@ class CatalogService
private function syncIcons(array $furnitureData, string $baseUrl): int
{
$downloaded = 0;
$iconsPath = '/var/www/Gamedata/icons';
$iconsPath = $this->gamedataPath('../icons');
$iconsUrl = rtrim($baseUrl, '/') . '/gamedata/icons';
$iconNames = [];
@@ -910,7 +939,7 @@ class CatalogService
private function syncCatalogImages(string $baseUrl): int
{
$downloaded = 0;
$catalogPath = '/var/www/Gamedata/catalogue';
$catalogPath = $this->gamedataPath('../catalogue');
$catalogUrl = rtrim($baseUrl, '/') . '/gamedata/catalogue';
$commonImages = [
@@ -951,7 +980,7 @@ class CatalogService
{
Log::info('[CatalogService] Starting catalog_clothing sync');
$figureMapPath = '/var/www/Gamedata/config/FigureMap.json';
$figureMapPath = $this->gamedataPath('FigureMap.json');
if (! file_exists($figureMapPath)) {
throw new \Exception('FigureMap.json niet gevonden');
@@ -1100,4 +1129,20 @@ class CatalogService
'total' => DB::table('catalog_clothing')->count(),
];
}
private function estimatePrice(string $category): int
{
return match ($category) {
'chair', 'table', 'shelf', 'bed', 'rug', 'floor', 'lighting',
'wall_decoration', 'divider', 'window' => 15,
'teleport', 'gate', 'roller' => 25,
'wired', 'wired_effect', 'wired_condition', 'wired_trigger', 'wired_add_on' => 10,
'music', 'sound_fx' => 20,
'vending_machine', 'games', 'fortuna' => 30,
'pets' => 50,
'food' => 5,
'trophy', 'present', 'credit', 'tent', 'extras', 'leaderboards' => 10,
default => 10,
};
}
}