121 lines
5.0 KiB
PHP
121 lines
5.0 KiB
PHP
<?php
|
||
|
||
use App\Http\Middleware\HandleAppearance;
|
||
use App\Http\Middleware\HandleInertiaRequests;
|
||
use App\Http\Middleware\SetLocale;
|
||
use App\Http\Middleware\CheckBanned;
|
||
use App\Http\Middleware\DetectCiphertextInJson;
|
||
use App\Http\Middleware\GeoBlockMiddleware;
|
||
use App\Http\Middleware\MaintenanceModeMiddleware;
|
||
use Illuminate\Foundation\Application;
|
||
use Illuminate\Foundation\Configuration\Exceptions;
|
||
use Illuminate\Foundation\Configuration\Middleware;
|
||
use Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets;
|
||
|
||
return Application::configure(basePath: dirname(__DIR__))
|
||
->withRouting(
|
||
web: __DIR__.'/../routes/web.php',
|
||
commands: __DIR__.'/../routes/console.php',
|
||
health: '/up',
|
||
then: function () {
|
||
// B2B operator routes — served at /operator/* (no /api prefix)
|
||
\Illuminate\Support\Facades\Route::middleware(['throttle:60,1'])
|
||
->prefix('operator')
|
||
->group(base_path('routes/operator.php'));
|
||
},
|
||
)
|
||
->withSchedule(function (\Illuminate\Console\Scheduling\Schedule $schedule): void {
|
||
// Expire old user bonuses every hour
|
||
$schedule->call(function () {
|
||
app(\App\Services\BonusService::class)->expireBonuses();
|
||
})->hourly()->name('bonus:expire')->withoutOverlapping();
|
||
|
||
// Recalculate VIP levels based on total wager – runs daily at 03:00
|
||
$schedule->call(function () {
|
||
$thresholds = [5 => 10000, 4 => 2000, 3 => 500, 2 => 100, 1 => 0];
|
||
\App\Models\User::chunk(200, function ($users) use ($thresholds) {
|
||
foreach ($users as $user) {
|
||
$totalWager = \App\Models\GameBet::where('user_id', $user->id)->sum('wager_amount');
|
||
$newLevel = 1;
|
||
foreach ($thresholds as $level => $min) {
|
||
if ($totalWager >= $min) { $newLevel = $level; break; }
|
||
}
|
||
if ($user->vip_level !== $newLevel) {
|
||
$user->forceFill(['vip_level' => $newLevel])->save();
|
||
}
|
||
}
|
||
});
|
||
})->dailyAt('03:00')->name('vip:recalculate')->withoutOverlapping();
|
||
|
||
// Flag / notify inactive users (no login for 90 days) – runs weekly
|
||
$schedule->call(function () {
|
||
$cutoff = now()->subDays(90);
|
||
\App\Models\User::where('last_login_at', '<', $cutoff)
|
||
->where('is_banned', false)
|
||
->chunk(100, function ($users) {
|
||
foreach ($users as $user) {
|
||
\Illuminate\Support\Facades\Log::info('Inactive user flagged', [
|
||
'user_id' => $user->id,
|
||
'last_login_at' => $user->last_login_at,
|
||
]);
|
||
}
|
||
});
|
||
})->weekly()->name('users:cleanup-inactive')->withoutOverlapping();
|
||
})
|
||
->withMiddleware(function (Middleware $middleware): void {
|
||
$middleware->encryptCookies(except: ['appearance', 'sidebar_state', 'XSRF-TOKEN']);
|
||
|
||
$middleware->validateCsrfTokens(except: [
|
||
'api/webhooks/nowpayments',
|
||
'api/betix/*',
|
||
'wallet/deposits',
|
||
'locale',
|
||
'api/wallet/vault/*',
|
||
'api/wallet/vault',
|
||
// Operator B2B API — authenticated via license key, not CSRF
|
||
'operator/*',
|
||
]);
|
||
|
||
$middleware->web(append: [
|
||
HandleAppearance::class,
|
||
SetLocale::class,
|
||
HandleInertiaRequests::class,
|
||
AddLinkHeadersForPreloadedAssets::class,
|
||
CheckBanned::class,
|
||
DetectCiphertextInJson::class,
|
||
GeoBlockMiddleware::class,
|
||
MaintenanceModeMiddleware::class,
|
||
]);
|
||
|
||
// Route middleware aliases
|
||
$middleware->alias([
|
||
'restrict' => \App\Http\Middleware\EnforceRestriction::class,
|
||
'license.key' => \App\Http\Middleware\ValidateLicenseKey::class,
|
||
]);
|
||
})
|
||
->withExceptions(function (Exceptions $exceptions): void {
|
||
$exceptions->render(function (\Illuminate\Validation\ValidationException $e, $request) {
|
||
if ($request->is('api/*')) {
|
||
return response()->json([
|
||
'message' => 'The given data was invalid.',
|
||
'errors' => $e->errors(),
|
||
], 422);
|
||
}
|
||
});
|
||
|
||
$exceptions->render(function (\Illuminate\Auth\AuthenticationException $e, $request) {
|
||
if ($request->is('api/*')) {
|
||
return response()->json(['message' => 'Unauthenticated.'], 401);
|
||
}
|
||
});
|
||
|
||
$exceptions->render(function (\Symfony\Component\HttpKernel\Exception\HttpException $e, $request) {
|
||
if ($request->is('api/*')) {
|
||
return response()->json([
|
||
'message' => $e->getMessage() ?: 'An error occurred.',
|
||
'status' => $e->getStatusCode(),
|
||
], $e->getStatusCode());
|
||
}
|
||
});
|
||
})->create();
|