Files
Atomcms-edit/app/Services/TraxService.php
T
root f29ba72591 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
2026-06-29 18:28:19 +02:00

219 lines
6.5 KiB
PHP
Executable File

<?php
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 = $this->loadFurnitureData();
$inserted = 0;
$soundSets = [];
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
$classname = $item['classname'] ?? '';
// Collect sound_set items
if (str_starts_with((string) $classname, 'sound_set')) {
$setId = (int) str_replace('sound_set_', '', $classname);
$soundSets[] = [
'item_id' => $item['id'],
'set_id' => $setId,
'name' => $item['name'] ?? $classname,
];
}
}
// Insert sound sets into trax_songs (if table exists)
if (DB::getSchemaBuilder()->hasTable('soundsets')) {
DB::table('soundsets')->truncate();
foreach ($soundSets as $set) {
DB::table('soundsets')->insert([
'id' => $set['set_id'],
'name' => $set['name'],
'trackid' => $set['item_id'],
]);
$inserted++;
}
}
Log::info('[TraxService] Sound sets sync complete', [
'inserted' => $inserted,
]);
return [
'inserted' => $inserted,
'total_sets' => count($soundSets),
];
}
public function syncSoundtracks(): array
{
Log::info('[TraxService] Starting soundtracks sync');
$soundFiles = glob($this->soundsPath('sound_machine_sample_*.mp3'));
$existing = DB::table('soundtracks')->pluck('id')->toArray();
$inserted = 0;
foreach ($soundFiles as $file) {
$basename = basename($file, '.mp3');
preg_match('/sound_machine_sample_(\d+)/', $basename, $matches);
if (isset($matches[1]) && ($matches[1] !== '' && $matches[1] !== '0')) {
$sampleId = (int) $matches[1];
if (! in_array($sampleId, $existing)) {
DB::table('soundtracks')->insert([
'id' => $sampleId,
'code' => $basename,
'name' => 'Sample ' . $sampleId,
'author' => 'System',
'track' => '',
'length' => 0,
]);
$inserted++;
}
}
}
Log::info('[TraxService] Soundtracks sync complete', [
'inserted' => $inserted,
]);
return [
'inserted' => $inserted,
'total' => DB::table('soundtracks')->count(),
'samples' => count($soundFiles),
];
}
public function getStats(): array
{
return [
'soundtracks' => DB::table('soundtracks')->count(),
'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(),
];
}
private function countSoundSets(): int
{
$furnitureData = $this->loadFurnitureData();
$count = 0;
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
if (str_starts_with($item['classname'] ?? '', 'sound_set')) {
$count++;
}
}
return $count;
}
public function importFromUrl(string $baseUrl): array
{
Log::info('[TraxService] Importing sounds from: ' . $baseUrl);
$results = [
'downloaded' => 0,
'failed' => 0,
];
$soundPath = $this->soundsPath();
$soundUrl = rtrim($baseUrl, '/') . '/gamedata/sounds';
$furnitureData = $this->loadFurnitureData();
$sampleIds = [];
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
if (str_starts_with($item['classname'] ?? '', 'sound_set')) {
// Each sound set can have multiple samples
for ($i = 0; $i < 200; $i++) {
$sampleIds[] = $i;
}
}
}
$sampleIds = array_unique($sampleIds);
foreach ($sampleIds as $id) {
$filename = 'sound_machine_sample_' . $id . '.mp3';
$localFile = $soundPath . '/' . $filename;
if (! file_exists($localFile)) {
$url = $soundUrl . '/' . $filename;
$content = Http::timeout(10)->get($url)->body();
if ($content && strlen($content) > 1000) {
file_put_contents($localFile, $content);
$results['downloaded']++;
} else {
$results['failed']++;
}
}
}
Log::info('[TraxService] Import complete', $results);
return $results;
}
public function getTraxItems(): array
{
return DB::table('items_base')
->where('item_name', 'like', '%sound%')
->orWhere('item_name', 'like', '%trax%')
->orWhere('interaction_type', '=', 'sound')
->get()
->toArray();
}
public function linkSoundMachine(): bool
{
// Make sure sound_machine has correct interaction type
$machine = DB::table('items_base')
->where('item_name', 'sound_machine')
->first();
if ($machine) {
DB::table('items_base')
->where('id', $machine->id)
->update(['interaction_type' => 'sound_machine']);
Log::info('[TraxService] Sound machine linked');
return true;
}
return false;
}
}