api/app/Providers/AppServiceProvider.php

169 lines
5.5 KiB
PHP

<?php
namespace App\Providers;
use App\Broadcasting\ExpoPushChannel;
use App\Mail\ResetPasswordMail;
use App\Mail\VerifyAccount;
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
use Spatie\Health\Checks\Checks\DatabaseCheck;
use Spatie\Health\Checks\Checks\DebugModeCheck;
use Spatie\Health\Checks\Checks\EnvironmentCheck;
use Spatie\Health\Checks\Checks\HorizonCheck;
use Spatie\Health\Checks\Checks\MeilisearchCheck;
use Spatie\Health\Checks\Checks\OptimizedAppCheck;
use Spatie\Health\Checks\Checks\RedisCheck;
use Spatie\Health\Checks\Checks\ScheduleCheck;
use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck;
use Spatie\Health\Facades\Health;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configureRateLimiting();
if ($this->app->environment('production')) {
URL::forceScheme('https');
}
Notification::extend('expo', fn ($app) => $app->make(ExpoPushChannel::class));
VerifyEmail::createUrlUsing(function (User $notifiable): string {
$relativeUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes((int) config('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
],
absolute: false,
);
return url($relativeUrl);
});
VerifyEmail::toMailUsing(
fn (User $notifiable, string $verificationUrl): VerifyAccount => (new VerifyAccount($notifiable, $verificationUrl))
->to($notifiable->getEmailForVerification())
);
$passwordResetUrl = function (User $notifiable, string $token): string {
return route('password.reset', [
'email' => $notifiable->getEmailForPasswordReset(),
'token' => $token,
]);
};
ResetPassword::createUrlUsing($passwordResetUrl);
ResetPassword::toMailUsing(
fn (User $notifiable, string $token): ResetPasswordMail => (new ResetPasswordMail($notifiable, $passwordResetUrl($notifiable, $token)))
->to($notifiable->getEmailForPasswordReset())
);
Gate::define('viewApiDocs', fn (?User $user): bool => $user?->isAdmin() === true);
Health::checks([
DatabaseCheck::new(),
UsedDiskSpaceCheck::new(),
RedisCheck::new(),
ScheduleCheck::new(),
HorizonCheck::new(),
OptimizedAppCheck::new(),
EnvironmentCheck::new(),
DebugModeCheck::new(),
MeilisearchCheck::new()
->url('http://meilisearch:7700/health'),
]);
}
private function configureRateLimiting(): void
{
RateLimiter::for('auth', fn (Request $request): array => [
Limit::perMinute(20)->by('ip:'.$request->ip()),
Limit::perMinute(5)->by($this->loginRateLimitKey($request)),
]);
RateLimiter::for(
'account-action',
fn (Request $request): Limit => Limit::perMinute(5)->by($this->userRateLimitKey($request))
);
RateLimiter::for(
'content-write',
fn (Request $request): Limit => Limit::perMinute(30)->by($this->userRateLimitKey($request))
);
RateLimiter::for(
'engagement',
fn (Request $request): Limit => Limit::perMinute(60)->by($this->userRateLimitKey($request))
);
RateLimiter::for('ai-analysis', fn (Request $request): array => [
Limit::perMinute(5)->by('minute:'.$this->userRateLimitKey($request)),
Limit::perHour(30)->by('hour:'.$this->userRateLimitKey($request)),
]);
RateLimiter::for(
'external-service',
fn (Request $request): Limit => Limit::perMinute(10)->by($this->userRateLimitKey($request))
);
RateLimiter::for(
'device-token',
fn (Request $request): Limit => Limit::perMinute(30)->by($this->userRateLimitKey($request))
);
RateLimiter::for(
'rewarded-ad',
fn (Request $request): Limit => Limit::perMinute(10)->by($this->userRateLimitKey($request))
);
RateLimiter::for(
'notifications',
fn (Request $request): Limit => Limit::perMinute(3)->by($this->userRateLimitKey($request))
);
RateLimiter::for(
'reports',
fn (Request $request): Limit => Limit::perMinute(10)->by($this->userRateLimitKey($request))
);
}
private function loginRateLimitKey(Request $request): string
{
$email = strtolower((string) $request->input('email', ''));
return $email !== '' ? 'email:'.$email : 'ip:'.$request->ip();
}
private function userRateLimitKey(Request $request): string
{
$userId = $request->user()?->getAuthIdentifier();
return $userId !== null ? 'user:'.$userId : 'ip:'.$request->ip();
}
}