From 5cea5aab3c18965a8bbe74a95c2283a3385f37ab Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 04:56:22 -0700 Subject: [PATCH 01/48] Add Domain Blocks --- app/Models/UserDomainBlock.php | 21 ++++++++++++++ config/instance.php | 3 +- ...052413_create_user_domain_blocks_table.php | 29 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 app/Models/UserDomainBlock.php create mode 100644 database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php diff --git a/app/Models/UserDomainBlock.php b/app/Models/UserDomainBlock.php new file mode 100644 index 000000000..900e026f2 --- /dev/null +++ b/app/Models/UserDomainBlock.php @@ -0,0 +1,21 @@ +belongsTo(Profile::class, 'profile_id'); + } +} diff --git a/config/instance.php b/config/instance.php index 5161ecb80..6357afe63 100644 --- a/config/instance.php +++ b/config/instance.php @@ -110,7 +110,8 @@ return [ 'user_filters' => [ 'max_user_blocks' => env('PF_MAX_USER_BLOCKS', 50), - 'max_user_mutes' => env('PF_MAX_USER_MUTES', 50) + 'max_user_mutes' => env('PF_MAX_USER_MUTES', 50), + 'max_domain_blocks' => env('PF_MAX_DOMAIN_BLOCKS', 50), ], 'reports' => [ diff --git a/database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php b/database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php new file mode 100644 index 000000000..4cacbfcae --- /dev/null +++ b/database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php @@ -0,0 +1,29 @@ +id(); + $table->unsignedBigInteger('profile_id')->index(); + $table->string('domain'); + $table->unique(['profile_id', 'domain'], 'user_domain_blocks_by_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_domain_blocks'); + } +}; From 2136ffe3d8c73cac48f83f7e416b60a6602a8f49 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 05:30:52 -0700 Subject: [PATCH 02/48] Add localization --- resources/lang/en/profile.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/en/profile.php b/resources/lang/en/profile.php index 7851852a2..3206a94bc 100644 --- a/resources/lang/en/profile.php +++ b/resources/lang/en/profile.php @@ -12,4 +12,6 @@ return [ 'status.disabled.header' => 'Profile Unavailable', 'status.disabled.body' => 'Sorry, this profile is not available at the moment. Please try again shortly.', + + 'block.domain.max' => 'Max limit of domain blocks reached! You can only block :max domains at a time. Ask your admin to adjust this limit.' ]; From 2438324369edc1e0bef831015a2ed2f60c5a1e1d Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 05:32:21 -0700 Subject: [PATCH 03/48] Add template-vue settings blade view --- .../views/settings/template-vue.blade.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 resources/views/settings/template-vue.blade.php diff --git a/resources/views/settings/template-vue.blade.php b/resources/views/settings/template-vue.blade.php new file mode 100644 index 000000000..adcbb34c2 --- /dev/null +++ b/resources/views/settings/template-vue.blade.php @@ -0,0 +1,37 @@ +@extends('layouts.app') + +@section('content') +@if (session('status')) +
+ {{ session('status') }} +
+@endif +@if ($errors->any()) +
+ @foreach($errors->all() as $error) +

{{ $error }}

+ @endforeach +
+@endif +@if (session('error')) +
+ {{ session('error') }} +
+@endif + +
+
+
+
+
+ @include('settings.partial.sidebar') +
+ @yield('section') +
+
+
+
+
+
+ +@endsection From cef451e588c781df4e565a5b17613a443c4f0c15 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 05:36:59 -0700 Subject: [PATCH 04/48] Update routes --- resources/views/settings/privacy.blade.php | 5 +- .../views/settings/privacy/blocked.blade.php | 68 +++++++++---------- .../views/settings/privacy/muted.blade.php | 62 ++++++++--------- routes/web.php | 1 + 4 files changed, 64 insertions(+), 72 deletions(-) diff --git a/resources/views/settings/privacy.blade.php b/resources/views/settings/privacy.blade.php index 57f83c664..a1212141a 100644 --- a/resources/views/settings/privacy.blade.php +++ b/resources/views/settings/privacy.blade.php @@ -8,8 +8,9 @@
diff --git a/resources/views/settings/privacy/blocked.blade.php b/resources/views/settings/privacy/blocked.blade.php index e3b2eab2b..8906eae7d 100644 --- a/resources/views/settings/privacy/blocked.blade.php +++ b/resources/views/settings/privacy/blocked.blade.php @@ -2,40 +2,36 @@ @section('section') -
-

Blocked Users

-
-
- - @if($users->count() > 0) -
    - @foreach($users as $user) -
  • -
    - {{$user->username}} - - - @csrf - - - - -
    -
  • - @endforeach -
-
- {{$users->links()}} -
- @else -

You are not blocking any accounts.

- @endif +
+
+

+

Blocked Accounts

+
+
+
-@endsection \ No newline at end of file +@if($users->count() > 0) +
+ @foreach($users as $user) +
+
+ {{$user->username}} + +
+ @csrf + + +
+
+
+
+ @endforeach +
+
+ {{$users->links()}} +
+@else +

You are not blocking any accounts.

+@endif + +@endsection diff --git a/resources/views/settings/privacy/muted.blade.php b/resources/views/settings/privacy/muted.blade.php index a13b9b716..152b8d641 100644 --- a/resources/views/settings/privacy/muted.blade.php +++ b/resources/views/settings/privacy/muted.blade.php @@ -1,41 +1,35 @@ @extends('settings.template') @section('section') - -
-

Muted Users

-
-
- - @if($users->count() > 0) -
    +
    +
    +

    +

    Muted Accounts

    +
    +
    +
    +@if($users->count() > 0) +
    @foreach($users as $user) -
  • -
    - {{$user->username}} - -
    - @csrf - - -
    -
    -
    -
  • +
    +
    + {{$user->username}} + +
    + @csrf + + +
    +
    +
    +
    @endforeach -
-
+
+
{{$users->links()}} -
- @else -

You are not muting any accounts.

- @endif + +@else +

You are not muting any accounts.

+@endif -@endsection \ No newline at end of file +@endsection diff --git a/routes/web.php b/routes/web.php index b823b8729..b8149a605 100644 --- a/routes/web.php +++ b/routes/web.php @@ -489,6 +489,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact 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'); From 28da44beecd258c6e4f96a7c073b7ba374ab3723 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 05:42:56 -0700 Subject: [PATCH 05/48] Update PrivacySettings, add domainBlocks --- .../Controllers/Settings/PrivacySettings.php | 45 +++++-------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/Settings/PrivacySettings.php b/app/Http/Controllers/Settings/PrivacySettings.php index 9a5febe83..bd2222d48 100644 --- a/app/Http/Controllers/Settings/PrivacySettings.php +++ b/app/Http/Controllers/Settings/PrivacySettings.php @@ -14,6 +14,7 @@ use App\Util\Lexer\PrettyNumber; use App\Util\ActivityPub\Helpers; use Auth, Cache, DB; use Illuminate\Http\Request; +use App\Models\UserDomainBlock; trait PrivacySettings { @@ -149,47 +150,25 @@ trait PrivacySettings public function blockedInstances() { - $pid = Auth::user()->profile->id; - $filters = UserFilter::whereUserId($pid) - ->whereFilterableType('App\Instance') - ->whereFilterType('block') - ->orderByDesc('id') - ->paginate(10); - return view('settings.privacy.blocked-instances', compact('filters')); + // deprecated + abort(404); + } + + public function domainBlocks() + { + return view('settings.privacy.domain-blocks'); } public function blockedInstanceStore(Request $request) { - $this->validate($request, [ - 'domain' => 'required|url|min:1|max:120' - ]); - $domain = $request->input('domain'); - if(Helpers::validateUrl($domain) == false) { - return abort(400, 'Invalid domain'); - } - $domain = parse_url($domain, PHP_URL_HOST); - $instance = Instance::firstOrCreate(['domain' => $domain]); - $filter = new UserFilter; - $filter->user_id = Auth::user()->profile->id; - $filter->filterable_id = $instance->id; - $filter->filterable_type = 'App\Instance'; - $filter->filter_type = 'block'; - $filter->save(); - return response()->json(['msg' => 200]); + // deprecated + abort(404); } public function blockedInstanceUnblock(Request $request) { - $this->validate($request, [ - 'id' => 'required|integer|min:1' - ]); - $pid = Auth::user()->profile->id; - - $filter = UserFilter::whereFilterableType('App\Instance') - ->whereUserId($pid) - ->findOrFail($request->input('id')); - $filter->delete(); - return redirect(route('settings.privacy.blocked-instances')); + // deprecated + abort(404); } public function blockedKeywords() From 63c9ebe81f3ca1ae347575f83cd83ae20b3e1fa2 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 05:43:37 -0700 Subject: [PATCH 06/48] Update api routes --- routes/api.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/api.php b/routes/api.php index 10a4363c2..af40e27bc 100644 --- a/routes/api.php +++ b/routes/api.php @@ -51,9 +51,9 @@ Route::group(['prefix' => 'api'], function() use($middleware) { Route::get('blocks', 'Api\ApiV1Controller@accountBlocks')->middleware($middleware); Route::get('conversations', 'Api\ApiV1Controller@conversations')->middleware($middleware); Route::get('custom_emojis', 'Api\ApiV1Controller@customEmojis'); - Route::get('domain_blocks', 'Api\ApiV1Controller@accountDomainBlocks')->middleware($middleware); - Route::post('domain_blocks', 'Api\ApiV1Controller@accountDomainBlocks')->middleware($middleware); - Route::delete('domain_blocks', 'Api\ApiV1Controller@accountDomainBlocks')->middleware($middleware); + Route::get('domain_blocks', 'Api\V1\DomainBlockController@index')->middleware($middleware); + Route::post('domain_blocks', 'Api\V1\DomainBlockController@store')->middleware($middleware); + Route::delete('domain_blocks', 'Api\V1\DomainBlockController@delete')->middleware($middleware); Route::get('endorsements', 'Api\ApiV1Controller@accountEndorsements')->middleware($middleware); Route::get('favourites', 'Api\ApiV1Controller@accountFavourites')->middleware($middleware); Route::get('filters', 'Api\ApiV1Controller@accountFilters')->middleware($middleware); From 28da107f66b16c93fc2d983b3979571f1cef5526 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 05:56:37 -0700 Subject: [PATCH 07/48] Add DomainBlockController --- .../Api/V1/DomainBlockController.php | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 app/Http/Controllers/Api/V1/DomainBlockController.php diff --git a/app/Http/Controllers/Api/V1/DomainBlockController.php b/app/Http/Controllers/Api/V1/DomainBlockController.php new file mode 100644 index 000000000..5a6178e39 --- /dev/null +++ b/app/Http/Controllers/Api/V1/DomainBlockController.php @@ -0,0 +1,96 @@ +json($res, $code, $headers, JSON_UNESCAPED_SLASHES); + } + + public function index(Request $request) + { + abort_unless($request->user(), 403); + $this->validate($request, [ + 'limit' => 'sometimes|integer|min:1|max:200' + ]); + $limit = $request->input('limit', 100); + $id = $request->user()->profile_id; + $filters = UserDomainBlock::whereProfileId($id)->orderByDesc('id')->cursorPaginate($limit); + $links = null; + $headers = []; + + if($filters->nextCursor()) { + $links .= '<'.$filters->nextPageUrl().'&limit='.$limit.'>; rel="next"'; + } + + if($filters->previousCursor()) { + if($links != null) { + $links .= ', '; + } + $links .= '<'.$filters->previousPageUrl().'&limit='.$limit.'>; rel="prev"'; + } + + if($links) { + $headers = ['Link' => $links]; + } + return $this->json($filters->pluck('domain'), 200, $headers); + } + + public function store(Request $request) + { + abort_unless($request->user(), 403); + + $this->validate($request, [ + 'domain' => 'required|active_url|min:1|max:120' + ]); + + $pid = $request->user()->profile_id; + + $domain = trim($request->input('domain')); + + if(Helpers::validateUrl($domain) == false) { + return abort(500, 'Invalid domain or already blocked by server admins'); + } + + $domain = parse_url($domain, PHP_URL_HOST); + + abort_if(config_cache('pixelfed.domain.app') == $domain, 400, 'Cannot ban your own server'); + + $existingCount = UserDomainBlock::whereProfileId($pid)->count(); + $maxLimit = config('instance.user_filters.max_domain_blocks'); + $errorMsg = __('profile.block.domain.max', ['max' => $maxLimit]); + + abort_if($existingCount >= $maxLimit, 400, $errorMsg); + + $block = UserDomainBlock::updateOrInsert([ + 'profile_id' => $pid, + 'domain' => $domain + ]); + + return $this->json([]); + } + + public function delete(Request $request) + { + abort_unless($request->user(), 403); + + $this->validate($request, [ + 'domain' => 'required|min:1|max:120' + ]); + + $pid = $request->user()->profile_id; + + $domain = trim($request->input('domain')); + + $filters = UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->delete(); + + return $this->json([]); + } +} From e5d789e0ab51ff55d64d559829f4fd6999946bda Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 06:01:43 -0700 Subject: [PATCH 08/48] Add domain blocks setting view --- .../settings/privacy/domain-blocks.blade.php | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 resources/views/settings/privacy/domain-blocks.blade.php diff --git a/resources/views/settings/privacy/domain-blocks.blade.php b/resources/views/settings/privacy/domain-blocks.blade.php new file mode 100644 index 000000000..d93e0b58e --- /dev/null +++ b/resources/views/settings/privacy/domain-blocks.blade.php @@ -0,0 +1,272 @@ +@extends('settings.template-vue') + +@section('section') +
+
+
+

