From fb0bb9a34f63b58eb2729e3fc0ddbec8e26c2068 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 15 Feb 2024 20:58:43 -0700 Subject: [PATCH 01/14] Update Federation, use proper Content-Type headers for following/follower collections --- app/Http/Controllers/FederationController.php | 4 ++-- app/Util/ActivityPub/Helpers.php | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/FederationController.php b/app/Http/Controllers/FederationController.php index 6faea7050..55c7b4393 100644 --- a/app/Http/Controllers/FederationController.php +++ b/app/Http/Controllers/FederationController.php @@ -253,7 +253,7 @@ class FederationController extends Controller 'type' => 'OrderedCollection', 'totalItems' => $account['following_count'] ?? 0, ]; - return response()->json($obj); + return response()->json($obj)->header('Content-Type', 'application/activity+json'); } public function userFollowers(Request $request, $username) @@ -269,6 +269,6 @@ class FederationController extends Controller 'type' => 'OrderedCollection', 'totalItems' => $account['followers_count'] ?? 0, ]; - return response()->json($obj); + return response()->json($obj)->header('Content-Type', 'application/activity+json'); } } diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php index 5819dc0bc..6f5b8ae11 100644 --- a/app/Util/ActivityPub/Helpers.php +++ b/app/Util/ActivityPub/Helpers.php @@ -372,6 +372,10 @@ class Helpers { $idDomain = parse_url($id, PHP_URL_HOST); $urlDomain = parse_url($url, PHP_URL_HOST); + if($idDomain && $urlDomain && strtolower($idDomain) !== strtolower($urlDomain)) { + return; + } + if(!self::validateUrl($id)) { return; } @@ -455,14 +459,21 @@ class Helpers { public static function storeStatus($url, $profile, $activity) { + $originalUrl = $url; $id = isset($activity['id']) ? self::pluckval($activity['id']) : self::pluckval($activity['url']); $url = isset($activity['url']) && is_string($activity['url']) ? self::pluckval($activity['url']) : self::pluckval($id); $idDomain = parse_url($id, PHP_URL_HOST); $urlDomain = parse_url($url, PHP_URL_HOST); + $originalUrlDomain = parse_url($originalUrl, PHP_URL_HOST); if(!self::validateUrl($id) || !self::validateUrl($url)) { return; } + if( strtolower($originalUrlDomain) !== strtolower($idDomain) || + strtolower($originalUrlDomain) !== strtolower($urlDomain) ) { + return; + } + $reply_to = self::getReplyTo($activity); $ts = self::pluckval($activity['published']); @@ -763,7 +774,11 @@ class Helpers { if(!$res || isset($res['id']) == false) { return; } + $urlDomain = parse_url($url, PHP_URL_HOST); $domain = parse_url($res['id'], PHP_URL_HOST); + if(strtolower($urlDomain) !== strtolower($domain)) { + return; + } if(!isset($res['preferredUsername']) && !isset($res['nickname'])) { return; } @@ -831,6 +846,9 @@ class Helpers { public static function sendSignedObject($profile, $url, $body) { + if(app()->environment() !== 'production') { + return; + } ActivityPubDeliveryService::queue() ->from($profile) ->to($url) From 4c6ec20e36b24861b97e0c147ad6267e708b6394 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 15 Feb 2024 20:59:08 -0700 Subject: [PATCH 02/14] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3331d10d..d01a049fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Update ApiV1Controller, fix network timeline ([0faf59e3](https://github.com/pixelfed/pixelfed/commit/0faf59e3)) - Update public/network timelines, fix non-redis response and fix reblogs in home feed ([8b4ac5cc](https://github.com/pixelfed/pixelfed/commit/8b4ac5cc)) +- Update Federation, use proper Content-Type headers for following/follower collections ([fb0bb9a3](https://github.com/pixelfed/pixelfed/commit/fb0bb9a3)) - ([](https://github.com/pixelfed/pixelfed/commit/)) ## [v0.11.11 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.10...v0.11.11) From 1232cfc86a846807207fa26de81cb82bbc0d8e66 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 15 Feb 2024 21:22:41 -0700 Subject: [PATCH 03/14] Update ActivityPubFetchService, enforce stricter Content-Type validation --- app/Services/ActivityPubFetchService.php | 81 +++++++++++++++--------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/app/Services/ActivityPubFetchService.php b/app/Services/ActivityPubFetchService.php index cbf153ecb..4b515859c 100644 --- a/app/Services/ActivityPubFetchService.php +++ b/app/Services/ActivityPubFetchService.php @@ -11,38 +11,61 @@ use Illuminate\Http\Client\RequestException; class ActivityPubFetchService { - public static function get($url, $validateUrl = true) - { + public static function get($url, $validateUrl = true) + { if($validateUrl === true) { - if(!Helpers::validateUrl($url)) { - return 0; - } + if(!Helpers::validateUrl($url)) { + return 0; + } } - $baseHeaders = [ - 'Accept' => 'application/activity+json, application/ld+json', - ]; + $baseHeaders = [ + 'Accept' => 'application/activity+json, application/ld+json', + ]; - $headers = HttpSignature::instanceActorSign($url, false, $baseHeaders, 'get'); - $headers['Accept'] = 'application/activity+json, application/ld+json'; - $headers['User-Agent'] = 'PixelFedBot/1.0.0 (Pixelfed/'.config('pixelfed.version').'; +'.config('app.url').')'; + $headers = HttpSignature::instanceActorSign($url, false, $baseHeaders, 'get'); + $headers['Accept'] = 'application/activity+json, application/ld+json'; + $headers['User-Agent'] = 'PixelFedBot/1.0.0 (Pixelfed/'.config('pixelfed.version').'; +'.config('app.url').')'; - try { - $res = Http::withOptions(['allow_redirects' => false])->withHeaders($headers) - ->timeout(30) - ->connectTimeout(5) - ->retry(3, 500) - ->get($url); - } catch (RequestException $e) { - return; - } catch (ConnectionException $e) { - return; - } catch (Exception $e) { - return; - } - if(!$res->ok()) { - return; - } - return $res->body(); - } + try { + $res = Http::withOptions(['allow_redirects' => false]) + ->withHeaders($headers) + ->timeout(30) + ->connectTimeout(5) + ->retry(3, 500) + ->get($url); + } catch (RequestException $e) { + return; + } catch (ConnectionException $e) { + return; + } catch (Exception $e) { + return; + } + + if(!$res->ok()) { + return; + } + + if(!$res->hasHeader('Content-Type')) { + return; + } + + $acceptedTypes = [ + 'application/activity+json; charset=utf-8', + 'application/activity+json', + 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' + ]; + + $contentType = $res->getHeader('Content-Type')[0]; + + if(!$contentType) { + return; + } + + if(!in_array($contentType, $acceptedTypes)) { + return; + } + + return $res->body(); + } } From df5e61266c66b1b3537946723bf6d2feb0d4a551 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 15 Feb 2024 21:23:00 -0700 Subject: [PATCH 04/14] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d01a049fb..e3ddb7c15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Update ApiV1Controller, fix network timeline ([0faf59e3](https://github.com/pixelfed/pixelfed/commit/0faf59e3)) - Update public/network timelines, fix non-redis response and fix reblogs in home feed ([8b4ac5cc](https://github.com/pixelfed/pixelfed/commit/8b4ac5cc)) - Update Federation, use proper Content-Type headers for following/follower collections ([fb0bb9a3](https://github.com/pixelfed/pixelfed/commit/fb0bb9a3)) +- Update ActivityPubFetchService, enforce stricter Content-Type validation ([1232cfc8](https://github.com/pixelfed/pixelfed/commit/1232cfc8)) - ([](https://github.com/pixelfed/pixelfed/commit/)) ## [v0.11.11 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.10...v0.11.11) From 0f3ca194616d959807379a187d400643fabd5627 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 15 Feb 2024 21:41:18 -0700 Subject: [PATCH 05/14] Update status view, fix unlisted/private scope bug --- resources/views/status/show.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/status/show.blade.php b/resources/views/status/show.blade.php index b6927157a..d9743c92f 100644 --- a/resources/views/status/show.blade.php +++ b/resources/views/status/show.blade.php @@ -4,7 +4,7 @@ ]) @php -$s = \App\Services\StatusService::get($status->id); +$s = \App\Services\StatusService::get($status->id, false); $displayName = $s && $s['account'] ? $s['account']['display_name'] : false; $captionPreview = false; $domain = $displayName ? '@' . parse_url($s['account']['url'], PHP_URL_HOST) : ''; From 70fc44dfe53beedd92301be11381998bc8a678e7 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 15 Feb 2024 21:41:40 -0700 Subject: [PATCH 06/14] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3ddb7c15..322e5ff49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Update public/network timelines, fix non-redis response and fix reblogs in home feed ([8b4ac5cc](https://github.com/pixelfed/pixelfed/commit/8b4ac5cc)) - Update Federation, use proper Content-Type headers for following/follower collections ([fb0bb9a3](https://github.com/pixelfed/pixelfed/commit/fb0bb9a3)) - Update ActivityPubFetchService, enforce stricter Content-Type validation ([1232cfc8](https://github.com/pixelfed/pixelfed/commit/1232cfc8)) +- Update status view, fix unlisted/private scope bug ([0f3ca194](https://github.com/pixelfed/pixelfed/commit/0f3ca194)) - ([](https://github.com/pixelfed/pixelfed/commit/)) ## [v0.11.11 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.10...v0.11.11) From bc4d223714be1c6d58fb4af258c4368c96dbf8e8 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 15 Feb 2024 22:20:40 -0700 Subject: [PATCH 07/14] Update routes --- app/Providers/RouteServiceProvider.php | 17 +- routes/web-admin.php | 166 +++++ routes/web-api.php | 168 +++++ routes/web-portfolio.php | 23 + routes/web.php | 943 ++++++++----------------- 5 files changed, 677 insertions(+), 640 deletions(-) create mode 100644 routes/web-admin.php create mode 100644 routes/web-api.php create mode 100644 routes/web-portfolio.php diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 37aac4ac3..2452eb2a8 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -23,8 +23,6 @@ class RouteServiceProvider extends ServiceProvider */ public function boot() { - // - parent::boot(); } @@ -36,10 +34,7 @@ class RouteServiceProvider extends ServiceProvider public function map() { $this->mapApiRoutes(); - $this->mapWebRoutes(); - - // } /** @@ -51,6 +46,18 @@ class RouteServiceProvider extends ServiceProvider */ protected function mapWebRoutes() { + Route::middleware('web') + ->namespace($this->namespace) + ->group(base_path('routes/web-admin.php')); + + Route::middleware('web') + ->namespace($this->namespace) + ->group(base_path('routes/web-portfolio.php')); + + Route::middleware('web') + ->namespace($this->namespace) + ->group(base_path('routes/web-api.php')); + Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php')); diff --git a/routes/web-admin.php b/routes/web-admin.php new file mode 100644 index 000000000..72572b5c0 --- /dev/null +++ b/routes/web-admin.php @@ -0,0 +1,166 @@ +prefix('i/admin')->group(function () { + Route::redirect('/', '/dashboard'); + Route::redirect('timeline', config('app.url').'/timeline'); + Route::get('dashboard', 'AdminController@home')->name('admin.home'); + Route::get('stats', 'AdminController@stats')->name('admin.stats'); + Route::get('reports', 'AdminController@reports')->name('admin.reports'); + Route::get('reports/show/{id}', 'AdminController@showReport'); + Route::post('reports/show/{id}', 'AdminController@updateReport'); + Route::post('reports/bulk', 'AdminController@bulkUpdateReport'); + Route::get('reports/autospam/{id}', 'AdminController@showSpam'); + Route::post('reports/autospam/sync', 'AdminController@fixUncategorizedSpam'); + Route::post('reports/autospam/{id}', 'AdminController@updateSpam'); + Route::get('reports/autospam', 'AdminController@spam'); + Route::get('reports/appeals', 'AdminController@appeals'); + Route::get('reports/appeal/{id}', 'AdminController@showAppeal'); + Route::post('reports/appeal/{id}', 'AdminController@updateAppeal'); + Route::get('reports/email-verifications', 'AdminController@reportMailVerifications'); + Route::post('reports/email-verifications/ignore', 'AdminController@reportMailVerifyIgnore'); + Route::post('reports/email-verifications/approve', 'AdminController@reportMailVerifyApprove'); + Route::post('reports/email-verifications/clear-ignored', 'AdminController@reportMailVerifyClearIgnored'); + Route::redirect('stories', '/stories/list'); + Route::get('stories/list', 'AdminController@stories')->name('admin.stories'); + Route::redirect('statuses', '/statuses/list'); + Route::get('statuses/list', 'AdminController@statuses')->name('admin.statuses'); + Route::get('statuses/show/{id}', 'AdminController@showStatus'); + Route::redirect('profiles', '/i/admin/profiles/list'); + Route::get('profiles/list', 'AdminController@profiles')->name('admin.profiles'); + Route::get('profiles/edit/{id}', 'AdminController@profileShow'); + Route::redirect('users', '/users/list'); + Route::get('users/list', 'AdminController@users')->name('admin.users'); + Route::get('users/show/{id}', 'AdminController@userShow'); + Route::get('users/edit/{id}', 'AdminController@userEdit'); + Route::post('users/edit/{id}', 'AdminController@userEditSubmit'); + Route::get('users/activity/{id}', 'AdminController@userActivity'); + Route::get('users/message/{id}', 'AdminController@userMessage'); + Route::post('users/message/{id}', 'AdminController@userMessageSend'); + Route::get('users/modtools/{id}', 'AdminController@userModTools'); + Route::get('users/modlogs/{id}', 'AdminController@userModLogs'); + Route::post('users/modlogs/{id}', 'AdminController@userModLogsMessage'); + Route::post('users/modlogs/{id}/delete', 'AdminController@userModLogDelete'); + Route::get('users/delete/{id}', 'AdminController@userDelete'); + Route::post('users/delete/{id}', 'AdminController@userDeleteProcess'); + Route::post('users/moderation/update', 'AdminController@userModerate'); + Route::get('media', 'AdminController@media')->name('admin.media'); + Route::redirect('media/list', '/i/admin/media'); + Route::get('media/show/{id}', 'AdminController@mediaShow'); + Route::get('settings', 'AdminController@settings')->name('admin.settings'); + Route::post('settings', 'AdminController@settingsHomeStore'); + Route::get('settings/features', 'AdminController@settingsFeatures')->name('admin.settings.features'); + Route::get('settings/pages', 'AdminController@settingsPages')->name('admin.settings.pages'); + Route::get('settings/pages/edit', 'PageController@edit')->name('admin.settings.pages.edit'); + Route::post('settings/pages/edit', 'PageController@store'); + Route::post('settings/pages/delete', 'PageController@delete'); + Route::post('settings/pages/create', 'PageController@generatePage'); + Route::get('settings/maintenance', 'AdminController@settingsMaintenance')->name('admin.settings.maintenance'); + Route::get('settings/backups', 'AdminController@settingsBackups')->name('admin.settings.backups'); + Route::get('settings/storage', 'AdminController@settingsStorage')->name('admin.settings.storage'); + Route::get('settings/system', 'AdminController@settingsSystem')->name('admin.settings.system'); + + Route::get('instances', 'AdminController@instances')->name('admin.instances'); + Route::post('instances', 'AdminController@instanceScan'); + Route::get('instances/show/{id}', 'AdminController@instanceShow'); + Route::post('instances/edit/{id}', 'AdminController@instanceEdit'); + Route::get('apps/home', 'AdminController@appsHome')->name('admin.apps'); + Route::get('hashtags/home', 'AdminController@hashtagsHome')->name('admin.hashtags'); + Route::get('discover/home', 'AdminController@discoverHome')->name('admin.discover'); + Route::get('discover/category/create', 'AdminController@discoverCreateCategory')->name('admin.discover.create-category'); + Route::post('discover/category/create', 'AdminController@discoverCreateCategoryStore'); + Route::get('discover/category/edit/{id}', 'AdminController@discoverCategoryEdit'); + Route::post('discover/category/edit/{id}', 'AdminController@discoverCategoryUpdate'); + Route::post('discover/category/hashtag/create', 'AdminController@discoveryCategoryTagStore')->name('admin.discover.create-hashtag'); + + Route::get('messages/home', 'AdminController@messagesHome')->name('admin.messages'); + Route::get('messages/show/{id}', 'AdminController@messagesShow'); + Route::post('messages/mark-read', 'AdminController@messagesMarkRead'); + Route::redirect('site-news', '/i/admin/newsroom'); + Route::get('newsroom', 'AdminController@newsroomHome')->name('admin.newsroom.home'); + Route::get('newsroom/create', 'AdminController@newsroomCreate')->name('admin.newsroom.create'); + Route::get('newsroom/edit/{id}', 'AdminController@newsroomEdit'); + Route::post('newsroom/edit/{id}', 'AdminController@newsroomUpdate'); + Route::delete('newsroom/edit/{id}', 'AdminController@newsroomDelete'); + Route::post('newsroom/create', 'AdminController@newsroomStore'); + + Route::get('diagnostics/home', 'AdminController@diagnosticsHome')->name('admin.diagnostics'); + Route::post('diagnostics/decrypt', 'AdminController@diagnosticsDecrypt')->name('admin.diagnostics.decrypt'); + Route::get('custom-emoji/home', 'AdminController@customEmojiHome')->name('admin.custom-emoji'); + Route::post('custom-emoji/toggle-active/{id}', 'AdminController@customEmojiToggleActive'); + Route::get('custom-emoji/new', 'AdminController@customEmojiAdd'); + Route::post('custom-emoji/new', 'AdminController@customEmojiStore'); + Route::post('custom-emoji/delete/{id}', 'AdminController@customEmojiDelete'); + Route::get('custom-emoji/duplicates/{id}', 'AdminController@customEmojiShowDuplicates'); + + Route::get('directory/home', 'AdminController@directoryHome')->name('admin.directory'); + + Route::get('autospam/home', 'AdminController@autospamHome')->name('admin.autospam'); + + Route::redirect('asf/', 'asf/home'); + Route::get('asf/home', 'AdminShadowFilterController@home'); + Route::get('asf/create', 'AdminShadowFilterController@create'); + Route::get('asf/edit/{id}', 'AdminShadowFilterController@edit'); + Route::post('asf/edit/{id}', 'AdminShadowFilterController@storeEdit'); + Route::post('asf/create', 'AdminShadowFilterController@store'); + + Route::get('asf/home', 'AdminShadowFilterController@home'); + // Route::redirect('curated-onboarding/', 'curated-onboarding/home'); + // Route::get('curated-onboarding/home', 'AdminCuratedRegisterController@index')->name('admin.curated-onboarding'); + // Route::get('curated-onboarding/show/{id}/preview-details-message', 'AdminCuratedRegisterController@previewDetailsMessageShow'); + // Route::get('curated-onboarding/show/{id}/preview-message', 'AdminCuratedRegisterController@previewMessageShow'); + // Route::get('curated-onboarding/show/{id}', 'AdminCuratedRegisterController@show'); + + Route::prefix('api')->group(function() { + Route::get('stats', 'AdminController@getStats'); + Route::get('accounts', 'AdminController@getAccounts'); + Route::get('posts', 'AdminController@getPosts'); + Route::get('instances', 'AdminController@getInstances'); + Route::post('directory/save', 'AdminController@directoryStore'); + Route::get('directory/initial-data', 'AdminController@directoryInitialData'); + Route::get('directory/popular-posts', 'AdminController@directoryGetPopularPosts'); + Route::post('directory/add-by-id', 'AdminController@directoryGetAddPostByIdSearch'); + Route::delete('directory/banner-image', 'AdminController@directoryDeleteBannerImage'); + Route::post('directory/submit', 'AdminController@directoryHandleServerSubmission'); + Route::post('directory/testimonial/save', 'AdminController@directorySaveTestimonial'); + Route::post('directory/testimonial/delete', 'AdminController@directoryDeleteTestimonial'); + Route::post('directory/testimonial/update', 'AdminController@directoryUpdateTestimonial'); + Route::get('hashtags/stats', 'AdminController@hashtagsStats'); + Route::get('hashtags/query', 'AdminController@hashtagsApi'); + Route::get('hashtags/get', 'AdminController@hashtagsGet'); + Route::post('hashtags/update', 'AdminController@hashtagsUpdate'); + Route::post('hashtags/clear-trending-cache', 'AdminController@hashtagsClearTrendingCache'); + Route::get('instances/get', 'AdminController@getInstancesApi'); + Route::get('instances/stats', 'AdminController@getInstancesStatsApi'); + Route::get('instances/query', 'AdminController@getInstancesQueryApi'); + Route::post('instances/update', 'AdminController@postInstanceUpdateApi'); + Route::post('instances/create', 'AdminController@postInstanceCreateNewApi'); + Route::post('instances/delete', 'AdminController@postInstanceDeleteApi'); + Route::post('instances/refresh-stats', 'AdminController@postInstanceRefreshStatsApi'); + Route::get('instances/download-backup', 'AdminController@downloadBackup'); + Route::post('instances/import-data', 'AdminController@importBackup'); + Route::get('reports/stats', 'AdminController@reportsStats'); + Route::get('reports/all', 'AdminController@reportsApiAll'); + Route::get('reports/get/{id}', 'AdminController@reportsApiGet'); + Route::post('reports/handle', 'AdminController@reportsApiHandle'); + Route::get('reports/spam/all', 'AdminController@reportsApiSpamAll'); + Route::get('reports/spam/get/{id}', 'AdminController@reportsApiSpamGet'); + Route::post('reports/spam/handle', 'AdminController@reportsApiSpamHandle'); + Route::post('autospam/config', 'AdminController@getAutospamConfigApi'); + Route::post('autospam/reports/closed', 'AdminController@getAutospamReportsClosedApi'); + Route::post('autospam/train', 'AdminController@postAutospamTrainSpamApi'); + Route::post('autospam/search/non-spam', 'AdminController@postAutospamTrainNonSpamSearchApi'); + Route::post('autospam/train/non-spam', 'AdminController@postAutospamTrainNonSpamSubmitApi'); + Route::post('autospam/tokens/custom', 'AdminController@getAutospamCustomTokensApi'); + Route::post('autospam/tokens/store', 'AdminController@saveNewAutospamCustomTokensApi'); + Route::post('autospam/tokens/update', 'AdminController@updateAutospamCustomTokensApi'); + Route::post('autospam/tokens/export', 'AdminController@exportAutospamCustomTokensApi'); + Route::post('autospam/config/enable', 'AdminController@enableAutospamApi'); + Route::post('autospam/config/disable', 'AdminController@disableAutospamApi'); + // Route::get('instances/{id}/accounts', 'AdminController@getInstanceAccounts'); + // Route::get('curated-onboarding/show/{id}/activity-log', 'AdminCuratedRegisterController@apiActivityLog'); + // Route::post('curated-onboarding/show/{id}/message/preview', 'AdminCuratedRegisterController@apiMessagePreviewStore'); + // Route::post('curated-onboarding/show/{id}/message/send', 'AdminCuratedRegisterController@apiMessageSendStore'); + // Route::post('curated-onboarding/show/{id}/reject', 'AdminCuratedRegisterController@apiHandleReject'); + // Route::post('curated-onboarding/show/{id}/approve', 'AdminCuratedRegisterController@apiHandleApprove'); + }); +}); diff --git a/routes/web-api.php b/routes/web-api.php new file mode 100644 index 000000000..f51762439 --- /dev/null +++ b/routes/web-api.php @@ -0,0 +1,168 @@ +middleware(['validemail', 'twofactor', 'localization'])->group(function () { + Route::group(['prefix' => 'api'], function () { + Route::get('search', 'SearchController@searchAPI'); + Route::post('status/view', 'StatusController@storeView'); + Route::get('v1/polls/{id}', 'PollController@getPoll'); + Route::post('v1/polls/{id}/votes', 'PollController@vote'); + + Route::group(['prefix' => 'compose'], function() { + Route::group(['prefix' => 'v0'], function() { + Route::post('/media/upload', 'ComposeController@mediaUpload'); + Route::post('/media/update', 'ComposeController@mediaUpdate'); + Route::delete('/media/delete', 'ComposeController@mediaDelete'); + Route::get('/search/tag', 'ComposeController@searchTag'); + Route::get('/search/location', 'ComposeController@searchLocation'); + Route::get('/search/mention', 'ComposeController@searchMentionAutocomplete'); + Route::get('/search/hashtag', 'ComposeController@searchHashtagAutocomplete'); + + Route::post('/publish', 'ComposeController@store'); + Route::post('/publish/text', 'ComposeController@storeText'); + Route::get('/media/processing', 'ComposeController@mediaProcessingCheck'); + Route::get('/settings', 'ComposeController@composeSettings'); + Route::post('/poll', 'ComposeController@createPoll'); + }); + }); + + Route::group(['prefix' => 'direct'], function () { + Route::get('browse', 'DirectMessageController@browse'); + Route::post('create', 'DirectMessageController@create'); + Route::get('thread', 'DirectMessageController@thread'); + Route::post('mute', 'DirectMessageController@mute'); + Route::post('unmute', 'DirectMessageController@unmute'); + Route::delete('message', 'DirectMessageController@delete'); + Route::post('media', 'DirectMessageController@mediaUpload'); + Route::post('lookup', 'DirectMessageController@composeLookup'); + Route::post('read', 'DirectMessageController@read'); + }); + + Route::group(['prefix' => 'v2'], function() { + Route::get('config', 'ApiController@siteConfiguration'); + Route::get('discover', 'InternalApiController@discover'); + Route::get('discover/posts', 'InternalApiController@discoverPosts')->middleware('auth:api'); + Route::get('profile/{username}/status/{postid}', 'PublicApiController@status'); + Route::get('profile/{username}/status/{postid}/state', 'PublicApiController@statusState'); + Route::get('comments/{username}/status/{postId}', 'PublicApiController@statusComments'); + Route::get('status/{id}/replies', 'InternalApiController@statusReplies'); + Route::post('moderator/action', 'InternalApiController@modAction'); + Route::get('discover/categories', 'InternalApiController@discoverCategories'); + Route::get('loops', 'DiscoverController@loopsApi'); + Route::post('loops/watch', 'DiscoverController@loopWatch'); + Route::get('discover/tag', 'DiscoverController@getHashtags'); + Route::get('statuses/{id}/replies', 'Api\ApiV1Controller@statusReplies'); + Route::get('statuses/{id}/state', 'Api\ApiV1Controller@statusState'); + }); + + Route::group(['prefix' => 'pixelfed'], function() { + Route::group(['prefix' => 'v1'], function() { + Route::get('accounts/verify_credentials', 'ApiController@verifyCredentials'); + Route::get('accounts/relationships', 'Api\ApiV1Controller@accountRelationshipsById'); + Route::get('accounts/search', 'Api\ApiV1Controller@accountSearch'); + Route::get('accounts/{id}/statuses', 'PublicApiController@accountStatuses'); + Route::post('accounts/{id}/block', 'Api\ApiV1Controller@accountBlockById'); + Route::post('accounts/{id}/unblock', 'Api\ApiV1Controller@accountUnblockById'); + Route::get('statuses/{id}', 'PublicApiController@getStatus'); + Route::get('accounts/{id}', 'PublicApiController@account'); + Route::post('avatar/update', 'ApiController@avatarUpdate'); + Route::get('custom_emojis', 'Api\ApiV1Controller@customEmojis'); + Route::get('notifications', 'ApiController@notifications'); + Route::get('timelines/public', 'PublicApiController@publicTimelineApi'); + Route::get('timelines/home', 'PublicApiController@homeTimelineApi'); + Route::get('timelines/network', 'PublicApiController@networkTimelineApi'); + Route::get('newsroom/timeline', 'NewsroomController@timelineApi'); + Route::post('newsroom/markasread', 'NewsroomController@markAsRead'); + Route::get('favourites', 'Api\BaseApiController@accountLikes'); + Route::get('mutes', 'AccountController@accountMutes'); + Route::get('blocks', 'AccountController@accountBlocks'); + }); + + Route::group(['prefix' => 'v2'], function() { + Route::get('config', 'ApiController@siteConfiguration'); + Route::get('discover', 'InternalApiController@discover'); + Route::get('discover/posts', 'InternalApiController@discoverPosts'); + Route::get('discover/profiles', 'DiscoverController@profilesDirectoryApi'); + Route::get('profile/{username}/status/{postid}', 'PublicApiController@status'); + Route::get('comments/{username}/status/{postId}', 'PublicApiController@statusComments'); + Route::post('moderator/action', 'InternalApiController@modAction'); + Route::get('discover/categories', 'InternalApiController@discoverCategories'); + Route::get('loops', 'DiscoverController@loopsApi'); + Route::post('loops/watch', 'DiscoverController@loopWatch'); + Route::get('discover/tag', 'DiscoverController@getHashtags'); + Route::get('discover/posts/trending', 'DiscoverController@trendingApi'); + Route::get('discover/posts/hashtags', 'DiscoverController@trendingHashtags'); + Route::get('discover/posts/places', 'DiscoverController@trendingPlaces'); + Route::get('seasonal/yir', 'SeasonalController@getData'); + Route::post('seasonal/yir', 'SeasonalController@store'); + Route::get('mutes', 'AccountController@accountMutesV2'); + Route::get('blocks', 'AccountController@accountBlocksV2'); + Route::get('filters', 'AccountController@accountFiltersV2'); + Route::post('status/compose', 'InternalApiController@composePost'); + Route::get('status/{id}/replies', 'InternalApiController@statusReplies'); + Route::post('status/{id}/archive', 'ApiController@archive'); + Route::post('status/{id}/unarchive', 'ApiController@unarchive'); + Route::get('statuses/archives', 'ApiController@archivedPosts'); + Route::get('discover/memories', 'DiscoverController@myMemories'); + Route::get('discover/account-insights', 'DiscoverController@accountInsightsPopularPosts'); + Route::get('discover/server-timeline', 'DiscoverController@serverTimeline'); + Route::get('discover/meta', 'DiscoverController@enabledFeatures'); + Route::post('discover/admin/features', 'DiscoverController@updateFeatures'); + }); + + Route::get('discover/accounts/popular', 'Api\ApiV1Controller@discoverAccountsPopular'); + Route::post('web/change-language.json', 'SpaController@updateLanguage'); + }); + + Route::group(['prefix' => 'local'], function () { + // Route::post('status/compose', 'InternalApiController@composePost')->middleware('throttle:maxPostsPerHour,60')->middleware('throttle:maxPostsPerDay,1440'); + Route::get('exp/rec', 'ApiController@userRecommendations'); + Route::post('discover/tag/subscribe', 'HashtagFollowController@store'); + Route::get('discover/tag/list', 'HashtagFollowController@getTags'); + // Route::get('profile/sponsor/{id}', 'ProfileSponsorController@get'); + Route::get('bookmarks', 'InternalApiController@bookmarks'); + Route::get('collection/items/{id}', 'CollectionController@getItems'); + Route::post('collection/item', 'CollectionController@storeId'); + Route::delete('collection/item', 'CollectionController@deleteId'); + Route::get('collection/{id}', 'CollectionController@getCollection'); + Route::post('collection/{id}', 'CollectionController@store'); + Route::delete('collection/{id}', 'CollectionController@delete'); + Route::post('collection/{id}/publish', 'CollectionController@publish'); + Route::get('profile/collections/{id}', 'CollectionController@getUserCollections'); + + Route::post('compose/tag/untagme', 'MediaTagController@untagProfile'); + + Route::post('import/ig', 'ImportPostController@store'); + Route::get('import/ig/config', 'ImportPostController@getConfig'); + Route::post('import/ig/media', 'ImportPostController@storeMedia'); + Route::post('import/ig/existing', 'ImportPostController@getImportedFiles'); + Route::post('import/ig/posts', 'ImportPostController@getImportedPosts'); + Route::post('import/ig/processing', 'ImportPostController@getProcessingCount'); + }); + + Route::group(['prefix' => 'web/stories'], function () { + Route::get('v1/recent', 'StoryController@recent'); + Route::get('v1/viewers', 'StoryController@viewers'); + Route::get('v1/profile/{id}', 'StoryController@profile'); + Route::get('v1/exists/{id}', 'StoryController@exists'); + Route::get('v1/poll/results', 'StoryController@pollResults'); + Route::post('v1/viewed', 'StoryController@viewed'); + Route::post('v1/react', 'StoryController@react'); + Route::post('v1/comment', 'StoryController@comment'); + Route::post('v1/publish/poll', 'StoryController@publishStoryPoll'); + Route::post('v1/poll/vote', 'StoryController@storyPollVote'); + Route::post('v1/report', 'StoryController@storeReport'); + Route::post('v1/add', 'StoryController@apiV1Add'); + Route::post('v1/crop', 'StoryController@cropPhoto'); + Route::post('v1/publish', 'StoryController@publishStory'); + Route::delete('v1/delete/{id}', 'StoryController@apiV1Delete'); + }); + + Route::group(['prefix' => 'portfolio'], function () { + Route::post('self/curated.json', 'PortfolioController@storeCurated'); + Route::post('self/settings.json', 'PortfolioController@getSettings'); + Route::get('account/settings.json', 'PortfolioController@getAccountSettings'); + Route::post('self/update-settings.json', 'PortfolioController@storeSettings'); + Route::get('{username}/feed', 'PortfolioController@getFeed'); + }); + }); +}); diff --git a/routes/web-portfolio.php b/routes/web-portfolio.php new file mode 100644 index 000000000..3785aaac1 --- /dev/null +++ b/routes/web-portfolio.php @@ -0,0 +1,23 @@ +group(function () { + Route::redirect('redirect/home', config('app.url')); + Route::get('/', 'PortfolioController@index'); + Route::post('api/portfolio/self/curated.json', 'PortfolioController@storeCurated'); + Route::post('api/portfolio/self/settings.json', 'PortfolioController@getSettings'); + Route::get('api/portfolio/account/settings.json', 'PortfolioController@getAccountSettings'); + Route::post('api/portfolio/self/update-settings.json', 'PortfolioController@storeSettings'); + Route::get('api/portfolio/{username}/feed', 'PortfolioController@getFeed'); + + Route::prefix(config('portfolio.path'))->group(function() { + Route::get('/', 'PortfolioController@index'); + Route::get('settings', 'PortfolioController@settings')->name('portfolio.settings'); + Route::post('settings', 'PortfolioController@store'); + Route::get('{username}/{id}', 'PortfolioController@showPost'); + Route::get('{username}', 'PortfolioController@show'); + + Route::fallback(function () { + return view('errors.404'); + }); + }); +}); diff --git a/routes/web.php b/routes/web.php index 6c765ba56..3947bf5da 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,186 +1,12 @@ prefix('i/admin')->group(function () { - Route::redirect('/', '/dashboard'); - Route::redirect('timeline', config('app.url').'/timeline'); - Route::get('dashboard', 'AdminController@home')->name('admin.home'); - Route::get('stats', 'AdminController@stats')->name('admin.stats'); - Route::get('reports', 'AdminController@reports')->name('admin.reports'); - Route::get('reports/show/{id}', 'AdminController@showReport'); - Route::post('reports/show/{id}', 'AdminController@updateReport'); - Route::post('reports/bulk', 'AdminController@bulkUpdateReport'); - Route::get('reports/autospam/{id}', 'AdminController@showSpam'); - Route::post('reports/autospam/sync', 'AdminController@fixUncategorizedSpam'); - Route::post('reports/autospam/{id}', 'AdminController@updateSpam'); - Route::get('reports/autospam', 'AdminController@spam'); - Route::get('reports/appeals', 'AdminController@appeals'); - Route::get('reports/appeal/{id}', 'AdminController@showAppeal'); - Route::post('reports/appeal/{id}', 'AdminController@updateAppeal'); - Route::get('reports/email-verifications', 'AdminController@reportMailVerifications'); - Route::post('reports/email-verifications/ignore', 'AdminController@reportMailVerifyIgnore'); - Route::post('reports/email-verifications/approve', 'AdminController@reportMailVerifyApprove'); - Route::post('reports/email-verifications/clear-ignored', 'AdminController@reportMailVerifyClearIgnored'); - Route::redirect('stories', '/stories/list'); - Route::get('stories/list', 'AdminController@stories')->name('admin.stories'); - Route::redirect('statuses', '/statuses/list'); - Route::get('statuses/list', 'AdminController@statuses')->name('admin.statuses'); - Route::get('statuses/show/{id}', 'AdminController@showStatus'); - Route::redirect('profiles', '/i/admin/profiles/list'); - Route::get('profiles/list', 'AdminController@profiles')->name('admin.profiles'); - Route::get('profiles/edit/{id}', 'AdminController@profileShow'); - Route::redirect('users', '/users/list'); - Route::get('users/list', 'AdminController@users')->name('admin.users'); - Route::get('users/show/{id}', 'AdminController@userShow'); - Route::get('users/edit/{id}', 'AdminController@userEdit'); - Route::post('users/edit/{id}', 'AdminController@userEditSubmit'); - Route::get('users/activity/{id}', 'AdminController@userActivity'); - Route::get('users/message/{id}', 'AdminController@userMessage'); - Route::post('users/message/{id}', 'AdminController@userMessageSend'); - Route::get('users/modtools/{id}', 'AdminController@userModTools'); - Route::get('users/modlogs/{id}', 'AdminController@userModLogs'); - Route::post('users/modlogs/{id}', 'AdminController@userModLogsMessage'); - Route::post('users/modlogs/{id}/delete', 'AdminController@userModLogDelete'); - Route::get('users/delete/{id}', 'AdminController@userDelete'); - Route::post('users/delete/{id}', 'AdminController@userDeleteProcess'); - Route::post('users/moderation/update', 'AdminController@userModerate'); - Route::get('media', 'AdminController@media')->name('admin.media'); - Route::redirect('media/list', '/i/admin/media'); - Route::get('media/show/{id}', 'AdminController@mediaShow'); - Route::get('settings', 'AdminController@settings')->name('admin.settings'); - Route::post('settings', 'AdminController@settingsHomeStore'); - Route::get('settings/features', 'AdminController@settingsFeatures')->name('admin.settings.features'); - Route::get('settings/pages', 'AdminController@settingsPages')->name('admin.settings.pages'); - Route::get('settings/pages/edit', 'PageController@edit')->name('admin.settings.pages.edit'); - Route::post('settings/pages/edit', 'PageController@store'); - Route::post('settings/pages/delete', 'PageController@delete'); - Route::post('settings/pages/create', 'PageController@generatePage'); - Route::get('settings/maintenance', 'AdminController@settingsMaintenance')->name('admin.settings.maintenance'); - Route::get('settings/backups', 'AdminController@settingsBackups')->name('admin.settings.backups'); - Route::get('settings/storage', 'AdminController@settingsStorage')->name('admin.settings.storage'); - Route::get('settings/system', 'AdminController@settingsSystem')->name('admin.settings.system'); - - Route::get('instances', 'AdminController@instances')->name('admin.instances'); - Route::post('instances', 'AdminController@instanceScan'); - Route::get('instances/show/{id}', 'AdminController@instanceShow'); - Route::post('instances/edit/{id}', 'AdminController@instanceEdit'); - Route::get('apps/home', 'AdminController@appsHome')->name('admin.apps'); - Route::get('hashtags/home', 'AdminController@hashtagsHome')->name('admin.hashtags'); - Route::get('discover/home', 'AdminController@discoverHome')->name('admin.discover'); - Route::get('discover/category/create', 'AdminController@discoverCreateCategory')->name('admin.discover.create-category'); - Route::post('discover/category/create', 'AdminController@discoverCreateCategoryStore'); - Route::get('discover/category/edit/{id}', 'AdminController@discoverCategoryEdit'); - Route::post('discover/category/edit/{id}', 'AdminController@discoverCategoryUpdate'); - Route::post('discover/category/hashtag/create', 'AdminController@discoveryCategoryTagStore')->name('admin.discover.create-hashtag'); - - Route::get('messages/home', 'AdminController@messagesHome')->name('admin.messages'); - Route::get('messages/show/{id}', 'AdminController@messagesShow'); - Route::post('messages/mark-read', 'AdminController@messagesMarkRead'); - Route::redirect('site-news', '/i/admin/newsroom'); - Route::get('newsroom', 'AdminController@newsroomHome')->name('admin.newsroom.home'); - Route::get('newsroom/create', 'AdminController@newsroomCreate')->name('admin.newsroom.create'); - Route::get('newsroom/edit/{id}', 'AdminController@newsroomEdit'); - Route::post('newsroom/edit/{id}', 'AdminController@newsroomUpdate'); - Route::delete('newsroom/edit/{id}', 'AdminController@newsroomDelete'); - Route::post('newsroom/create', 'AdminController@newsroomStore'); - - Route::get('diagnostics/home', 'AdminController@diagnosticsHome')->name('admin.diagnostics'); - Route::post('diagnostics/decrypt', 'AdminController@diagnosticsDecrypt')->name('admin.diagnostics.decrypt'); - Route::get('custom-emoji/home', 'AdminController@customEmojiHome')->name('admin.custom-emoji'); - Route::post('custom-emoji/toggle-active/{id}', 'AdminController@customEmojiToggleActive'); - Route::get('custom-emoji/new', 'AdminController@customEmojiAdd'); - Route::post('custom-emoji/new', 'AdminController@customEmojiStore'); - Route::post('custom-emoji/delete/{id}', 'AdminController@customEmojiDelete'); - Route::get('custom-emoji/duplicates/{id}', 'AdminController@customEmojiShowDuplicates'); - - Route::get('directory/home', 'AdminController@directoryHome')->name('admin.directory'); - - Route::get('autospam/home', 'AdminController@autospamHome')->name('admin.autospam'); - - Route::redirect('asf/', 'asf/home'); - Route::get('asf/home', 'AdminShadowFilterController@home'); - Route::get('asf/create', 'AdminShadowFilterController@create'); - Route::get('asf/edit/{id}', 'AdminShadowFilterController@edit'); - Route::post('asf/edit/{id}', 'AdminShadowFilterController@storeEdit'); - Route::post('asf/create', 'AdminShadowFilterController@store'); - - Route::prefix('api')->group(function() { - Route::get('stats', 'AdminController@getStats'); - Route::get('accounts', 'AdminController@getAccounts'); - Route::get('posts', 'AdminController@getPosts'); - Route::get('instances', 'AdminController@getInstances'); - Route::post('directory/save', 'AdminController@directoryStore'); - Route::get('directory/initial-data', 'AdminController@directoryInitialData'); - Route::get('directory/popular-posts', 'AdminController@directoryGetPopularPosts'); - Route::post('directory/add-by-id', 'AdminController@directoryGetAddPostByIdSearch'); - Route::delete('directory/banner-image', 'AdminController@directoryDeleteBannerImage'); - Route::post('directory/submit', 'AdminController@directoryHandleServerSubmission'); - Route::post('directory/testimonial/save', 'AdminController@directorySaveTestimonial'); - Route::post('directory/testimonial/delete', 'AdminController@directoryDeleteTestimonial'); - Route::post('directory/testimonial/update', 'AdminController@directoryUpdateTestimonial'); - Route::get('hashtags/stats', 'AdminController@hashtagsStats'); - Route::get('hashtags/query', 'AdminController@hashtagsApi'); - Route::get('hashtags/get', 'AdminController@hashtagsGet'); - Route::post('hashtags/update', 'AdminController@hashtagsUpdate'); - Route::post('hashtags/clear-trending-cache', 'AdminController@hashtagsClearTrendingCache'); - Route::get('instances/get', 'AdminController@getInstancesApi'); - Route::get('instances/stats', 'AdminController@getInstancesStatsApi'); - Route::get('instances/query', 'AdminController@getInstancesQueryApi'); - Route::post('instances/update', 'AdminController@postInstanceUpdateApi'); - Route::post('instances/create', 'AdminController@postInstanceCreateNewApi'); - Route::post('instances/delete', 'AdminController@postInstanceDeleteApi'); - Route::post('instances/refresh-stats', 'AdminController@postInstanceRefreshStatsApi'); - Route::get('instances/download-backup', 'AdminController@downloadBackup'); - Route::post('instances/import-data', 'AdminController@importBackup'); - Route::get('reports/stats', 'AdminController@reportsStats'); - Route::get('reports/all', 'AdminController@reportsApiAll'); - Route::get('reports/get/{id}', 'AdminController@reportsApiGet'); - Route::post('reports/handle', 'AdminController@reportsApiHandle'); - Route::get('reports/spam/all', 'AdminController@reportsApiSpamAll'); - Route::get('reports/spam/get/{id}', 'AdminController@reportsApiSpamGet'); - Route::post('reports/spam/handle', 'AdminController@reportsApiSpamHandle'); - Route::post('autospam/config', 'AdminController@getAutospamConfigApi'); - Route::post('autospam/reports/closed', 'AdminController@getAutospamReportsClosedApi'); - Route::post('autospam/train', 'AdminController@postAutospamTrainSpamApi'); - Route::post('autospam/search/non-spam', 'AdminController@postAutospamTrainNonSpamSearchApi'); - Route::post('autospam/train/non-spam', 'AdminController@postAutospamTrainNonSpamSubmitApi'); - Route::post('autospam/tokens/custom', 'AdminController@getAutospamCustomTokensApi'); - Route::post('autospam/tokens/store', 'AdminController@saveNewAutospamCustomTokensApi'); - Route::post('autospam/tokens/update', 'AdminController@updateAutospamCustomTokensApi'); - Route::post('autospam/tokens/export', 'AdminController@exportAutospamCustomTokensApi'); - Route::post('autospam/config/enable', 'AdminController@enableAutospamApi'); - Route::post('autospam/config/disable', 'AdminController@disableAutospamApi'); - }); -}); - -Route::domain(config('portfolio.domain'))->group(function () { - Route::redirect('redirect/home', config('app.url')); - Route::get('/', 'PortfolioController@index'); - Route::post('api/portfolio/self/curated.json', 'PortfolioController@storeCurated'); - Route::post('api/portfolio/self/settings.json', 'PortfolioController@getSettings'); - Route::get('api/portfolio/account/settings.json', 'PortfolioController@getAccountSettings'); - Route::post('api/portfolio/self/update-settings.json', 'PortfolioController@storeSettings'); - Route::get('api/portfolio/{username}/feed', 'PortfolioController@getFeed'); - - Route::prefix(config('portfolio.path'))->group(function() { - Route::get('/', 'PortfolioController@index'); - Route::get('settings', 'PortfolioController@settings')->name('portfolio.settings'); - Route::post('settings', 'PortfolioController@store'); - Route::get('{username}/{id}', 'PortfolioController@showPost'); - Route::get('{username}', 'PortfolioController@show'); - - Route::fallback(function () { - return view('errors.404'); - }); - }); -}); - Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofactor', 'localization'])->group(function () { - Route::get('/', 'SiteController@home')->name('timeline.personal'); - Route::redirect('/home', '/')->name('home'); - Route::get('web/directory', 'LandingController@directoryRedirect'); - Route::get('web/explore', 'LandingController@exploreRedirect'); + Route::get('/', 'SiteController@home')->name('timeline.personal'); + Route::redirect('/home', '/')->name('home'); + Route::get('web/directory', 'LandingController@directoryRedirect'); + Route::get('web/explore', 'LandingController@exploreRedirect'); - Auth::routes(); + Auth::routes(); Route::get('auth/raw/mastodon/start', 'RemoteAuthController@startRedirect'); Route::post('auth/raw/mastodon/config', 'RemoteAuthController@getConfig'); Route::post('auth/raw/mastodon/domains', 'RemoteAuthController@getAuthDomains'); @@ -203,489 +29,336 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact Route::get('auth/pci/{id}/{code}', 'ParentalControlsController@inviteRegister'); Route::post('auth/pci/{id}/{code}', 'ParentalControlsController@inviteRegisterStore'); + // Route::get('auth/sign_up', 'CuratedRegisterController@index'); + // Route::post('auth/sign_up', 'CuratedRegisterController@proceed'); + // Route::get('auth/sign_up/concierge/response-sent', 'CuratedRegisterController@conciergeResponseSent'); + // Route::get('auth/sign_up/concierge', 'CuratedRegisterController@concierge'); + // Route::post('auth/sign_up/concierge', 'CuratedRegisterController@conciergeStore'); + // Route::get('auth/sign_up/concierge/form', 'CuratedRegisterController@conciergeFormShow'); + // Route::post('auth/sign_up/concierge/form', 'CuratedRegisterController@conciergeFormStore'); + // Route::get('auth/sign_up/confirm', 'CuratedRegisterController@confirmEmail'); + // Route::post('auth/sign_up/confirm', 'CuratedRegisterController@confirmEmailHandle'); + // Route::get('auth/sign_up/confirmed', 'CuratedRegisterController@emailConfirmed'); Route::get('auth/forgot/email', 'UserEmailForgotController@index')->name('email.forgot'); Route::post('auth/forgot/email', 'UserEmailForgotController@store')->middleware('throttle:10,900,forgotEmail'); - Route::get('discover', 'DiscoverController@home')->name('discover'); + Route::get('discover', 'DiscoverController@home')->name('discover'); - Route::group(['prefix' => 'api'], function () { - Route::get('search', 'SearchController@searchAPI'); - Route::post('status/view', 'StatusController@storeView'); - Route::get('v1/polls/{id}', 'PollController@getPoll'); - Route::post('v1/polls/{id}/votes', 'PollController@vote'); + Route::get('discover/tags/{hashtag}', 'DiscoverController@showTags'); + Route::get('discover/places', 'PlaceController@directoryHome')->name('discover.places'); + Route::get('discover/places/{id}/{slug}', 'PlaceController@show'); + Route::get('discover/location/country/{country}', 'PlaceController@directoryCities'); - Route::group(['prefix' => 'compose'], function() { - Route::group(['prefix' => 'v0'], function() { - Route::post('/media/upload', 'ComposeController@mediaUpload'); - Route::post('/media/update', 'ComposeController@mediaUpdate'); - Route::delete('/media/delete', 'ComposeController@mediaDelete'); - Route::get('/search/tag', 'ComposeController@searchTag'); - Route::get('/search/location', 'ComposeController@searchLocation'); - Route::get('/search/mention', 'ComposeController@searchMentionAutocomplete'); - Route::get('/search/hashtag', 'ComposeController@searchHashtagAutocomplete'); + Route::group(['prefix' => 'i'], function () { + Route::redirect('/', '/'); + Route::get('compose', 'StatusController@compose')->name('compose'); + Route::post('comment', 'CommentController@store'); + Route::post('delete', 'StatusController@delete'); + Route::post('mute', 'AccountController@mute'); + Route::post('unmute', 'AccountController@unmute'); + Route::post('block', 'AccountController@block'); + Route::post('unblock', 'AccountController@unblock'); + Route::post('like', 'LikeController@store'); + Route::post('share', 'StatusController@storeShare'); + Route::post('follow', 'FollowerController@store'); + Route::post('bookmark', 'BookmarkController@store'); + Route::get('lang/{locale}', 'SiteController@changeLocale'); + Route::get('restored', 'AccountController@accountRestored'); - Route::post('/publish', 'ComposeController@store'); - Route::post('/publish/text', 'ComposeController@storeText'); - Route::get('/media/processing', 'ComposeController@mediaProcessingCheck'); - Route::get('/settings', 'ComposeController@composeSettings'); - Route::post('/poll', 'ComposeController@createPoll'); - }); - }); + Route::get('verify-email', 'AccountController@verifyEmail'); + Route::post('verify-email', 'AccountController@sendVerifyEmail'); + Route::get('verify-email/request', 'InternalApiController@requestEmailVerification'); + Route::post('verify-email/request', 'InternalApiController@requestEmailVerificationStore'); + Route::get('confirm-email/{userToken}/{randomToken}', 'AccountController@confirmVerifyEmail'); - Route::group(['prefix' => 'direct'], function () { - Route::get('browse', 'DirectMessageController@browse'); - Route::post('create', 'DirectMessageController@create'); - Route::get('thread', 'DirectMessageController@thread'); - Route::post('mute', 'DirectMessageController@mute'); - Route::post('unmute', 'DirectMessageController@unmute'); - Route::delete('message', 'DirectMessageController@delete'); - Route::post('media', 'DirectMessageController@mediaUpload'); - Route::post('lookup', 'DirectMessageController@composeLookup'); - Route::post('read', 'DirectMessageController@read'); - }); + Route::get('auth/sudo', 'AccountController@sudoMode'); + Route::post('auth/sudo', 'AccountController@sudoModeVerify'); + Route::get('auth/checkpoint', 'AccountController@twoFactorCheckpoint'); + Route::post('auth/checkpoint', 'AccountController@twoFactorVerify'); - Route::group(['prefix' => 'v2'], function() { - Route::get('config', 'ApiController@siteConfiguration'); - Route::get('discover', 'InternalApiController@discover'); - Route::get('discover/posts', 'InternalApiController@discoverPosts')->middleware('auth:api'); - Route::get('profile/{username}/status/{postid}', 'PublicApiController@status'); - Route::get('profile/{username}/status/{postid}/state', 'PublicApiController@statusState'); - Route::get('comments/{username}/status/{postId}', 'PublicApiController@statusComments'); - Route::get('status/{id}/replies', 'InternalApiController@statusReplies'); - Route::post('moderator/action', 'InternalApiController@modAction'); - Route::get('discover/categories', 'InternalApiController@discoverCategories'); - Route::get('loops', 'DiscoverController@loopsApi'); - Route::post('loops/watch', 'DiscoverController@loopWatch'); - Route::get('discover/tag', 'DiscoverController@getHashtags'); - Route::get('statuses/{id}/replies', 'Api\ApiV1Controller@statusReplies'); - Route::get('statuses/{id}/state', 'Api\ApiV1Controller@statusState'); - }); + Route::get('results', 'SearchController@results'); + Route::post('visibility', 'StatusController@toggleVisibility'); - Route::group(['prefix' => 'pixelfed'], function() { - Route::group(['prefix' => 'v1'], function() { - Route::get('accounts/verify_credentials', 'ApiController@verifyCredentials'); - Route::get('accounts/relationships', 'Api\ApiV1Controller@accountRelationshipsById'); - Route::get('accounts/search', 'Api\ApiV1Controller@accountSearch'); - Route::get('accounts/{id}/statuses', 'PublicApiController@accountStatuses'); - Route::post('accounts/{id}/block', 'Api\ApiV1Controller@accountBlockById'); - Route::post('accounts/{id}/unblock', 'Api\ApiV1Controller@accountUnblockById'); - Route::get('statuses/{id}', 'PublicApiController@getStatus'); - Route::get('accounts/{id}', 'PublicApiController@account'); - Route::post('avatar/update', 'ApiController@avatarUpdate'); - Route::get('custom_emojis', 'Api\ApiV1Controller@customEmojis'); - Route::get('notifications', 'ApiController@notifications'); - Route::get('timelines/public', 'PublicApiController@publicTimelineApi'); - Route::get('timelines/home', 'PublicApiController@homeTimelineApi'); - Route::get('timelines/network', 'PublicApiController@networkTimelineApi'); - Route::get('newsroom/timeline', 'NewsroomController@timelineApi'); - Route::post('newsroom/markasread', 'NewsroomController@markAsRead'); - Route::get('favourites', 'Api\BaseApiController@accountLikes'); - Route::get('mutes', 'AccountController@accountMutes'); - Route::get('blocks', 'AccountController@accountBlocks'); - }); + Route::post('metro/dark-mode', 'SettingsController@metroDarkMode'); - Route::group(['prefix' => 'v2'], function() { - Route::get('config', 'ApiController@siteConfiguration'); - Route::get('discover', 'InternalApiController@discover'); - Route::get('discover/posts', 'InternalApiController@discoverPosts'); - Route::get('discover/profiles', 'DiscoverController@profilesDirectoryApi'); - Route::get('profile/{username}/status/{postid}', 'PublicApiController@status'); - Route::get('comments/{username}/status/{postId}', 'PublicApiController@statusComments'); - Route::post('moderator/action', 'InternalApiController@modAction'); - Route::get('discover/categories', 'InternalApiController@discoverCategories'); - Route::get('loops', 'DiscoverController@loopsApi'); - Route::post('loops/watch', 'DiscoverController@loopWatch'); - Route::get('discover/tag', 'DiscoverController@getHashtags'); - Route::get('discover/posts/trending', 'DiscoverController@trendingApi'); - Route::get('discover/posts/hashtags', 'DiscoverController@trendingHashtags'); - Route::get('discover/posts/places', 'DiscoverController@trendingPlaces'); - Route::get('seasonal/yir', 'SeasonalController@getData'); - Route::post('seasonal/yir', 'SeasonalController@store'); - Route::get('mutes', 'AccountController@accountMutesV2'); - Route::get('blocks', 'AccountController@accountBlocksV2'); - Route::get('filters', 'AccountController@accountFiltersV2'); - Route::post('status/compose', 'InternalApiController@composePost'); - Route::get('status/{id}/replies', 'InternalApiController@statusReplies'); - Route::post('status/{id}/archive', 'ApiController@archive'); - Route::post('status/{id}/unarchive', 'ApiController@unarchive'); - Route::get('statuses/archives', 'ApiController@archivedPosts'); - Route::get('discover/memories', 'DiscoverController@myMemories'); - Route::get('discover/account-insights', 'DiscoverController@accountInsightsPopularPosts'); - Route::get('discover/server-timeline', 'DiscoverController@serverTimeline'); - Route::get('discover/meta', 'DiscoverController@enabledFeatures'); - Route::post('discover/admin/features', 'DiscoverController@updateFeatures'); - }); + Route::group(['prefix' => 'report'], function () { + Route::get('/', 'ReportController@showForm')->name('report.form'); + Route::post('/', 'ReportController@formStore'); + Route::get('not-interested', 'ReportController@notInterestedForm')->name('report.not-interested'); + Route::get('spam', 'ReportController@spamForm')->name('report.spam'); + Route::get('spam/comment', 'ReportController@spamCommentForm')->name('report.spam.comment'); + Route::get('spam/post', 'ReportController@spamPostForm')->name('report.spam.post'); + Route::get('spam/profile', 'ReportController@spamProfileForm')->name('report.spam.profile'); + Route::get('sensitive/comment', 'ReportController@sensitiveCommentForm')->name('report.sensitive.comment'); + Route::get('sensitive/post', 'ReportController@sensitivePostForm')->name('report.sensitive.post'); + Route::get('sensitive/profile', 'ReportController@sensitiveProfileForm')->name('report.sensitive.profile'); + Route::get('abusive/comment', 'ReportController@abusiveCommentForm')->name('report.abusive.comment'); + Route::get('abusive/post', 'ReportController@abusivePostForm')->name('report.abusive.post'); + Route::get('abusive/profile', 'ReportController@abusiveProfileForm')->name('report.abusive.profile'); + }); - Route::get('discover/accounts/popular', 'Api\ApiV1Controller@discoverAccountsPopular'); - Route::post('web/change-language.json', 'SpaController@updateLanguage'); - }); + Route::get('collections/create', 'CollectionController@create'); - Route::group(['prefix' => 'local'], function () { - // Route::post('status/compose', 'InternalApiController@composePost')->middleware('throttle:maxPostsPerHour,60')->middleware('throttle:maxPostsPerDay,1440'); - Route::get('exp/rec', 'ApiController@userRecommendations'); - Route::post('discover/tag/subscribe', 'HashtagFollowController@store'); - Route::get('discover/tag/list', 'HashtagFollowController@getTags'); - // Route::get('profile/sponsor/{id}', 'ProfileSponsorController@get'); - Route::get('bookmarks', 'InternalApiController@bookmarks'); - Route::get('collection/items/{id}', 'CollectionController@getItems'); - Route::post('collection/item', 'CollectionController@storeId'); - Route::delete('collection/item', 'CollectionController@deleteId'); - Route::get('collection/{id}', 'CollectionController@getCollection'); - Route::post('collection/{id}', 'CollectionController@store'); - Route::delete('collection/{id}', 'CollectionController@delete'); - Route::post('collection/{id}/publish', 'CollectionController@publish'); - Route::get('profile/collections/{id}', 'CollectionController@getUserCollections'); + Route::get('me', 'ProfileController@meRedirect'); + Route::get('intent/follow', 'SiteController@followIntent'); + Route::get('rs/{id}', 'StoryController@remoteStory'); + Route::get('stories/new', 'StoryController@compose'); + Route::get('my/story', 'StoryController@iRedirect'); + Route::get('web/profile/_/{id}', 'InternalApiController@remoteProfile'); + Route::get('web/post/_/{profileId}/{statusid}', 'InternalApiController@remoteStatus'); - Route::post('compose/tag/untagme', 'MediaTagController@untagProfile'); - - Route::post('import/ig', 'ImportPostController@store'); - Route::get('import/ig/config', 'ImportPostController@getConfig'); - Route::post('import/ig/media', 'ImportPostController@storeMedia'); - Route::post('import/ig/existing', 'ImportPostController@getImportedFiles'); - Route::post('import/ig/posts', 'ImportPostController@getImportedPosts'); - Route::post('import/ig/processing', 'ImportPostController@getProcessingCount'); - }); - - Route::group(['prefix' => 'web/stories'], function () { - Route::get('v1/recent', 'StoryController@recent'); - Route::get('v1/viewers', 'StoryController@viewers'); - Route::get('v1/profile/{id}', 'StoryController@profile'); - Route::get('v1/exists/{id}', 'StoryController@exists'); - Route::get('v1/poll/results', 'StoryController@pollResults'); - Route::post('v1/viewed', 'StoryController@viewed'); - Route::post('v1/react', 'StoryController@react'); - Route::post('v1/comment', 'StoryController@comment'); - Route::post('v1/publish/poll', 'StoryController@publishStoryPoll'); - Route::post('v1/poll/vote', 'StoryController@storyPollVote'); - Route::post('v1/report', 'StoryController@storeReport'); - Route::post('v1/add', 'StoryController@apiV1Add'); - Route::post('v1/crop', 'StoryController@cropPhoto'); - Route::post('v1/publish', 'StoryController@publishStory'); - Route::delete('v1/delete/{id}', 'StoryController@apiV1Delete'); - }); - - Route::group(['prefix' => 'portfolio'], function () { - Route::post('self/curated.json', 'PortfolioController@storeCurated'); - Route::post('self/settings.json', 'PortfolioController@getSettings'); - Route::get('account/settings.json', 'PortfolioController@getAccountSettings'); - Route::post('self/update-settings.json', 'PortfolioController@storeSettings'); - Route::get('{username}/feed', 'PortfolioController@getFeed'); - }); - }); - - Route::get('discover/tags/{hashtag}', 'DiscoverController@showTags'); - Route::get('discover/places', 'PlaceController@directoryHome')->name('discover.places'); - Route::get('discover/places/{id}/{slug}', 'PlaceController@show'); - Route::get('discover/location/country/{country}', 'PlaceController@directoryCities'); - - Route::group(['prefix' => 'i'], function () { - Route::redirect('/', '/'); - Route::get('compose', 'StatusController@compose')->name('compose'); - Route::post('comment', 'CommentController@store'); - Route::post('delete', 'StatusController@delete'); - Route::post('mute', 'AccountController@mute'); - Route::post('unmute', 'AccountController@unmute'); - Route::post('block', 'AccountController@block'); - Route::post('unblock', 'AccountController@unblock'); - Route::post('like', 'LikeController@store'); - Route::post('share', 'StatusController@storeShare'); - Route::post('follow', 'FollowerController@store'); - Route::post('bookmark', 'BookmarkController@store'); - Route::get('lang/{locale}', 'SiteController@changeLocale'); - Route::get('restored', 'AccountController@accountRestored'); - - Route::get('verify-email', 'AccountController@verifyEmail'); - Route::post('verify-email', 'AccountController@sendVerifyEmail'); - Route::get('verify-email/request', 'InternalApiController@requestEmailVerification'); - Route::post('verify-email/request', 'InternalApiController@requestEmailVerificationStore'); - Route::get('confirm-email/{userToken}/{randomToken}', 'AccountController@confirmVerifyEmail'); - - Route::get('auth/sudo', 'AccountController@sudoMode'); - Route::post('auth/sudo', 'AccountController@sudoModeVerify'); - Route::get('auth/checkpoint', 'AccountController@twoFactorCheckpoint'); - Route::post('auth/checkpoint', 'AccountController@twoFactorVerify'); - - Route::get('results', 'SearchController@results'); - Route::post('visibility', 'StatusController@toggleVisibility'); - - Route::post('metro/dark-mode', 'SettingsController@metroDarkMode'); - - Route::group(['prefix' => 'report'], function () { - Route::get('/', 'ReportController@showForm')->name('report.form'); - Route::post('/', 'ReportController@formStore'); - Route::get('not-interested', 'ReportController@notInterestedForm')->name('report.not-interested'); - Route::get('spam', 'ReportController@spamForm')->name('report.spam'); - Route::get('spam/comment', 'ReportController@spamCommentForm')->name('report.spam.comment'); - Route::get('spam/post', 'ReportController@spamPostForm')->name('report.spam.post'); - Route::get('spam/profile', 'ReportController@spamProfileForm')->name('report.spam.profile'); - Route::get('sensitive/comment', 'ReportController@sensitiveCommentForm')->name('report.sensitive.comment'); - Route::get('sensitive/post', 'ReportController@sensitivePostForm')->name('report.sensitive.post'); - Route::get('sensitive/profile', 'ReportController@sensitiveProfileForm')->name('report.sensitive.profile'); - Route::get('abusive/comment', 'ReportController@abusiveCommentForm')->name('report.abusive.comment'); - Route::get('abusive/post', 'ReportController@abusivePostForm')->name('report.abusive.post'); - Route::get('abusive/profile', 'ReportController@abusiveProfileForm')->name('report.abusive.profile'); - }); - - Route::get('collections/create', 'CollectionController@create'); - - Route::get('me', 'ProfileController@meRedirect'); - Route::get('intent/follow', 'SiteController@followIntent'); - Route::get('rs/{id}', 'StoryController@remoteStory'); - Route::get('stories/new', 'StoryController@compose'); - Route::get('my/story', 'StoryController@iRedirect'); - Route::get('web/profile/_/{id}', 'InternalApiController@remoteProfile'); - Route::get('web/post/_/{profileId}/{statusid}', 'InternalApiController@remoteStatus'); - - Route::group(['prefix' => 'import', 'middleware' => 'dangerzone'], function() { - Route::get('job/{uuid}/1', 'ImportController@instagramStepOne'); - Route::post('job/{uuid}/1', 'ImportController@instagramStepOneStore'); - Route::get('job/{uuid}/2', 'ImportController@instagramStepTwo'); - Route::post('job/{uuid}/2', 'ImportController@instagramStepTwoStore'); - Route::get('job/{uuid}/3', 'ImportController@instagramStepThree'); - Route::post('job/{uuid}/3', 'ImportController@instagramStepThreeStore'); - }); - - Route::get('redirect', 'SiteController@redirectUrl'); - Route::post('admin/media/block/add', 'MediaBlocklistController@add'); - Route::post('admin/media/block/delete', 'MediaBlocklistController@delete'); - - Route::get('warning', 'AccountInterstitialController@get'); - Route::post('warning', 'AccountInterstitialController@read'); - Route::get('my2020', 'SeasonalController@yearInReview'); - - Route::get('web/my-portfolio', 'PortfolioController@myRedirect'); - Route::get('web/hashtag/{tag}', 'SpaController@hashtagRedirect'); - Route::get('web/username/{id}', 'SpaController@usernameRedirect'); - Route::get('web/post/{id}', 'SpaController@webPost'); - Route::get('web/profile/{id}', 'SpaController@webProfile'); - - Route::get('web/{q}', 'SpaController@index')->where('q', '.*'); - Route::get('web', 'SpaController@index'); - }); - - Route::group(['prefix' => 'account'], function () { - Route::redirect('/', '/'); - Route::get('direct', 'AccountController@direct'); - Route::get('direct/t/{id}', 'AccountController@directMessage'); - Route::get('activity', 'AccountController@notifications')->name('notifications'); - Route::get('follow-requests', 'AccountController@followRequests')->name('follow-requests'); - Route::post('follow-requests', 'AccountController@followRequestHandle'); - Route::get('follow-requests.json', 'AccountController@followRequestsJson'); - Route::get('portfolio/{username}.json', 'PortfolioController@getApFeed'); - Route::get('portfolio/{username}.rss', 'PortfolioController@getRssFeed'); - }); - - Route::group(['prefix' => 'settings'], function () { - Route::redirect('/', '/settings/home'); - Route::get('home', 'SettingsController@home') - ->name('settings'); - Route::post('home', 'SettingsController@homeUpdate'); - Route::get('avatar', 'SettingsController@avatar')->name('settings.avatar'); - Route::post('avatar', 'AvatarController@store'); - Route::delete('avatar', 'AvatarController@deleteAvatar'); - Route::get('password', 'SettingsController@password')->name('settings.password')->middleware('dangerzone'); - Route::post('password', 'SettingsController@passwordUpdate')->middleware('dangerzone'); - Route::get('email', 'SettingsController@email')->name('settings.email')->middleware('dangerzone'); - Route::post('email', 'SettingsController@emailUpdate')->middleware('dangerzone'); - Route::get('notifications', 'SettingsController@notifications')->name('settings.notifications'); - Route::get('privacy', 'SettingsController@privacy')->name('settings.privacy'); - Route::post('privacy', 'SettingsController@privacyStore'); - Route::get('privacy/muted-users', 'SettingsController@mutedUsers')->name('settings.privacy.muted-users'); - Route::post('privacy/muted-users', 'SettingsController@mutedUsersUpdate'); - Route::get('privacy/blocked-users', 'SettingsController@blockedUsers')->name('settings.privacy.blocked-users'); - Route::post('privacy/blocked-users', 'SettingsController@blockedUsersUpdate'); - Route::get('privacy/domain-blocks', 'SettingsController@domainBlocks')->name('settings.privacy.domain-blocks'); - Route::get('privacy/blocked-instances', 'SettingsController@blockedInstances')->name('settings.privacy.blocked-instances'); - Route::post('privacy/blocked-instances', 'SettingsController@blockedInstanceStore'); - Route::post('privacy/blocked-instances/unblock', 'SettingsController@blockedInstanceUnblock')->name('settings.privacy.blocked-instances.unblock'); - Route::get('privacy/blocked-keywords', 'SettingsController@blockedKeywords')->name('settings.privacy.blocked-keywords'); - Route::post('privacy/account', 'SettingsController@privateAccountOptions')->name('settings.privacy.account'); - Route::group(['prefix' => 'remove', 'middleware' => 'dangerzone'], function() { - Route::get('request/temporary', 'SettingsController@removeAccountTemporary')->name('settings.remove.temporary'); - Route::post('request/temporary', 'SettingsController@removeAccountTemporarySubmit'); - Route::get('request/permanent', 'SettingsController@removeAccountPermanent')->name('settings.remove.permanent'); - Route::post('request/permanent', 'SettingsController@removeAccountPermanentSubmit'); - }); - - Route::group(['prefix' => 'security', 'middleware' => 'dangerzone'], function() { - Route::get( - '/', - 'SettingsController@security' - )->name('settings.security'); - Route::get( - '2fa/setup', - 'SettingsController@securityTwoFactorSetup' - )->name('settings.security.2fa.setup'); - Route::post( - '2fa/setup', - 'SettingsController@securityTwoFactorSetupStore' - ); - Route::get( - '2fa/edit', - 'SettingsController@securityTwoFactorEdit' - )->name('settings.security.2fa.edit'); - Route::post( - '2fa/edit', - 'SettingsController@securityTwoFactorUpdate' - ); - Route::get( - '2fa/recovery-codes', - 'SettingsController@securityTwoFactorRecoveryCodes' - )->name('settings.security.2fa.recovery'); - Route::post( - '2fa/recovery-codes', - 'SettingsController@securityTwoFactorRecoveryCodesRegenerate' - ); - - }); - - Route::get('parental-controls', 'ParentalControlsController@index')->name('settings.parental-controls')->middleware('dangerzone'); - Route::get('parental-controls/add', 'ParentalControlsController@add')->name('settings.pc.add')->middleware('dangerzone'); - Route::post('parental-controls/add', 'ParentalControlsController@store')->middleware('dangerzone'); - Route::get('parental-controls/manage/{id}', 'ParentalControlsController@view')->middleware('dangerzone'); - Route::post('parental-controls/manage/{id}', 'ParentalControlsController@update')->middleware('dangerzone'); - Route::get('parental-controls/manage/{id}/cancel-invite', 'ParentalControlsController@cancelInvite')->name('settings.pc.cancel-invite')->middleware('dangerzone'); - Route::post('parental-controls/manage/{id}/cancel-invite', 'ParentalControlsController@cancelInviteHandle')->middleware('dangerzone'); - Route::get('parental-controls/manage/{id}/stop-managing', 'ParentalControlsController@stopManaging')->name('settings.pc.stop-managing')->middleware('dangerzone'); - Route::post('parental-controls/manage/{id}/stop-managing', 'ParentalControlsController@stopManagingHandle')->middleware('dangerzone'); - - Route::get('applications', 'SettingsController@applications')->name('settings.applications')->middleware('dangerzone'); - Route::get('data-export', 'SettingsController@dataExport')->name('settings.dataexport')->middleware('dangerzone'); - Route::post('data-export/following', 'SettingsController@exportFollowing')->middleware('dangerzone'); - Route::post('data-export/followers', 'SettingsController@exportFollowers')->middleware('dangerzone'); - Route::post('data-export/mute-block-list', 'SettingsController@exportMuteBlockList')->middleware('dangerzone'); - Route::post('data-export/account', 'SettingsController@exportAccount')->middleware('dangerzone'); - Route::post('data-export/statuses', 'SettingsController@exportStatuses')->middleware('dangerzone'); - Route::get('developers', 'SettingsController@developers')->name('settings.developers')->middleware('dangerzone'); - Route::get('labs', 'SettingsController@labs')->name('settings.labs'); - Route::post('labs', 'SettingsController@labsStore'); - - Route::get('accessibility', 'SettingsController@accessibility')->name('settings.accessibility'); - Route::post('accessibility', 'SettingsController@accessibilityStore'); - - Route::group(['prefix' => 'relationships'], function() { - Route::redirect('/', '/settings/relationships/home'); - Route::get('home', 'SettingsController@relationshipsHome')->name('settings.relationships'); - }); - Route::get('invites/create', 'UserInviteController@create')->name('settings.invites.create'); - Route::post('invites/create', 'UserInviteController@store'); - Route::get('invites', 'UserInviteController@show')->name('settings.invites'); - // Route::get('sponsor', 'SettingsController@sponsor')->name('settings.sponsor'); - // Route::post('sponsor', 'SettingsController@sponsorStore'); Route::group(['prefix' => 'import', 'middleware' => 'dangerzone'], function() { - Route::get('/', 'SettingsController@dataImport')->name('settings.import'); - Route::prefix('instagram')->group(function() { - Route::get('/', 'ImportController@instagram')->name('settings.import.ig'); - Route::post('/', 'ImportController@instagramStart'); - }); - Route::prefix('mastodon')->group(function() { - Route::get('/', 'ImportController@mastodon')->name('settings.import.mastodon'); - }); - }); + Route::get('job/{uuid}/1', 'ImportController@instagramStepOne'); + Route::post('job/{uuid}/1', 'ImportController@instagramStepOneStore'); + Route::get('job/{uuid}/2', 'ImportController@instagramStepTwo'); + Route::post('job/{uuid}/2', 'ImportController@instagramStepTwoStore'); + Route::get('job/{uuid}/3', 'ImportController@instagramStepThree'); + Route::post('job/{uuid}/3', 'ImportController@instagramStepThreeStore'); + }); - Route::get('timeline', 'SettingsController@timelineSettings')->name('settings.timeline'); - Route::post('timeline', 'SettingsController@updateTimelineSettings'); - Route::get('media', 'SettingsController@mediaSettings')->name('settings.media'); - Route::post('media', 'SettingsController@updateMediaSettings'); + Route::get('redirect', 'SiteController@redirectUrl'); + Route::post('admin/media/block/add', 'MediaBlocklistController@add'); + Route::post('admin/media/block/delete', 'MediaBlocklistController@delete'); + + Route::get('warning', 'AccountInterstitialController@get'); + Route::post('warning', 'AccountInterstitialController@read'); + Route::get('my2020', 'SeasonalController@yearInReview'); + + Route::get('web/my-portfolio', 'PortfolioController@myRedirect'); + Route::get('web/hashtag/{tag}', 'SpaController@hashtagRedirect'); + Route::get('web/username/{id}', 'SpaController@usernameRedirect'); + Route::get('web/post/{id}', 'SpaController@webPost'); + Route::get('web/profile/{id}', 'SpaController@webProfile'); + + Route::get('web/{q}', 'SpaController@index')->where('q', '.*'); + Route::get('web', 'SpaController@index'); + }); + + Route::group(['prefix' => 'account'], function () { + Route::redirect('/', '/'); + Route::get('direct', 'AccountController@direct'); + Route::get('direct/t/{id}', 'AccountController@directMessage'); + Route::get('activity', 'AccountController@notifications')->name('notifications'); + Route::get('follow-requests', 'AccountController@followRequests')->name('follow-requests'); + Route::post('follow-requests', 'AccountController@followRequestHandle'); + Route::get('follow-requests.json', 'AccountController@followRequestsJson'); + Route::get('portfolio/{username}.json', 'PortfolioController@getApFeed'); + Route::get('portfolio/{username}.rss', 'PortfolioController@getRssFeed'); + }); + + Route::group(['prefix' => 'settings'], function () { + Route::redirect('/', '/settings/home'); + Route::get('home', 'SettingsController@home') + ->name('settings'); + Route::post('home', 'SettingsController@homeUpdate'); + Route::get('avatar', 'SettingsController@avatar')->name('settings.avatar'); + Route::post('avatar', 'AvatarController@store'); + Route::delete('avatar', 'AvatarController@deleteAvatar'); + Route::get('password', 'SettingsController@password')->name('settings.password')->middleware('dangerzone'); + Route::post('password', 'SettingsController@passwordUpdate')->middleware('dangerzone'); + Route::get('email', 'SettingsController@email')->name('settings.email')->middleware('dangerzone'); + Route::post('email', 'SettingsController@emailUpdate')->middleware('dangerzone'); + Route::get('notifications', 'SettingsController@notifications')->name('settings.notifications'); + Route::get('privacy', 'SettingsController@privacy')->name('settings.privacy'); + Route::post('privacy', 'SettingsController@privacyStore'); + Route::get('privacy/muted-users', 'SettingsController@mutedUsers')->name('settings.privacy.muted-users'); + Route::post('privacy/muted-users', 'SettingsController@mutedUsersUpdate'); + Route::get('privacy/blocked-users', 'SettingsController@blockedUsers')->name('settings.privacy.blocked-users'); + Route::post('privacy/blocked-users', 'SettingsController@blockedUsersUpdate'); + Route::get('privacy/domain-blocks', 'SettingsController@domainBlocks')->name('settings.privacy.domain-blocks'); + Route::get('privacy/blocked-instances', 'SettingsController@blockedInstances')->name('settings.privacy.blocked-instances'); + Route::post('privacy/blocked-instances', 'SettingsController@blockedInstanceStore'); + Route::post('privacy/blocked-instances/unblock', 'SettingsController@blockedInstanceUnblock')->name('settings.privacy.blocked-instances.unblock'); + Route::get('privacy/blocked-keywords', 'SettingsController@blockedKeywords')->name('settings.privacy.blocked-keywords'); + Route::post('privacy/account', 'SettingsController@privateAccountOptions')->name('settings.privacy.account'); + Route::group(['prefix' => 'remove', 'middleware' => 'dangerzone'], function() { + Route::get('request/temporary', 'SettingsController@removeAccountTemporary')->name('settings.remove.temporary'); + Route::post('request/temporary', 'SettingsController@removeAccountTemporarySubmit'); + Route::get('request/permanent', 'SettingsController@removeAccountPermanent')->name('settings.remove.permanent'); + Route::post('request/permanent', 'SettingsController@removeAccountPermanentSubmit'); + }); + + Route::group(['prefix' => 'security', 'middleware' => 'dangerzone'], function() { + Route::get( + '/', + 'SettingsController@security' + )->name('settings.security'); + Route::get( + '2fa/setup', + 'SettingsController@securityTwoFactorSetup' + )->name('settings.security.2fa.setup'); + Route::post( + '2fa/setup', + 'SettingsController@securityTwoFactorSetupStore' + ); + Route::get( + '2fa/edit', + 'SettingsController@securityTwoFactorEdit' + )->name('settings.security.2fa.edit'); + Route::post( + '2fa/edit', + 'SettingsController@securityTwoFactorUpdate' + ); + Route::get( + '2fa/recovery-codes', + 'SettingsController@securityTwoFactorRecoveryCodes' + )->name('settings.security.2fa.recovery'); + Route::post( + '2fa/recovery-codes', + 'SettingsController@securityTwoFactorRecoveryCodesRegenerate' + ); + + }); + + Route::get('parental-controls', 'ParentalControlsController@index')->name('settings.parental-controls')->middleware('dangerzone'); + Route::get('parental-controls/add', 'ParentalControlsController@add')->name('settings.pc.add')->middleware('dangerzone'); + Route::post('parental-controls/add', 'ParentalControlsController@store')->middleware('dangerzone'); + Route::get('parental-controls/manage/{id}', 'ParentalControlsController@view')->middleware('dangerzone'); + Route::post('parental-controls/manage/{id}', 'ParentalControlsController@update')->middleware('dangerzone'); + Route::get('parental-controls/manage/{id}/cancel-invite', 'ParentalControlsController@cancelInvite')->name('settings.pc.cancel-invite')->middleware('dangerzone'); + Route::post('parental-controls/manage/{id}/cancel-invite', 'ParentalControlsController@cancelInviteHandle')->middleware('dangerzone'); + Route::get('parental-controls/manage/{id}/stop-managing', 'ParentalControlsController@stopManaging')->name('settings.pc.stop-managing')->middleware('dangerzone'); + Route::post('parental-controls/manage/{id}/stop-managing', 'ParentalControlsController@stopManagingHandle')->middleware('dangerzone'); + + Route::get('applications', 'SettingsController@applications')->name('settings.applications')->middleware('dangerzone'); + Route::get('data-export', 'SettingsController@dataExport')->name('settings.dataexport')->middleware('dangerzone'); + Route::post('data-export/following', 'SettingsController@exportFollowing')->middleware('dangerzone'); + Route::post('data-export/followers', 'SettingsController@exportFollowers')->middleware('dangerzone'); + Route::post('data-export/mute-block-list', 'SettingsController@exportMuteBlockList')->middleware('dangerzone'); + Route::post('data-export/account', 'SettingsController@exportAccount')->middleware('dangerzone'); + Route::post('data-export/statuses', 'SettingsController@exportStatuses')->middleware('dangerzone'); + Route::get('developers', 'SettingsController@developers')->name('settings.developers')->middleware('dangerzone'); + Route::get('labs', 'SettingsController@labs')->name('settings.labs'); + Route::post('labs', 'SettingsController@labsStore'); + + Route::get('accessibility', 'SettingsController@accessibility')->name('settings.accessibility'); + Route::post('accessibility', 'SettingsController@accessibilityStore'); + + Route::group(['prefix' => 'relationships'], function() { + Route::redirect('/', '/settings/relationships/home'); + Route::get('home', 'SettingsController@relationshipsHome')->name('settings.relationships'); + }); + Route::get('invites/create', 'UserInviteController@create')->name('settings.invites.create'); + Route::post('invites/create', 'UserInviteController@store'); + Route::get('invites', 'UserInviteController@show')->name('settings.invites'); + // Route::get('sponsor', 'SettingsController@sponsor')->name('settings.sponsor'); + // Route::post('sponsor', 'SettingsController@sponsorStore'); + Route::group(['prefix' => 'import', 'middleware' => 'dangerzone'], function() { + Route::get('/', 'SettingsController@dataImport')->name('settings.import'); + Route::prefix('instagram')->group(function() { + Route::get('/', 'ImportController@instagram')->name('settings.import.ig'); + Route::post('/', 'ImportController@instagramStart'); + }); + Route::prefix('mastodon')->group(function() { + Route::get('/', 'ImportController@mastodon')->name('settings.import.mastodon'); + }); + }); + + Route::get('timeline', 'SettingsController@timelineSettings')->name('settings.timeline'); + Route::post('timeline', 'SettingsController@updateTimelineSettings'); + Route::get('media', 'SettingsController@mediaSettings')->name('settings.media'); + Route::post('media', 'SettingsController@updateMediaSettings'); Route::group(['prefix' => 'account/aliases', 'middleware' => 'dangerzone'], function() { Route::get('manage', 'ProfileAliasController@index'); Route::post('manage', 'ProfileAliasController@store'); Route::post('manage/delete', 'ProfileAliasController@delete'); }); - }); + }); - Route::group(['prefix' => 'site'], function () { - Route::redirect('/', '/'); - Route::get('about', 'SiteController@about')->name('site.about'); - Route::view('help', 'site.help')->name('site.help'); - Route::view('developer-api', 'site.developer')->name('site.developers'); - Route::view('fediverse', 'site.fediverse')->name('site.fediverse'); - Route::view('open-source', 'site.opensource')->name('site.opensource'); - Route::view('banned-instances', 'site.bannedinstances')->name('site.bannedinstances'); - Route::get('terms', 'SiteController@terms')->name('site.terms'); - Route::get('privacy', 'SiteController@privacy')->name('site.privacy'); - Route::view('platform', 'site.platform')->name('site.platform'); - Route::view('language', 'site.language')->name('site.language'); - Route::get('contact', 'ContactController@show')->name('site.contact'); - Route::post('contact', 'ContactController@store'); - Route::group(['prefix'=>'kb'], function() { - Route::view('getting-started', 'site.help.getting-started')->name('help.getting-started'); - Route::view('sharing-media', 'site.help.sharing-media')->name('help.sharing-media'); - Route::view('your-profile', 'site.help.your-profile')->name('help.your-profile'); - Route::view('stories', 'site.help.stories')->name('help.stories'); - Route::view('embed', 'site.help.embed')->name('help.embed'); - Route::view('hashtags', 'site.help.hashtags')->name('help.hashtags'); - Route::view('instance-actor', 'site.help.instance-actor')->name('help.instance-actor'); - Route::view('discover', 'site.help.discover')->name('help.discover'); - Route::view('direct-messages', 'site.help.dm')->name('help.dm'); - Route::view('timelines', 'site.help.timelines')->name('help.timelines'); - Route::view('what-is-the-fediverse', 'site.help.what-is-fediverse')->name('help.what-is-fediverse'); - Route::view('safety-tips', 'site.help.safety-tips')->name('help.safety-tips'); + Route::group(['prefix' => 'site'], function () { + Route::redirect('/', '/'); + Route::get('about', 'SiteController@about')->name('site.about'); + Route::view('help', 'site.help')->name('site.help'); + Route::view('developer-api', 'site.developer')->name('site.developers'); + Route::view('fediverse', 'site.fediverse')->name('site.fediverse'); + Route::view('open-source', 'site.opensource')->name('site.opensource'); + Route::view('banned-instances', 'site.bannedinstances')->name('site.bannedinstances'); + Route::get('terms', 'SiteController@terms')->name('site.terms'); + Route::get('privacy', 'SiteController@privacy')->name('site.privacy'); + Route::view('platform', 'site.platform')->name('site.platform'); + Route::view('language', 'site.language')->name('site.language'); + Route::get('contact', 'ContactController@show')->name('site.contact'); + Route::post('contact', 'ContactController@store'); + Route::group(['prefix'=>'kb'], function() { + Route::view('getting-started', 'site.help.getting-started')->name('help.getting-started'); + Route::view('sharing-media', 'site.help.sharing-media')->name('help.sharing-media'); + Route::view('your-profile', 'site.help.your-profile')->name('help.your-profile'); + Route::view('stories', 'site.help.stories')->name('help.stories'); + Route::view('embed', 'site.help.embed')->name('help.embed'); + Route::view('hashtags', 'site.help.hashtags')->name('help.hashtags'); + Route::view('instance-actor', 'site.help.instance-actor')->name('help.instance-actor'); + Route::view('discover', 'site.help.discover')->name('help.discover'); + Route::view('direct-messages', 'site.help.dm')->name('help.dm'); + Route::view('timelines', 'site.help.timelines')->name('help.timelines'); + Route::view('what-is-the-fediverse', 'site.help.what-is-fediverse')->name('help.what-is-fediverse'); + Route::view('safety-tips', 'site.help.safety-tips')->name('help.safety-tips'); - Route::get('community-guidelines', 'SiteController@communityGuidelines')->name('help.community-guidelines'); - Route::view('controlling-visibility', 'site.help.controlling-visibility')->name('help.controlling-visibility'); - Route::view('blocking-accounts', 'site.help.blocking-accounts')->name('help.blocking-accounts'); - Route::view('report-something', 'site.help.report-something')->name('help.report-something'); - Route::view('data-policy', 'site.help.data-policy')->name('help.data-policy'); - Route::view('labs-deprecation', 'site.help.labs-deprecation')->name('help.labs-deprecation'); - Route::view('tagging-people', 'site.help.tagging-people')->name('help.tagging-people'); - Route::view('licenses', 'site.help.licenses')->name('help.licenses'); - Route::view('instance-max-users-limit', 'site.help.instance-max-users')->name('help.instance-max-users-limit'); - Route::view('import', 'site.help.import')->name('help.import'); - Route::view('parental-controls', 'site.help.parental-controls'); - }); - Route::get('newsroom/{year}/{month}/{slug}', 'NewsroomController@show'); - Route::get('newsroom/archive', 'NewsroomController@archive'); - Route::get('newsroom/search', 'NewsroomController@search'); - Route::get('newsroom', 'NewsroomController@index'); - Route::get('legal-notice', 'SiteController@legalNotice'); - }); + Route::get('community-guidelines', 'SiteController@communityGuidelines')->name('help.community-guidelines'); + Route::view('controlling-visibility', 'site.help.controlling-visibility')->name('help.controlling-visibility'); + Route::view('blocking-accounts', 'site.help.blocking-accounts')->name('help.blocking-accounts'); + Route::view('report-something', 'site.help.report-something')->name('help.report-something'); + Route::view('data-policy', 'site.help.data-policy')->name('help.data-policy'); + Route::view('labs-deprecation', 'site.help.labs-deprecation')->name('help.labs-deprecation'); + Route::view('tagging-people', 'site.help.tagging-people')->name('help.tagging-people'); + Route::view('licenses', 'site.help.licenses')->name('help.licenses'); + Route::view('instance-max-users-limit', 'site.help.instance-max-users')->name('help.instance-max-users-limit'); + Route::view('import', 'site.help.import')->name('help.import'); + Route::view('parental-controls', 'site.help.parental-controls'); + // Route::view('email-confirmation-issues', 'site.help.email-confirmation-issues')->name('help.email-confirmation-issues'); + // Route::view('curated-onboarding', 'site.help.curated-onboarding')->name('help.curated-onboarding'); + }); + Route::get('newsroom/{year}/{month}/{slug}', 'NewsroomController@show'); + Route::get('newsroom/archive', 'NewsroomController@archive'); + Route::get('newsroom/search', 'NewsroomController@search'); + Route::get('newsroom', 'NewsroomController@index'); + Route::get('legal-notice', 'SiteController@legalNotice'); + }); - Route::group(['prefix' => 'timeline'], function () { - Route::redirect('/', '/'); - Route::get('public', 'TimelineController@local')->name('timeline.public'); - Route::get('network', 'TimelineController@network')->name('timeline.network'); - }); + Route::group(['prefix' => 'timeline'], function () { + Route::redirect('/', '/'); + Route::get('public', 'TimelineController@local')->name('timeline.public'); + Route::get('network', 'TimelineController@network')->name('timeline.network'); + }); - Route::group(['prefix' => 'users'], function () { - Route::redirect('/', '/'); - Route::get('{user}.atom', 'ProfileController@showAtomFeed')->where('user', '.*'); - Route::get('{username}/outbox', 'FederationController@userOutbox'); - Route::get('{username}/followers', 'FederationController@userFollowers'); - Route::get('{username}/following', 'FederationController@userFollowing'); - Route::get('{username}', 'ProfileController@permalinkRedirect'); - }); + Route::group(['prefix' => 'users'], function () { + Route::redirect('/', '/'); + Route::get('{user}.atom', 'ProfileController@showAtomFeed')->where('user', '.*'); + Route::get('{username}/outbox', 'FederationController@userOutbox'); + Route::get('{username}/followers', 'FederationController@userFollowers'); + Route::get('{username}/following', 'FederationController@userFollowing'); + Route::get('{username}', 'ProfileController@permalinkRedirect'); + }); - Route::group(['prefix' => 'installer'], function() { - Route::get('api/requirements', 'InstallController@getRequirements')->withoutMiddleware(['web']); - Route::post('precheck/database', 'InstallController@precheckDatabase')->withoutMiddleware(['web']); - Route::post('store', 'InstallController@store')->withoutMiddleware(['web']); - Route::get('/', 'InstallController@index')->withoutMiddleware(['web']); - Route::get('/{q}', 'InstallController@index')->withoutMiddleware(['web'])->where('q', '.*'); - }); + Route::group(['prefix' => 'installer'], function() { + Route::get('api/requirements', 'InstallController@getRequirements')->withoutMiddleware(['web']); + Route::post('precheck/database', 'InstallController@precheckDatabase')->withoutMiddleware(['web']); + Route::post('store', 'InstallController@store')->withoutMiddleware(['web']); + Route::get('/', 'InstallController@index')->withoutMiddleware(['web']); + Route::get('/{q}', 'InstallController@index')->withoutMiddleware(['web'])->where('q', '.*'); + }); - Route::group(['prefix' => 'e'], function() { - Route::get('terms', 'MobileController@terms'); - Route::get('privacy', 'MobileController@privacy'); - }); + Route::group(['prefix' => 'e'], function() { + Route::get('terms', 'MobileController@terms'); + Route::get('privacy', 'MobileController@privacy'); + }); - Route::get('auth/invite/a/{code}', 'AdminInviteController@index'); - Route::post('api/v1.1/auth/invite/admin/re', 'AdminInviteController@apiRegister')->middleware('throttle:5,1440'); + Route::get('auth/invite/a/{code}', 'AdminInviteController@index'); + Route::post('api/v1.1/auth/invite/admin/re', 'AdminInviteController@apiRegister')->middleware('throttle:5,1440'); - Route::get('storage/m/_v2/{pid}/{mhash}/{uhash}/{f}', 'MediaController@fallbackRedirect'); - Route::get('stories/{username}', 'ProfileController@stories'); - Route::get('p/{id}', 'StatusController@shortcodeRedirect'); - Route::get('c/{collection}', 'CollectionController@show'); - Route::get('p/{username}/{id}/c', 'CommentController@showAll'); - Route::get('p/{username}/{id}/embed', 'StatusController@showEmbed'); - Route::get('p/{username}/{id}/edit', 'StatusController@edit'); - Route::post('p/{username}/{id}/edit', 'StatusController@editStore'); - Route::get('p/{username}/{id}.json', 'StatusController@showObject'); - Route::get('p/{username}/{id}', 'StatusController@show'); - Route::get('{username}/embed', 'ProfileController@embed'); - Route::get('{username}/live', 'LiveStreamController@showProfilePlayer'); - Route::get('@{username}@{domain}', 'SiteController@legacyWebfingerRedirect'); - Route::get('@{username}', 'SiteController@legacyProfileRedirect'); - Route::get('{username}', 'ProfileController@show'); + Route::get('storage/m/_v2/{pid}/{mhash}/{uhash}/{f}', 'MediaController@fallbackRedirect'); + Route::get('stories/{username}', 'ProfileController@stories'); + Route::get('p/{id}', 'StatusController@shortcodeRedirect'); + Route::get('c/{collection}', 'CollectionController@show'); + Route::get('p/{username}/{id}/c', 'CommentController@showAll'); + Route::get('p/{username}/{id}/embed', 'StatusController@showEmbed'); + Route::get('p/{username}/{id}/edit', 'StatusController@edit'); + Route::post('p/{username}/{id}/edit', 'StatusController@editStore'); + Route::get('p/{username}/{id}.json', 'StatusController@showObject'); + Route::get('p/{username}/{id}', 'StatusController@show'); + Route::get('{username}/embed', 'ProfileController@embed'); + Route::get('{username}/live', 'LiveStreamController@showProfilePlayer'); + Route::get('@{username}@{domain}', 'SiteController@legacyWebfingerRedirect'); + Route::get('@{username}', 'SiteController@legacyProfileRedirect'); + Route::get('{username}', 'ProfileController@show'); }); From 40b45b2a115fa1fa6b923e55e55db7615ae2d472 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 16 Feb 2024 02:15:39 -0700 Subject: [PATCH 08/14] Update Autospam, add live filters to block remote activities based on comma separated keywords --- app/Util/ActivityPub/Helpers.php | 17 +++++++++++++++++ app/Util/ActivityPub/Inbox.php | 16 ++++++++++++++++ config/autospam.php | 5 +++++ 3 files changed, 38 insertions(+) diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php index 6f5b8ae11..e25fd93b7 100644 --- a/app/Util/ActivityPub/Helpers.php +++ b/app/Util/ActivityPub/Helpers.php @@ -315,6 +315,23 @@ class Helpers { return; } + if(config('autospam.live_filters.enabled')) { + $filters = config('autospam.live_filters.filters'); + if(!empty($filters) && isset($res['content']) && !empty($res['content']) && strlen($filters) > 3) { + $filters = array_map('trim', explode(',', $filters)); + $content = $res['content']; + foreach($filters as $filter) { + $filter = trim($filter); + if(!$filter || !strlen($filter)) { + continue; + } + if(str_contains($content, $filter)) { + return; + } + } + } + } + if(isset($res['object'])) { $activity = $res; } else { diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index 5c9959e17..b6ae80893 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -197,6 +197,22 @@ class Inbox public function handleCreateActivity() { $activity = $this->payload['object']; + if(config('autospam.live_filters.enabled')) { + $filters = config('autospam.live_filters.filters'); + if(!empty($filters) && isset($activity['content']) && !empty($activity['content']) && strlen($filters) > 3) { + $filters = array_map('trim', explode(',', $filters)); + $content = $activity['content']; + foreach($filters as $filter) { + $filter = trim($filter); + if(!$filter || !strlen($filter)) { + continue; + } + if(str_contains($content, $filter)) { + return; + } + } + } + } $actor = $this->actorFirstOrCreate($this->payload['actor']); if(!$actor || $actor->domain == null) { return; diff --git a/config/autospam.php b/config/autospam.php index 39975ec9b..bc0ce4681 100644 --- a/config/autospam.php +++ b/config/autospam.php @@ -33,5 +33,10 @@ return [ 'nlp' => [ 'enabled' => false, 'spam_sample_limit' => env('PF_AUTOSPAM_NLP_SPAM_SAMPLE_LIMIT', 200), + ], + + 'live_filters' => [ + 'enabled' => env('PF_AUTOSPAM_LIVE_FILTERS_ENABLED', false), + 'filters' => env('PF_AUTOSPAM_LIVE_FILTERS_CSV', ''), ] ]; From 83eadbb811c098e84ac108188f9262cfeced4e40 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 16 Feb 2024 02:20:11 -0700 Subject: [PATCH 09/14] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 322e5ff49..ab6220584 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.11...dev) +### Features +- Autospam Live Filters - block remote activities based on comma separated keywords ([40b45b2a](https://github.com/pixelfed/pixelfed/commit/40b45b2a)) + ### Updated - Update ApiV1Controller, fix network timeline ([0faf59e3](https://github.com/pixelfed/pixelfed/commit/0faf59e3)) From b0fb1988299ec690bcfe97f159d8f6a59a578343 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 16 Feb 2024 02:58:27 -0700 Subject: [PATCH 10/14] Add Software Update banner to admin home feeds --- .../Controllers/SoftwareUpdateController.php | 21 ++++++ .../Internal/SoftwareUpdateService.php | 68 ++++++++++++++++++ config/instance.php | 6 +- resources/assets/components/Home.vue | 71 ++++++++++++++++++- routes/web-api.php | 6 ++ 5 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/SoftwareUpdateController.php create mode 100644 app/Services/Internal/SoftwareUpdateService.php diff --git a/app/Http/Controllers/SoftwareUpdateController.php b/app/Http/Controllers/SoftwareUpdateController.php new file mode 100644 index 000000000..e29359830 --- /dev/null +++ b/app/Http/Controllers/SoftwareUpdateController.php @@ -0,0 +1,21 @@ +middleware('auth'); + $this->middleware('admin'); + } + + public function getSoftwareUpdateCheck(Request $request) + { + $res = SoftwareUpdateService::get(); + return $res; + } +} diff --git a/app/Services/Internal/SoftwareUpdateService.php b/app/Services/Internal/SoftwareUpdateService.php new file mode 100644 index 000000000..40aaf867e --- /dev/null +++ b/app/Services/Internal/SoftwareUpdateService.php @@ -0,0 +1,68 @@ + $curVersion, + 'latest' => [ + 'version' => null, + 'published_at' => null, + 'url' => null, + ], + 'running_latest' => $hideWarning ? true : null + ]; + } + + return [ + 'current' => $curVersion, + 'latest' => [ + 'version' => $versions['latest']['version'], + 'published_at' => $versions['latest']['published_at'], + 'url' => $versions['latest']['url'], + ], + 'running_latest' => strval($versions['latest']['version']) === strval($curVersion) + ]; + } + + public static function fetchLatest() + { + try { + $res = Http::withOptions(['allow_redirects' => false]) + ->timeout(5) + ->connectTimeout(5) + ->retry(2, 500) + ->get('https://versions.pixelfed.org/versions.json'); + } catch (RequestException $e) { + return; + } catch (ConnectionException $e) { + return; + } catch (Exception $e) { + return; + } + + if(!$res->ok()) { + return; + } + + return $res->json(); + } +} diff --git a/config/instance.php b/config/instance.php index 7d5463055..d1566da4a 100644 --- a/config/instance.php +++ b/config/instance.php @@ -140,5 +140,9 @@ return [ 'max_children' => env('INSTANCE_PARENTAL_CONTROLS_MAX_CHILDREN', 1), 'auto_verify_email' => true, ], - ] + ], + + 'software-update' => [ + 'disable_failed_warning' => env('INSTANCE_SOFTWARE_UPDATE_DISABLE_FAILED_WARNING', false) + ], ]; diff --git a/resources/assets/components/Home.vue b/resources/assets/components/Home.vue index c75ed0a13..dac188281 100644 --- a/resources/assets/components/Home.vue +++ b/resources/assets/components/Home.vue @@ -9,6 +9,48 @@
+ + + @@ -59,7 +101,6 @@ "rightbar": Rightbar, "story-carousel": StoryCarousel, }, - data() { return { isLoaded: false, @@ -67,7 +108,10 @@ recommended: [], trending: [], storiesEnabled: false, - shouldRefresh: false + shouldRefresh: false, + showUpdateWarning: false, + showUpdateConnectionWarning: false, + updateInfo: undefined, } }, @@ -84,10 +128,33 @@ this.profile = window._sharedData.user; this.isLoaded = true; this.storiesEnabled = window.App?.config?.features?.hasOwnProperty('stories') ? window.App.config.features.stories : false; + + if(this.profile.is_admin) { + this.softwareUpdateCheck(); + } }, updateProfile(delta) { this.profile = Object.assign(this.profile, delta); + }, + + softwareUpdateCheck() { + axios.get('/api/web-admin/software-update/check') + .then(res => { + if(!res || !res.data || !res.data.hasOwnProperty('running_latest') || res.data.running_latest) { + return; + } + if(res.data.running_latest === null) { + this.updateInfo = res.data; + this.showUpdateConnectionWarning = true; + return; + } + this.updateInfo = res.data; + this.showUpdateWarning = !res.data.running_latest; + }) + .catch(err => { + this.showUpdateConnectionWarning = true; + }) } } } diff --git a/routes/web-api.php b/routes/web-api.php index f51762439..e19c36b6c 100644 --- a/routes/web-api.php +++ b/routes/web-api.php @@ -1,5 +1,7 @@ middleware(['validemail', 'twofactor', 'localization'])->group(function () { Route::group(['prefix' => 'api'], function () { Route::get('search', 'SearchController@searchAPI'); @@ -7,6 +9,10 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact Route::get('v1/polls/{id}', 'PollController@getPoll'); Route::post('v1/polls/{id}/votes', 'PollController@vote'); + Route::group(['prefix' => 'web-admin'], function() { + Route::get('software-update/check', [SoftwareUpdateController::class, 'getSoftwareUpdateCheck']); + }); + Route::group(['prefix' => 'compose'], function() { Route::group(['prefix' => 'v0'], function() { Route::post('/media/upload', 'ComposeController@mediaUpload'); From 56b736c325fcc960e68952dca1c5ea3582ecc0ca Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 16 Feb 2024 03:00:51 -0700 Subject: [PATCH 11/14] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6220584..00a1b6677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features - Autospam Live Filters - block remote activities based on comma separated keywords ([40b45b2a](https://github.com/pixelfed/pixelfed/commit/40b45b2a)) +- Added Software Update banner to admin home feeds ([b0fb1988](https://github.com/pixelfed/pixelfed/commit/b0fb1988)) ### Updated From f07843a0f2424f574271e5b33be429ce9fd41e80 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 16 Feb 2024 03:01:51 -0700 Subject: [PATCH 12/14] Update compiled assets --- public/js/home.chunk.88eeebf6c53d4dca.js | Bin 0 -> 243768 bytes ...ome.chunk.88eeebf6c53d4dca.js.LICENSE.txt} | 0 public/js/home.chunk.ada2cbf0ec3271bd.js | Bin 240067 -> 0 bytes public/js/manifest.js | Bin 4006 -> 4006 bytes public/mix-manifest.json | Bin 5243 -> 5243 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/js/home.chunk.88eeebf6c53d4dca.js rename public/js/{home.chunk.ada2cbf0ec3271bd.js.LICENSE.txt => home.chunk.88eeebf6c53d4dca.js.LICENSE.txt} (100%) delete mode 100644 public/js/home.chunk.ada2cbf0ec3271bd.js diff --git a/public/js/home.chunk.88eeebf6c53d4dca.js b/public/js/home.chunk.88eeebf6c53d4dca.js new file mode 100644 index 0000000000000000000000000000000000000000..1f6666262bbfa8b8cd1b892145acde29e1e0fa55 GIT binary patch literal 243768 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�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 diff --git a/public/js/manifest.js b/public/js/manifest.js index de48f35bf8f9b085ba3d10d64a58b001904be26e..7e718244da63b48e887e0c55f9477f66b0ded65a 100644 GIT binary patch delta 29 kcmZ1`zf69E8>@hYMQUnlQkq$^sd0))N^;`nFxCJr0FL>KKN}^G6Qkp?(vayl5VN%NGFxCJr0FX)v3;+NC diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 5162e3d9600a6bfa0576e8587bebea95b11e3e69..5a2fbe031e14a9659da9ded1c10cc4e775b7b7cb 100644 GIT binary patch delta 147 zcmeyZ@mpg9BZq>ynSqf>qLG1#S+a3rN}5Tcv2jwGSz?;0fkAR|nuXb9MGo;;3yaj$ zRG@IOsd0))N^+uJR@!nrlafr$EiF@$5)+dx h&CD$=j4ezJj1A2V%oB}MER)SACoqX@Ucqsh8vv@KECv7o From 66f6640072a45d077c76d79a45f610fc27c1a44e Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 16 Feb 2024 03:08:50 -0700 Subject: [PATCH 13/14] Bump version to 0.11.12 --- CHANGELOG.md | 6 ++++-- config/pixelfed.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00a1b6677..cbbea5c31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Release Notes -## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.11...dev) +## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.12...dev) +- ([](https://github.com/pixelfed/pixelfed/commit/)) + +## [v0.11.12 (2024-02-16)](https://github.com/pixelfed/pixelfed/compare/v0.11.11...v0.11.12) ### Features - Autospam Live Filters - block remote activities based on comma separated keywords ([40b45b2a](https://github.com/pixelfed/pixelfed/commit/40b45b2a)) @@ -13,7 +16,6 @@ - Update Federation, use proper Content-Type headers for following/follower collections ([fb0bb9a3](https://github.com/pixelfed/pixelfed/commit/fb0bb9a3)) - Update ActivityPubFetchService, enforce stricter Content-Type validation ([1232cfc8](https://github.com/pixelfed/pixelfed/commit/1232cfc8)) - Update status view, fix unlisted/private scope bug ([0f3ca194](https://github.com/pixelfed/pixelfed/commit/0f3ca194)) -- ([](https://github.com/pixelfed/pixelfed/commit/)) ## [v0.11.11 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.10...v0.11.11) diff --git a/config/pixelfed.php b/config/pixelfed.php index 9ed7fc616..50164218c 100644 --- a/config/pixelfed.php +++ b/config/pixelfed.php @@ -23,7 +23,7 @@ return [ | This value is the version of your Pixelfed instance. | */ - 'version' => '0.11.11', + 'version' => '0.11.12', /* |-------------------------------------------------------------------------- From d835e0adaaa16db3bfb0560851f917574e78fb91 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Fri, 16 Feb 2024 06:57:04 -0700 Subject: [PATCH 14/14] Update Inbox, cast live filters to lowercase --- app/Util/ActivityPub/Helpers.php | 4 ++-- app/Util/ActivityPub/Inbox.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php index e25fd93b7..bcf4f359c 100644 --- a/app/Util/ActivityPub/Helpers.php +++ b/app/Util/ActivityPub/Helpers.php @@ -321,11 +321,11 @@ class Helpers { $filters = array_map('trim', explode(',', $filters)); $content = $res['content']; foreach($filters as $filter) { - $filter = trim($filter); + $filter = trim(strtolower($filter)); if(!$filter || !strlen($filter)) { continue; } - if(str_contains($content, $filter)) { + if(str_contains(strtolower($content), $filter)) { return; } } diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index b6ae80893..62ab3be75 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -203,11 +203,11 @@ class Inbox $filters = array_map('trim', explode(',', $filters)); $content = $activity['content']; foreach($filters as $filter) { - $filter = trim($filter); + $filter = trim(strtolower($filter)); if(!$filter || !strlen($filter)) { continue; } - if(str_contains($content, $filter)) { + if(str_contains(strtolower($content), $filter)) { return; } }