You've already forked Atomcms-edit
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:
@@ -69,7 +69,7 @@ class AutoDetectService
|
||||
return $found;
|
||||
}
|
||||
|
||||
return '/var/www/emulator-source';
|
||||
return $this->emulatorPath('../emulator-source');
|
||||
}
|
||||
|
||||
public function detectEmulatorServiceName(): string
|
||||
@@ -214,9 +214,9 @@ class AutoDetectService
|
||||
}
|
||||
|
||||
$possiblePaths = [
|
||||
'/var/www/Client', '/var/www/client', '/var/www/Nitro', '/var/www/nitro',
|
||||
'/var/www/html/Client', '/var/www/html/client',
|
||||
'/var/www/atomcms/public/Client', '/var/www/atomcms/public/nitro',
|
||||
$this->clientPath('../Client'), $this->clientPath('../client'), $this->clientPath('../Nitro'), $this->clientPath('../nitro'),
|
||||
$this->clientPath('../../html/Client'), $this->clientPath('../../html/client'),
|
||||
$this->clientPath('../../../atomcms/public/Client'), $this->clientPath('../../../atomcms/public/nitro'),
|
||||
];
|
||||
|
||||
foreach ($possiblePaths as $path) {
|
||||
@@ -225,7 +225,7 @@ class AutoDetectService
|
||||
}
|
||||
}
|
||||
|
||||
return '/var/www/Client';
|
||||
return $this->clientPath('../Client');
|
||||
}
|
||||
|
||||
public function detectNitroBuildPath(): string
|
||||
@@ -247,8 +247,8 @@ class AutoDetectService
|
||||
}
|
||||
|
||||
$possiblePaths = [
|
||||
'/var/www/nitro-client/dist', '/var/www/nitro-V3/dist',
|
||||
'/var/www/atomcms/nitro-client/dist',
|
||||
$this->clientPath('dist'), $this->clientPath('../nitro-V3/dist'),
|
||||
base_path('nitro-client/dist'),
|
||||
];
|
||||
|
||||
foreach ($possiblePaths as $path) {
|
||||
@@ -290,7 +290,7 @@ class AutoDetectService
|
||||
|
||||
// Last resort: check common paths
|
||||
$possiblePaths = [
|
||||
'/var/www/atomcms/public/gamedata', '/var/www/html/gamedata',
|
||||
$this->gamedataPath('../../../atomcms/public/gamedata'), $this->gamedataPath('../../html/gamedata'),
|
||||
'/opt/gamedata', '/root/gamedata',
|
||||
];
|
||||
|
||||
@@ -300,7 +300,7 @@ class AutoDetectService
|
||||
}
|
||||
}
|
||||
|
||||
return '/var/www/gamedata';
|
||||
return $this->gamedataPath('../gamedata');
|
||||
}
|
||||
|
||||
public function detectFurnitureIconsPath(): string
|
||||
@@ -317,7 +317,7 @@ class AutoDetectService
|
||||
return $iconsPath;
|
||||
}
|
||||
|
||||
return '/var/www/Gamedata/icons';
|
||||
return $this->gamedataPath('../icons');
|
||||
}
|
||||
|
||||
public function detectCatalogIconsPath(): string
|
||||
@@ -334,7 +334,7 @@ class AutoDetectService
|
||||
return $cataloguePath;
|
||||
}
|
||||
|
||||
return '/var/www/Gamedata/catalogue';
|
||||
return $this->gamedataPath('../catalogue');
|
||||
}
|
||||
|
||||
// ─── Nginx ─────────────────────────────────────────────────
|
||||
@@ -700,6 +700,39 @@ class AutoDetectService
|
||||
return trim($result->output()) === 'yes';
|
||||
}
|
||||
|
||||
private function emulatorPath(string $path = ''): string
|
||||
{
|
||||
$base = env('NITRO_EMULATOR_PATH', '/var/www/emulator');
|
||||
|
||||
if ($path === '') {
|
||||
return $base;
|
||||
}
|
||||
|
||||
return rtrim($base, '/') . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
private function clientPath(string $path = ''): string
|
||||
{
|
||||
$base = env('NITRO_CLIENT_DIR', '/var/www/nitro-client');
|
||||
|
||||
if ($path === '') {
|
||||
return $base;
|
||||
}
|
||||
|
||||
return rtrim($base, '/') . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
private function gamedataPath(string $path = ''): string
|
||||
{
|
||||
$base = env('NITRO_GAMEDATA_DIR', '/var/www/Gamedata/config');
|
||||
|
||||
if ($path === '') {
|
||||
return $base;
|
||||
}
|
||||
|
||||
return rtrim($base, '/') . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
private function getSetting(string $key): ?string
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ readonly class PointsService
|
||||
$collection = User::where('radio_points', '>', 0)
|
||||
->orderBy('radio_points', 'desc')
|
||||
->limit($limit)
|
||||
->get(['id', 'username', 'avatar', 'radio_points']);
|
||||
->get(['id', 'username', 'look', 'radio_points']);
|
||||
|
||||
return $collection->map(fn (User $user, int $index): array => [
|
||||
'rank' => $index + 1,
|
||||
|
||||
@@ -3,18 +3,33 @@
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class TraxService
|
||||
{
|
||||
private function gamedataPath(string $path = ''): string
|
||||
{
|
||||
return rtrim(env('NITRO_GAMEDATA_DIR', '/var/www/Gamedata/config'), '/') . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
private function soundsPath(string $path = ''): string
|
||||
{
|
||||
return rtrim(dirname($this->gamedataPath()), '/') . '/sounds/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
private function loadFurnitureData(): array
|
||||
{
|
||||
$path = $this->gamedataPath('FurnitureData.json');
|
||||
|
||||
return json_decode(file_get_contents($path), true) ?? [];
|
||||
}
|
||||
|
||||
public function syncSoundSets(): array
|
||||
{
|
||||
Log::info('[TraxService] Starting sound sets sync');
|
||||
|
||||
$furnitureData = json_decode(
|
||||
file_get_contents('/var/www/Gamedata/config/FurnitureData.json'),
|
||||
true,
|
||||
);
|
||||
$furnitureData = $this->loadFurnitureData();
|
||||
|
||||
$inserted = 0;
|
||||
$soundSets = [];
|
||||
@@ -61,7 +76,7 @@ class TraxService
|
||||
{
|
||||
Log::info('[TraxService] Starting soundtracks sync');
|
||||
|
||||
$soundFiles = glob('/var/www/Gamedata/sounds/sound_machine_sample_*.mp3');
|
||||
$soundFiles = glob($this->soundsPath('sound_machine_sample_*.mp3'));
|
||||
$existing = DB::table('soundtracks')->pluck('id')->toArray();
|
||||
|
||||
$inserted = 0;
|
||||
@@ -102,7 +117,7 @@ class TraxService
|
||||
{
|
||||
return [
|
||||
'soundtracks' => DB::table('soundtracks')->count(),
|
||||
'sound_samples' => count(glob('/var/www/Gamedata/sounds/sound_machine_sample_*.mp3') ?: []),
|
||||
'sound_samples' => count(glob($this->soundsPath('sound_machine_sample_*.mp3')) ?: []),
|
||||
'room_trax' => DB::table('room_trax')->count(),
|
||||
'trax_playlist' => DB::table('trax_playlist')->count(),
|
||||
'soundsets' => $this->countSoundSets(),
|
||||
@@ -111,10 +126,7 @@ class TraxService
|
||||
|
||||
private function countSoundSets(): int
|
||||
{
|
||||
$furnitureData = json_decode(
|
||||
file_get_contents('/var/www/Gamedata/config/FurnitureData.json'),
|
||||
true,
|
||||
);
|
||||
$furnitureData = $this->loadFurnitureData();
|
||||
|
||||
$count = 0;
|
||||
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
|
||||
@@ -135,14 +147,10 @@ class TraxService
|
||||
'failed' => 0,
|
||||
];
|
||||
|
||||
$soundPath = '/var/www/Gamedata/sounds';
|
||||
$soundPath = $this->soundsPath();
|
||||
$soundUrl = rtrim($baseUrl, '/') . '/gamedata/sounds';
|
||||
|
||||
// Get list of sound samples from FurnitureData
|
||||
$furnitureData = json_decode(
|
||||
file_get_contents('/var/www/Gamedata/config/FurnitureData.json'),
|
||||
true,
|
||||
);
|
||||
$furnitureData = $this->loadFurnitureData();
|
||||
|
||||
$sampleIds = [];
|
||||
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
|
||||
@@ -162,7 +170,7 @@ class TraxService
|
||||
|
||||
if (! file_exists($localFile)) {
|
||||
$url = $soundUrl . '/' . $filename;
|
||||
$content = @file_get_contents($url);
|
||||
$content = Http::timeout(10)->get($url)->body();
|
||||
|
||||
if ($content && strlen($content) > 1000) {
|
||||
file_put_contents($localFile, $content);
|
||||
|
||||
Reference in New Issue
Block a user