+

Domain Blocks

+
+
+ +

You can block entire domains, this prevents users on that instance from interacting with your content and from you seeing content from that domain on public feeds.

+ +
+ +
+ +
+ +
+
+
+
+ +
+ +
+
+
+ + +
+
+
+
+ + + + + + +
+
+
+ + + +
+
+

You are not blocking any domains.

+
+
+
+@endsection + +@push('scripts') + +@endpush From d3f032b2ec1cbef33066ec131961eb31dc4a76b6 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 06:11:13 -0700 Subject: [PATCH 09/48] Update FollowerService, add quickCheck to follows method for non cold-boot checks --- app/Services/FollowerService.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Services/FollowerService.php b/app/Services/FollowerService.php index 8b5eeced9..dcb7a1158 100644 --- a/app/Services/FollowerService.php +++ b/app/Services/FollowerService.php @@ -89,12 +89,16 @@ class FollowerService return Redis::zCard(self::FOLLOWING_KEY . $id); } - public static function follows(string $actor, string $target) + public static function follows(string $actor, string $target, $quickCheck = false) { if($actor == $target) { return false; } + if($quickCheck) { + return (bool) Redis::zScore(self::FOLLOWERS_KEY . $target, $actor); + } + if(self::followerCount($target, false) && self::followingCount($actor, false)) { self::cacheSyncCheck($target, 'followers'); return (bool) Redis::zScore(self::FOLLOWERS_KEY . $target, $actor); From 60e053c93671d0e790cde02aa1c51bc011cca17a Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 06:22:56 -0700 Subject: [PATCH 10/48] Update ApiV1Controller, update discoverAccountsPopular method --- app/Http/Controllers/Api/ApiV1Controller.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index f5e1202c0..e429a8681 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -3619,25 +3619,31 @@ class ApiV1Controller extends Controller $pid = $request->user()->profile_id; - $ids = Cache::remember('api:v1.1:discover:accounts:popular', 86400, function() { + $ids = Cache::remember('api:v1.1:discover:accounts:popular', 3600, function() { return DB::table('profiles') ->where('is_private', false) ->whereNull('status') ->orderByDesc('profiles.followers_count') - ->limit(20) + ->limit(30) ->get(); }); - + $filters = UserFilterService::filters($pid); $ids = $ids->map(function($profile) { return AccountService::get($profile->id, true); }) ->filter(function($profile) use($pid) { - return $profile && isset($profile['id']); + return $profile && isset($profile['id'], $profile['locked']) && !$profile['locked']; }) ->filter(function($profile) use($pid) { return $profile['id'] != $pid; }) - ->take(6) + ->filter(function($profile) use($pid) { + return !FollowerService::follows($pid, $profile['id'], true); + }) + ->filter(function($profile) use($filters) { + return !in_array($profile['id'], $filters); + }) + ->take(16) ->values(); return $this->json($ids); From 7016d195202e335253ab1143c1212434e23193ee Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Dec 2023 17:04:16 -0700 Subject: [PATCH 11/48] Update Privacy Settings view, change button to Blocked Domains and add l10n --- resources/lang/en/profile.php | 6 +++++- resources/views/settings/privacy.blade.php | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/lang/en/profile.php b/resources/lang/en/profile.php index 3206a94bc..04a2bcd90 100644 --- a/resources/lang/en/profile.php +++ b/resources/lang/en/profile.php @@ -13,5 +13,9 @@ return [ 'status.disabled.header' => 'Profile Unavailable', 'status.disabled.body' => 'Sorry, this profile is not available at the moment. Please try again shortly.', - 'block.domain.max' => 'Max limit of domain blocks reached! You can only block :max domains at a time. Ask your admin to adjust this limit.' + 'block.domain.max' => 'Max limit of domain blocks reached! You can only block :max domains at a time. Ask your admin to adjust this limit.', + + 'mutedAccounts' => 'Muted Accounts', + 'blockedAccounts' => 'Blocked Accounts', + 'blockedDomains' => 'Blocked Domains', ]; diff --git a/resources/views/settings/privacy.blade.php b/resources/views/settings/privacy.blade.php index a1212141a..369ddbbb4 100644 --- a/resources/views/settings/privacy.blade.php +++ b/resources/views/settings/privacy.blade.php @@ -8,9 +8,9 @@
From e7c08fbbb2ed55acc04d2bd47a510c618319490e Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:32:48 -0700 Subject: [PATCH 12/48] Update AccountService, add blocksDomain method --- app/Services/AccountService.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Services/AccountService.php b/app/Services/AccountService.php index fa1613cae..98e878845 100644 --- a/app/Services/AccountService.php +++ b/app/Services/AccountService.php @@ -7,6 +7,7 @@ use App\Profile; use App\Status; use App\User; use App\UserSetting; +use App\Models\UserDomainBlock; use App\Transformer\Api\AccountTransformer; use League\Fractal; use League\Fractal\Serializer\ArraySerializer; @@ -234,4 +235,13 @@ class AccountService } return; } + + public static function blocksDomain($pid, $domain = false) + { + if(!$domain) { + return; + } + + return UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->exists(); + } } From a7f96d81949af8029edcbbaabc50ad63030485f9 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:34:53 -0700 Subject: [PATCH 13/48] Update Inbox, add user domain blocks to Direct Message handler --- app/Util/ActivityPub/Inbox.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index 0dd4722e9..c0ad390b8 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -372,7 +372,11 @@ class Inbox ->whereUsername(array_last(explode('/', $activity['to'][0]))) ->firstOrFail(); - if(in_array($actor->id, $profile->blockedIds()->toArray())) { + if(!$actor || in_array($actor->id, $profile->blockedIds()->toArray())) { + return; + } + + if(AccountService::blocksDomain($profile->id, $actor->domain) == true) { return; } From c89dc45e8d8010282a5605537a665151d6c7790c Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:37:34 -0700 Subject: [PATCH 14/48] Update Inbox, add user domain blocks to Follow handler --- app/Util/ActivityPub/Inbox.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index c0ad390b8..176890e70 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -514,6 +514,10 @@ class Inbox return; } + if(AccountService::blocksDomain($target->id, $actor->domain) == true) { + return; + } + if( Follower::whereProfileId($actor->id) ->whereFollowingId($target->id) From 279fb28e2a8d5749fb590b89ed47ff4c89ea9379 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:42:21 -0700 Subject: [PATCH 15/48] Update Inbox, add user domain blocks to Announce handler --- app/Util/ActivityPub/Inbox.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index 176890e70..6818a8e87 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -589,6 +589,10 @@ class Inbox return; } + if(AccountService::blocksDomain($parent->profile_id, $actor->domain) == true) { + return; + } + $blocks = UserFilterService::blocks($parent->profile_id); if($blocks && in_array($actor->id, $blocks)) { return; From 3fbf8f159ef24ba202cd727916724fe782eaa50d Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:46:09 -0700 Subject: [PATCH 16/48] Update Inbox, add user domain blocks to Accept handler --- app/Util/ActivityPub/Inbox.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index 6818a8e87..15d7d3445 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -646,6 +646,10 @@ class Inbox return; } + if(AccountService::blocksDomain($target->id, $actor->domain) == true) { + return; + } + $request = FollowRequest::whereFollowerId($actor->id) ->whereFollowingId($target->id) ->whereIsRejected(false) From e32e50da7bc2a5c4d05379854752f3a780990628 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:47:03 -0700 Subject: [PATCH 17/48] Update Inbox, add user domain blocks to Like handler --- app/Util/ActivityPub/Inbox.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index 15d7d3445..defb75fa4 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -775,6 +775,10 @@ class Inbox return; } + if(AccountService::blocksDomain($status->profile_id, $profile->domain) == true) { + return; + } + $blocks = UserFilterService::blocks($status->profile_id); if($blocks && in_array($profile->id, $blocks)) { return; From 491468612f99dc5ae88fde79672767a74f943a8e Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:49:31 -0700 Subject: [PATCH 18/48] Update Inbox, add user domain blocks to Undo handler --- app/Util/ActivityPub/Inbox.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index defb75fa4..09676301f 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -836,6 +836,9 @@ class Inbox if(!$status) { return; } + if(AccountService::blocksDomain($status->profile_id, $profile->domain) == true) { + return; + } FeedRemoveRemotePipeline::dispatch($status->id, $status->profile_id)->onQueue('feed'); Status::whereProfileId($profile->id) ->whereReblogOfId($status->id) @@ -857,6 +860,9 @@ class Inbox if(!$following) { return; } + if(AccountService::blocksDomain($following->id, $profile->domain) == true) { + return; + } Follower::whereProfileId($profile->id) ->whereFollowingId($following->id) ->delete(); @@ -882,6 +888,9 @@ class Inbox if(!$status) { return; } + if(AccountService::blocksDomain($status->profile_id, $profile->domain) == true) { + return; + } Like::whereProfileId($profile->id) ->whereStatusId($status->id) ->forceDelete(); From 8a0ceaf801af614457ae65197d18bb0fe69372d2 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Dec 2023 22:57:53 -0700 Subject: [PATCH 19/48] Update Inbox, add user domain blocks to Story reaction handlers --- app/Util/ActivityPub/Inbox.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index 09676301f..b8bb780c8 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -944,6 +944,10 @@ class Inbox return; } + if(AccountService::blocksDomain($story->profile_id, $profile->domain) == true) { + return; + } + if(!FollowerService::follows($profile->id, $story->profile_id)) { return; } @@ -1014,6 +1018,10 @@ class Inbox $actorProfile = Helpers::profileFetch($actor); + if(AccountService::blocksDomain($targetProfile->id, $actorProfile->domain) == true) { + return; + } + if(!FollowerService::follows($actorProfile->id, $targetProfile->id)) { return; } @@ -1132,6 +1140,11 @@ class Inbox $actorProfile = Helpers::profileFetch($actor); + + if(AccountService::blocksDomain($targetProfile->id, $actorProfile->domain) == true) { + return; + } + if(!FollowerService::follows($actorProfile->id, $targetProfile->id)) { return; } From 819e7d3b328e8a14361a4df78976d0cb41f27ce7 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 01:10:48 -0700 Subject: [PATCH 20/48] Add FeedRemoveDomainPipeline --- .../FeedRemoveDomainPipeline.php | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php diff --git a/app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php b/app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php new file mode 100644 index 000000000..2168ee054 --- /dev/null +++ b/app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php @@ -0,0 +1,92 @@ +pid . ':d-' . $this->domain; + } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [(new WithoutOverlapping("hts:feed:remove:domain:{$this->pid}:d-{$this->domain}"))->shared()->dontRelease()]; + } + + /** + * Create a new job instance. + */ + public function __construct($pid, $domain) + { + $this->pid = $pid; + $this->domain = $domain; + } + + /** + * Execute the job. + */ + public function handle(): void + { + if(!config('exp.cached_home_timeline')) { + return; + } + if(!$this->pid || !$this->domain) { + return; + } + $domain = strtolower($this->domain); + $pid = $this->pid; + $posts = HomeTimelineService::get($pid, '0', '-1'); + + foreach($posts as $post) { + $status = StatusService::get($post, false); + if(!$status || !isset($status['url'])) { + HomeTimelineService::rem($pid, $post); + continue; + } + $host = strtolower(parse_url($status['url'], PHP_URL_HOST)); + if($host === strtolower(config('pixelfed.domain.app')) || !$host) { + continue; + } + if($host === $domain) { + HomeTimelineService::rem($pid, $status['id']); + } + } + } +} From 5c1591fdffbdb1e842d7e2049be6ad967f485864 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 01:20:14 -0700 Subject: [PATCH 21/48] Add job batches migration --- ..._12_19_081928_create_job_batches_table.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 database/migrations/2023_12_19_081928_create_job_batches_table.php diff --git a/database/migrations/2023_12_19_081928_create_job_batches_table.php b/database/migrations/2023_12_19_081928_create_job_batches_table.php new file mode 100644 index 000000000..50e38c20f --- /dev/null +++ b/database/migrations/2023_12_19_081928_create_job_batches_table.php @@ -0,0 +1,35 @@ +string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('job_batches'); + } +}; From a492a95a0eb4e2a75d84d9da1cc807d8c3d85bb2 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 04:01:41 -0700 Subject: [PATCH 22/48] Update AdminShadowFilter, fix deleted profile bug --- app/Http/Controllers/AdminShadowFilterController.php | 3 ++- app/Models/AdminShadowFilter.php | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/AdminShadowFilterController.php b/app/Http/Controllers/AdminShadowFilterController.php index 461e1d0c2..e181be5c1 100644 --- a/app/Http/Controllers/AdminShadowFilterController.php +++ b/app/Http/Controllers/AdminShadowFilterController.php @@ -19,7 +19,8 @@ class AdminShadowFilterController extends Controller { $filter = $request->input('filter'); $searchQuery = $request->input('q'); - $filters = AdminShadowFilter::when($filter, function($q, $filter) { + $filters = AdminShadowFilter::whereHas('profile') + ->when($filter, function($q, $filter) { if($filter == 'all') { return $q; } else if($filter == 'inactive') { diff --git a/app/Models/AdminShadowFilter.php b/app/Models/AdminShadowFilter.php index f98086f7f..8a163feeb 100644 --- a/app/Models/AdminShadowFilter.php +++ b/app/Models/AdminShadowFilter.php @@ -5,6 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use App\Services\AccountService; +use App\Profile; class AdminShadowFilter extends Model { @@ -24,4 +25,9 @@ class AdminShadowFilter extends Model return; } + + public function profile() + { + return $this->belongsTo(Profile::class, 'item_id'); + } } From 1664a5bc52fc62dc4f5c099cc75c189ccc4b0e27 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 05:46:06 -0700 Subject: [PATCH 23/48] Update FollowerService, add $silent param to remove method to more efficently purge relationships --- app/Services/FollowerService.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/Services/FollowerService.php b/app/Services/FollowerService.php index dcb7a1158..cec8f7068 100644 --- a/app/Services/FollowerService.php +++ b/app/Services/FollowerService.php @@ -35,16 +35,18 @@ class FollowerService Cache::forget('profile:following:' . $actor); } - public static function remove($actor, $target) + public static function remove($actor, $target, $silent = false) { Redis::zrem(self::FOLLOWING_KEY . $actor, $target); Redis::zrem(self::FOLLOWERS_KEY . $target, $actor); - Cache::forget('pf:services:follower:audience:' . $actor); - Cache::forget('pf:services:follower:audience:' . $target); - AccountService::del($actor); - AccountService::del($target); - RelationshipService::refresh($actor, $target); - Cache::forget('profile:following:' . $actor); + if($silent !== true) { + AccountService::del($actor); + AccountService::del($target); + RelationshipService::refresh($actor, $target); + Cache::forget('profile:following:' . $actor); + } else { + RelationshipService::forget($actor, $target); + } } public static function followers($id, $start = 0, $stop = 10) From 484a377a449c7d1c59e304c158e7c1514ec60649 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 06:02:58 -0700 Subject: [PATCH 24/48] Add ProfilePurgeFollowersByDomain pipeline job --- .../ProfilePurgeFollowersByDomain.php | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 app/Jobs/ProfilePipeline/ProfilePurgeFollowersByDomain.php diff --git a/app/Jobs/ProfilePipeline/ProfilePurgeFollowersByDomain.php b/app/Jobs/ProfilePipeline/ProfilePurgeFollowersByDomain.php new file mode 100644 index 000000000..24fcdc832 --- /dev/null +++ b/app/Jobs/ProfilePipeline/ProfilePurgeFollowersByDomain.php @@ -0,0 +1,119 @@ +pid . ':d-' . $this->domain; + } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [(new WithoutOverlapping("followers:v1:purge-by-domain:{$this->pid}:d-{$this->domain}"))->shared()->dontRelease()]; + } + + /** + * Create a new job instance. + */ + public function __construct($pid, $domain) + { + $this->pid = $pid; + $this->domain = $domain; + } + + /** + * Execute the job. + */ + public function handle(): void + { + if ($this->batch()->cancelled()) { + return; + } + + $pid = $this->pid; + $domain = $this->domain; + + $query = 'SELECT f.* + FROM followers f + JOIN profiles p ON p.id = f.profile_id OR p.id = f.following_id + WHERE (f.profile_id = ? OR f.following_id = ?) + AND p.domain = ?;'; + $params = [$pid, $pid, $domain]; + + foreach(DB::cursor($query, $params) as $n) { + if(!$n || !$n->id) { + continue; + } + $follower = Follower::find($n->id); + if($follower->following_id == $pid && $follower->profile_id) { + FollowerService::remove($follower->profile_id, $pid, true); + $follower->delete(); + } else if ($follower->profile_id == $pid && $follower->following_id) { + FollowerService::remove($follower->following_id, $pid, true); + $follower->delete(); + } + } + + $profile = Profile::find($pid); + + $followerCount = DB::table('profiles') + ->join('followers', 'profiles.id', '=', 'followers.following_id') + ->where('followers.following_id', $pid) + ->count(); + + $followingCount = DB::table('profiles') + ->join('followers', 'profiles.id', '=', 'followers.following_id') + ->where('followers.profile_id', $pid) + ->count(); + + $profile->followers_count = $followerCount; + $profile->following_count = $followingCount; + $profile->save(); + + AccountService::del($profile->id); + } +} From 9d621108b06d1470369f647d13f46c1af174d0c4 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 06:03:36 -0700 Subject: [PATCH 25/48] Add ProfilePurgeNotificationsByDomain pipeline job --- .../ProfilePurgeNotificationsByDomain.php | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 app/Jobs/ProfilePipeline/ProfilePurgeNotificationsByDomain.php diff --git a/app/Jobs/ProfilePipeline/ProfilePurgeNotificationsByDomain.php b/app/Jobs/ProfilePipeline/ProfilePurgeNotificationsByDomain.php new file mode 100644 index 000000000..ea5a45e4a --- /dev/null +++ b/app/Jobs/ProfilePipeline/ProfilePurgeNotificationsByDomain.php @@ -0,0 +1,91 @@ +pid . ':d-' . $this->domain; + } + + /** + * Get the middleware the job should pass through. + * + * @return array + */ + public function middleware(): array + { + return [(new WithoutOverlapping("notify:v1:purge-by-domain:{$this->pid}:d-{$this->domain}"))->shared()->dontRelease()]; + } + + /** + * Create a new job instance. + */ + public function __construct($pid, $domain) + { + $this->pid = $pid; + $this->domain = $domain; + } + + /** + * Execute the job. + */ + public function handle(): void + { + if ($this->batch()->cancelled()) { + return; + } + + $pid = $this->pid; + $domain = $this->domain; + + $query = 'SELECT notifications.* + FROM profiles + JOIN notifications on profiles.id = notifications.actor_id + WHERE notifications.profile_id = ? + AND profiles.domain = ?'; + $params = [$pid, $domain]; + + foreach(DB::cursor($query, $params) as $n) { + if(!$n || !$n->id) { + continue; + } + Notification::where('id', $n->id)->delete(); + NotificationService::del($pid, $n->id); + } + } +} From 54adbeb0592d4a3b1ad1d66f48227ca09f0c8d7b Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 06:04:03 -0700 Subject: [PATCH 26/48] Update FeedRemoveDomainPipeline, make batchable --- app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php b/app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php index 2168ee054..018ea3794 100644 --- a/app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php +++ b/app/Jobs/HomeFeedPipeline/FeedRemoveDomainPipeline.php @@ -2,6 +2,7 @@ namespace App\Jobs\HomeFeedPipeline; +use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; @@ -15,7 +16,7 @@ use App\Services\HomeTimelineService; class FeedRemoveDomainPipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $pid; protected $domain; @@ -67,6 +68,11 @@ class FeedRemoveDomainPipeline implements ShouldQueue, ShouldBeUniqueUntilProces if(!config('exp.cached_home_timeline')) { return; } + + if ($this->batch()->cancelled()) { + return; + } + if(!$this->pid || !$this->domain) { return; } From 87bba03d23b60d7e1940b26ac0f633a0cfaaeb5d Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 06:24:51 -0700 Subject: [PATCH 27/48] Update DomainBlockController, dispatch jobies --- .../Api/V1/DomainBlockController.php | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Api/V1/DomainBlockController.php b/app/Http/Controllers/Api/V1/DomainBlockController.php index 5a6178e39..53a209ed9 100644 --- a/app/Http/Controllers/Api/V1/DomainBlockController.php +++ b/app/Http/Controllers/Api/V1/DomainBlockController.php @@ -6,6 +6,12 @@ use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\UserDomainBlock; use App\Util\ActivityPub\Helpers; +use Illuminate\Bus\Batch; +use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Cache; +use App\Jobs\HomeFeedPipeline\FeedRemoveDomainPipeline; +use App\Jobs\ProfilePipeline\ProfilePurgeNotificationsByDomain; +use App\Jobs\ProfilePipeline\ProfilePurgeFollowersByDomain; class DomainBlockController extends Controller { @@ -59,7 +65,7 @@ class DomainBlockController extends Controller return abort(500, 'Invalid domain or already blocked by server admins'); } - $domain = parse_url($domain, PHP_URL_HOST); + $domain = strtolower(parse_url($domain, PHP_URL_HOST)); abort_if(config_cache('pixelfed.domain.app') == $domain, 400, 'Cannot ban your own server'); @@ -69,11 +75,23 @@ class DomainBlockController extends Controller abort_if($existingCount >= $maxLimit, 400, $errorMsg); - $block = UserDomainBlock::updateOrInsert([ + $block = UserDomainBlock::updateOrCreate([ 'profile_id' => $pid, 'domain' => $domain ]); + if($block->wasRecentlyCreated) { + Bus::batch([ + [ + new FeedRemoveDomainPipeline($pid, $domain), + new ProfilePurgeNotificationsByDomain($pid, $domain), + new ProfilePurgeFollowersByDomain($pid, $domain) + ] + ])->allowFailures()->onQueue('feed')->dispatch(); + + Cache::forget('profile:following:' . $pid); + } + return $this->json([]); } @@ -87,7 +105,7 @@ class DomainBlockController extends Controller $pid = $request->user()->profile_id; - $domain = trim($request->input('domain')); + $domain = strtolower(trim($request->input('domain'))); $filters = UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->delete(); From 795132df1830dd99d5cb0d09de121ddbb3fbad39 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 19 Dec 2023 06:25:39 -0700 Subject: [PATCH 28/48] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b545a30..2f75a72a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,8 @@ - Update Inbox, improve tombstone query efficiency ([759a4393](https://github.com/pixelfed/pixelfed/commit/759a4393)) - Update AccountService, add setLastActive method ([ebbd98e7](https://github.com/pixelfed/pixelfed/commit/ebbd98e7)) - Update ApiV1Controller, set last_active_at ([b6419545](https://github.com/pixelfed/pixelfed/commit/b6419545)) +- Update AdminShadowFilter, fix deleted profile bug ([a492a95a](https://github.com/pixelfed/pixelfed/commit/a492a95a)) +- Update FollowerService, add $silent param to remove method to more efficently purge relationships ([1664a5bc](https://github.com/pixelfed/pixelfed/commit/1664a5bc)) - ([](https://github.com/pixelfed/pixelfed/commit/)) ## [v0.11.9 (2023-08-21)](https://github.com/pixelfed/pixelfed/compare/v0.11.8...v0.11.9) From dd16189fc81ab0b0501fa61894d12985089b7316 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Wed, 20 Dec 2023 23:10:57 -0700 Subject: [PATCH 29/48] Update ImageResize job, add more logging --- app/Jobs/ImageOptimizePipeline/ImageResize.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Jobs/ImageOptimizePipeline/ImageResize.php b/app/Jobs/ImageOptimizePipeline/ImageResize.php index 9bb896a40..c1b4ea7f0 100644 --- a/app/Jobs/ImageOptimizePipeline/ImageResize.php +++ b/app/Jobs/ImageOptimizePipeline/ImageResize.php @@ -9,6 +9,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Log; class ImageResize implements ShouldQueue { @@ -46,6 +47,7 @@ class ImageResize implements ShouldQueue } $path = storage_path('app/'.$media->media_path); if (!is_file($path) || $media->skip_optimize) { + Log::info('Tried to optimize media that does not exist or is not readable. ' . $path); return; } @@ -57,6 +59,7 @@ class ImageResize implements ShouldQueue $img = new Image(); $img->resizeImage($media); } catch (Exception $e) { + Log::error($e); } ImageThumbnail::dispatch($media)->onQueue('mmo'); From ae1db1e3ab079fc6c8965df6761031e3b4a49baf Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Wed, 20 Dec 2023 23:17:27 -0700 Subject: [PATCH 30/48] Update migration --- .../2023_12_16_052413_create_user_domain_blocks_table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php b/database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php index 4cacbfcae..16f8f3fb2 100644 --- a/database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php +++ b/database/migrations/2023_12_16_052413_create_user_domain_blocks_table.php @@ -14,7 +14,7 @@ return new class extends Migration Schema::create('user_domain_blocks', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('profile_id')->index(); - $table->string('domain'); + $table->string('domain')->index(); $table->unique(['profile_id', 'domain'], 'user_domain_blocks_by_id'); }); } From 0455dd1996269eb4450f9f778f3ae4159e719c5a Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Wed, 20 Dec 2023 23:55:26 -0700 Subject: [PATCH 31/48] Update UserFilter model, add user relation --- app/UserFilter.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/UserFilter.php b/app/UserFilter.php index b0af2d777..dfa0d4662 100644 --- a/app/UserFilter.php +++ b/app/UserFilter.php @@ -33,4 +33,9 @@ class UserFilter extends Model { return $this->belongsTo(Instance::class, 'filterable_id'); } + + public function user() + { + return $this->belongsTo(Profile::class, 'user_id'); + } } From 29aa87c282f141a788a5b95b7c8312a12966c14e Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 00:17:20 -0700 Subject: [PATCH 32/48] Update HomeFeedPipeline jobs, add domain block filtering --- .../HomeFeedPipeline/FeedInsertPipeline.php | 22 +++++++++++++++++-- .../FeedInsertRemotePipeline.php | 22 +++++++++++++++++-- .../HashtagInsertFanoutPipeline.php | 18 +++++++++++++-- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/app/Jobs/HomeFeedPipeline/FeedInsertPipeline.php b/app/Jobs/HomeFeedPipeline/FeedInsertPipeline.php index 19a546e83..4237a7b1a 100644 --- a/app/Jobs/HomeFeedPipeline/FeedInsertPipeline.php +++ b/app/Jobs/HomeFeedPipeline/FeedInsertPipeline.php @@ -11,6 +11,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing; use App\UserFilter; +use App\Models\UserDomainBlock; use App\Services\FollowerService; use App\Services\HomeTimelineService; use App\Services\StatusService; @@ -69,7 +70,7 @@ class FeedInsertPipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing $sid = $this->sid; $status = StatusService::get($sid, false); - if(!$status || !isset($status['account']) || !isset($status['account']['id'])) { + if(!$status || !isset($status['account']) || !isset($status['account']['id'], $status['url'])) { return; } @@ -85,7 +86,24 @@ class FeedInsertPipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing return; } - $skipIds = UserFilter::whereFilterableType('App\Profile')->whereFilterableId($status['account']['id'])->whereIn('filter_type', ['mute', 'block'])->pluck('user_id')->toArray(); + $domain = strtolower(parse_url($status['url'], PHP_URL_HOST)); + $skipIds = []; + + if(strtolower(config('pixelfed.domain.app')) !== $domain) { + $skipIds = UserDomainBlock::where('domain', $domain)->pluck('profile_id')->toArray(); + } + + $filters = UserFilter::whereFilterableType('App\Profile') + ->whereFilterableId($status['account']['id']) + ->whereIn('filter_type', ['mute', 'block']) + ->pluck('user_id') + ->toArray(); + + if($filters && count($filters)) { + $skipIds = array_merge($skipIds, $filters); + } + + $skipIds = array_unique(array_values($skipIds)); foreach($ids as $id) { if(!in_array($id, $skipIds)) { diff --git a/app/Jobs/HomeFeedPipeline/FeedInsertRemotePipeline.php b/app/Jobs/HomeFeedPipeline/FeedInsertRemotePipeline.php index e24696bd8..6c4ce0c35 100644 --- a/app/Jobs/HomeFeedPipeline/FeedInsertRemotePipeline.php +++ b/app/Jobs/HomeFeedPipeline/FeedInsertRemotePipeline.php @@ -11,6 +11,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing; use App\UserFilter; +use App\Models\UserDomainBlock; use App\Services\FollowerService; use App\Services\HomeTimelineService; use App\Services\StatusService; @@ -69,7 +70,7 @@ class FeedInsertRemotePipeline implements ShouldQueue, ShouldBeUniqueUntilProces $sid = $this->sid; $status = StatusService::get($sid, false); - if(!$status || !isset($status['account']) || !isset($status['account']['id'])) { + if(!$status || !isset($status['account']) || !isset($status['account']['id'], $status['url'])) { return; } @@ -83,7 +84,24 @@ class FeedInsertRemotePipeline implements ShouldQueue, ShouldBeUniqueUntilProces return; } - $skipIds = UserFilter::whereFilterableType('App\Profile')->whereFilterableId($status['account']['id'])->whereIn('filter_type', ['mute', 'block'])->pluck('user_id')->toArray(); + $domain = strtolower(parse_url($status['url'], PHP_URL_HOST)); + $skipIds = []; + + if(strtolower(config('pixelfed.domain.app')) !== $domain) { + $skipIds = UserDomainBlock::where('domain', $domain)->pluck('profile_id')->toArray(); + } + + $filters = UserFilter::whereFilterableType('App\Profile') + ->whereFilterableId($status['account']['id']) + ->whereIn('filter_type', ['mute', 'block']) + ->pluck('user_id') + ->toArray(); + + if($filters && count($filters)) { + $skipIds = array_merge($skipIds, $filters); + } + + $skipIds = array_unique(array_values($skipIds)); foreach($ids as $id) { if(!in_array($id, $skipIds)) { diff --git a/app/Jobs/HomeFeedPipeline/HashtagInsertFanoutPipeline.php b/app/Jobs/HomeFeedPipeline/HashtagInsertFanoutPipeline.php index a200c06e8..eca598e49 100644 --- a/app/Jobs/HomeFeedPipeline/HashtagInsertFanoutPipeline.php +++ b/app/Jobs/HomeFeedPipeline/HashtagInsertFanoutPipeline.php @@ -11,6 +11,7 @@ use Illuminate\Queue\SerializesModels; use App\Hashtag; use App\StatusHashtag; use App\UserFilter; +use App\Models\UserDomainBlock; use App\Services\HashtagFollowService; use App\Services\HomeTimelineService; use App\Services\StatusService; @@ -77,7 +78,7 @@ class HashtagInsertFanoutPipeline implements ShouldQueue, ShouldBeUniqueUntilPro $sid = $hashtag->status_id; $status = StatusService::get($sid, false); - if(!$status || !isset($status['account']) || !isset($status['account']['id'])) { + if(!$status || !isset($status['account']) || !isset($status['account']['id'], $status['url'])) { return; } @@ -85,7 +86,20 @@ class HashtagInsertFanoutPipeline implements ShouldQueue, ShouldBeUniqueUntilPro return; } - $skipIds = UserFilter::whereFilterableType('App\Profile')->whereFilterableId($status['account']['id'])->whereIn('filter_type', ['mute', 'block'])->pluck('user_id')->toArray(); + $domain = strtolower(parse_url($status['url'], PHP_URL_HOST)); + $skipIds = []; + + if(strtolower(config('pixelfed.domain.app')) !== $domain) { + $skipIds = UserDomainBlock::where('domain', $domain)->pluck('profile_id')->toArray(); + } + + $filters = UserFilter::whereFilterableType('App\Profile')->whereFilterableId($status['account']['id'])->whereIn('filter_type', ['mute', 'block'])->pluck('user_id')->toArray(); + + if($filters && count($filters)) { + $skipIds = array_merge($skipIds, $filters); + } + + $skipIds = array_unique(array_values($skipIds)); $ids = HashtagFollowService::getPidByHid($hashtag->hashtag_id); From b3148b788eb81135e95c32e7631972a65e62f768 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 00:21:33 -0700 Subject: [PATCH 33/48] Update HomeTimelineService, add domain blocks filtering to warmCache method --- app/Services/HomeTimelineService.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/Services/HomeTimelineService.php b/app/Services/HomeTimelineService.php index 6a2db0482..08d990591 100644 --- a/app/Services/HomeTimelineService.php +++ b/app/Services/HomeTimelineService.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Redis; use App\Follower; use App\Status; +use App\Models\UserDomainBlock; class HomeTimelineService { @@ -81,6 +82,8 @@ class HomeTimelineService $following = array_diff($following, $filters); } + $domainBlocks = UserDomainBlock::whereProfileId($id)->pluck('domain')->toArray(); + $ids = Status::where('id', '>', $minId) ->whereIn('profile_id', $following) ->whereNull(['in_reply_to_id', 'reblog_of_id']) @@ -91,6 +94,16 @@ class HomeTimelineService ->pluck('id'); foreach($ids as $pid) { + $status = StatusService::get($pid, false); + if(!$status || !isset($status['account'], $status['url'])) { + continue; + } + if($domainBlocks && count($domainBlocks)) { + $domain = strtolower(parse_url($status['url'], PHP_URL_HOST)); + if(in_array($domain, $domainBlocks)) { + continue; + } + } self::add($id, $pid); } From 6d55cb27eed3bace3168e4350e4deac0192ed8dc Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 00:42:26 -0700 Subject: [PATCH 34/48] Update UserFilterService, add domainBlocks method --- app/Services/UserFilterService.php | 271 +++++++++++++++-------------- 1 file changed, 143 insertions(+), 128 deletions(-) diff --git a/app/Services/UserFilterService.php b/app/Services/UserFilterService.php index 1dcdc819a..5673db60c 100644 --- a/app/Services/UserFilterService.php +++ b/app/Services/UserFilterService.php @@ -4,145 +4,160 @@ namespace App\Services; use Cache; use App\UserFilter; +use App\Models\UserDomainBlock; use Illuminate\Support\Facades\Redis; class UserFilterService { - const USER_MUTES_KEY = 'pf:services:mutes:ids:'; - const USER_BLOCKS_KEY = 'pf:services:blocks:ids:'; + const USER_MUTES_KEY = 'pf:services:mutes:ids:'; + const USER_BLOCKS_KEY = 'pf:services:blocks:ids:'; + const USER_DOMAIN_KEY = 'pf:services:domain-blocks:ids:'; - public static function mutes(int $profile_id) - { - $key = self::USER_MUTES_KEY . $profile_id; - $warm = Cache::has($key . ':cached-v0'); - if($warm) { - return Redis::zrevrange($key, 0, -1) ?? []; - } else { - if(Redis::zrevrange($key, 0, -1)) { - return Redis::zrevrange($key, 0, -1); - } - $ids = UserFilter::whereFilterType('mute') - ->whereUserId($profile_id) - ->pluck('filterable_id') - ->map(function($id) { - $acct = AccountService::get($id, true); - if(!$acct) { - return false; - } - return $acct['id']; - }) - ->filter(function($res) { - return $res; - }) - ->values() - ->toArray(); - foreach ($ids as $muted_id) { - Redis::zadd($key, (int) $muted_id, (int) $muted_id); - } - Cache::set($key . ':cached-v0', 1, 7776000); - return $ids; - } - } + public static function mutes(int $profile_id) + { + $key = self::USER_MUTES_KEY . $profile_id; + $warm = Cache::has($key . ':cached-v0'); + if($warm) { + return Redis::zrevrange($key, 0, -1) ?? []; + } else { + if(Redis::zrevrange($key, 0, -1)) { + return Redis::zrevrange($key, 0, -1); + } + $ids = UserFilter::whereFilterType('mute') + ->whereUserId($profile_id) + ->pluck('filterable_id') + ->map(function($id) { + $acct = AccountService::get($id, true); + if(!$acct) { + return false; + } + return $acct['id']; + }) + ->filter(function($res) { + return $res; + }) + ->values() + ->toArray(); + foreach ($ids as $muted_id) { + Redis::zadd($key, (int) $muted_id, (int) $muted_id); + } + Cache::set($key . ':cached-v0', 1, 7776000); + return $ids; + } + } - public static function blocks(int $profile_id) - { - $key = self::USER_BLOCKS_KEY . $profile_id; - $warm = Cache::has($key . ':cached-v0'); - if($warm) { - return Redis::zrevrange($key, 0, -1) ?? []; - } else { - if(Redis::zrevrange($key, 0, -1)) { - return Redis::zrevrange($key, 0, -1); - } - $ids = UserFilter::whereFilterType('block') - ->whereUserId($profile_id) - ->pluck('filterable_id') - ->map(function($id) { - $acct = AccountService::get($id, true); - if(!$acct) { - return false; - } - return $acct['id']; - }) - ->filter(function($res) { - return $res; - }) - ->values() - ->toArray(); - foreach ($ids as $blocked_id) { - Redis::zadd($key, (int) $blocked_id, (int) $blocked_id); - } - Cache::set($key . ':cached-v0', 1, 7776000); - return $ids; - } - } + public static function blocks(int $profile_id) + { + $key = self::USER_BLOCKS_KEY . $profile_id; + $warm = Cache::has($key . ':cached-v0'); + if($warm) { + return Redis::zrevrange($key, 0, -1) ?? []; + } else { + if(Redis::zrevrange($key, 0, -1)) { + return Redis::zrevrange($key, 0, -1); + } + $ids = UserFilter::whereFilterType('block') + ->whereUserId($profile_id) + ->pluck('filterable_id') + ->map(function($id) { + $acct = AccountService::get($id, true); + if(!$acct) { + return false; + } + return $acct['id']; + }) + ->filter(function($res) { + return $res; + }) + ->values() + ->toArray(); + foreach ($ids as $blocked_id) { + Redis::zadd($key, (int) $blocked_id, (int) $blocked_id); + } + Cache::set($key . ':cached-v0', 1, 7776000); + return $ids; + } + } - public static function filters(int $profile_id) - { - return array_unique(array_merge(self::mutes($profile_id), self::blocks($profile_id))); - } + public static function filters(int $profile_id) + { + return array_unique(array_merge(self::mutes($profile_id), self::blocks($profile_id))); + } - public static function mute(int $profile_id, int $muted_id) - { - if($profile_id == $muted_id) { - return false; - } - $key = self::USER_MUTES_KEY . $profile_id; - $mutes = self::mutes($profile_id); - $exists = in_array($muted_id, $mutes); - if(!$exists) { - Redis::zadd($key, $muted_id, $muted_id); - } - return true; - } + public static function mute(int $profile_id, int $muted_id) + { + if($profile_id == $muted_id) { + return false; + } + $key = self::USER_MUTES_KEY . $profile_id; + $mutes = self::mutes($profile_id); + $exists = in_array($muted_id, $mutes); + if(!$exists) { + Redis::zadd($key, $muted_id, $muted_id); + } + return true; + } - public static function unmute(int $profile_id, string $muted_id) - { - if($profile_id == $muted_id) { - return false; - } - $key = self::USER_MUTES_KEY . $profile_id; - $mutes = self::mutes($profile_id); - $exists = in_array($muted_id, $mutes); - if($exists) { - Redis::zrem($key, $muted_id); - } - return true; - } + public static function unmute(int $profile_id, string $muted_id) + { + if($profile_id == $muted_id) { + return false; + } + $key = self::USER_MUTES_KEY . $profile_id; + $mutes = self::mutes($profile_id); + $exists = in_array($muted_id, $mutes); + if($exists) { + Redis::zrem($key, $muted_id); + } + return true; + } - public static function block(int $profile_id, int $blocked_id) - { - if($profile_id == $blocked_id) { - return false; - } - $key = self::USER_BLOCKS_KEY . $profile_id; - $exists = in_array($blocked_id, self::blocks($profile_id)); - if(!$exists) { - Redis::zadd($key, $blocked_id, $blocked_id); - } - return true; - } + public static function block(int $profile_id, int $blocked_id) + { + if($profile_id == $blocked_id) { + return false; + } + $key = self::USER_BLOCKS_KEY . $profile_id; + $exists = in_array($blocked_id, self::blocks($profile_id)); + if(!$exists) { + Redis::zadd($key, $blocked_id, $blocked_id); + } + return true; + } - public static function unblock(int $profile_id, string $blocked_id) - { - if($profile_id == $blocked_id) { - return false; - } - $key = self::USER_BLOCKS_KEY . $profile_id; - $exists = in_array($blocked_id, self::blocks($profile_id)); - if($exists) { - Redis::zrem($key, $blocked_id); - } - return $exists; - } + public static function unblock(int $profile_id, string $blocked_id) + { + if($profile_id == $blocked_id) { + return false; + } + $key = self::USER_BLOCKS_KEY . $profile_id; + $exists = in_array($blocked_id, self::blocks($profile_id)); + if($exists) { + Redis::zrem($key, $blocked_id); + } + return $exists; + } - public static function blockCount(int $profile_id) - { - return Redis::zcard(self::USER_BLOCKS_KEY . $profile_id); - } + public static function blockCount(int $profile_id) + { + return Redis::zcard(self::USER_BLOCKS_KEY . $profile_id); + } - public static function muteCount(int $profile_id) - { - return Redis::zcard(self::USER_MUTES_KEY . $profile_id); - } + public static function muteCount(int $profile_id) + { + return Redis::zcard(self::USER_MUTES_KEY . $profile_id); + } + + public static function domainBlocks($pid, $purge = false) + { + if($purge) { + Cache::forget(self::USER_DOMAIN_KEY . $pid); + } + return Cache::remember( + self::USER_DOMAIN_KEY . $pid, + 21600, + function() use($pid) { + return UserDomainBlock::whereProfileId($pid)->pluck('domain')->toArray(); + }); + } } From 6d8121413865c00f6ccc8e46ff2e367bb603bd66 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 00:44:54 -0700 Subject: [PATCH 35/48] Update DomainBlockController, purge domainBlocks cache --- app/Http/Controllers/Api/V1/DomainBlockController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Http/Controllers/Api/V1/DomainBlockController.php b/app/Http/Controllers/Api/V1/DomainBlockController.php index 53a209ed9..2186c0936 100644 --- a/app/Http/Controllers/Api/V1/DomainBlockController.php +++ b/app/Http/Controllers/Api/V1/DomainBlockController.php @@ -6,6 +6,7 @@ use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\UserDomainBlock; use App\Util\ActivityPub\Helpers; +use App\Services\UserFilterService; use Illuminate\Bus\Batch; use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Cache; @@ -90,6 +91,7 @@ class DomainBlockController extends Controller ])->allowFailures()->onQueue('feed')->dispatch(); Cache::forget('profile:following:' . $pid); + UserFilterService::domainBlocks($pid, true); } return $this->json([]); @@ -109,6 +111,8 @@ class DomainBlockController extends Controller $filters = UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->delete(); + UserFilterService::domainBlocks($pid, true); + return $this->json([]); } } From 21947835f8969a6100d275897bc84f337b958994 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 00:46:24 -0700 Subject: [PATCH 36/48] Update ApiV1Controller, use domainBlock filtering on public/network feeds --- app/Http/Controllers/Api/ApiV1Controller.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index e429a8681..be77b5606 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -31,6 +31,7 @@ use App\{ UserSetting, UserFilter, }; +use App\Models\UserDomainBlock; use League\Fractal; use App\Transformer\Api\Mastodon\v1\{ AccountTransformer, @@ -2422,6 +2423,7 @@ class ApiV1Controller extends Controller $local = $request->has('local'); $filtered = $user ? UserFilterService::filters($user->profile_id) : []; AccountService::setLastActive($user->id); + $domainBlocks = UserFilterService::domainBlocks($user->profile_id); if($remote && config('instance.timeline.network.cached')) { Cache::remember('api:v1:timelines:network:cache_check', 10368000, function() { @@ -2496,6 +2498,13 @@ class ApiV1Controller extends Controller ->filter(function($s) use($filtered) { return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; }) + ->filter(function($s) use($domainBlocks) { + if(!$domainBlocks || !count($domainBlocks)) { + return $s; + } + $domain = strtolower(parse_url($s['url'], PHP_URL_HOST)); + return !in_array($domain, $domainBlocks); + }) ->take($limit) ->values(); From c3f16c87a31eedb50e1070ec0f00c3d8cb6f6882 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 01:05:49 -0700 Subject: [PATCH 37/48] Update SearchApiV2Service, add user domain blocks filtering --- app/Services/SearchApiV2Service.php | 35 ++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/app/Services/SearchApiV2Service.php b/app/Services/SearchApiV2Service.php index 90691f0bd..f926c2c27 100644 --- a/app/Services/SearchApiV2Service.php +++ b/app/Services/SearchApiV2Service.php @@ -95,7 +95,15 @@ class SearchApiV2Service if(substr($webfingerQuery, 0, 1) !== '@') { $webfingerQuery = '@' . $webfingerQuery; } - $banned = InstanceService::getBannedDomains(); + $banned = InstanceService::getBannedDomains() ?? []; + $domainBlocks = UserFilterService::domainBlocks($user->profile_id); + if($domainBlocks && count($domainBlocks)) { + $banned = array_unique( + array_values( + array_merge($banned, $domainBlocks) + ) + ); + } $operator = config('database.default') === 'pgsql' ? 'ilike' : 'like'; $results = Profile::select('username', 'id', 'followers_count', 'domain') ->where('username', $operator, $query) @@ -172,8 +180,18 @@ class SearchApiV2Service 'hashtags' => [], 'statuses' => [], ]; + $user = request()->user(); $mastodonMode = self::$mastodonMode; $query = urldecode($this->query->input('q')); + $banned = InstanceService::getBannedDomains(); + $domainBlocks = UserFilterService::domainBlocks($user->profile_id); + if($domainBlocks && count($domainBlocks)) { + $banned = array_unique( + array_values( + array_merge($banned, $domainBlocks) + ) + ); + } if(substr($query, 0, 1) === '@' && !Str::contains($query, '.')) { $default['accounts'] = $this->accounts(substr($query, 1)); return $default; @@ -197,7 +215,11 @@ class SearchApiV2Service } catch (\Exception $e) { return $default; } - if($res && isset($res['id'])) { + if($res && isset($res['id'], $res['url'])) { + $domain = strtolower(parse_url($res['url'], PHP_URL_HOST)); + if(in_array($domain, $banned)) { + return $default; + } $default['accounts'][] = $res; return $default; } else { @@ -212,6 +234,10 @@ class SearchApiV2Service return $default; } if($res && isset($res['id'])) { + $domain = strtolower(parse_url($res['url'], PHP_URL_HOST)); + if(in_array($domain, $banned)) { + return $default; + } $default['accounts'][] = $res; return $default; } else { @@ -221,6 +247,9 @@ class SearchApiV2Service if($sid = Status::whereUri($query)->first()) { $s = StatusService::get($sid->id, false); + if(!$s) { + return $default; + } if(in_array($s['visibility'], ['public', 'unlisted'])) { $default['statuses'][] = $s; return $default; @@ -229,7 +258,7 @@ class SearchApiV2Service try { $res = ActivityPubFetchService::get($query); - $banned = InstanceService::getBannedDomains(); + if($res) { $json = json_decode($res, true); From fcbcd7ec73bb89bb56156fbc0dc929994444751e Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 01:53:49 -0700 Subject: [PATCH 38/48] Update Delete pipelines, delete status hashtags quietly --- app/Jobs/DeletePipeline/DeleteRemoteStatusPipeline.php | 5 +---- app/Jobs/StatusPipeline/RemoteStatusDelete.php | 5 +---- app/Jobs/StatusPipeline/StatusDelete.php | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/app/Jobs/DeletePipeline/DeleteRemoteStatusPipeline.php b/app/Jobs/DeletePipeline/DeleteRemoteStatusPipeline.php index 353509c6c..824323cda 100644 --- a/app/Jobs/DeletePipeline/DeleteRemoteStatusPipeline.php +++ b/app/Jobs/DeletePipeline/DeleteRemoteStatusPipeline.php @@ -76,10 +76,7 @@ class DeleteRemoteStatusPipeline implements ShouldQueue }); Mention::whereStatusId($status->id)->forceDelete(); Report::whereObjectType('App\Status')->whereObjectId($status->id)->delete(); - $statusHashtags = StatusHashtag::whereStatusId($status->id)->get(); - foreach($statusHashtags as $stag) { - $stag->delete(); - } + StatusHashtag::whereStatusId($status->id)->deleteQuietly(); StatusView::whereStatusId($status->id)->delete(); Status::whereReblogOfId($status->id)->forceDelete(); $status->forceDelete(); diff --git a/app/Jobs/StatusPipeline/RemoteStatusDelete.php b/app/Jobs/StatusPipeline/RemoteStatusDelete.php index 78c41ed3d..9898d3c82 100644 --- a/app/Jobs/StatusPipeline/RemoteStatusDelete.php +++ b/app/Jobs/StatusPipeline/RemoteStatusDelete.php @@ -174,10 +174,7 @@ class RemoteStatusDelete implements ShouldQueue, ShouldBeUniqueUntilProcessing ->whereObjectId($status->id) ->delete(); StatusArchived::whereStatusId($status->id)->delete(); - $statusHashtags = StatusHashtag::whereStatusId($status->id)->get(); - foreach($statusHashtags as $stag) { - $stag->delete(); - } + StatusHashtag::whereStatusId($status->id)->deleteQuietly(); StatusView::whereStatusId($status->id)->delete(); Status::whereInReplyToId($status->id)->update(['in_reply_to_id' => null]); diff --git a/app/Jobs/StatusPipeline/StatusDelete.php b/app/Jobs/StatusPipeline/StatusDelete.php index c0ced1368..a053bfe75 100644 --- a/app/Jobs/StatusPipeline/StatusDelete.php +++ b/app/Jobs/StatusPipeline/StatusDelete.php @@ -151,10 +151,7 @@ class StatusDelete implements ShouldQueue ->delete(); StatusArchived::whereStatusId($status->id)->delete(); - $statusHashtags = StatusHashtag::whereStatusId($status->id)->get(); - foreach($statusHashtags as $stag) { - $stag->delete(); - } + StatusHashtag::whereStatusId($status->id)->deleteQuietly(); StatusView::whereStatusId($status->id)->delete(); Status::whereInReplyToId($status->id)->update(['in_reply_to_id' => null]); From 89b8e87477caedfe4cec4534a370be184b8a9190 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 02:03:15 -0700 Subject: [PATCH 39/48] Update ApiV1Controller, apply user domain blocks filtering to hashtag timelines --- app/Http/Controllers/Api/ApiV1Controller.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index be77b5606..798d9ee55 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -3285,6 +3285,7 @@ class ApiV1Controller extends Controller $limit = $request->input('limit', 20); $onlyMedia = $request->input('only_media', true); $pe = $request->has(self::PF_API_ENTITY_KEY); + $pid = $request->user()->profile_id; if($min || $max) { $minMax = SnowflakeService::byDate(now()->subMonths(6)); @@ -3296,7 +3297,8 @@ class ApiV1Controller extends Controller } } - $filters = UserFilterService::filters($request->user()->profile_id); + $filters = UserFilterService::filters($pid); + $domainBlocks = UserFilterService::domainBlocks($pid); if(!$min && !$max) { $id = 1; @@ -3322,10 +3324,11 @@ class ApiV1Controller extends Controller if($onlyMedia && !isset($i['media_attachments']) || !count($i['media_attachments'])) { return false; } - return $i && isset($i['account']); + return $i && isset($i['account'], $i['url']); }) - ->filter(function($i) use($filters) { - return !in_array($i['account']['id'], $filters); + ->filter(function($i) use($filters, $domainBlocks) { + $domain = strtolower(parse_url($i['url'], PHP_URL_HOST)); + return !in_array($i['account']['id'], $filters) && !in_array($domain, $domainBlocks); }) ->values() ->toArray(); From 5169936062d801936ac688c0f4e220c4794f5e62 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 02:05:26 -0700 Subject: [PATCH 40/48] Update MarkerService, fix php deprecation warning --- app/Services/MarkerService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/MarkerService.php b/app/Services/MarkerService.php index 6b407b567..130f0b017 100644 --- a/app/Services/MarkerService.php +++ b/app/Services/MarkerService.php @@ -13,7 +13,7 @@ class MarkerService return Cache::get(self::CACHE_KEY . $timeline . ':' . $profileId); } - public static function set($profileId, $timeline = 'home', $entityId) + public static function set($profileId, $timeline = 'home', $entityId = false) { $existing = self::get($profileId, $timeline); $key = self::CACHE_KEY . $timeline . ':' . $profileId; From 6c39df7fb38781efd0b7464d96ace0cb0912add1 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 02:08:44 -0700 Subject: [PATCH 41/48] Update Inbox, import AccountService --- app/Util/ActivityPub/Inbox.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php index b8bb780c8..e26f0a48c 100644 --- a/app/Util/ActivityPub/Inbox.php +++ b/app/Util/ActivityPub/Inbox.php @@ -39,6 +39,7 @@ use App\Util\ActivityPub\Validator\Like as LikeValidator; use App\Util\ActivityPub\Validator\UndoFollow as UndoFollowValidator; use App\Util\ActivityPub\Validator\UpdatePersonValidator; +use App\Services\AccountService; use App\Services\PollService; use App\Services\FollowerService; use App\Services\ReblogService; From e98df1196f079ccad4e44897457f035b8efeadcf Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 03:34:31 -0700 Subject: [PATCH 42/48] Add migration --- ...1_103223_purge_deleted_status_hashtags.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 database/migrations/2023_12_21_103223_purge_deleted_status_hashtags.php diff --git a/database/migrations/2023_12_21_103223_purge_deleted_status_hashtags.php b/database/migrations/2023_12_21_103223_purge_deleted_status_hashtags.php new file mode 100644 index 000000000..bf2acc34e --- /dev/null +++ b/database/migrations/2023_12_21_103223_purge_deleted_status_hashtags.php @@ -0,0 +1,25 @@ +lazyById(200)->each->deleteQuietly(); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; From 3e28cf661ba683a51256363de6deba4b372aa32f Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 03:35:47 -0700 Subject: [PATCH 43/48] Add user domain block commands --- app/Console/Commands/AddUserDomainBlock.php | 99 +++++++++++++++++++ .../Commands/DeleteUserDomainBlock.php | 88 +++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 app/Console/Commands/AddUserDomainBlock.php create mode 100644 app/Console/Commands/DeleteUserDomainBlock.php diff --git a/app/Console/Commands/AddUserDomainBlock.php b/app/Console/Commands/AddUserDomainBlock.php new file mode 100644 index 000000000..33f441cdc --- /dev/null +++ b/app/Console/Commands/AddUserDomainBlock.php @@ -0,0 +1,99 @@ +validateDomain($domain); + $this->processBlocks($domain); + return; + } + + protected function validateDomain($domain) + { + if(!strpos($domain, '.')) { + $this->error('Invalid domain'); + return; + } + + if(str_starts_with($domain, 'https://')) { + $domain = str_replace('https://', '', $domain); + } + + if(str_starts_with($domain, 'http://')) { + $domain = str_replace('http://', '', $domain); + } + + $valid = filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME|FILTER_NULL_ON_FAILURE); + if(!$valid) { + $this->error('Invalid domain'); + return; + } + + $domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST)); + + if($domain === config('pixelfed.domain.app')) { + $this->error('Invalid domain'); + return; + } + + $confirmed = confirm('Are you sure you want to block ' . $domain . '?'); + if(!$confirmed) { + return; + } + + return $domain; + } + + protected function processBlocks($domain) + { + progress( + label: 'Updating user domain blocks...', + steps: User::lazyById(500), + callback: fn ($user) => $this->performTask($user, $domain), + ); + } + + protected function performTask($user, $domain) + { + if(!$user->profile_id || $user->delete_after) { + return; + } + + if($user->status != null && $user->status != 'disabled') { + return; + } + + UserDomainBlock::updateOrCreate([ + 'profile_id' => $user->profile_id, + 'domain' => $domain + ]); + } +} diff --git a/app/Console/Commands/DeleteUserDomainBlock.php b/app/Console/Commands/DeleteUserDomainBlock.php new file mode 100644 index 000000000..80c139f2b --- /dev/null +++ b/app/Console/Commands/DeleteUserDomainBlock.php @@ -0,0 +1,88 @@ +validateDomain($domain); + $this->processUnblocks($domain); + return; + } + + protected function validateDomain($domain) + { + if(!strpos($domain, '.')) { + $this->error('Invalid domain'); + return; + } + + if(str_starts_with($domain, 'https://')) { + $domain = str_replace('https://', '', $domain); + } + + if(str_starts_with($domain, 'http://')) { + $domain = str_replace('http://', '', $domain); + } + + $valid = filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME|FILTER_NULL_ON_FAILURE); + if(!$valid) { + $this->error('Invalid domain'); + return; + } + + $domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST)); + + if($domain === config('pixelfed.domain.app')) { + $this->error('Invalid domain'); + return; + } + + $confirmed = confirm('Are you sure you want to unblock ' . $domain . '?'); + if(!$confirmed) { + return; + } + + return $domain; + } + + protected function processUnblocks($domain) + { + progress( + label: 'Updating user domain blocks...', + steps: UserDomainBlock::whereDomain($domain)->lazyById(500), + callback: fn ($domainBlock) => $this->performTask($domainBlock), + ); + } + + protected function performTask($domainBlock) + { + $domainBlock->deleteQuietly(); + } +} From f3f0175c8494d9bf45931e02fa8c6ccb9558968d Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 03:47:23 -0700 Subject: [PATCH 44/48] Add DefaultDomainBlock model + migration --- app/Models/DefaultDomainBlock.php | 13 +++++++++ ...103_create_default_domain_blocks_table.php | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 app/Models/DefaultDomainBlock.php create mode 100644 database/migrations/2023_12_21_104103_create_default_domain_blocks_table.php diff --git a/app/Models/DefaultDomainBlock.php b/app/Models/DefaultDomainBlock.php new file mode 100644 index 000000000..d90816a32 --- /dev/null +++ b/app/Models/DefaultDomainBlock.php @@ -0,0 +1,13 @@ +id(); + $table->string('domain')->unique()->index(); + $table->text('note')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('default_domain_blocks'); + } +}; From 519c7a3735c7893cee93486dd5a27a43fee6e3d1 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 03:48:08 -0700 Subject: [PATCH 45/48] Update domain block commands --- app/Console/Commands/AddUserDomainBlock.php | 5 +++++ app/Console/Commands/DeleteUserDomainBlock.php | 3 +++ 2 files changed, 8 insertions(+) diff --git a/app/Console/Commands/AddUserDomainBlock.php b/app/Console/Commands/AddUserDomainBlock.php index 33f441cdc..7128879e1 100644 --- a/app/Console/Commands/AddUserDomainBlock.php +++ b/app/Console/Commands/AddUserDomainBlock.php @@ -4,6 +4,7 @@ namespace App\Console\Commands; use Illuminate\Console\Command; use App\User; +use App\Models\DefaultDomainBlock; use App\Models\UserDomainBlock; use function Laravel\Prompts\text; use function Laravel\Prompts\confirm; @@ -31,6 +32,7 @@ class AddUserDomainBlock extends Command public function handle() { $domain = text('Enter domain you want to block'); + $domain = strtolower($domain); $domain = $this->validateDomain($domain); $this->processBlocks($domain); return; @@ -74,6 +76,9 @@ class AddUserDomainBlock extends Command protected function processBlocks($domain) { + DefaultDomainBlock::updateOrCreate([ + 'domain' => $domain + ]); progress( label: 'Updating user domain blocks...', steps: User::lazyById(500), diff --git a/app/Console/Commands/DeleteUserDomainBlock.php b/app/Console/Commands/DeleteUserDomainBlock.php index 80c139f2b..9cc1c1ded 100644 --- a/app/Console/Commands/DeleteUserDomainBlock.php +++ b/app/Console/Commands/DeleteUserDomainBlock.php @@ -4,6 +4,7 @@ namespace App\Console\Commands; use Illuminate\Console\Command; use App\User; +use App\Models\DefaultDomainBlock; use App\Models\UserDomainBlock; use function Laravel\Prompts\text; use function Laravel\Prompts\confirm; @@ -31,6 +32,7 @@ class DeleteUserDomainBlock extends Command public function handle() { $domain = text('Enter domain you want to unblock'); + $domain = strtolower($domain); $domain = $this->validateDomain($domain); $this->processUnblocks($domain); return; @@ -74,6 +76,7 @@ class DeleteUserDomainBlock extends Command protected function processUnblocks($domain) { + DefaultDomainBlock::whereDomain($domain)->delete(); progress( label: 'Updating user domain blocks...', steps: UserDomainBlock::whereDomain($domain)->lazyById(500), From fa0380ac3be5534ef6f113347fd9feaec8e88d8c Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 04:47:09 -0700 Subject: [PATCH 46/48] Update UserObserver, add default domain blocks logic --- app/Observers/UserObserver.php | 207 +++++++++++++++++++++------------ 1 file changed, 131 insertions(+), 76 deletions(-) diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index ec4ef9f34..d587bd7e8 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -7,90 +7,52 @@ use App\Follower; use App\Profile; use App\User; use App\UserSetting; +use App\Services\UserFilterService; +use App\Models\DefaultDomainBlock; +use App\Models\UserDomainBlock; use App\Jobs\FollowPipeline\FollowPipeline; use DB; use App\Services\FollowerService; class UserObserver { - /** - * Listen to the User created event. - * - * @param \App\User $user - * - * @return void - */ - public function saved(User $user) - { - if($user->status == 'deleted') { - return; - } + /** + * Handle the notification "created" event. + * + * @param \App\User $user + * @return void + */ + public function created(User $user): void + { + $this->handleUser($user); + } - if(Profile::whereUsername($user->username)->exists()) { - return; + /** + * Listen to the User saved event. + * + * @param \App\User $user + * + * @return void + */ + public function saved(User $user) + { + $this->handleUser($user); + } + + /** + * Listen to the User updated event. + * + * @param \App\User $user + * + * @return void + */ + public function updated(User $user): void + { + $this->handleUser($user); + if($user->profile) { + $this->applyDefaultDomainBlocks($user); } - - if (empty($user->profile)) { - $profile = DB::transaction(function() use($user) { - $profile = new Profile(); - $profile->user_id = $user->id; - $profile->username = $user->username; - $profile->name = $user->name; - $pkiConfig = [ - 'digest_alg' => 'sha512', - 'private_key_bits' => 2048, - 'private_key_type' => OPENSSL_KEYTYPE_RSA, - ]; - $pki = openssl_pkey_new($pkiConfig); - openssl_pkey_export($pki, $pki_private); - $pki_public = openssl_pkey_get_details($pki); - $pki_public = $pki_public['key']; - - $profile->private_key = $pki_private; - $profile->public_key = $pki_public; - $profile->save(); - return $profile; - }); - - DB::transaction(function() use($user, $profile) { - $user = User::findOrFail($user->id); - $user->profile_id = $profile->id; - $user->save(); - - CreateAvatar::dispatch($profile); - }); - - if(config_cache('account.autofollow') == true) { - $names = config_cache('account.autofollow_usernames'); - $names = explode(',', $names); - - if(!$names || !last($names)) { - return; - } - - $profiles = Profile::whereIn('username', $names)->get(); - - if($profiles) { - foreach($profiles as $p) { - $follower = new Follower; - $follower->profile_id = $profile->id; - $follower->following_id = $p->id; - $follower->save(); - - FollowPipeline::dispatch($follower); - } - } - } - } - - if (empty($user->settings)) { - DB::transaction(function() use($user) { - UserSetting::firstOrCreate([ - 'user_id' => $user->id - ]); - }); - } - } + } /** * Handle the user "deleted" event. @@ -102,4 +64,97 @@ class UserObserver { FollowerService::delCache($user->profile_id); } + + protected function handleUser($user) + { + if(in_array($user->status, ['deleted', 'delete'])) { + return; + } + + if(Profile::whereUsername($user->username)->exists()) { + return; + } + + if (empty($user->profile)) { + $profile = DB::transaction(function() use($user) { + $profile = new Profile(); + $profile->user_id = $user->id; + $profile->username = $user->username; + $profile->name = $user->name; + $pkiConfig = [ + 'digest_alg' => 'sha512', + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + ]; + $pki = openssl_pkey_new($pkiConfig); + openssl_pkey_export($pki, $pki_private); + $pki_public = openssl_pkey_get_details($pki); + $pki_public = $pki_public['key']; + + $profile->private_key = $pki_private; + $profile->public_key = $pki_public; + $profile->save(); + $this->applyDefaultDomainBlocks($user); + return $profile; + }); + + + DB::transaction(function() use($user, $profile) { + $user = User::findOrFail($user->id); + $user->profile_id = $profile->id; + $user->save(); + + CreateAvatar::dispatch($profile); + }); + + if(config_cache('account.autofollow') == true) { + $names = config_cache('account.autofollow_usernames'); + $names = explode(',', $names); + + if(!$names || !last($names)) { + return; + } + + $profiles = Profile::whereIn('username', $names)->get(); + + if($profiles) { + foreach($profiles as $p) { + $follower = new Follower; + $follower->profile_id = $profile->id; + $follower->following_id = $p->id; + $follower->save(); + + FollowPipeline::dispatch($follower); + } + } + } + } + + if (empty($user->settings)) { + DB::transaction(function() use($user) { + UserSetting::firstOrCreate([ + 'user_id' => $user->id + ]); + }); + } + } + + protected function applyDefaultDomainBlocks($user) + { + if($user->profile_id == null) { + return; + } + $defaultDomainBlocks = DefaultDomainBlock::pluck('domain')->toArray(); + + if(!$defaultDomainBlocks || !count($defaultDomainBlocks)) { + return; + } + + foreach($defaultDomainBlocks as $domain) { + UserDomainBlock::updateOrCreate([ + 'profile_id' => $user->profile_id, + 'domain' => strtolower(trim($domain)) + ]); + } + } } From d8f46f47a1106b5c5d78efaa4a67abb9696b965f Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 04:48:56 -0700 Subject: [PATCH 47/48] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f75a72a2..8ab35fec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Added `app:hashtag-cached-count-update` command to update cached_count of hashtags and add to scheduler to run every 25 minutes past the hour ([1e31fee6](https://github.com/pixelfed/pixelfed/commit/1e31fee6)) - Added `app:hashtag-related-generate` command to generate related hashtags ([176b4ed7](https://github.com/pixelfed/pixelfed/commit/176b4ed7)) - Added Mutual Followers API endpoint ([33dbbe46](https://github.com/pixelfed/pixelfed/commit/33dbbe46)) +- Added User Domain Blocks ([#4834](https://github.com/pixelfed/pixelfed/pull/4834)) ([fa0380ac](https://github.com/pixelfed/pixelfed/commit/fa0380ac)) ### Federation - Update Privacy Settings, add support for Mastodon `indexable` search flag ([fc24630e](https://github.com/pixelfed/pixelfed/commit/fc24630e)) From 73a0f528ab6cc7a5e272ec2dff2c1df37e1f39d3 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 21 Dec 2023 05:00:35 -0700 Subject: [PATCH 48/48] Update user domain block commands --- app/Console/Commands/AddUserDomainBlock.php | 10 ++++++---- app/Console/Commands/DeleteUserDomainBlock.php | 15 ++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/Console/Commands/AddUserDomainBlock.php b/app/Console/Commands/AddUserDomainBlock.php index 7128879e1..6d5c192bf 100644 --- a/app/Console/Commands/AddUserDomainBlock.php +++ b/app/Console/Commands/AddUserDomainBlock.php @@ -34,6 +34,10 @@ class AddUserDomainBlock extends Command $domain = text('Enter domain you want to block'); $domain = strtolower($domain); $domain = $this->validateDomain($domain); + if(!$domain || empty($domain)) { + $this->error('Invalid domain'); + return; + } $this->processBlocks($domain); return; } @@ -41,7 +45,6 @@ class AddUserDomainBlock extends Command protected function validateDomain($domain) { if(!strpos($domain, '.')) { - $this->error('Invalid domain'); return; } @@ -53,14 +56,13 @@ class AddUserDomainBlock extends Command $domain = str_replace('http://', '', $domain); } + $domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST)); + $valid = filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME|FILTER_NULL_ON_FAILURE); if(!$valid) { - $this->error('Invalid domain'); return; } - $domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST)); - if($domain === config('pixelfed.domain.app')) { $this->error('Invalid domain'); return; diff --git a/app/Console/Commands/DeleteUserDomainBlock.php b/app/Console/Commands/DeleteUserDomainBlock.php index 9cc1c1ded..405b6fe76 100644 --- a/app/Console/Commands/DeleteUserDomainBlock.php +++ b/app/Console/Commands/DeleteUserDomainBlock.php @@ -34,6 +34,10 @@ class DeleteUserDomainBlock extends Command $domain = text('Enter domain you want to unblock'); $domain = strtolower($domain); $domain = $this->validateDomain($domain); + if(!$domain || empty($domain)) { + $this->error('Invalid domain'); + return; + } $this->processUnblocks($domain); return; } @@ -41,7 +45,6 @@ class DeleteUserDomainBlock extends Command protected function validateDomain($domain) { if(!strpos($domain, '.')) { - $this->error('Invalid domain'); return; } @@ -53,16 +56,14 @@ class DeleteUserDomainBlock extends Command $domain = str_replace('http://', '', $domain); } + $domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST)); + $valid = filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME|FILTER_NULL_ON_FAILURE); if(!$valid) { - $this->error('Invalid domain'); return; } - $domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST)); - if($domain === config('pixelfed.domain.app')) { - $this->error('Invalid domain'); return; } @@ -77,6 +78,10 @@ class DeleteUserDomainBlock extends Command protected function processUnblocks($domain) { DefaultDomainBlock::whereDomain($domain)->delete(); + if(!UserDomainBlock::whereDomain($domain)->count()) { + $this->info('No results found!'); + return; + } progress( label: 'Updating user domain blocks...', steps: UserDomainBlock::whereDomain($domain)->lazyById(500),