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:
@@ -43,10 +43,6 @@ class Handler extends ExceptionHandler
|
|||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $e) {
|
||||||
$this->handleExceptionAlert($e);
|
$this->handleExceptionAlert($e);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->renderable(function (Throwable $e) {
|
|
||||||
$this->handleExceptionAlert($e);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleExceptionAlert(Throwable $e): void
|
private function handleExceptionAlert(Throwable $e): void
|
||||||
|
|||||||
@@ -104,15 +104,15 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
'emulator_database_username' => $this->getSetting('emulator_database_username', ''),
|
'emulator_database_username' => $this->getSetting('emulator_database_username', ''),
|
||||||
'emulator_database_password' => $this->getSetting('emulator_database_password', ''),
|
'emulator_database_password' => $this->getSetting('emulator_database_password', ''),
|
||||||
'emulator_version' => $this->getSetting('emulator_version', 'Onbekend'),
|
'emulator_version' => $this->getSetting('emulator_version', 'Onbekend'),
|
||||||
'nitro_emulator_path' => $this->getSetting('nitro_emulator_path', '/var/www/emulator'),
|
'nitro_emulator_path' => $this->getSetting('nitro_emulator_path', $this->emulatorPath()),
|
||||||
'nitro_emulator_service' => $this->getSetting('nitro_emulator_service', 'emulator'),
|
'nitro_emulator_service' => $this->getSetting('nitro_emulator_service', 'emulator'),
|
||||||
'nitro_db_name' => $this->getSetting('nitro_db_name', 'habbo'),
|
'nitro_db_name' => $this->getSetting('nitro_db_name', 'habbo'),
|
||||||
'nitro_sql_dir' => $this->getSetting('nitro_sql_dir', '/var/www/emulator/Database Updates'),
|
'nitro_sql_dir' => $this->getSetting('nitro_sql_dir', $this->emulatorPath('Database Updates')),
|
||||||
'nitro_backup_dir' => $this->getSetting('nitro_backup_dir', '/var/www/emulator/Database Updates/backups'),
|
'nitro_backup_dir' => $this->getSetting('nitro_backup_dir', $this->emulatorPath('Database Updates/backups')),
|
||||||
'nitro_gamedata_dir' => $this->getSetting('nitro_gamedata_dir', '/var/www/Gamedata/config'),
|
'nitro_gamedata_dir' => $this->getSetting('nitro_gamedata_dir', $this->gamedataPath()),
|
||||||
'nitro_client_dir' => $this->getSetting('nitro_client_dir', '/var/www/Nitro-V3/public/configuration'),
|
'nitro_client_dir' => $this->getSetting('nitro_client_dir', $this->nitroV3Path('public/configuration')),
|
||||||
'nitro_client_src' => $this->getSetting('nitro_client_src', '/var/www/Nitro-V3'),
|
'nitro_client_src' => $this->getSetting('nitro_client_src', $this->nitroV3Path()),
|
||||||
'nitro_renderer_src' => $this->getSetting('nitro_renderer_src', '/var/www/Nitro_Render_V3'),
|
'nitro_renderer_src' => $this->getSetting('nitro_renderer_src', $this->nitroRendererV3Path()),
|
||||||
'hotel_alert_message' => '',
|
'hotel_alert_message' => '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -350,9 +350,9 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
$serviceStatus = $this->runCommand('systemctl is-active ' . escapeshellarg($serviceName) . ' 2>/dev/null') ?: 'inactive';
|
$serviceStatus = $this->runCommand('systemctl is-active ' . escapeshellarg($serviceName) . ' 2>/dev/null') ?: 'inactive';
|
||||||
$serviceColor = $serviceStatus === 'active' ? '#22c55e' : '#ef4444';
|
$serviceColor = $serviceStatus === 'active' ? '#22c55e' : '#ef4444';
|
||||||
|
|
||||||
$nitroClientPath = $this->getSetting('nitro_client_path', '/var/www/nitro-client');
|
$nitroClientPath = $this->getSetting('nitro_client_path', $this->nitroClientPath());
|
||||||
$nitroRendererPath = $this->getSetting('nitro_renderer_path', '/var/www/nitro-renderer');
|
$nitroRendererPath = $this->getSetting('nitro_renderer_path', $this->nitroRendererPath());
|
||||||
$nitroWebroot = $this->getSetting('nitro_webroot', '/var/www/Client');
|
$nitroWebroot = $this->getSetting('nitro_webroot', $this->clientWebrootPath());
|
||||||
|
|
||||||
$clientCommit = $this->getGitCommit($nitroClientPath);
|
$clientCommit = $this->getGitCommit($nitroClientPath);
|
||||||
$rendererCommit = $this->getGitCommit($nitroRendererPath);
|
$rendererCommit = $this->getGitCommit($nitroRendererPath);
|
||||||
@@ -685,7 +685,7 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
$settings->set('emulator_jar_direct_url', $this->data['emulator_jar_direct_url'] ?? '');
|
$settings->set('emulator_jar_direct_url', $this->data['emulator_jar_direct_url'] ?? '');
|
||||||
$settings->set('emulator_jar_path', $this->data['emulator_jar_path'] ?? '/root/emulator');
|
$settings->set('emulator_jar_path', $this->data['emulator_jar_path'] ?? '/root/emulator');
|
||||||
$settings->set('emulator_source_repo', $this->data['emulator_source_repo'] ?? '');
|
$settings->set('emulator_source_repo', $this->data['emulator_source_repo'] ?? '');
|
||||||
$settings->set('emulator_source_path', $this->data['emulator_source_path'] ?? '/var/www/emulator-source');
|
$settings->set('emulator_source_path', $this->data['emulator_source_path'] ?? $this->emulatorSourcePath());
|
||||||
$settings->set('emulator_github_branch', $this->data['emulator_github_branch'] ?? 'main');
|
$settings->set('emulator_github_branch', $this->data['emulator_github_branch'] ?? 'main');
|
||||||
$settings->set('emulator_database_host', $this->data['emulator_database_host'] ?? '127.0.0.1');
|
$settings->set('emulator_database_host', $this->data['emulator_database_host'] ?? '127.0.0.1');
|
||||||
$settings->set('emulator_database_name', $this->data['emulator_database_name'] ?? '');
|
$settings->set('emulator_database_name', $this->data['emulator_database_name'] ?? '');
|
||||||
@@ -885,4 +885,60 @@ final class Commandocentrum extends Page implements HasForms
|
|||||||
{
|
{
|
||||||
return $this->runCommand('cat ' . escapeshellarg($path) . ' 2>/dev/null');
|
return $this->runCommand('cat ' . escapeshellarg($path) . ' 2>/dev/null');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function emulatorPath(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_EMULATOR_PATH', '/var/www/emulator'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function nitroClientPath(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_CLIENT_DIR', '/var/www/nitro-client'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function gamedataPath(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_GAMEDATA_DIR', '/var/www/Gamedata/config'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function nitroV3Path(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_V3_DIR', '/var/www/Nitro-V3'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function nitroRendererV3Path(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_RENDERER_V3_DIR', '/var/www/Nitro_Render_V3'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function nitroRendererPath(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_RENDERER_DIR', '/var/www/nitro-renderer'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function clientWebrootPath(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_CLIENT_WEBROOT', '/var/www/Client'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function emulatorSourcePath(string $path = ''): string
|
||||||
|
{
|
||||||
|
$base = rtrim(env('NITRO_EMULATOR_SOURCE_DIR', '/var/www/emulator-source'), '/');
|
||||||
|
|
||||||
|
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1175,9 +1175,12 @@ final class RadioSettings extends Page implements HasForms
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache::flush();
|
Cache::forget('radio_settings_*');
|
||||||
|
Cache::forget('radio_ranks');
|
||||||
|
Cache::forget('radio_stream_status');
|
||||||
|
Cache::forget('radio_nowplaying');
|
||||||
|
Cache::forget('radio_listeners');
|
||||||
Artisan::call('config:clear');
|
Artisan::call('config:clear');
|
||||||
Artisan::call('cache:clear');
|
|
||||||
|
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->success()
|
->success()
|
||||||
@@ -1264,9 +1267,12 @@ final class RadioSettings extends Page implements HasForms
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache::flush();
|
Cache::forget('radio_settings_*');
|
||||||
|
Cache::forget('radio_ranks');
|
||||||
|
Cache::forget('radio_stream_status');
|
||||||
|
Cache::forget('radio_nowplaying');
|
||||||
|
Cache::forget('radio_listeners');
|
||||||
Artisan::call('config:clear');
|
Artisan::call('config:clear');
|
||||||
Artisan::call('cache:clear');
|
|
||||||
|
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->success()
|
->success()
|
||||||
@@ -1349,9 +1355,12 @@ final class RadioSettings extends Page implements HasForms
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache::flush();
|
Cache::forget('radio_settings_*');
|
||||||
|
Cache::forget('radio_ranks');
|
||||||
|
Cache::forget('radio_stream_status');
|
||||||
|
Cache::forget('radio_nowplaying');
|
||||||
|
Cache::forget('radio_listeners');
|
||||||
Artisan::call('config:clear');
|
Artisan::call('config:clear');
|
||||||
Artisan::call('cache:clear');
|
|
||||||
|
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->success()
|
->success()
|
||||||
@@ -1557,6 +1566,10 @@ final class RadioSettings extends Page implements HasForms
|
|||||||
private function clearCache(): void
|
private function clearCache(): void
|
||||||
{
|
{
|
||||||
Cache::forget(self::CACHE_KEY);
|
Cache::forget(self::CACHE_KEY);
|
||||||
|
Cache::forget('radio_ranks');
|
||||||
|
Cache::forget('radio_stream_status');
|
||||||
|
Cache::forget('radio_nowplaying');
|
||||||
|
Cache::forget('radio_listeners');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getSettingBool(string $key): bool
|
private function getSettingBool(string $key): bool
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ class AuthController extends Controller
|
|||||||
{
|
{
|
||||||
$article = WebsiteArticle::where('slug', $slug)->firstOrFail();
|
$article = WebsiteArticle::where('slug', $slug)->firstOrFail();
|
||||||
|
|
||||||
|
$request->validate([
|
||||||
|
'comment' => ['required', 'string', 'max:5000'],
|
||||||
|
]);
|
||||||
|
|
||||||
$comment = $article->comments()->create([
|
$comment = $article->comments()->create([
|
||||||
'user_id' => $request->user()->id,
|
'user_id' => $request->user()->id,
|
||||||
'comment' => strip_tags((string) $request->input('comment')),
|
'comment' => strip_tags((string) $request->input('comment')),
|
||||||
|
|||||||
@@ -23,14 +23,7 @@ class RadioLeaderboardController extends Controller
|
|||||||
default => $this->getAllTimeLeaderboard(),
|
default => $this->getAllTimeLeaderboard(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extract top 3 from already-fetched data
|
return view('community.radio.leaderboard', ['users' => $users, 'period' => $period]);
|
||||||
$topUserData = array_slice($users, 0, 3);
|
|
||||||
$topUserIds = array_column($topUserData, 'id');
|
|
||||||
$topUsers = User::whereIn('id', $topUserIds)
|
|
||||||
->get()
|
|
||||||
->keyBy('id');
|
|
||||||
|
|
||||||
return view('community.radio.leaderboard', ['users' => $users, 'topUsers' => $topUsers, 'period' => $period]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getAllTimeLeaderboard(): array
|
private function getAllTimeLeaderboard(): array
|
||||||
@@ -43,7 +36,7 @@ class RadioLeaderboardController extends Controller
|
|||||||
'rank' => $index + 1,
|
'rank' => $index + 1,
|
||||||
'id' => $user->id,
|
'id' => $user->id,
|
||||||
'username' => $user->username,
|
'username' => $user->username,
|
||||||
'avatar' => $user->avatar ?? null,
|
'avatar' => $user->avatar,
|
||||||
'points' => $user->radio_points,
|
'points' => $user->radio_points,
|
||||||
])
|
])
|
||||||
->toArray();
|
->toArray();
|
||||||
@@ -79,7 +72,7 @@ class RadioLeaderboardController extends Controller
|
|||||||
'rank' => $index + 1,
|
'rank' => $index + 1,
|
||||||
'id' => $item->user_id,
|
'id' => $item->user_id,
|
||||||
'username' => $user?->username ?? 'Onbekend',
|
'username' => $user?->username ?? 'Onbekend',
|
||||||
'avatar' => $user?->avatar ?? null,
|
'avatar' => $user?->avatar,
|
||||||
'points' => (int) $item->total_points,
|
'points' => (int) $item->total_points,
|
||||||
];
|
];
|
||||||
})->toArray();
|
})->toArray();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use App\Models\Help\WebsiteHelpCenterTicket;
|
|||||||
use App\Models\Help\WebsiteHelpCenterTicketReply;
|
use App\Models\Help\WebsiteHelpCenterTicketReply;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class TicketController extends Controller
|
class TicketController extends Controller
|
||||||
@@ -26,10 +27,15 @@ class TicketController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getCachedCategories(): \Illuminate\Database\Eloquent\Collection
|
||||||
|
{
|
||||||
|
return Cache::remember('help_categories', 3600, fn () => WebsiteHelpCenterCategory::get());
|
||||||
|
}
|
||||||
|
|
||||||
public function create(): View
|
public function create(): View
|
||||||
{
|
{
|
||||||
return view('help-center.tickets.create', [
|
return view('help-center.tickets.create', [
|
||||||
'categories' => WebsiteHelpCenterCategory::get(),
|
'categories' => $this->getCachedCategories(),
|
||||||
'openTickets' => WebsiteHelpCenterTicket::where('open', true)->where('user_id', Auth::id())->get(),
|
'openTickets' => WebsiteHelpCenterTicket::where('open', true)->where('user_id', Auth::id())->get(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -38,6 +44,8 @@ class TicketController extends Controller
|
|||||||
{
|
{
|
||||||
Auth::user()->tickets()->create($request->validated());
|
Auth::user()->tickets()->create($request->validated());
|
||||||
|
|
||||||
|
Cache::forget('help_categories');
|
||||||
|
|
||||||
return redirect()->back()->with('success', __('Ticket submitted!'));
|
return redirect()->back()->with('success', __('Ticket submitted!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +65,7 @@ class TicketController extends Controller
|
|||||||
|
|
||||||
return view('help-center.tickets.edit', [
|
return view('help-center.tickets.edit', [
|
||||||
'ticket' => $ticket,
|
'ticket' => $ticket,
|
||||||
'categories' => WebsiteHelpCenterCategory::get(),
|
'categories' => $this->getCachedCategories(),
|
||||||
'openTickets' => WebsiteHelpCenterTicket::where('open', true)->where('id', '!=', $ticket->id)->where('user_id', Auth::id())->get(),
|
'openTickets' => WebsiteHelpCenterTicket::where('open', true)->where('id', '!=', $ticket->id)->where('user_id', Auth::id())->get(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -72,6 +80,8 @@ class TicketController extends Controller
|
|||||||
|
|
||||||
$ticket->update($request->validated());
|
$ticket->update($request->validated());
|
||||||
|
|
||||||
|
Cache::forget('help_categories');
|
||||||
|
|
||||||
return to_route('help-center.ticket.show', $ticket)->with('success', __('Ticket updated!'));
|
return to_route('help-center.ticket.show', $ticket)->with('success', __('Ticket updated!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-1
@@ -9,6 +9,7 @@ use App\Models\Articles\WebsiteArticleComment;
|
|||||||
use App\Models\Community\Staff\WebsiteStaffApplications;
|
use App\Models\Community\Staff\WebsiteStaffApplications;
|
||||||
use App\Models\Community\Staff\WebsiteTeam;
|
use App\Models\Community\Staff\WebsiteTeam;
|
||||||
use App\Models\Game\Furniture\Item;
|
use App\Models\Game\Furniture\Item;
|
||||||
|
use App\Models\Game\Guild\Guild;
|
||||||
use App\Models\Game\Guild\GuildMember;
|
use App\Models\Game\Guild\GuildMember;
|
||||||
use App\Models\Game\Permission;
|
use App\Models\Game\Permission;
|
||||||
use App\Models\Game\Player\MessengerFriendship;
|
use App\Models\Game\Player\MessengerFriendship;
|
||||||
@@ -35,6 +36,7 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
@@ -294,7 +296,12 @@ class User extends Authenticatable implements FilamentUser, HasName
|
|||||||
return $this->hasMany(CameraWeb::class);
|
return $this->hasMany(CameraWeb::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function guilds(): HasMany
|
public function guilds(): HasManyThrough
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(Guild::class, GuildMember::class, 'user_id', 'id', 'id', 'guild_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function guildMemberships(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(GuildMember::class, 'user_id');
|
return $this->hasMany(GuildMember::class, 'user_id');
|
||||||
}
|
}
|
||||||
@@ -377,6 +384,16 @@ class User extends Authenticatable implements FilamentUser, HasName
|
|||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAvatarAttribute(): ?string
|
||||||
|
{
|
||||||
|
$imager = setting('avatar_imager');
|
||||||
|
if ($imager && $this->look) {
|
||||||
|
return $imager . $this->look . '&direction=2&head_direction=3&gesture=sml&action=wav';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function getFilamentName(): string
|
public function getFilamentName(): string
|
||||||
{
|
{
|
||||||
return $this->username ?? 'Guest';
|
return $this->username ?? 'Guest';
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
});
|
});
|
||||||
|
|
||||||
$settingsService = app(SettingsService::class);
|
$settingsService = app(SettingsService::class);
|
||||||
$badgePath = $settingsService->getOrDefault('badge_path_filesystem', '/var/www/gamedata/c_images/album1584');
|
$badgePath = $settingsService->getOrDefault('badge_path_filesystem', dirname(env('NITRO_GAMEDATA_DIR', '/var/www/Gamedata/config')) . '/c_images/album1584');
|
||||||
Config::set('filesystems.disks.badges.root', $badgePath);
|
Config::set('filesystems.disks.badges.root', $badgePath);
|
||||||
|
|
||||||
$adsPath = $settingsService->getOrDefault('ads_path_filesystem', '/var/www/gamedata/custom');
|
$adsPath = $settingsService->getOrDefault('ads_path_filesystem', dirname(env('NITRO_GAMEDATA_DIR', '/var/www/Gamedata/config')) . '/custom');
|
||||||
Config::set('filesystems.disks.ads.root', $adsPath);
|
Config::set('filesystems.disks.ads.root', $adsPath);
|
||||||
|
|
||||||
WebsiteDrawBadge::observe(WebsiteDrawBadgeObserver::class);
|
WebsiteDrawBadge::observe(WebsiteDrawBadgeObserver::class);
|
||||||
|
|||||||
@@ -62,12 +62,15 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
return Limit::perMinute($maxAttempts)->by($key);
|
return Limit::perMinute($maxAttempts)->by($key);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Strict rate limit for login attempts (security) - increased for usability
|
// Strict rate limit for registration (prevent spam)
|
||||||
RateLimiter::for('login', fn (Request $request) => Limit::perMinute(20)->by($request->ip()));
|
RateLimiter::for('register', fn (Request $request) => Limit::perHour(3)->by($request->ip()));
|
||||||
|
|
||||||
// Two-factor authentication rate limit
|
// Two-factor authentication rate limit
|
||||||
RateLimiter::for('two-factor', fn (Request $request) => Limit::perMinute(15)->by($request->ip()));
|
RateLimiter::for('two-factor', fn (Request $request) => Limit::perMinute(15)->by($request->ip()));
|
||||||
|
|
||||||
|
// Strict photo upload rate limit
|
||||||
|
RateLimiter::for('upload', fn (Request $request) => Limit::perMinute(10)->by($request->user()?->id ?? $request->ip()));
|
||||||
|
|
||||||
// Rate limit for radio endpoints (high traffic)
|
// Rate limit for radio endpoints (high traffic)
|
||||||
RateLimiter::for('radio', function (Request $request) {
|
RateLimiter::for('radio', function (Request $request) {
|
||||||
$key = $request->get('radio_api_key_id')
|
$key = $request->get('radio_api_key_id')
|
||||||
@@ -76,5 +79,8 @@ class RouteServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
return Limit::perMinute(120)->by((string) $key);
|
return Limit::perMinute(120)->by((string) $key);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Rate limit for SSE (long-lived connections - low rate)
|
||||||
|
RateLimiter::for('sse', fn (Request $request) => Limit::perMinute(6)->by($request->ip()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class AutoDetectService
|
|||||||
return $found;
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/var/www/emulator-source';
|
return $this->emulatorPath('../emulator-source');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function detectEmulatorServiceName(): string
|
public function detectEmulatorServiceName(): string
|
||||||
@@ -214,9 +214,9 @@ class AutoDetectService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$possiblePaths = [
|
$possiblePaths = [
|
||||||
'/var/www/Client', '/var/www/client', '/var/www/Nitro', '/var/www/nitro',
|
$this->clientPath('../Client'), $this->clientPath('../client'), $this->clientPath('../Nitro'), $this->clientPath('../nitro'),
|
||||||
'/var/www/html/Client', '/var/www/html/client',
|
$this->clientPath('../../html/Client'), $this->clientPath('../../html/client'),
|
||||||
'/var/www/atomcms/public/Client', '/var/www/atomcms/public/nitro',
|
$this->clientPath('../../../atomcms/public/Client'), $this->clientPath('../../../atomcms/public/nitro'),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($possiblePaths as $path) {
|
foreach ($possiblePaths as $path) {
|
||||||
@@ -225,7 +225,7 @@ class AutoDetectService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/var/www/Client';
|
return $this->clientPath('../Client');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function detectNitroBuildPath(): string
|
public function detectNitroBuildPath(): string
|
||||||
@@ -247,8 +247,8 @@ class AutoDetectService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$possiblePaths = [
|
$possiblePaths = [
|
||||||
'/var/www/nitro-client/dist', '/var/www/nitro-V3/dist',
|
$this->clientPath('dist'), $this->clientPath('../nitro-V3/dist'),
|
||||||
'/var/www/atomcms/nitro-client/dist',
|
base_path('nitro-client/dist'),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($possiblePaths as $path) {
|
foreach ($possiblePaths as $path) {
|
||||||
@@ -290,7 +290,7 @@ class AutoDetectService
|
|||||||
|
|
||||||
// Last resort: check common paths
|
// Last resort: check common paths
|
||||||
$possiblePaths = [
|
$possiblePaths = [
|
||||||
'/var/www/atomcms/public/gamedata', '/var/www/html/gamedata',
|
$this->gamedataPath('../../../atomcms/public/gamedata'), $this->gamedataPath('../../html/gamedata'),
|
||||||
'/opt/gamedata', '/root/gamedata',
|
'/opt/gamedata', '/root/gamedata',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -300,7 +300,7 @@ class AutoDetectService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/var/www/gamedata';
|
return $this->gamedataPath('../gamedata');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function detectFurnitureIconsPath(): string
|
public function detectFurnitureIconsPath(): string
|
||||||
@@ -317,7 +317,7 @@ class AutoDetectService
|
|||||||
return $iconsPath;
|
return $iconsPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/var/www/Gamedata/icons';
|
return $this->gamedataPath('../icons');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function detectCatalogIconsPath(): string
|
public function detectCatalogIconsPath(): string
|
||||||
@@ -334,7 +334,7 @@ class AutoDetectService
|
|||||||
return $cataloguePath;
|
return $cataloguePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/var/www/Gamedata/catalogue';
|
return $this->gamedataPath('../catalogue');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Nginx ─────────────────────────────────────────────────
|
// ─── Nginx ─────────────────────────────────────────────────
|
||||||
@@ -700,6 +700,39 @@ class AutoDetectService
|
|||||||
return trim($result->output()) === 'yes';
|
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
|
private function getSetting(string $key): ?string
|
||||||
{
|
{
|
||||||
try {
|
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
|
public function createCatalogPages(): array
|
||||||
{
|
{
|
||||||
$created = 0;
|
$created = 0;
|
||||||
@@ -157,21 +201,6 @@ class CatalogService
|
|||||||
return ['pages_created' => $created];
|
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
|
private function createMainPage(string $name, int $order): int
|
||||||
{
|
{
|
||||||
$exists = DB::table('catalog_pages')->where('caption', $name)->first();
|
$exists = DB::table('catalog_pages')->where('caption', $name)->first();
|
||||||
@@ -362,7 +391,7 @@ class CatalogService
|
|||||||
|
|
||||||
// 1. Download FurnitureData.json
|
// 1. Download FurnitureData.json
|
||||||
$furnitureUrl = $baseUrl . '/gamedata/config/FurnitureData.json';
|
$furnitureUrl = $baseUrl . '/gamedata/config/FurnitureData.json';
|
||||||
$localPath = '/var/www/Gamedata/config/FurnitureData.json';
|
$localPath = $this->gamedataPath('FurnitureData.json');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$content = @file_get_contents($furnitureUrl);
|
$content = @file_get_contents($furnitureUrl);
|
||||||
@@ -384,7 +413,7 @@ class CatalogService
|
|||||||
try {
|
try {
|
||||||
$content = @file_get_contents($productUrl);
|
$content = @file_get_contents($productUrl);
|
||||||
if ($content && json_decode($content, true)) {
|
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);
|
$data = json_decode($content, true);
|
||||||
$results['product'] = count($data['productdata'] ?? []);
|
$results['product'] = count($data['productdata'] ?? []);
|
||||||
Log::info('[CatalogService] Downloaded ProductData.json');
|
Log::info('[CatalogService] Downloaded ProductData.json');
|
||||||
@@ -407,7 +436,7 @@ class CatalogService
|
|||||||
private function downloadIconsFromUrl(string $baseUrl): int
|
private function downloadIconsFromUrl(string $baseUrl): int
|
||||||
{
|
{
|
||||||
$downloaded = 0;
|
$downloaded = 0;
|
||||||
$iconsPath = '/var/www/Gamedata/icons';
|
$iconsPath = $this->gamedataPath('../icons');
|
||||||
$url = $baseUrl . '/gamedata/icons';
|
$url = $baseUrl . '/gamedata/icons';
|
||||||
|
|
||||||
// Try to list files via HTTP (may not work)
|
// Try to list files via HTTP (may not work)
|
||||||
@@ -456,7 +485,7 @@ class CatalogService
|
|||||||
private function downloadCatalogImagesFromUrl(string $baseUrl): int
|
private function downloadCatalogImagesFromUrl(string $baseUrl): int
|
||||||
{
|
{
|
||||||
$downloaded = 0;
|
$downloaded = 0;
|
||||||
$catalogPath = '/var/www/Gamedata/catalogue';
|
$catalogPath = $this->gamedataPath('../catalogue');
|
||||||
$url = $baseUrl . '/gamedata/catalogue';
|
$url = $baseUrl . '/gamedata/catalogue';
|
||||||
|
|
||||||
// Download common catalogue images
|
// Download common catalogue images
|
||||||
@@ -521,7 +550,7 @@ class CatalogService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save to file
|
// 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);
|
file_put_contents($sqlPath, $sql);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -711,7 +740,7 @@ class CatalogService
|
|||||||
|
|
||||||
// Step 1: Download FurnitureData.json
|
// Step 1: Download FurnitureData.json
|
||||||
$furnitureUrl = rtrim($baseUrl, '/') . '/gamedata/config/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);
|
$furnitureContent = @file_get_contents($furnitureUrl);
|
||||||
if ($furnitureContent && $furnitureData = json_decode($furnitureContent, true)) {
|
if ($furnitureContent && $furnitureData = json_decode($furnitureContent, true)) {
|
||||||
@@ -730,7 +759,7 @@ class CatalogService
|
|||||||
$productUrl = rtrim($baseUrl, '/') . '/gamedata/config/ProductData.json';
|
$productUrl = rtrim($baseUrl, '/') . '/gamedata/config/ProductData.json';
|
||||||
$productContent = @file_get_contents($productUrl);
|
$productContent = @file_get_contents($productUrl);
|
||||||
if ($productContent && json_decode($productContent, true)) {
|
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;
|
$results['product'] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,7 +906,7 @@ class CatalogService
|
|||||||
private function syncIcons(array $furnitureData, string $baseUrl): int
|
private function syncIcons(array $furnitureData, string $baseUrl): int
|
||||||
{
|
{
|
||||||
$downloaded = 0;
|
$downloaded = 0;
|
||||||
$iconsPath = '/var/www/Gamedata/icons';
|
$iconsPath = $this->gamedataPath('../icons');
|
||||||
$iconsUrl = rtrim($baseUrl, '/') . '/gamedata/icons';
|
$iconsUrl = rtrim($baseUrl, '/') . '/gamedata/icons';
|
||||||
|
|
||||||
$iconNames = [];
|
$iconNames = [];
|
||||||
@@ -910,7 +939,7 @@ class CatalogService
|
|||||||
private function syncCatalogImages(string $baseUrl): int
|
private function syncCatalogImages(string $baseUrl): int
|
||||||
{
|
{
|
||||||
$downloaded = 0;
|
$downloaded = 0;
|
||||||
$catalogPath = '/var/www/Gamedata/catalogue';
|
$catalogPath = $this->gamedataPath('../catalogue');
|
||||||
$catalogUrl = rtrim($baseUrl, '/') . '/gamedata/catalogue';
|
$catalogUrl = rtrim($baseUrl, '/') . '/gamedata/catalogue';
|
||||||
|
|
||||||
$commonImages = [
|
$commonImages = [
|
||||||
@@ -951,7 +980,7 @@ class CatalogService
|
|||||||
{
|
{
|
||||||
Log::info('[CatalogService] Starting catalog_clothing sync');
|
Log::info('[CatalogService] Starting catalog_clothing sync');
|
||||||
|
|
||||||
$figureMapPath = '/var/www/Gamedata/config/FigureMap.json';
|
$figureMapPath = $this->gamedataPath('FigureMap.json');
|
||||||
|
|
||||||
if (! file_exists($figureMapPath)) {
|
if (! file_exists($figureMapPath)) {
|
||||||
throw new \Exception('FigureMap.json niet gevonden');
|
throw new \Exception('FigureMap.json niet gevonden');
|
||||||
@@ -1100,4 +1129,20 @@ class CatalogService
|
|||||||
'total' => DB::table('catalog_clothing')->count(),
|
'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)
|
$collection = User::where('radio_points', '>', 0)
|
||||||
->orderBy('radio_points', 'desc')
|
->orderBy('radio_points', 'desc')
|
||||||
->limit($limit)
|
->limit($limit)
|
||||||
->get(['id', 'username', 'avatar', 'radio_points']);
|
->get(['id', 'username', 'look', 'radio_points']);
|
||||||
|
|
||||||
return $collection->map(fn (User $user, int $index): array => [
|
return $collection->map(fn (User $user, int $index): array => [
|
||||||
'rank' => $index + 1,
|
'rank' => $index + 1,
|
||||||
|
|||||||
@@ -3,18 +3,33 @@
|
|||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class TraxService
|
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
|
public function syncSoundSets(): array
|
||||||
{
|
{
|
||||||
Log::info('[TraxService] Starting sound sets sync');
|
Log::info('[TraxService] Starting sound sets sync');
|
||||||
|
|
||||||
$furnitureData = json_decode(
|
$furnitureData = $this->loadFurnitureData();
|
||||||
file_get_contents('/var/www/Gamedata/config/FurnitureData.json'),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
$inserted = 0;
|
$inserted = 0;
|
||||||
$soundSets = [];
|
$soundSets = [];
|
||||||
@@ -61,7 +76,7 @@ class TraxService
|
|||||||
{
|
{
|
||||||
Log::info('[TraxService] Starting soundtracks sync');
|
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();
|
$existing = DB::table('soundtracks')->pluck('id')->toArray();
|
||||||
|
|
||||||
$inserted = 0;
|
$inserted = 0;
|
||||||
@@ -102,7 +117,7 @@ class TraxService
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'soundtracks' => DB::table('soundtracks')->count(),
|
'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(),
|
'room_trax' => DB::table('room_trax')->count(),
|
||||||
'trax_playlist' => DB::table('trax_playlist')->count(),
|
'trax_playlist' => DB::table('trax_playlist')->count(),
|
||||||
'soundsets' => $this->countSoundSets(),
|
'soundsets' => $this->countSoundSets(),
|
||||||
@@ -111,10 +126,7 @@ class TraxService
|
|||||||
|
|
||||||
private function countSoundSets(): int
|
private function countSoundSets(): int
|
||||||
{
|
{
|
||||||
$furnitureData = json_decode(
|
$furnitureData = $this->loadFurnitureData();
|
||||||
file_get_contents('/var/www/Gamedata/config/FurnitureData.json'),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
|
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
|
||||||
@@ -135,14 +147,10 @@ class TraxService
|
|||||||
'failed' => 0,
|
'failed' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
$soundPath = '/var/www/Gamedata/sounds';
|
$soundPath = $this->soundsPath();
|
||||||
$soundUrl = rtrim($baseUrl, '/') . '/gamedata/sounds';
|
$soundUrl = rtrim($baseUrl, '/') . '/gamedata/sounds';
|
||||||
|
|
||||||
// Get list of sound samples from FurnitureData
|
$furnitureData = $this->loadFurnitureData();
|
||||||
$furnitureData = json_decode(
|
|
||||||
file_get_contents('/var/www/Gamedata/config/FurnitureData.json'),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
$sampleIds = [];
|
$sampleIds = [];
|
||||||
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
|
foreach ($furnitureData['roomitemtypes']['furnitype'] ?? [] as $item) {
|
||||||
@@ -162,7 +170,7 @@ class TraxService
|
|||||||
|
|
||||||
if (! file_exists($localFile)) {
|
if (! file_exists($localFile)) {
|
||||||
$url = $soundUrl . '/' . $filename;
|
$url = $soundUrl . '/' . $filename;
|
||||||
$content = @file_get_contents($url);
|
$content = Http::timeout(10)->get($url)->body();
|
||||||
|
|
||||||
if ($content && strlen($content) > 1000) {
|
if ($content && strlen($content) > 1000) {
|
||||||
file_put_contents($localFile, $content);
|
file_put_contents($localFile, $content);
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('radio_song_requests', function (Blueprint $table) {
|
||||||
|
$table->index(['is_approved', 'is_played', 'submitted_at'], 'idx_radio_song_requests_queue');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('website_help_center_tickets', function (Blueprint $table) {
|
||||||
|
$table->index(['user_id', 'open'], 'idx_help_tickets_user_open');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_shouts', function (Blueprint $table) {
|
||||||
|
$table->index('approved', 'idx_radio_shouts_approved');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_applications', function (Blueprint $table) {
|
||||||
|
$table->index('status', 'idx_radio_applications_status');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_history', function (Blueprint $table) {
|
||||||
|
$table->index(['user_id', 'ended_at'], 'idx_radio_history_user_ended');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_song_requests', function (Blueprint $table) {
|
||||||
|
$table->index(['user_id', 'submitted_at'], 'idx_radio_song_requests_user_date');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_giveaway_participants', function (Blueprint $table) {
|
||||||
|
$table->index(['giveaway_id', 'user_id'], 'idx_giveaway_participants_entry');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('camera_web', function (Blueprint $table) {
|
||||||
|
$table->index('user_id', 'idx_camera_web_user_id');
|
||||||
|
$table->index('timestamp', 'idx_camera_web_timestamp');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_song_votes', function (Blueprint $table) {
|
||||||
|
$table->index(['song_request_id', 'user_id'], 'idx_radio_song_votes_entry');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('radio_song_requests', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_radio_song_requests_queue');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('website_help_center_tickets', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_help_tickets_user_open');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_shouts', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_radio_shouts_approved');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_applications', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_radio_applications_status');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_history', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_radio_history_user_ended');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_song_requests', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_radio_song_requests_user_date');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_giveaway_participants', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_giveaway_participants_entry');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('camera_web', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_camera_web_user_id');
|
||||||
|
$table->dropIndex('idx_camera_web_timestamp');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('radio_song_votes', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('idx_radio_song_votes_entry');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Vendored
-19
@@ -1,22 +1,3 @@
|
|||||||
window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
|
window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
|
||||||
|
|
||||||
/**
|
|
||||||
* Echo exposes an expressive API for subscribing to channels and listening
|
|
||||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
|
||||||
* allows your team to easily build robust real-time web applications.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// import Echo from 'laravel-echo';
|
|
||||||
|
|
||||||
// import Pusher from 'pusher-js';
|
|
||||||
// window.Pusher = Pusher;
|
|
||||||
|
|
||||||
// window.Echo = new Echo({
|
|
||||||
// broadcaster: 'pusher',
|
|
||||||
// key: import.meta.env.VITE_PUSHER_APP_KEY,
|
|
||||||
// wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
|
|
||||||
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
|
|
||||||
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
|
|
||||||
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
|
|
||||||
// enabledTransports: ['ws', 'wss'],
|
|
||||||
// });
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<div class="p-3" style="color: var(--color-text)">
|
<div class="p-3" style="color: var(--color-text)">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="mb-4 text-sm">
|
<div class="mb-4 text-sm">
|
||||||
{!! $position->description !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($position->description) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 text-sm font-semibold">
|
<div class="mb-4 text-sm font-semibold">
|
||||||
{{ __('Application Deadline :date', [
|
{{ __('Application Deadline :date', [
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<div class="p-3" style="color: var(--color-text)">
|
<div class="p-3" style="color: var(--color-text)">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="mb-4 text-sm">
|
<div class="mb-4 text-sm">
|
||||||
{!! $position->description !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($position->description) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 text-sm font-semibold">
|
<div class="mb-4 text-sm font-semibold">
|
||||||
{{ __('Application Deadline :date', ['date' => $position->apply_to ? $position->apply_to->format('F j, Y, g:i A') : __('No deadline set')]) }}
|
{{ __('Application Deadline :date', ['date' => $position->apply_to ? $position->apply_to->format('F j, Y, g:i A') : __('No deadline set')]) }}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<div class="text-sm" style="color: var(--color-text);">
|
<div class="text-sm" style="color: var(--color-text);">
|
||||||
<img class="px-2" style="float: right !important;" src="{{ asset('/assets/images/help-center/' . $category->image_url) }}" alt="">
|
<img class="px-2" style="float: right !important;" src="{{ asset('/assets/images/help-center/' . $category->image_url) }}" alt="">
|
||||||
{!! str_replace(':hotel', $hotelName, $category->content) !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean(str_replace(':hotel', $hotelName, $category->content)) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 inline-block">
|
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 inline-block">
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<div class="text-sm" style="color: var(--color-text);">
|
<div class="text-sm" style="color: var(--color-text);">
|
||||||
{!! str_replace(':hotel', $hotelName, $category->content) !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean(str_replace(':hotel', $hotelName, $category->content)) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 inline-block">
|
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 inline-block">
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article class="prose-xl text-sm" style="color: var(--color-text); width: 100%;">
|
<article class="prose-xl text-sm" style="color: var(--color-text); width: 100%;">
|
||||||
{!! $ticket->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($ticket->content) }}
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4 text-sm" style="color: var(--color-text);">
|
<div class="p-4 text-sm" style="color: var(--color-text);">
|
||||||
{!! $reply->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($reply->content) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4 text-sm" style="color: var(--color-text);">
|
<div class="p-4 text-sm" style="color: var(--color-text);">
|
||||||
{!! $reply->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($reply->content) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -75,7 +75,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- TODO: Selfhost --}}
|
|
||||||
<script src="{{ config('habbo.cdn.sweetalert2_js') }}"></script>
|
<script src="{{ config('habbo.cdn.sweetalert2_js') }}"></script>
|
||||||
<script src="{{ config('habbo.cdn.html2canvas_js') }}"></script>
|
<script src="{{ config('habbo.cdn.html2canvas_js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -258,7 +258,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-semibold text-[var(--color-text)] mb-2">{{ __('Important Information') }}</h3>
|
<h3 class="text-lg font-semibold text-[var(--color-text)] mb-2">{{ __('Important Information') }}</h3>
|
||||||
<div class="prose prose-invert max-w-none text-[var(--color-text-muted)]">
|
<div class="prose prose-invert max-w-none text-[var(--color-text-muted)]">
|
||||||
<p>{!! setting('maintenance_message') !!}</p>
|
<p>{{ \Stevebauman\Purify\Facades\Purify::clean(setting('maintenance_message')) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -329,6 +329,6 @@
|
|||||||
@endguest
|
@endguest
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" defer></script>
|
<script src="{{ config('habbo.cdn.turnstile_js', 'https://challenges.cloudflare.com/turnstile/v0/api.js') }}" defer></script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-2" id="article-content">
|
<div class="px-2" id="article-content">
|
||||||
{!! $article->full_story !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($article->full_story) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('community.partials.article-reactions')
|
@include('community.partials.article-reactions')
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
<div class="text-center dark:text-gray-400">
|
<div class="text-center dark:text-gray-400">
|
||||||
<div class="mb-4 text-sm">
|
<div class="mb-4 text-sm">
|
||||||
{!! $position->description !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($position->description) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 text-sm font-semibold">
|
<div class="mb-4 text-sm font-semibold">
|
||||||
{{ __('Application Deadline :date', [
|
{{ __('Application Deadline :date', [
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
<div class="text-center dark:text-gray-400">
|
<div class="text-center dark:text-gray-400">
|
||||||
<div class="mb-4 text-sm">
|
<div class="mb-4 text-sm">
|
||||||
{!! $position->description !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($position->description) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 text-sm font-semibold">
|
<div class="mb-4 text-sm font-semibold">
|
||||||
{{ __('Application Deadline :date', ['date' => $position->apply_to ? $position->apply_to->format('F j, Y, g:i A') : __('No deadline set')]) }}
|
{{ __('Application Deadline :date', ['date' => $position->apply_to ? $position->apply_to->format('F j, Y, g:i A') : __('No deadline set')]) }}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="px-2 text-sm text-gray-200">
|
<div class="px-2 text-sm text-gray-200">
|
||||||
<img class="px-2" style="float: right !important;"
|
<img class="px-2" style="float: right !important;"
|
||||||
src="{{ asset('/assets/images/help-center/' . $category->image_url) }}" alt="">
|
src="{{ asset('/assets/images/help-center/' . $category->image_url) }}" alt="">
|
||||||
{!! $category->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($category->content) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 ml-2">
|
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 ml-2">
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
</x-slot:title>
|
</x-slot:title>
|
||||||
|
|
||||||
<div class="px-2 text-sm text-gray-200">
|
<div class="px-2 text-sm text-gray-200">
|
||||||
{!! $category->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($category->content) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 ml-2">
|
<a data-turbolinks="false" href="{{ $category->button_url ?? '#' }}" class="mt-4 ml-2">
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article class="prose-xl mt-8" style="width: 100%;">
|
<article class="prose-xl mt-8" style="width: 100%;">
|
||||||
{!! $ticket->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($ticket->content) }}
|
||||||
</article>
|
</article>
|
||||||
</x-content.content-card>
|
</x-content.content-card>
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
{!! $reply->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($reply->content) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
{!! $reply->content !!}
|
{{ \Stevebauman\Purify\Facades\Purify::clean($reply->content) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -72,7 +72,6 @@
|
|||||||
</x-content.content-card>
|
</x-content.content-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- TODO: Selfhost --}}
|
|
||||||
<script src="{{ config('habbo.cdn.sweetalert2_js') }}"></script>
|
<script src="{{ config('habbo.cdn.sweetalert2_js') }}"></script>
|
||||||
<script src="{{ config('habbo.cdn.html2canvas_js') }}"></script>
|
<script src="{{ config('habbo.cdn.html2canvas_js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -139,7 +139,7 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
<article class="mt-4 text-lg lg:text-xl max-w-[600px] text-wrap">
|
<article class="mt-4 text-lg lg:text-xl max-w-[600px] text-wrap">
|
||||||
<p>{!! setting('maintenance_message') !!}</p>
|
<p>{{ \Stevebauman\Purify\Facades\Purify::clean(setting('maintenance_message')) }}</p>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,6 +162,6 @@
|
|||||||
@endguest
|
@endguest
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" defer></script>
|
<script src="{{ config('habbo.cdn.turnstile_js', 'https://challenges.cloudflare.com/turnstile/v0/api.js') }}" defer></script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="radio-container text-white">
|
<div class="radio-container text-white">
|
||||||
@if(!$enabled)
|
@if(!$isOnline)
|
||||||
<!-- Radio Disabled State -->
|
<!-- Radio Disabled State -->
|
||||||
<div class="min-h-screen flex items-center justify-center p-6">
|
<div class="min-h-screen flex items-center justify-center p-6">
|
||||||
<div class="text-center max-w-md">
|
<div class="text-center max-w-md">
|
||||||
|
|||||||
+3
-3
@@ -29,7 +29,7 @@ Route::prefix('auth')->group(function () {
|
|||||||
Route::post('/login', [AuthController::class, 'login']);
|
Route::post('/login', [AuthController::class, 'login']);
|
||||||
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');
|
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');
|
||||||
Route::get('/user', [AuthController::class, 'user'])->middleware('auth:sanctum');
|
Route::get('/user', [AuthController::class, 'user'])->middleware('auth:sanctum');
|
||||||
Route::post('/register', [AuthController::class, 'register']);
|
Route::post('/register', [AuthController::class, 'register'])->middleware('throttle:register');
|
||||||
Route::put('/user', [AuthController::class, 'updateUser'])->middleware('auth:sanctum');
|
Route::put('/user', [AuthController::class, 'updateUser'])->middleware('auth:sanctum');
|
||||||
Route::put('/user/password', [AuthController::class, 'updatePassword'])->middleware('auth:sanctum');
|
Route::put('/user/password', [AuthController::class, 'updatePassword'])->middleware('auth:sanctum');
|
||||||
});
|
});
|
||||||
@@ -81,7 +81,7 @@ Route::get('/radio/listeners', [RadioController::class, 'listeners'])->middlewar
|
|||||||
Route::get('/radio/shouts', [RadioController::class, 'getShouts'])->middleware('throttle:100,1')->name('api.radio.shouts');
|
Route::get('/radio/shouts', [RadioController::class, 'getShouts'])->middleware('throttle:100,1')->name('api.radio.shouts');
|
||||||
|
|
||||||
// Radio SSE (Server-Sent Events) stream
|
// Radio SSE (Server-Sent Events) stream
|
||||||
Route::get('/radio/sse', [\App\Http\Controllers\Radio\SseController::class, 'stream'])->name('api.radio.sse');
|
Route::get('/radio/sse', [\App\Http\Controllers\Radio\SseController::class, 'stream'])->middleware('throttle:sse')->name('api.radio.sse');
|
||||||
|
|
||||||
// Radio embed config
|
// Radio embed config
|
||||||
Route::get('/radio/embed/config', [\App\Http\Controllers\Radio\EmbedController::class, 'config'])->middleware('throttle:100,1')->name('api.radio.embed.config');
|
Route::get('/radio/embed/config', [\App\Http\Controllers\Radio\EmbedController::class, 'config'])->middleware('throttle:100,1')->name('api.radio.embed.config');
|
||||||
@@ -108,7 +108,7 @@ Route::post('/help/tickets', [HelpApiController::class, 'create'])->middleware('
|
|||||||
Route::post('/help/tickets/{id}/reply', [HelpApiController::class, 'reply'])->middleware('auth:sanctum');
|
Route::post('/help/tickets/{id}/reply', [HelpApiController::class, 'reply'])->middleware('auth:sanctum');
|
||||||
|
|
||||||
// Photo Upload
|
// Photo Upload
|
||||||
Route::post('/photos/upload', [MediaApiController::class, 'upload'])->middleware('auth:sanctum');
|
Route::post('/photos/upload', [MediaApiController::class, 'upload'])->middleware(['auth:sanctum', 'throttle:upload']);
|
||||||
|
|
||||||
// Shop Purchase
|
// Shop Purchase
|
||||||
Route::post('/shop/packages/{packageId}/purchase', [ShopApiController::class, 'purchase'])->middleware('auth:sanctum');
|
Route::post('/shop/packages/{packageId}/purchase', [ShopApiController::class, 'purchase'])->middleware('auth:sanctum');
|
||||||
|
|||||||
Reference in New Issue
Block a user