AppServiceProvider.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <?php
  2. namespace App\Providers;
  3. use App\Services\PaliSeriesesService;
  4. use App\Services\RomanizeService;
  5. use App\Services\UserService;
  6. use App\Tools\QueryBuilderMacro;
  7. use App\View\Composers\BlogViewComposer;
  8. use Carbon\CarbonImmutable;
  9. use Godruoyi\Snowflake\LaravelSequenceResolver;
  10. use Godruoyi\Snowflake\Snowflake;
  11. use Illuminate\Cache\RateLimiting\Limit;
  12. use Illuminate\Database\Query\Builder as QueryBuilder;
  13. use Illuminate\Http\Request;
  14. use Illuminate\Support\Facades\Date;
  15. use Illuminate\Support\Facades\DB;
  16. use Illuminate\Support\Facades\RateLimiter;
  17. use Illuminate\Support\Facades\View;
  18. use Illuminate\Support\ServiceProvider;
  19. use Illuminate\Validation\Rules\Password;
  20. class AppServiceProvider extends ServiceProvider
  21. {
  22. /**
  23. * Register any application services.
  24. */
  25. public function register(): void
  26. {
  27. /*
  28. |--------------------------------------------------------------------------
  29. | Snowflake ID Generator
  30. |--------------------------------------------------------------------------
  31. */
  32. $this->app->singleton('snowflake', function () {
  33. return (new Snowflake(
  34. config('mint.snowflake.data_center_id'),
  35. config('mint.snowflake.worker_id')
  36. ))
  37. ->setStartTimeStamp(
  38. strtotime(config('mint.snowflake.start')) * 1000
  39. )
  40. ->setSequenceResolver(
  41. new LaravelSequenceResolver(
  42. $this->app->get('cache')->store()
  43. )
  44. );
  45. });
  46. /*
  47. |--------------------------------------------------------------------------
  48. | Romanize Service
  49. |--------------------------------------------------------------------------
  50. */
  51. $this->app->singleton(RomanizeService::class);
  52. /*
  53. |--------------------------------------------------------------------------
  54. | Pali Serieses Service
  55. |--------------------------------------------------------------------------
  56. */
  57. $this->app->singleton(PaliSeriesesService::class);
  58. /*
  59. |--------------------------------------------------------------------------
  60. | User Service
  61. |--------------------------------------------------------------------------
  62. |
  63. | 必须是 singleton:它在请求内维护一张 uuid => 摘要的身份映射,同一个用户
  64. | 在一次请求里被解析多次时只查一次库、只签一次头像 URL。每次 make 新实例
  65. | 那张映射就永远是空的。
  66. |
  67. */
  68. $this->app->singleton(UserService::class);
  69. }
  70. /**
  71. * Bootstrap any application services.
  72. */
  73. public function boot(): void
  74. {
  75. /*
  76. |--------------------------------------------------------------------------
  77. | Laravel 13 Default Production Behaviors
  78. |--------------------------------------------------------------------------
  79. */
  80. $this->configureDefaults();
  81. /*
  82. |--------------------------------------------------------------------------
  83. | Custom Query Builder Macros
  84. |--------------------------------------------------------------------------
  85. */
  86. QueryBuilder::mixin(
  87. $this->app->make(QueryBuilderMacro::class)
  88. );
  89. /*
  90. |--------------------------------------------------------------------------
  91. | View Composers
  92. |--------------------------------------------------------------------------
  93. */
  94. View::composer('blog.*', BlogViewComposer::class);
  95. View::composer('layouts.blog', BlogViewComposer::class);
  96. /*
  97. |--------------------------------------------------------------------------
  98. | Rate Limiters
  99. |--------------------------------------------------------------------------
  100. |
  101. | 找回密码会往外发邮件:按 IP 限住撞库式的批量请求,按邮箱限住对同一个人的
  102. | 邮件轰炸。设新密码的请求不带 email,只按 IP 计。
  103. |
  104. */
  105. RateLimiter::for('password-resets', fn (Request $request): array => array_filter([
  106. Limit::perMinute(10)->by('ip:'.$request->ip()),
  107. $request->filled('email')
  108. ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
  109. : null,
  110. ]));
  111. // 注册验证码同样往外发邮件,口径与找回密码一致
  112. RateLimiter::for('email-certifications', fn (Request $request): array => array_filter([
  113. Limit::perMinute(10)->by('ip:'.$request->ip()),
  114. $request->filled('email')
  115. ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
  116. : null,
  117. ]));
  118. // 比对验证码、建账号:按 IP 限,挡住对 6 位验证码的穷举(单个码另有 5 次试错上限)
  119. RateLimiter::for('sign-up', fn (Request $request): Limit => Limit::perMinute(20)->by('ip:'.$request->ip()));
  120. // 登录:按「账号 + IP」挡住对单个账号的猜密码,按 IP 挡住换着账号撞库
  121. RateLimiter::for('sessions', fn (Request $request): array => [
  122. Limit::perMinute(5)->by('login:'.mb_strtolower((string) $request->input('login')).'|'.$request->ip()),
  123. Limit::perMinute(30)->by('ip:'.$request->ip()),
  124. ]);
  125. }
  126. /**
  127. * Configure default behaviors for production-ready applications.
  128. */
  129. protected function configureDefaults(): void
  130. {
  131. /*
  132. |--------------------------------------------------------------------------
  133. | Use Immutable Dates
  134. |--------------------------------------------------------------------------
  135. */
  136. Date::use(CarbonImmutable::class);
  137. /*
  138. |--------------------------------------------------------------------------
  139. | Prevent destructive DB commands in production
  140. |--------------------------------------------------------------------------
  141. */
  142. DB::prohibitDestructiveCommands(
  143. app()->isProduction(),
  144. );
  145. /*
  146. |--------------------------------------------------------------------------
  147. | Strong password defaults in production
  148. |--------------------------------------------------------------------------
  149. */
  150. Password::defaults(
  151. fn (): ?Password => app()->isProduction()
  152. ? Password::min(12)
  153. ->mixedCase()
  154. ->letters()
  155. ->numbers()
  156. ->symbols()
  157. ->uncompromised()
  158. : null,
  159. );
  160. }
  161. }