the browser-facing portion of osu!
at master 40 kB view raw
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 6use App\Http\Middleware\ThrottleRequests; 7 8Route::get('wiki/images/{path}', 'WikiController@image')->name('wiki.image')->where('path', '.+'); 9Route::get('media-url', 'ProxyMediaController')->name('media-url'); 10 11Route::group(['middleware' => ['web']], function () { 12 Route::group(['as' => 'admin.', 'prefix' => 'admin', 'namespace' => 'Admin'], function () { 13 Route::get('/beatmapsets/{beatmapset}/covers', 'BeatmapsetsController@covers')->name('beatmapsets.covers'); 14 Route::post('/beatmapsets/{beatmapset}/covers/regenerate', 'BeatmapsetsController@regenerateCovers')->name('beatmapsets.covers.regenerate'); 15 Route::post('/beatmapsets/{beatmapset}/covers/remove', 'BeatmapsetsController@removeCovers')->name('beatmapsets.covers.remove'); 16 Route::resource('beatmapsets', 'BeatmapsetsController', ['only' => ['show', 'update']]); 17 18 Route::post('contests/{contest}/zip', 'ContestsController@gimmeZip')->name('contests.get-zip'); 19 Route::resource('contests', 'ContestsController', ['only' => ['index', 'show']]); 20 21 Route::resource('user-contest-entries', 'UserContestEntriesController', ['only' => ['destroy']]); 22 Route::post('user-contest-entries/{user_contest_entry}/restore', 'UserContestEntriesController@restore')->name('user-contest-entries.restore'); 23 24 Route::resource('logs', 'LogsController', ['only' => ['index']]); 25 26 Route::get('/', 'PagesController@root')->name('root'); 27 28 Route::group(['as' => 'forum.', 'prefix' => 'forum', 'namespace' => 'Forum'], function () { 29 Route::resource('forum-covers', 'ForumCoversController', ['only' => ['index', 'store', 'update']]); 30 }); 31 }); 32 33 Route::group(['prefix' => 'beatmaps'], function () { 34 // featured artists 35 Route::group(['as' => 'artists.', 'prefix' => 'artists'], function () { 36 Route::resource('tracks', 'ArtistTracksController', ['only' => ['index']]); 37 }); 38 Route::resource('artists', 'ArtistsController', ['only' => ['index', 'show']]); 39 Route::resource('artists/tracks', 'ArtistTracksController', ['only' => 'show']); 40 41 Route::resource('packs', 'BeatmapPacksController', ['only' => ['index', 'show']]); 42 43 Route::group(['as' => 'beatmaps.', 'prefix' => '{beatmap}'], function () { 44 Route::get('scores', 'BeatmapsController@scores')->name('scores'); 45 Route::get('solo-scores', 'BeatmapsController@soloScores')->name('solo-scores'); 46 Route::post('update-owner', 'BeatmapsController@updateOwner')->name('update-owner'); 47 }); 48 }); 49 50 Route::resource('beatmaps', 'BeatmapsController', ['only' => ['show']]); 51 52 Route::group(['prefix' => 'beatmapsets'], function () { 53 route_redirect('beatmap-discussions', 'beatmapsets.discussions.index'); 54 route_redirect('beatmap-discussions/{beatmap_discussion}', 'beatmapsets.discussions.destroy', 'delete'); 55 route_redirect('beatmap-discussions/{beatmap_discussion}', 'beatmapsets.discussions.show'); 56 route_redirect('beatmap-discussions/{beatmap_discussion}/vote', 'beatmapsets.discussions.vote', 'put'); 57 route_redirect('beatmap-discussions/{beatmap_discussion}/restore', 'beatmapsets.discussions.restore', 'post'); 58 route_redirect('beatmap-discussions/{beatmap_discussion}/deny-kudosu', 'beatmapsets.discussions.deny-kudosu', 'post'); 59 route_redirect('beatmap-discussions/{beatmap_discussion}/allow-kudosu', 'beatmapsets.discussions.allow-kudosu', 'post'); 60 61 route_redirect('beatmap-discussion-posts/{beatmap_discussion_post}', 'beatmapsets.discussions.posts.destroy', 'delete'); 62 route_redirect('beatmap-discussion-posts/{beatmap_discussion_post}', 'beatmapsets.discussions.posts.store', 'post'); 63 route_redirect('beatmap-discussion-posts/{beatmap_discussion_post}', 'beatmapsets.discussions.posts.update', 'put'); 64 route_redirect('beatmap-discussion-posts/{beatmap_discussion_post}/restore', 'beatmapsets.discussions.posts.restore', 'post'); 65 route_redirect('beatmap-discussion-posts', 'beatmapsets.discussions.posts.index'); 66 }); 67 68 Route::group(['prefix' => 'beatmapsets', 'as' => 'beatmapsets.'], function () { 69 Route::resource('events', 'BeatmapsetEventsController', ['only' => ['index']]); 70 // keeping old link alive 71 route_redirect('watches', 'follows.index'); 72 Route::resource('watches', 'BeatmapsetWatchesController', ['only' => ['update', 'destroy']]); 73 74 Route::group(['prefix' => 'discussions', 'as' => 'discussions.'], function () { 75 Route::put('{discussion}/vote', 'BeatmapDiscussionsController@vote')->name('vote'); 76 Route::post('{discussion}/restore', 'BeatmapDiscussionsController@restore')->name('restore'); 77 Route::post('{discussion}/deny-kudosu', 'BeatmapDiscussionsController@denyKudosu')->name('deny-kudosu'); 78 Route::post('{discussion}/allow-kudosu', 'BeatmapDiscussionsController@allowKudosu')->name('allow-kudosu'); 79 80 Route::group(['prefix' => 'posts', 'as' => 'posts.'], function () { 81 Route::post('{post}/restore', 'BeatmapDiscussionPostsController@restore')->name('restore'); 82 }); 83 84 Route::resource('posts', 'BeatmapDiscussionPostsController', ['only' => ['destroy', 'index', 'show', 'store', 'update']]); 85 Route::resource('votes', 'BeatmapsetDiscussionVotesController', ['only' => ['index']]); 86 }); 87 88 Route::resource('discussions', 'BeatmapDiscussionsController', ['only' => ['destroy', 'index', 'show']]); 89 90 Route::group(['namespace' => 'Beatmapsets'], function () { 91 Route::apiResource('{beatmapset}/favourites', 'FavouritesController', ['only' => ['store']]); 92 }); 93 }); 94 Route::get('beatmapsets/search', 'BeatmapsetsController@search')->name('beatmapsets.search'); 95 Route::get('beatmapsets/{beatmapset}/discussion/{beatmap?}/{mode?}/{filter?}', 'BeatmapsetsController@discussion')->name('beatmapsets.discussion'); 96 Route::post('beatmapsets/{beatmapset}/discussion/review', 'BeatmapDiscussionsController@review')->name('beatmapsets.discussion.review'); 97 Route::get('beatmapsets/{beatmapset}/discussion-last-update', 'BeatmapsetsController@discussionLastUpdate')->name('beatmapsets.discussion-last-update'); 98 Route::post('beatmapsets/{beatmapset}/discussion-lock', 'BeatmapsetsController@discussionLock')->name('beatmapsets.discussion-lock'); 99 Route::post('beatmapsets/{beatmapset}/discussion-unlock', 'BeatmapsetsController@discussionUnlock')->name('beatmapsets.discussion-unlock'); 100 Route::get('beatmapsets/{beatmapset}/download', 'BeatmapsetsController@download')->name('beatmapsets.download'); 101 Route::put('beatmapsets/{beatmapset}/love', 'BeatmapsetsController@love')->name('beatmapsets.love'); 102 Route::delete('beatmapsets/{beatmapset}/love', 'BeatmapsetsController@removeFromLoved')->name('beatmapsets.remove-from-loved'); 103 Route::put('beatmapsets/{beatmapset}/nominate', 'BeatmapsetsController@nominate')->name('beatmapsets.nominate'); 104 Route::resource('beatmapsets', 'BeatmapsetsController', ['only' => ['destroy', 'index', 'show', 'update']]); 105 106 Route::group(['prefix' => 'scores', 'as' => 'scores.'], function () { 107 Route::get('{score}/download', 'ScoresController@download')->name('download'); 108 Route::get('{rulesetOrScore}/{score}/download', 'ScoresController@download')->name('download-legacy'); 109 110 Route::get('{rulesetOrScore}/{score?}', 'ScoresController@show')->name('show'); 111 }); 112 113 Route::delete('score-pins', 'ScorePinsController@destroy')->name('score-pins.destroy'); 114 Route::put('score-pins', 'ScorePinsController@reorder')->name('score-pins.reorder'); 115 Route::resource('score-pins', 'ScorePinsController', ['only' => ['store']]); 116 117 Route::resource('client-verifications', 'ClientVerificationsController', ['only' => ['create', 'store']]); 118 119 Route::resource('comments', 'CommentsController', ['except' => ['create', 'edit']]); 120 Route::post('comments/{comment}/pin', 'CommentsController@pinStore')->name('comments.pin'); 121 Route::delete('comments/{comment}/pin', 'CommentsController@pinDestroy'); 122 Route::post('comments/{comment}/restore', 'CommentsController@restore')->name('comments.restore'); 123 Route::post('comments/{comment}/vote', 'CommentsController@voteStore')->name('comments.vote'); 124 Route::delete('comments/{comment}/vote', 'CommentsController@voteDestroy'); 125 126 Route::group(['prefix' => 'community'], function () { 127 Route::resource('contests', 'ContestsController', ['only' => ['index', 'show']]); 128 Route::get('contests/{contest}/judge', 'ContestsController@judge')->name('contests.judge'); 129 130 131 Route::get('contest-entries/{contest_entry}/results', 'ContestEntriesController@judgeResults')->name('contest-entries.judge-results'); 132 Route::put('contest-entries/{contest_entry}/judge-vote', 'ContestEntriesController@judgeVote')->name('contest-entries.judge-vote'); 133 Route::put('contest-entries/{contest_entry}/vote', 'ContestEntriesController@vote')->name('contest-entries.vote'); 134 Route::resource('contest-entries', 'ContestEntriesController', ['only' => ['store', 'destroy']]); 135 136 Route::post('livestreams/promote', 'LivestreamsController@promote')->name('livestreams.promote'); 137 Route::resource('livestreams', 'LivestreamsController', ['only' => ['index']]); 138 139 Route::resource('matches', 'MatchesController', ['only' => ['show']]); 140 141 Route::post('tournaments/{tournament}/unregister', 'TournamentsController@unregister')->name('tournaments.unregister'); 142 Route::post('tournaments/{tournament}/register', 'TournamentsController@register')->name('tournaments.register'); 143 Route::resource('tournaments', 'TournamentsController', ['only' => ['index', 'show']]); 144 145 Route::group(['as' => 'forum.', 'namespace' => 'Forum'], function () { 146 Route::group(['prefix' => 'forums'], function () { 147 Route::resource('forum-covers', 'ForumCoversController', ['only' => ['store', 'update', 'destroy']]); 148 149 Route::get('posts/{post}/raw', 'PostsController@raw')->name('posts.raw'); 150 Route::post('posts/{post}/restore', 'PostsController@restore')->name('posts.restore'); 151 Route::resource('posts', 'PostsController', ['only' => ['destroy', 'edit', 'show', 'update']]); 152 153 Route::group(['as' => 'topics.', 'prefix' => 'topics/{topic}'], function () { 154 Route::resource('logs', 'TopicLogsController', ['only' => 'index']); 155 156 Route::post('edit-poll', 'TopicsController@editPollPost')->name('edit-poll.store'); 157 Route::get('edit-poll', 'TopicsController@editPollGet')->name('edit-poll'); 158 159 Route::post('issue-tag', 'TopicsController@issueTag')->name('issue-tag'); 160 Route::post('lock', 'TopicsController@lock')->name('lock'); 161 Route::post('move', 'TopicsController@move')->name('move'); 162 Route::post('pin', 'TopicsController@pin')->name('pin'); 163 Route::post('reply', 'TopicsController@reply')->name('reply'); 164 Route::post('restore', 'TopicsController@restore')->name('restore'); 165 Route::post('vote', 'TopicsController@vote')->name('vote'); 166 Route::post('vote-feature', 'TopicsController@voteFeature')->name('vote-feature'); 167 }); 168 169 Route::post('topics/preview', 'TopicsController@preview')->name('preview'); 170 Route::resource('topics', 'TopicsController', ['only' => ['create', 'destroy', 'show', 'store', 'update']]); 171 172 Route::resource('topic-covers', 'TopicCoversController', ['only' => ['store', 'update', 'destroy']]); 173 174 // keeping old link alive 175 route_redirect('topic-watches', 'follows.index'); 176 Route::resource('topic-watches', 'TopicWatchesController', ['only' => ['update']]); 177 }); 178 179 Route::post('forums/mark-as-read', 'ForumsController@markAsRead')->name('forums.mark-as-read'); 180 Route::resource('forums', 'ForumsController', ['only' => ['index', 'show']]); 181 }); 182 183 Route::group(['as' => 'chat.', 'prefix' => 'chat', 'namespace' => 'Chat'], function () { 184 Route::post('ack', 'ChatController@ack')->name('ack'); 185 Route::post('new', 'ChatController@newConversation')->name('new'); 186 Route::get('presence', 'ChatController@presence')->name('presence'); 187 Route::get('updates', 'ChatController@updates')->name('updates'); 188 Route::group(['as' => 'channels.', 'prefix' => 'channels'], function () { 189 Route::apiResource('{channel}/messages', 'Channels\MessagesController', ['only' => ['index', 'store']]); 190 Route::apiResource('{channel}/users', 'Channels\UsersController', ['only' => ['index']]); 191 Route::put('{channel}/users/{user}', 'ChannelsController@join')->name('join'); 192 Route::delete('{channel}/users/{user}', 'ChannelsController@part')->name('part'); 193 Route::put('{channel}/mark-as-read/{message}', 'ChannelsController@markAsRead')->name('mark-as-read'); 194 }); 195 Route::apiResource('channels', 'ChannelsController', ['only' => ['index', 'show', 'store']]); 196 }); 197 Route::resource('chat', 'ChatController', ['only' => ['index']]); 198 }); 199 200 Route::get('groups/history', 'GroupHistoryController@index')->name('group-history.index'); 201 Route::resource('groups', 'GroupsController', ['only' => ['show']]); 202 203 Route::group(['prefix' => 'home'], function () { 204 Route::group(['as' => 'account.', 'prefix' => 'account'], function () { 205 Route::get('edit', 'AccountController@edit')->name('edit'); 206 // Uploading file doesn't quite work with PUT/PATCH. 207 // Reference: https://bugs.php.net/bug.php?id=55815 208 // Note that hhvm behaves differently (the same as POST). 209 Route::post('avatar', 'AccountController@avatar')->name('avatar'); 210 Route::put('country', 'AccountController@updateCountry')->name('country'); 211 Route::post('cover', 'AccountController@cover')->name('cover'); 212 Route::put('email', 'AccountController@updateEmail')->name('email'); 213 Route::put('notification-options', 'AccountController@updateNotificationOptions')->name('notification-options'); 214 Route::put('options', 'AccountController@updateOptions')->name('options'); 215 Route::put('password', 'AccountController@updatePassword')->name('password'); 216 Route::post('reissue-code', 'AccountController@reissueCode')->name('reissue-code'); 217 Route::resource('sessions', 'Account\SessionsController', ['only' => ['destroy']]); 218 Route::get('verify', 'AccountController@verifyLink'); 219 Route::post('verify', 'AccountController@verify')->name('verify'); 220 Route::put('/', 'AccountController@update')->name('update'); 221 222 Route::get('github-users/callback', 'Account\GithubUsersController@callback')->name('github-users.callback'); 223 Route::resource('github-users', 'Account\GithubUsersController', ['only' => ['create']]); 224 Route::delete('github-users', 'Account\GithubUsersController@destroy')->name('github-users.destroy'); 225 }); 226 227 Route::get('quick-search', 'HomeController@quickSearch')->name('quick-search'); 228 Route::get('search', 'HomeController@search')->name('search'); 229 Route::post('bbcode-preview', 'HomeController@bbcodePreview')->name('bbcode-preview'); 230 Route::get('changelog/{stream}/{build}', 'ChangelogController@build')->name('changelog.build'); 231 Route::post('changelog/github', 'ChangelogController@github'); 232 Route::resource('changelog', 'ChangelogController', ['only' => ['index', 'show']]); 233 Route::get('download', 'HomeController@getDownload')->name('download'); 234 Route::post('set-locale', 'HomeController@setLocale')->name('set-locale'); 235 Route::get('support', 'HomeController@supportTheGame')->name('support-the-game'); 236 Route::get('testflight', 'HomeController@testflight')->name('testflight'); 237 238 Route::get('password-reset', 'PasswordResetController@index')->name('password-reset'); 239 Route::post('password-reset', 'PasswordResetController@create'); 240 Route::put('password-reset', 'PasswordResetController@update'); 241 Route::delete('password-reset', 'PasswordResetController@destroy'); 242 Route::get('password-reset/reset', 'PasswordResetController@reset')->name('password-reset.reset'); 243 Route::post('password-reset/resend-mail', 'PasswordResetController@resendMail')->name('password-reset.resend-mail'); 244 245 Route::get('download-quota-check', 'HomeController@downloadQuotaCheck')->name('download-quota-check'); 246 247 Route::resource('blocks', 'BlocksController', ['only' => ['store', 'destroy']]); 248 Route::resource('friends', 'FriendsController', ['only' => ['index', 'store', 'destroy']]); 249 Route::resource('news', 'NewsController', ['only' => ['index', 'show', 'store', 'update']]); 250 251 Route::get('messages/users/{user}', 'HomeController@messageUser')->name('messages.users.show'); 252 253 Route::resource('follows', 'FollowsController', ['only' => ['store']]); 254 Route::get('follows/{subtype?}', 'FollowsController@index')->name('follows.index'); 255 Route::delete('follows', 'FollowsController@destroy')->name('follows.destroy'); 256 }); 257 258 Route::resource('legacy-api-key', 'LegacyApiKeyController', ['only' => ['store']]); 259 Route::delete('legacy-api-key', 'LegacyApiKeyController@destroy')->name('legacy-api-key.destroy'); 260 261 Route::resource('legacy-irc-key', 'LegacyIrcKeyController', ['only' => ['store']]); 262 Route::delete('legacy-irc-key', 'LegacyIrcKeyController@destroy')->name('legacy-irc-key.destroy'); 263 264 Route::resource('notifications', 'NotificationsController', ['only' => ['index']]); 265 Route::get('notifications/endpoint', 'NotificationsController@endpoint')->name('notifications.endpoint'); 266 Route::post('notifications/mark-read', 'NotificationsController@markRead')->name('notifications.mark-read'); 267 Route::delete('notifications', 'NotificationsController@batchDestroy'); 268 269 Route::get('legal/{locale?}/{path?}', 'LegalController@show')->name('legal'); 270 Route::put('legal/{locale}/{path}', 'LegalController@update'); 271 272 Route::group(['prefix' => 'multiplayer', 'as' => 'multiplayer.', 'namespace' => 'Multiplayer'], function () { 273 Route::resource('rooms', 'RoomsController', ['only' => ['show']]); 274 }); 275 276 Route::get('news/{tumblrId}', 'NewsController@redirect'); 277 278 Route::group(['as' => 'oauth.', 'prefix' => 'oauth', 'namespace' => 'OAuth'], function () { 279 Route::resource('authorized-clients', 'AuthorizedClientsController', ['only' => ['destroy']]); 280 Route::resource('clients', 'ClientsController', ['except' => ['create', 'edit', 'show']]); 281 Route::post('clients/{client}/reset-secret', 'ClientsController@resetSecret')->name('clients.reset-secret'); 282 }); 283 284 Route::get('rankings/kudosu', 'RankingController@kudosu')->name('rankings.kudosu'); 285 Route::resource('rankings/daily-challenge', 'Ranking\DailyChallengeController', ['only' => ['index', 'show']]); 286 Route::get('rankings/{mode?}/{type?}', 'RankingController@index')->name('rankings'); 287 288 Route::resource('reports', 'ReportsController', ['only' => ['store']]); 289 290 Route::resource('seasons', 'SeasonsController', ['only' => 'show']); 291 Route::get('seasons/{season}/rooms', 'SeasonsController@rooms')->name('seasons.rooms'); 292 293 Route::post('session', 'SessionsController@store')->name('login'); 294 Route::delete('session', 'SessionsController@destroy')->name('logout'); 295 296 Route::post('user-cover-presets/batch-activate', 'UserCoverPresetsController@batchActivate')->name('user-cover-presets.batch-activate'); 297 Route::resource('user-cover-presets', 'UserCoverPresetsController', ['only' => ['index', 'store', 'update']]); 298 299 Route::group(['as' => 'teams.', 'prefix' => 'teams/{team}'], function () { 300 Route::post('part', 'TeamsController@part')->name('part'); 301 Route::resource('members', 'Teams\MembersController', ['only' => ['destroy', 'index']]); 302 }); 303 Route::resource('teams', 'TeamsController', ['only' => ['destroy', 'edit', 'show', 'update']]); 304 305 Route::post('users/check-username-availability', 'UsersController@checkUsernameAvailability')->name('users.check-username-availability'); 306 Route::get('users/lookup', 'Users\LookupController@index')->name('users.lookup'); 307 Route::get('users/disabled', 'UsersController@disabled')->name('users.disabled'); 308 Route::get('users/create', 'UsersController@create')->name('users.create'); 309 Route::post('users/store-web', 'UsersController@storeWeb')->name('users.store-web'); 310 311 Route::group(['as' => 'users.', 'prefix' => 'users/{user}'], function () { 312 Route::get('extra-pages/{page}', 'UsersController@extraPages')->name('extra-page'); 313 Route::put('page', 'UsersController@updatePage')->name('page'); 314 Route::group(['namespace' => 'Users'], function () { 315 Route::resource('{typeGroup}', 'MultiplayerController', ['only' => 'index'])->where(['typeGroup' => 'multiplayer|playlists|realtime'])->names('multiplayer'); 316 317 Route::group(['as' => 'modding.', 'prefix' => 'modding'], function () { 318 Route::get('/', 'ModdingHistoryController@index')->name('index'); 319 Route::get('/posts', 'ModdingHistoryController@posts')->name('posts'); 320 Route::get('/votes-given', 'ModdingHistoryController@votesGiven')->name('votes-given'); 321 Route::get('/votes-received', 'ModdingHistoryController@votesReceived')->name('votes-received'); 322 }); 323 }); 324 325 Route::get('kudosu', 'UsersController@kudosu')->name('kudosu'); 326 Route::get('recent_activity', 'UsersController@recentActivity')->name('recent-activity'); 327 Route::get('scores/{type}', 'UsersController@scores')->name('scores'); 328 Route::get('beatmapsets/{type}', 'UsersController@beatmapsets')->name('beatmapsets'); 329 }); 330 331 Route::get('users/{user}/posts', 'UsersController@posts')->name('users.posts'); 332 Route::get('users/{user}/{mode?}', 'UsersController@show')->name('users.show'); 333 Route::resource('users', 'UsersController', ['only' => ['store']]); 334 335 Route::get('wiki/{locale}/Sitemap', 'WikiController@sitemap')->name('wiki.sitemap'); 336 Route::get('wiki/{locale?}/{path?}', 'WikiController@show')->name('wiki.show')->where('path', '.+'); 337 Route::put('wiki/{locale}/{path}', 'WikiController@update')->where('path', '.+'); 338 Route::get('wiki-suggestions', 'WikiController@suggestions')->name('wiki-suggestions'); 339 340 // FIXME: someone split this crap up into proper controllers 341 Route::group(['as' => 'store.', 'prefix' => 'store'], function () { 342 route_redirect('/', 'store.products.index'); 343 344 Route::get('listing', 'StoreController@getListing')->name('products.index'); 345 Route::get('invoice/{invoice}', 'StoreController@getInvoice')->name('invoice.show'); 346 347 Route::group(['namespace' => 'Store'], function () { 348 Route::post('products/{product}/notification-request', 'NotificationRequestsController@store')->name('notification-request'); 349 Route::delete('products/{product}/notification-request', 'NotificationRequestsController@destroy'); 350 351 // Store splitting starts here 352 Route::get('cart', 'CartController@show')->name('cart.show'); 353 Route::delete('cart', 'CartController@empty')->name('cart.empty'); 354 Route::resource('cart', 'CartController', ['only' => ['store']]); 355 356 Route::resource('checkout', 'CheckoutController', ['only' => ['show', 'store']]); 357 358 Route::resource('orders', 'OrdersController', ['only' => ['destroy', 'index']]); 359 360 route_redirect('product/{product}', 'store.products.show'); 361 Route::resource('products', 'ProductsController', ['only' => ['show']]); 362 }); 363 }); 364 365 Route::group(['as' => 'payments.', 'prefix' => 'payments', 'namespace' => 'Payments'], function () { 366 Route::group(['as' => 'paypal.', 'prefix' => 'paypal'], function () { 367 Route::get('approved', 'PaypalController@approved')->name('approved'); 368 Route::get('declined', 'PaypalController@declined')->name('declined'); 369 Route::post('create', 'PaypalController@create')->name('create'); 370 Route::post('ipn', 'PaypalController@ipn')->name('ipn'); 371 }); 372 373 Route::group(['as' => 'xsolla.', 'prefix' => 'xsolla'], function () { 374 Route::get('completed', 'XsollaController@completed')->name('completed'); 375 Route::post('token', 'XsollaController@token')->name('token'); 376 Route::post('callback', 'XsollaController@callback')->name('callback'); 377 }); 378 379 Route::group(['as' => 'shopify.', 'prefix' => 'shopify'], function () { 380 Route::post('callback', 'ShopifyController@callback')->name('callback'); 381 }); 382 }); 383 384 // TODO: update to redirect to root later 385 Route::get('/home', 'HomeController@index'); 386 387 Route::get('/', 'HomeController@index')->name('home'); 388 389 // redirects go here 390 route_redirect('forum/p/{post}', 'forum.posts.show'); 391 route_redirect('po/{post}', 'forum.posts.show:'); 392 route_redirect('forum/t/{topic}', 'forum.topics.show'); 393 route_redirect('forum/{forum}', 'forum.forums.show'); 394 // redirects to beatmapset anyways so there's no point 395 // in having an another redirect on top of that 396 Route::get('b/{beatmap}', 'BeatmapsController@show')->name('redirect:beatmaps.show'); 397 route_redirect('g/{group}', 'groups.show'); 398 route_redirect('s/{beatmapset}', 'beatmapsets.show'); 399 route_redirect('u/{user}', 'users.show'); 400 route_redirect('forum', 'forum.forums.index'); 401 route_redirect('mp/{match}', 'matches.show'); 402 route_redirect('help/wiki/{path?}', 'wiki.show')->where('path', '.+'); 403}); 404 405// API 406// require-scopes is not in the api group at the moment to reduce the number of things that need immediate fixing. 407// There's also a different group which skips throttle middleware. 408Route::group(['as' => 'api.', 'prefix' => 'api', 'middleware' => ['api', ThrottleRequests::getApiThrottle(), 'require-scopes']], function () { 409 Route::group(['prefix' => 'v2'], function () { 410 Route::group(['middleware' => ['require-scopes:any']], function () { 411 Route::post('session/verify', 'AccountController@verify')->name('verify'); 412 Route::post('session/verify/reissue', 'AccountController@reissueCode')->name('verify.reissue'); 413 }); 414 415 Route::group(['as' => 'beatmaps.', 'prefix' => 'beatmaps'], function () { 416 Route::get('lookup', 'BeatmapsController@lookup')->name('lookup'); 417 418 Route::apiResource('packs', 'BeatmapPacksController', ['only' => ['index', 'show']]); 419 420 Route::group(['prefix' => '{beatmap}'], function () { 421 Route::get('scores/users/{user}', 'BeatmapsController@userScore')->name('user.score'); 422 Route::get('scores/users/{user}/all', 'BeatmapsController@userScoreAll')->name('user.scores'); 423 Route::get('scores', 'BeatmapsController@scores')->name('scores'); 424 Route::get('solo-scores', 'BeatmapsController@soloScores')->name('solo-scores'); 425 426 Route::group(['as' => 'solo.', 'prefix' => 'solo'], function () { 427 Route::post('scores', 'ScoreTokensController@store')->name('score-tokens.store'); 428 429 Route::group(['namespace' => 'Solo'], function () { 430 Route::put('scores/{token}', 'ScoresController@store')->name('scores.store'); 431 }); 432 }); 433 434 Route::apiResource('tags', 'BeatmapTagsController', ['only' => ['store', 'destroy']]); 435 }); 436 }); 437 438 Route::resource('beatmaps', 'BeatmapsController', ['only' => ['index', 'show']]); 439 Route::post('beatmaps/{beatmap}/attributes', 'BeatmapsController@attributes')->name('beatmaps.attributes'); 440 441 Route::group(['as' => 'beatmapsets.', 'prefix' => 'beatmapsets'], function () { 442 Route::apiResource('events', 'BeatmapsetEventsController', ['only' => ['index']]); 443 444 Route::group(['as' => 'discussions.', 'prefix' => 'discussions'], function () { 445 Route::apiResource('posts', 'BeatmapDiscussionPostsController', ['only' => ['index']]); 446 Route::apiResource('votes', 'BeatmapsetDiscussionVotesController', ['only' => ['index']]); 447 }); 448 449 Route::resource('discussions', 'BeatmapDiscussionsController', ['only' => ['index']]); 450 451 // TODO: move other beatmapset routes here 452 Route::group(['namespace' => 'Beatmapsets'], function () { 453 Route::apiResource('{beatmapset}/favourites', 'FavouritesController', ['only' => ['store']]); 454 }); 455 }); 456 457 Route::resource('blocks', 'BlocksController', ['only' => ['destroy', 'index', 'store']]); 458 459 Route::apiResource('comments', 'CommentsController'); 460 Route::post('comments/{comment}/vote', 'CommentsController@voteStore')->name('comments.vote'); 461 Route::delete('comments/{comment}/vote', 'CommentsController@voteDestroy'); 462 463 Route::group(['as' => 'chat.', 'prefix' => 'chat', 'namespace' => 'Chat'], function () { 464 Route::post('ack', 'ChatController@ack')->name('ack'); 465 Route::post('new', 'ChatController@newConversation')->name('new'); 466 Route::get('updates', 'ChatController@updates')->name('updates'); 467 Route::get('presence', 'ChatController@presence')->name('presence'); 468 Route::group(['as' => 'channels.', 'prefix' => 'channels'], function () { 469 Route::apiResource('{channel}/messages', 'Channels\MessagesController', ['only' => ['index', 'store']]); 470 Route::put('{channel}/users/{user}', 'ChannelsController@join')->name('join'); 471 Route::delete('{channel}/users/{user}', 'ChannelsController@part')->name('part'); 472 Route::put('{channel}/mark-as-read/{message}', 'ChannelsController@markAsRead')->name('mark-as-read'); 473 }); 474 Route::apiResource('channels', 'ChannelsController', ['only' => ['index', 'show', 'store']]); 475 }); 476 477 Route::get('changelog/{stream}/{build}', 'ChangelogController@build')->name('changelog.build'); 478 Route::resource('changelog', 'ChangelogController', ['only' => ['index', 'show']]); 479 480 Route::apiResource('events', 'EventsController', ['only' => ['index']]); 481 482 Route::group(['as' => 'forum.', 'namespace' => 'Forum'], function () { 483 Route::group(['prefix' => 'forums'], function () { 484 Route::post('topics/{topic}/reply', 'TopicsController@reply')->name('topics.reply'); 485 Route::resource('topics', 'TopicsController', ['only' => ['show', 'store', 'update']]); 486 Route::resource('posts', 'PostsController', ['only' => ['update']]); 487 }); 488 }); 489 Route::resource('matches', 'MatchesController', ['only' => ['index', 'show']]); 490 491 Route::resource('reports', 'ReportsController', ['only' => ['store']]); 492 493 Route::group(['as' => 'rooms.', 'prefix' => 'rooms'], function () { 494 Route::put('{room}/users/{user}', 'Multiplayer\RoomsController@join')->name('join'); 495 Route::delete('{room}/users/{user}', 'Multiplayer\RoomsController@part')->name('part'); 496 Route::get('{room}/leaderboard', 'Multiplayer\RoomsController@leaderboard'); 497 Route::group(['as' => 'playlist.', 'prefix' => '{room}/playlist'], function () { 498 Route::get('{playlist}/scores/users/{user}', 'Multiplayer\Rooms\Playlist\ScoresController@showUser'); 499 Route::apiResource('{playlist}/scores', 'Multiplayer\Rooms\Playlist\ScoresController', ['only' => ['index', 'show', 'store', 'update']]); 500 }); 501 }); 502 503 Route::apiResource('rooms', 'Multiplayer\RoomsController', ['only' => ['index', 'show', 'store', 'destroy']]); 504 505 Route::apiResource('seasonal-backgrounds', 'SeasonalBackgroundsController', ['only' => ['index']]); 506 507 Route::group(['prefix' => 'scores', 'as' => 'scores.'], function () { 508 Route::get('{score}/download', 'ScoresController@download')->middleware(ThrottleRequests::getApiThrottle('scores_download'))->name('download'); 509 Route::get('{rulesetOrScore}/{score}/download', 'ScoresController@download')->middleware(ThrottleRequests::getApiThrottle('scores_download'))->name('download-legacy'); 510 511 Route::get('{rulesetOrScore}/{score?}', 'ScoresController@show')->name('show'); 512 513 Route::get('/', 'ScoresController@index'); 514 }); 515 516 // Beatmapsets 517 // GET /api/v2/beatmapsets/search/:filters 518 Route::get('beatmapsets/search', 'BeatmapsetsController@search'); 519 // GET /api/v2/beatmapsets/lookup 520 Route::get('beatmapsets/lookup', 'BeatmapsetsController@lookup'); 521 // GET /api/v2/beatmapsets/:beatmapset_id 522 Route::resource('beatmapsets', 'BeatmapsetsController', ['only' => ['show']]); 523 524 // Friends 525 Route::resource('friends', 'FriendsController', ['only' => ['index', 'store', 'destroy']]); 526 527 // GET /api/v2/me/download-quota-check 528 Route::get('me/download-quota-check', 'HomeController@downloadQuotaCheck')->name('download-quota-check'); 529 // GET /api/v2/me 530 Route::get('me/{mode?}', 'UsersController@me')->name('me'); 531 532 Route::delete('oauth/tokens/current', 'OAuth\TokensController@destroyCurrent')->name('oauth.tokens.current'); 533 534 Route::apiResource('news', 'NewsController', ['only' => ['index', 'show']]); 535 536 // Notifications 537 // GET /api/v2/notifications 538 Route::resource('notifications', 'NotificationsController', ['only' => ['index']]); 539 // POST /api/v2/notifications/mark-read 540 Route::post('notifications/mark-read', 'NotificationsController@markRead')->name('notifications.mark-read'); 541 542 Route::get('rankings/kudosu', 'RankingController@kudosu'); 543 // GET /api/v2/rankings/:mode/:type 544 Route::get('rankings/{mode}/{type}', 'RankingController@index')->name('rankings'); 545 Route::resource('spotlights', 'SpotlightsController', ['only' => ['index']]); 546 547 Route::get('search', 'HomeController@search'); 548 549 Route::get('users/lookup', 'Users\LookupController@index')->name('users.lookup'); 550 // GET /api/v2/users/:user_id/kudosu 551 Route::get('users/{user}/kudosu', 'UsersController@kudosu'); 552 // GET /api/v2/users/:user_id/scores/:type [best, firsts, recent] 553 Route::get('users/{user}/scores/{type}', 'UsersController@scores'); 554 // GET /api/v2/users/:user_id/beatmapsets/:type [most_played, favourite, ranked, pending, graveyard] 555 Route::get('users/{user}/beatmapsets/{type}', 'UsersController@beatmapsets'); 556 // GET /api/v2/users/:user_id/recent_activity 557 Route::get('users/{user}/recent_activity', 'UsersController@recentActivity'); 558 // GET /api/v2/users/:user_id/:mode [osu, taiko, fruits, mania] 559 Route::get('users/{user}/{mode?}', 'UsersController@show')->name('users.show'); 560 Route::resource('users', 'UsersController', ['only' => ['index']]); 561 562 Route::get('wiki/{locale}/{path}', 'WikiController@show')->name('wiki.show')->where('path', '.+'); 563 564 // Tags 565 Route::apiResource('tags', 'TagsController', ['only' => ['index']]); 566 }); 567}); 568 569// Duplicate of the other api group but without throttle. 570Route::group(['as' => 'api.', 'prefix' => 'api/v2', 'middleware' => ['api', 'require-scopes']], function () { 571 // GET /api/v2/beatmapsets/:beatmapset/download 572 Route::get('beatmapsets/{beatmapset}/download', 'BeatmapsetsController@download'); 573}); 574 575// Callbacks for legacy systems to interact with 576Route::group(['prefix' => '_lio', 'middleware' => 'lio', 'as' => 'interop.'], function () { 577 Route::post('generate-notification', 'LegacyInterOpController@generateNotification'); 578 Route::post('index-beatmapset/{beatmapset}', 'LegacyInterOpController@indexBeatmapset'); 579 Route::post('/refresh-beatmapset-cache/{beatmapset}', 'LegacyInterOpController@refreshBeatmapsetCache'); 580 Route::post('user-send-message', 'LegacyInterOpController@userSendMessage'); 581 Route::post('user-batch-mark-channel-as-read', 'LegacyInterOpController@userBatchMarkChannelAsRead'); 582 Route::post('user-batch-send-message', 'LegacyInterOpController@userBatchSendMessage'); 583 Route::delete('/user-sessions/{user}', 'LegacyInterOpController@userSessionsDestroy'); 584 Route::post('user-index/{user}', 'LegacyInterOpController@userIndex'); 585 Route::post('user-recalculate-ranked-scores/{user}', 'LegacyInterOpController@userRecalculateRankedScores'); 586 Route::get('/news', 'LegacyInterOpController@news'); 587 588 Route::group(['namespace' => 'InterOp'], function () { 589 Route::post('artist-tracks/reindex-all', 'ArtistTracksController@reindexAll'); 590 591 Route::group(['as' => 'beatmapsets.', 'prefix' => 'beatmapsets'], function () { 592 Route::group(['prefix' => '{beatmapset}'], function () { 593 Route::post('broadcast-new', 'BeatmapsetsController@broadcastNew')->name('broadcast-new'); 594 Route::post('broadcast-revive', 'BeatmapsetsController@broadcastRevive')->name('broadcast-revive'); 595 Route::post('broadcast-update', 'BeatmapsetsController@broadcastUpdate')->name('broadcast-update'); 596 Route::post('disqualify', 'BeatmapsetsController@disqualify')->name('disqualify'); 597 }); 598 }); 599 Route::resource('beatmapsets', 'BeatmapsetsController', ['only' => ['destroy']]); 600 601 Route::group(['as' => 'indexing.', 'prefix' => 'indexing'], function () { 602 Route::apiResource('bulk', 'Indexing\BulkController', ['only' => ['store']]); 603 }); 604 605 Route::group(['as' => 'multiplayer.', 'namespace' => 'Multiplayer', 'prefix' => 'multiplayer'], function () { 606 Route::put('rooms/{room}/users/{user}', 'RoomsController@join')->name('rooms.join'); 607 Route::delete('rooms/{room}/users/{user}', 'RoomsController@part')->name('rooms.part'); 608 Route::apiResource('rooms', 'RoomsController', ['only' => ['store']]); 609 }); 610 611 Route::post('user-achievement/{user}/{achievement}/{beatmap?}', 'UsersController@achievement')->name('users.achievement'); 612 613 Route::group(['as' => 'user-group.'], function () { 614 Route::put('users/{user}/groups/{group}', 'UserGroupsController@update')->name('update'); 615 Route::delete('users/{user}/groups/{group}', 'UserGroupsController@destroy')->name('destroy'); 616 Route::post('users/{user}/groups/{group}/default', 'UserGroupsController@setDefault')->name('set-default'); 617 }); 618 619 Route::apiResource('users', 'UsersController', ['only' => ['store']]); 620 }); 621}); 622 623Route::get('opensearch.xml', 'HomeController@opensearch'); 624Route::any('{catchall}', 'FallbackController@index')->where('catchall', '.*')->fallback();