1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Providers;
7
8use App\Hashing\OsuBcryptHasher;
9use App\Libraries\MorphMap;
10use App\Libraries\OsuCookieJar;
11use App\Libraries\OsuMessageSelector;
12use App\Libraries\RateLimiter;
13use App\Singletons;
14use Illuminate\Database\Eloquent\Relations\Relation;
15use Illuminate\Http\Request;
16use Illuminate\Queue\Events\JobProcessed;
17use Illuminate\Support\ServiceProvider;
18use Knuckles\Scribe\Scribe;
19use Laravel\Octane\Contracts\DispatchesTasks;
20use Laravel\Octane\SequentialTaskDispatcher;
21use Laravel\Octane\Swoole\SwooleTaskDispatcher;
22use Queue;
23use Swoole\Http\Server;
24
25class AppServiceProvider extends ServiceProvider
26{
27 const LOCAL_CACHE_SINGLETONS = [
28 'chat-filters' => Singletons\ChatFilters::class,
29 'countries' => Singletons\Countries::class,
30 'groups' => Singletons\Groups::class,
31 'layout-cache' => Singletons\LayoutCache::class,
32 'medals' => Singletons\Medals::class,
33 'smilies' => Singletons\Smilies::class,
34 'tags' => Singletons\Tags::class,
35 'user-cover-presets' => Singletons\UserCoverPresets::class,
36 ];
37
38 const SINGLETONS = [
39 'OsuAuthorize' => Singletons\OsuAuthorize::class,
40 'assets-manifest' => Singletons\AssetsManifest::class,
41 'clean-html' => Singletons\CleanHTML::class,
42 'ip2asn' => Singletons\Ip2Asn::class,
43 'local-cache-manager' => Singletons\LocalCacheManager::class,
44 'mods' => Singletons\Mods::class,
45 'route-section' => Singletons\RouteSection::class,
46 'score-pins' => Singletons\UserScorePins::class,
47 ];
48
49 /**
50 * Bootstrap any application services.
51 *
52 * @return void
53 */
54 public function boot()
55 {
56 Relation::morphMap(MorphMap::flippedMap());
57
58 $GLOBALS['cfg'] = \Config::all();
59
60 Queue::after(function (JobProcessed $event) {
61 app('OsuAuthorize')->resetCache();
62 app('local-cache-manager')->incrementResetTicker();
63
64 datadog_increment(
65 'queue.run',
66 [
67 'job' => $event->job->payload()['data']['commandName'],
68 'queue' => $event->job->getQueue(),
69 ]
70 );
71 });
72
73 $this->app->make('translator')->setSelector(new OsuMessageSelector());
74
75 app('url')->forceScheme(substr($GLOBALS['cfg']['app']['url'], 0, 5) === 'https' ? 'https' : 'http');
76
77 Request::setTrustedProxies($GLOBALS['cfg']['trustedproxy']['proxies'], $GLOBALS['cfg']['trustedproxy']['headers']);
78
79 // newest scribe tries to rename {modelName} parameters to {id}
80 // but it kind of doesn't work with our route handlers.
81 Scribe::normalizeEndpointUrlUsing(fn ($url) => $url);
82
83 \Hash::extend('osubcrypt', fn () => new OsuBcryptHasher());
84 }
85
86 /**
87 * Register any application services.
88 *
89 * This service provider is a great spot to register your various container
90 * bindings with the application.
91 *
92 * @return void
93 */
94 public function register()
95 {
96 foreach (array_merge(static::SINGLETONS, static::LOCAL_CACHE_SINGLETONS) as $name => $class) {
97 $this->app->singleton($name, fn () => new $class());
98 }
99 $localCacheManager = app('local-cache-manager');
100 foreach (static::LOCAL_CACHE_SINGLETONS as $name => $_class) {
101 $localCacheManager->registerSingleton(app($name));
102 }
103
104 $this->app->singleton('cookie', function ($app) {
105 $config = $GLOBALS['cfg']['session'];
106
107 return (new OsuCookieJar())->setDefaultPathAndDomain(
108 $config['path'],
109 $config['domain'],
110 $config['secure'],
111 $config['same_site'] ?? null
112 );
113 });
114
115 $this->app->singleton(RateLimiter::class, function ($app) {
116 return new RateLimiter($app->make('cache')->driver(
117 $app['config']->get('cache.limiter')
118 ));
119 });
120
121 // pre-bind to avoid SwooleHttpTaskDispatcher and fallback when not running in a swoole context.
122 $this->app->bind(
123 DispatchesTasks::class,
124 fn ($app) => $app->bound(Server::class) ? new SwooleTaskDispatcher() : new SequentialTaskDispatcher()
125 );
126
127 $env = $this->app->environment();
128 if ($env === 'testing' || $env === 'dusk.local') {
129 // This is needed for testing with Dusk.
130 $this->app->register(AdditionalDuskServiceProvider::class);
131 }
132 }
133}