From c96167f2f7c6acfca07faccf928809fcf5fa9b9d Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 11 Mar 2024 00:29:55 -0600 Subject: [PATCH 01/41] Update config_cache --- app/Http/Controllers/Api/ApiV1Controller.php | 4 ++-- app/Http/Controllers/ImportPostController.php | 2 +- app/Util/Site/Config.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index 0463e681e..2da53762a 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -1650,11 +1650,11 @@ class ApiV1Controller extends Controller 'configuration' => [ 'media_attachments' => [ 'image_matrix_limit' => 16777216, - 'image_size_limit' => config('pixelfed.max_photo_size') * 1024, + 'image_size_limit' => config_cache('pixelfed.max_photo_size') * 1024, 'supported_mime_types' => explode(',', config('pixelfed.media_types')), 'video_frame_rate_limit' => 120, 'video_matrix_limit' => 2304000, - 'video_size_limit' => config('pixelfed.max_photo_size') * 1024, + 'video_size_limit' => config_cache('pixelfed.max_photo_size') * 1024, ], 'polls' => [ 'max_characters_per_option' => 50, diff --git a/app/Http/Controllers/ImportPostController.php b/app/Http/Controllers/ImportPostController.php index 55f575a6e..84f230622 100644 --- a/app/Http/Controllers/ImportPostController.php +++ b/app/Http/Controllers/ImportPostController.php @@ -179,7 +179,7 @@ class ImportPostController extends Controller 'required', 'file', $mimes, - 'max:' . config('pixelfed.max_photo_size') + 'max:' . config_cache('pixelfed.max_photo_size') ] ]); diff --git a/app/Util/Site/Config.php b/app/Util/Site/Config.php index 02944defe..038eef99e 100644 --- a/app/Util/Site/Config.php +++ b/app/Util/Site/Config.php @@ -30,7 +30,7 @@ class Config 'version' => config('pixelfed.version'), 'open_registration' => (bool) config_cache('pixelfed.open_registration'), 'uploader' => [ - 'max_photo_size' => (int) config('pixelfed.max_photo_size'), + 'max_photo_size' => (int) config_cache('pixelfed.max_photo_size'), 'max_caption_length' => (int) config_cache('pixelfed.max_caption_length'), 'max_altext_length' => (int) config_cache('pixelfed.max_altext_length', 150), 'album_limit' => (int) config_cache('pixelfed.max_album_length'), From 8a89e3c9637147fe53b16098c2e6285f6a3f12e5 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 11 Mar 2024 21:25:04 -0600 Subject: [PATCH 02/41] Update captcha, use config_cache helper --- app/Http/Controllers/Auth/ForgotPasswordController.php | 2 +- app/Http/Controllers/Auth/LoginController.php | 6 +++--- app/Http/Controllers/Auth/RegisterController.php | 2 +- app/Http/Controllers/Auth/ResetPasswordController.php | 2 +- app/Http/Controllers/UserEmailForgotController.php | 2 +- app/Services/ConfigCacheService.php | 8 ++++++++ resources/views/auth/email/forgot.blade.php | 2 +- resources/views/auth/login.blade.php | 6 +++--- resources/views/auth/passwords/email.blade.php | 2 +- resources/views/auth/passwords/reset.blade.php | 2 +- .../parental-controls/invite-register-form.blade.php | 2 +- 11 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 618c495e2..22562e985 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -62,7 +62,7 @@ class ForgotPasswordController extends Controller usleep(random_int(100000, 3000000)); - if(config('captcha.enabled')) { + if((bool) config_cache('captcha.enabled')) { $rules = [ 'email' => 'required|email', 'h-captcha-response' => 'required|captcha' diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 627a879cc..86ee52c84 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -74,10 +74,10 @@ class LoginController extends Controller $messages = []; if( - config('captcha.enabled') || - config('captcha.active.login') || + (bool) config_cache('captcha.enabled') && + (bool) config_cache('captcha.active.login') || ( - config('captcha.triggers.login.enabled') && + (bool) config_cache('captcha.triggers.login.enabled') && request()->session()->has('login_attempts') && request()->session()->get('login_attempts') >= config('captcha.triggers.login.attempts') ) diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 8bdd57bf8..7568fca09 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -137,7 +137,7 @@ class RegisterController extends Controller 'password' => 'required|string|min:'.config('pixelfed.min_password_length').'|confirmed', ]; - if(config('captcha.enabled') || config('captcha.active.register')) { + if((bool) config_cache('captcha.enabled') && (bool) config_cache('captcha.active.register')) { $rules['h-captcha-response'] = 'required|captcha'; } diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index a92c4e38d..166ec01e3 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -50,7 +50,7 @@ class ResetPasswordController extends Controller { usleep(random_int(100000, 3000000)); - if(config('captcha.enabled')) { + if((bool) config_cache('captcha.enabled')) { return [ 'token' => 'required', 'email' => 'required|email', diff --git a/app/Http/Controllers/UserEmailForgotController.php b/app/Http/Controllers/UserEmailForgotController.php index 33378c4d0..3889b9802 100644 --- a/app/Http/Controllers/UserEmailForgotController.php +++ b/app/Http/Controllers/UserEmailForgotController.php @@ -34,7 +34,7 @@ class UserEmailForgotController extends Controller 'username.exists' => 'This username is no longer active or does not exist!' ]; - if(config('captcha.enabled') || config('captcha.active.login') || config('captcha.active.register')) { + if((bool) config_cache('captcha.enabled')) { $rules['h-captcha-response'] = 'required|captcha'; $messages['h-captcha-response.required'] = 'You need to complete the captcha!'; } diff --git a/app/Services/ConfigCacheService.php b/app/Services/ConfigCacheService.php index 7537830fc..8205dc3bd 100644 --- a/app/Services/ConfigCacheService.php +++ b/app/Services/ConfigCacheService.php @@ -89,6 +89,14 @@ class ConfigCacheService 'pixelfed.app_registration_confirm_rate_limit_decay', 'instance.embed.profile', 'instance.embed.post', + + 'captcha.enabled', + 'captcha.secret', + 'captcha.sitekey', + 'captcha.active.login', + 'captcha.active.register', + 'captcha.triggers.login.enabled', + 'captcha.triggers.login.attempts', // 'system.user_mode' ]; diff --git a/resources/views/auth/email/forgot.blade.php b/resources/views/auth/email/forgot.blade.php index 898d19fb5..e4b67d792 100644 --- a/resources/views/auth/email/forgot.blade.php +++ b/resources/views/auth/email/forgot.blade.php @@ -65,7 +65,7 @@ - @if(config('captcha.enabled') || config('captcha.active.login') || config('captcha.active.register')) + @if((bool) config_cache('captcha.enabled'))
{!! Captcha::display(['data-theme' => 'dark']) !!} diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 9df9ea8c9..0f77f778e 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -76,10 +76,10 @@
@if( - config('captcha.enabled') || - config('captcha.active.login') || + (bool) config_cache('captcha.enabled') && + (bool) config_cache('captcha.active.login') || ( - config('captcha.triggers.login.enabled') && + (bool) config_cache('captcha.triggers.login.enabled') && request()->session()->has('login_attempts') && request()->session()->get('login_attempts') >= config('captcha.triggers.login.attempts') ) diff --git a/resources/views/auth/passwords/email.blade.php b/resources/views/auth/passwords/email.blade.php index 4f2825e29..19461fa29 100644 --- a/resources/views/auth/passwords/email.blade.php +++ b/resources/views/auth/passwords/email.blade.php @@ -54,7 +54,7 @@ - @if(config('captcha.enabled')) + @if((bool) config_cache('captcha.enabled'))
{!! Captcha::display(['data-theme' => 'dark']) !!} diff --git a/resources/views/auth/passwords/reset.blade.php b/resources/views/auth/passwords/reset.blade.php index 1a740fa7d..ecabcaddf 100644 --- a/resources/views/auth/passwords/reset.blade.php +++ b/resources/views/auth/passwords/reset.blade.php @@ -109,7 +109,7 @@
- @if(config('captcha.enabled')) + @if((bool) config_cache('captcha.enabled'))
{!! Captcha::display(['data-theme' => 'dark']) !!} diff --git a/resources/views/settings/parental-controls/invite-register-form.blade.php b/resources/views/settings/parental-controls/invite-register-form.blade.php index 5b894e8d2..a21808efa 100644 --- a/resources/views/settings/parental-controls/invite-register-form.blade.php +++ b/resources/views/settings/parental-controls/invite-register-form.blade.php @@ -91,7 +91,7 @@
- @if(config('captcha.enabled') || config('captcha.active.register')) + @if((bool) config_cache('captcha.enabled'))
{!! Captcha::display() !!}
From 481314cd238e5356003e6faa961213dfce59e257 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 11 Mar 2024 22:42:26 -0600 Subject: [PATCH 03/41] Update custom emoji, add config_cache support --- app/Http/Controllers/AdminController.php | 12 ++++++------ app/Models/CustomEmoji.php | 2 +- app/Services/ConfigCacheService.php | 1 + app/Services/CustomEmojiService.php | 4 ++-- resources/views/admin/diagnostics/home.blade.php | 4 ++-- resources/views/auth/register.blade.php | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index e54908a41..102c1a901 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -424,7 +424,7 @@ class AdminController extends Controller public function customEmojiHome(Request $request) { - if(!config('federation.custom_emoji.enabled')) { + if(!(bool) config_cache('federation.custom_emoji.enabled')) { return view('admin.custom-emoji.not-enabled'); } $this->validate($request, [ @@ -497,7 +497,7 @@ class AdminController extends Controller public function customEmojiToggleActive(Request $request, $id) { - abort_unless(config('federation.custom_emoji.enabled'), 404); + abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404); $emoji = CustomEmoji::findOrFail($id); $emoji->disabled = !$emoji->disabled; $emoji->save(); @@ -508,13 +508,13 @@ class AdminController extends Controller public function customEmojiAdd(Request $request) { - abort_unless(config('federation.custom_emoji.enabled'), 404); + abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404); return view('admin.custom-emoji.add'); } public function customEmojiStore(Request $request) { - abort_unless(config('federation.custom_emoji.enabled'), 404); + abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404); $this->validate($request, [ 'shortcode' => [ 'required', @@ -545,7 +545,7 @@ class AdminController extends Controller public function customEmojiDelete(Request $request, $id) { - abort_unless(config('federation.custom_emoji.enabled'), 404); + abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404); $emoji = CustomEmoji::findOrFail($id); Storage::delete("public/{$emoji->media_path}"); Cache::forget('pf:custom_emoji'); @@ -555,7 +555,7 @@ class AdminController extends Controller public function customEmojiShowDuplicates(Request $request, $id) { - abort_unless(config('federation.custom_emoji.enabled'), 404); + abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404); $emoji = CustomEmoji::orderBy('id')->whereDisabled(false)->whereShortcode($id)->firstOrFail(); $emojis = CustomEmoji::whereShortcode($id)->where('id', '!=', $emoji->id)->cursorPaginate(10); return view('admin.custom-emoji.duplicates', compact('emoji', 'emojis')); diff --git a/app/Models/CustomEmoji.php b/app/Models/CustomEmoji.php index 1ff026a19..47aa0d1a8 100644 --- a/app/Models/CustomEmoji.php +++ b/app/Models/CustomEmoji.php @@ -18,7 +18,7 @@ class CustomEmoji extends Model public static function scan($text, $activitypub = false) { - if(config('federation.custom_emoji.enabled') == false) { + if((bool) config_cache('federation.custom_emoji.enabled') == false) { return []; } diff --git a/app/Services/ConfigCacheService.php b/app/Services/ConfigCacheService.php index 8205dc3bd..7e60942ca 100644 --- a/app/Services/ConfigCacheService.php +++ b/app/Services/ConfigCacheService.php @@ -97,6 +97,7 @@ class ConfigCacheService 'captcha.active.register', 'captcha.triggers.login.enabled', 'captcha.triggers.login.attempts', + 'federation.custom_emoji.enabled', // 'system.user_mode' ]; diff --git a/app/Services/CustomEmojiService.php b/app/Services/CustomEmojiService.php index a95c93a2a..468772b5f 100644 --- a/app/Services/CustomEmojiService.php +++ b/app/Services/CustomEmojiService.php @@ -13,7 +13,7 @@ class CustomEmojiService { public static function get($shortcode) { - if(config('federation.custom_emoji.enabled') == false) { + if((bool) config_cache('federation.custom_emoji.enabled') == false) { return; } @@ -22,7 +22,7 @@ class CustomEmojiService public static function import($url, $id = false) { - if(config('federation.custom_emoji.enabled') == false) { + if((bool) config_cache('federation.custom_emoji.enabled') == false) { return; } diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index db44a2332..204f6ce67 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -358,7 +358,7 @@ FEDERATION PF_NETWORK_TIMELINE - {{config_cache('federation.network_timeline') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('federation.network_timeline') ? '✅ true' : '❌ false' }} FEDERATION @@ -368,7 +368,7 @@ FEDERATION CUSTOM_EMOJI - {{config_cache('federation.custom_emoji.enabled') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('federation.custom_emoji.enabled') ? '✅ true' : '❌ false' }} FEDERATION diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index a2c008bd7..3cb70c7fe 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -81,7 +81,7 @@ - @if(config('captcha.enabled') || config('captcha.active.register')) + @if((bool) config_cache('captcha.enabled') && (bool) config_cache('captcha.active.register'))
{!! Captcha::display() !!}
From 75081e609a897fe0a6a9b64d5c43fdea1501704c Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 11 Mar 2024 23:26:30 -0600 Subject: [PATCH 04/41] Update ProfileController, handle permalink redirect bug --- app/Http/Controllers/ProfileController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 6471ed760..1dbb9a046 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -172,6 +172,8 @@ class ProfileController extends Controller $user = $this->getCachedUser($username); + abort_if(!$user, 404); + return redirect($user->url()); } From 8a0c456edc28842eaaaeacede8d2165c73eb3571 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 11 Mar 2024 23:27:43 -0600 Subject: [PATCH 05/41] Update admin css, use font-display:swap for nucleo icons --- resources/assets/sass/lib/nucleo.css | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/assets/sass/lib/nucleo.css b/resources/assets/sass/lib/nucleo.css index b03698950..4171a5523 100644 --- a/resources/assets/sass/lib/nucleo.css +++ b/resources/assets/sass/lib/nucleo.css @@ -10,6 +10,7 @@ License - nucleoapp.com/license/ src: url('/fonts/nucleo-icons.eot') format('embedded-opentype'), url('/fonts/nucleo-icons.woff2') format('woff2'), url('/fonts/nucleo-icons.woff') format('woff'), url('/fonts/nucleo-icons.ttf') format('truetype'), url('/fonts/nucleo-icons.svg') format('svg'); font-weight: normal; font-style: normal; + font-display: swap; } /*------------------------ base class definition From f08aab223124e87879c5ae365b3cef5aeecbb102 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 11 Mar 2024 23:43:23 -0600 Subject: [PATCH 06/41] Update PixelfedDirectoryController, fix boolean cast bug --- .../PixelfedDirectoryController.php | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/app/Http/Controllers/PixelfedDirectoryController.php b/app/Http/Controllers/PixelfedDirectoryController.php index cfe3f690a..65a86cba9 100644 --- a/app/Http/Controllers/PixelfedDirectoryController.php +++ b/app/Http/Controllers/PixelfedDirectoryController.php @@ -2,37 +2,38 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; use App\Models\ConfigCache; -use Storage; use App\Services\AccountService; use App\Services\StatusService; +use Illuminate\Http\Request; use Illuminate\Support\Str; +use Storage; class PixelfedDirectoryController extends Controller { public function get(Request $request) { - if(!$request->filled('sk')) { + if (! $request->filled('sk')) { abort(404); } - if(!config_cache('pixelfed.directory.submission-key')) { + if (! config_cache('pixelfed.directory.submission-key')) { abort(404); } - if(!hash_equals(config_cache('pixelfed.directory.submission-key'), $request->input('sk'))) { + if (! hash_equals(config_cache('pixelfed.directory.submission-key'), $request->input('sk'))) { abort(403); } $res = $this->buildListing(); - return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + + return response()->json($res, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } public function buildListing() { $res = config_cache('pixelfed.directory'); - if($res) { + if ($res) { $res = is_string($res) ? json_decode($res, true) : $res; } @@ -41,40 +42,40 @@ class PixelfedDirectoryController extends Controller $res['_ts'] = config_cache('pixelfed.directory.submission-ts'); $res['version'] = config_cache('pixelfed.version'); - if(empty($res['summary'])) { + if (empty($res['summary'])) { $summary = ConfigCache::whereK('app.short_description')->pluck('v'); $res['summary'] = $summary ? $summary[0] : null; } - if(isset($res['admin'])) { + if (isset($res['admin'])) { $res['admin'] = AccountService::get($res['admin'], true); } - if(isset($res['banner_image']) && !empty($res['banner_image'])) { + if (isset($res['banner_image']) && ! empty($res['banner_image'])) { $res['banner_image'] = url(Storage::url($res['banner_image'])); } - if(isset($res['favourite_posts'])) { - $res['favourite_posts'] = collect($res['favourite_posts'])->map(function($id) { + if (isset($res['favourite_posts'])) { + $res['favourite_posts'] = collect($res['favourite_posts'])->map(function ($id) { return StatusService::get($id); }) - ->filter(function($post) { - return $post && isset($post['account']); - }) - ->map(function($post) { - return [ - 'avatar' => $post['account']['avatar'], - 'display_name' => $post['account']['display_name'], - 'username' => $post['account']['username'], - 'media' => $post['media_attachments'][0]['url'], - 'url' => $post['url'] - ]; - }) - ->values(); + ->filter(function ($post) { + return $post && isset($post['account']); + }) + ->map(function ($post) { + return [ + 'avatar' => $post['account']['avatar'], + 'display_name' => $post['account']['display_name'], + 'username' => $post['account']['username'], + 'media' => $post['media_attachments'][0]['url'], + 'url' => $post['url'], + ]; + }) + ->values(); } $guidelines = ConfigCache::whereK('app.rules')->first(); - if($guidelines) { + if ($guidelines) { $res['community_guidelines'] = json_decode($guidelines->v, true); } @@ -85,27 +86,27 @@ class PixelfedDirectoryController extends Controller $res['curated_onboarding'] = $curatedOnboarding; $oauthEnabled = ConfigCache::whereK('pixelfed.oauth_enabled')->first(); - if($oauthEnabled) { + if ($oauthEnabled) { $keys = file_exists(storage_path('oauth-public.key')) && file_exists(storage_path('oauth-private.key')); $res['oauth_enabled'] = (bool) $oauthEnabled && $keys; } $activityPubEnabled = ConfigCache::whereK('federation.activitypub.enabled')->first(); - if($activityPubEnabled) { + if ($activityPubEnabled) { $res['activitypub_enabled'] = (bool) $activityPubEnabled; } $res['feature_config'] = [ 'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','), 'image_quality' => config_cache('pixelfed.image_quality'), - 'optimize_image' => config_cache('pixelfed.optimize_image'), + 'optimize_image' => (bool) config_cache('pixelfed.optimize_image'), 'max_photo_size' => config_cache('pixelfed.max_photo_size'), 'max_caption_length' => config_cache('pixelfed.max_caption_length'), 'max_altext_length' => config_cache('pixelfed.max_altext_length'), - 'enforce_account_limit' => config_cache('pixelfed.enforce_account_limit'), + 'enforce_account_limit' => (bool) config_cache('pixelfed.enforce_account_limit'), 'max_account_size' => config_cache('pixelfed.max_account_size'), 'max_album_length' => config_cache('pixelfed.max_album_length'), - 'account_deletion' => config_cache('pixelfed.account_deletion'), + 'account_deletion' => (bool) config_cache('pixelfed.account_deletion'), ]; $res['is_eligible'] = $this->validVal($res, 'admin') && @@ -115,24 +116,25 @@ class PixelfedDirectoryController extends Controller $this->validVal($res, 'privacy_pledge') && $this->validVal($res, 'location'); - if(config_cache('pixelfed.directory.testimonials')) { + if (config_cache('pixelfed.directory.testimonials')) { $res['testimonials'] = collect(json_decode(config_cache('pixelfed.directory.testimonials'), true)) - ->map(function($testimonial) { + ->map(function ($testimonial) { $profile = AccountService::get($testimonial['profile_id']); + return [ 'profile' => [ 'username' => $profile['username'], 'display_name' => $profile['display_name'], 'avatar' => $profile['avatar'], - 'created_at' => $profile['created_at'] + 'created_at' => $profile['created_at'], ], - 'body' => $testimonial['body'] + 'body' => $testimonial['body'], ]; }); } $res['features_enabled'] = [ - 'stories' => (bool) config_cache('instance.stories.enabled') + 'stories' => (bool) config_cache('instance.stories.enabled'), ]; $res['stats'] = [ @@ -150,19 +152,18 @@ class PixelfedDirectoryController extends Controller protected function validVal($res, $val, $count = false, $minLen = false) { - if(!isset($res[$val])) { + if (! isset($res[$val])) { return false; } - if($count) { + if ($count) { return count($res[$val]) >= $count; } - if($minLen) { + if ($minLen) { return strlen($res[$val]) >= $minLen; } return $res[$val]; } - } From f2f2a8097cbf7f59a58e3b10d93cd01ca8fea577 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 00:02:15 -0600 Subject: [PATCH 07/41] Update PixelfedDirectoryController, use cached stats --- .../Controllers/PixelfedDirectoryController.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/PixelfedDirectoryController.php b/app/Http/Controllers/PixelfedDirectoryController.php index 65a86cba9..0d2113a04 100644 --- a/app/Http/Controllers/PixelfedDirectoryController.php +++ b/app/Http/Controllers/PixelfedDirectoryController.php @@ -7,7 +7,10 @@ use App\Services\AccountService; use App\Services\StatusService; use Illuminate\Http\Request; use Illuminate\Support\Str; +use Cache; use Storage; +use App\Status; +use App\User; class PixelfedDirectoryController extends Controller { @@ -137,9 +140,15 @@ class PixelfedDirectoryController extends Controller 'stories' => (bool) config_cache('instance.stories.enabled'), ]; + $statusesCount = Cache::remember('api:nodeinfo:statuses', 21600, function() { + return Status::whereLocal(true)->count(); + }); + $usersCount = Cache::remember('api:nodeinfo:users', 43200, function() { + return User::count(); + }); $res['stats'] = [ - 'user_count' => \App\User::count(), - 'post_count' => \App\Status::whereNull('uri')->count(), + 'user_count' => (int) $usersCount, + 'post_count' => (int) $statusesCount, ]; $res['primary_locale'] = config('app.locale'); From ad506e901deacb66359df20b7e9589acc8159e3b Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 00:03:10 -0600 Subject: [PATCH 08/41] Update AdminDirectoryController, fix type casting --- app/Http/Controllers/Admin/AdminDirectoryController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Admin/AdminDirectoryController.php b/app/Http/Controllers/Admin/AdminDirectoryController.php index 8d3e4b7fc..2a4cc5ce8 100644 --- a/app/Http/Controllers/Admin/AdminDirectoryController.php +++ b/app/Http/Controllers/Admin/AdminDirectoryController.php @@ -84,14 +84,14 @@ trait AdminDirectoryController $res['feature_config'] = [ 'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','), 'image_quality' => config_cache('pixelfed.image_quality'), - 'optimize_image' => config_cache('pixelfed.optimize_image'), + 'optimize_image' => (bool) config_cache('pixelfed.optimize_image'), 'max_photo_size' => config_cache('pixelfed.max_photo_size'), 'max_caption_length' => config_cache('pixelfed.max_caption_length'), 'max_altext_length' => config_cache('pixelfed.max_altext_length'), - 'enforce_account_limit' => config_cache('pixelfed.enforce_account_limit'), + 'enforce_account_limit' => (bool) config_cache('pixelfed.enforce_account_limit'), 'max_account_size' => config_cache('pixelfed.max_account_size'), 'max_album_length' => config_cache('pixelfed.max_album_length'), - 'account_deletion' => config_cache('pixelfed.account_deletion'), + 'account_deletion' => (bool) config_cache('pixelfed.account_deletion'), ]; if(config_cache('pixelfed.directory.testimonials')) { From a72188a7db499ca057cdd1164c8b734322d270db Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 00:35:33 -0600 Subject: [PATCH 09/41] Update image pipeline, use config_cache --- app/Jobs/ImageOptimizePipeline/ImageOptimize.php | 2 +- app/Jobs/ImageOptimizePipeline/ImageResize.php | 2 +- app/Jobs/ImageOptimizePipeline/ImageUpdate.php | 2 +- app/Services/ConfigCacheService.php | 4 ++++ app/Util/Site/Config.php | 6 +++--- resources/views/admin/diagnostics/home.blade.php | 4 ++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/Jobs/ImageOptimizePipeline/ImageOptimize.php b/app/Jobs/ImageOptimizePipeline/ImageOptimize.php index 0448ade6a..e2d558143 100644 --- a/app/Jobs/ImageOptimizePipeline/ImageOptimize.php +++ b/app/Jobs/ImageOptimizePipeline/ImageOptimize.php @@ -45,7 +45,7 @@ class ImageOptimize implements ShouldQueue return; } - if(config('pixelfed.optimize_image') == false) { + if((bool) config_cache('pixelfed.optimize_image') == false) { ImageThumbnail::dispatch($media)->onQueue('mmo'); return; } else { diff --git a/app/Jobs/ImageOptimizePipeline/ImageResize.php b/app/Jobs/ImageOptimizePipeline/ImageResize.php index c1b4ea7f0..2aa51a532 100644 --- a/app/Jobs/ImageOptimizePipeline/ImageResize.php +++ b/app/Jobs/ImageOptimizePipeline/ImageResize.php @@ -51,7 +51,7 @@ class ImageResize implements ShouldQueue return; } - if(!config('pixelfed.optimize_image')) { + if((bool) config_cache('pixelfed.optimize_image') === false) { ImageThumbnail::dispatch($media)->onQueue('mmo'); return; } diff --git a/app/Jobs/ImageOptimizePipeline/ImageUpdate.php b/app/Jobs/ImageOptimizePipeline/ImageUpdate.php index 550448699..9012529f2 100644 --- a/app/Jobs/ImageOptimizePipeline/ImageUpdate.php +++ b/app/Jobs/ImageOptimizePipeline/ImageUpdate.php @@ -61,7 +61,7 @@ class ImageUpdate implements ShouldQueue return; } - if(config('pixelfed.optimize_image')) { + if((bool) config_cache('pixelfed.optimize_image')) { if (in_array($media->mime, $this->protectedMimes) == true) { ImageOptimizer::optimize($thumb); if(!$media->skip_optimize) { diff --git a/app/Services/ConfigCacheService.php b/app/Services/ConfigCacheService.php index 7e60942ca..c5bb9bca9 100644 --- a/app/Services/ConfigCacheService.php +++ b/app/Services/ConfigCacheService.php @@ -98,6 +98,10 @@ class ConfigCacheService 'captcha.triggers.login.enabled', 'captcha.triggers.login.attempts', 'federation.custom_emoji.enabled', + + 'pixelfed.optimize_image', + 'pixelfed.optimize_video', + 'pixelfed.max_collection_length', // 'system.user_mode' ]; diff --git a/app/Util/Site/Config.php b/app/Util/Site/Config.php index 038eef99e..e661d82fe 100644 --- a/app/Util/Site/Config.php +++ b/app/Util/Site/Config.php @@ -36,10 +36,10 @@ class Config 'album_limit' => (int) config_cache('pixelfed.max_album_length'), 'image_quality' => (int) config_cache('pixelfed.image_quality'), - 'max_collection_length' => (int) config('pixelfed.max_collection_length', 18), + 'max_collection_length' => (int) config_cache('pixelfed.max_collection_length', 18), - 'optimize_image' => (bool) config('pixelfed.optimize_image'), - 'optimize_video' => (bool) config('pixelfed.optimize_video'), + 'optimize_image' => (bool) config_cache('pixelfed.optimize_image'), + 'optimize_video' => (bool) config_cache('pixelfed.optimize_video'), 'media_types' => config_cache('pixelfed.media_types'), 'mime_types' => config_cache('pixelfed.media_types') ? explode(',', config_cache('pixelfed.media_types')) : [], diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index 204f6ce67..74f6100f0 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -750,12 +750,12 @@ PIXELFED PF_OPTIMIZE_IMAGES - {{config_cache('pixelfed.optimize_image') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('pixelfed.optimize_image') ? '✅ true' : '❌ false' }} PIXELFED PF_OPTIMIZE_VIDEOS - {{config_cache('pixelfed.optimize_video') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('pixelfed.optimize_video') ? '✅ true' : '❌ false' }} PIXELFED From 665581d80c48510d54e8bb605296b27768a95d95 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 01:03:33 -0600 Subject: [PATCH 10/41] Update cloud storage, use config_cache --- app/Console/Commands/AvatarStorage.php | 10 +- .../Commands/AvatarStorageDeepClean.php | 2 +- app/Console/Commands/CloudMediaMigrate.php | 6 +- app/Console/Commands/FixMediaDriver.php | 2 +- app/Console/Commands/MediaCloudUrlRewrite.php | 2 +- .../Commands/MediaS3GarbageCollector.php | 2 +- app/Console/Kernel.php | 2 +- app/Http/Controllers/ComposeController.php | 2 +- app/Http/Controllers/MediaController.php | 37 +- app/Http/Controllers/RemoteAuthController.php | 170 ++++----- app/Jobs/AvatarPipeline/AvatarOptimize.php | 152 ++++---- app/Jobs/AvatarPipeline/RemoteAvatarFetch.php | 157 ++++---- .../RemoteAvatarFetchFromUrl.php | 133 ++++--- .../MediaPipeline/MediaDeletePipeline.php | 97 ++--- ...MediaFixLocalFilesystemCleanupPipeline.php | 97 ++--- app/Observers/AvatarObserver.php | 14 +- app/Services/ConfigCacheService.php | 1 + app/Services/MediaStorageService.php | 117 +++--- app/Util/ActivityPub/Helpers.php | 337 +++++++++--------- .../views/admin/diagnostics/home.blade.php | 2 +- 20 files changed, 669 insertions(+), 673 deletions(-) diff --git a/app/Console/Commands/AvatarStorage.php b/app/Console/Commands/AvatarStorage.php index 054802f42..a6bb70e3d 100644 --- a/app/Console/Commands/AvatarStorage.php +++ b/app/Console/Commands/AvatarStorage.php @@ -82,7 +82,7 @@ class AvatarStorage extends Command $this->line(' '); - if(config_cache('pixelfed.cloud_storage')) { + if((bool) config_cache('pixelfed.cloud_storage')) { $this->info('✅ - Cloud storage configured'); $this->line(' '); } @@ -92,7 +92,7 @@ class AvatarStorage extends Command $this->line(' '); } - if(config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud')) { + if((bool) config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud')) { $disk = Storage::disk(config_cache('filesystems.cloud')); $exists = $disk->exists('cache/avatars/default.jpg'); $state = $exists ? '✅' : '❌'; @@ -100,7 +100,7 @@ class AvatarStorage extends Command $this->info($msg); } - $options = config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud') ? + $options = (bool) config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud') ? [ 'Cancel', 'Upload default avatar to cloud', @@ -164,7 +164,7 @@ class AvatarStorage extends Command protected function uploadAvatarsToCloud() { - if(!config_cache('pixelfed.cloud_storage') || !config('instance.avatar.local_to_cloud')) { + if(!(bool) config_cache('pixelfed.cloud_storage') || !config('instance.avatar.local_to_cloud')) { $this->error('Enable cloud storage and avatar cloud storage to perform this action'); return; } @@ -213,7 +213,7 @@ class AvatarStorage extends Command return; } - if(config_cache('pixelfed.cloud_storage') == false && config_cache('federation.avatars.store_local') == false) { + if((bool) config_cache('pixelfed.cloud_storage') == false && config_cache('federation.avatars.store_local') == false) { $this->error('You have cloud storage disabled and local avatar storage disabled, we cannot refetch avatars.'); return; } diff --git a/app/Console/Commands/AvatarStorageDeepClean.php b/app/Console/Commands/AvatarStorageDeepClean.php index 5840142f5..6f773bd42 100644 --- a/app/Console/Commands/AvatarStorageDeepClean.php +++ b/app/Console/Commands/AvatarStorageDeepClean.php @@ -44,7 +44,7 @@ class AvatarStorageDeepClean extends Command $this->line(' '); $storage = [ - 'cloud' => boolval(config_cache('pixelfed.cloud_storage')), + 'cloud' => (bool) config_cache('pixelfed.cloud_storage'), 'local' => boolval(config_cache('federation.avatars.store_local')) ]; diff --git a/app/Console/Commands/CloudMediaMigrate.php b/app/Console/Commands/CloudMediaMigrate.php index 0f2d177b8..174b33e83 100644 --- a/app/Console/Commands/CloudMediaMigrate.php +++ b/app/Console/Commands/CloudMediaMigrate.php @@ -35,12 +35,16 @@ class CloudMediaMigrate extends Command */ public function handle() { - $enabled = config('pixelfed.cloud_storage'); + $enabled = (bool) config_cache('pixelfed.cloud_storage'); if(!$enabled) { $this->error('Cloud storage not enabled. Exiting...'); return; } + if(!$this->confirm('Are you sure you want to proceed?')) { + return; + } + $limit = $this->option('limit'); $hugeMode = $this->option('huge'); diff --git a/app/Console/Commands/FixMediaDriver.php b/app/Console/Commands/FixMediaDriver.php index c743d6c64..a20b0574e 100644 --- a/app/Console/Commands/FixMediaDriver.php +++ b/app/Console/Commands/FixMediaDriver.php @@ -37,7 +37,7 @@ class FixMediaDriver extends Command return Command::SUCCESS; } - if(config_cache('pixelfed.cloud_storage') == false) { + if((bool) config_cache('pixelfed.cloud_storage') == false) { $this->error('Cloud storage not enabled, exiting...'); return Command::SUCCESS; } diff --git a/app/Console/Commands/MediaCloudUrlRewrite.php b/app/Console/Commands/MediaCloudUrlRewrite.php index 54329f7c7..367c22d1f 100644 --- a/app/Console/Commands/MediaCloudUrlRewrite.php +++ b/app/Console/Commands/MediaCloudUrlRewrite.php @@ -47,7 +47,7 @@ class MediaCloudUrlRewrite extends Command implements PromptsForMissingInput protected function preflightCheck() { - if(config_cache('pixelfed.cloud_storage') != true) { + if(!(bool) config_cache('pixelfed.cloud_storage')) { $this->info('Error: Cloud storage is not enabled!'); $this->error('Aborting...'); exit; diff --git a/app/Console/Commands/MediaS3GarbageCollector.php b/app/Console/Commands/MediaS3GarbageCollector.php index b6cda43c3..e66fdd2a8 100644 --- a/app/Console/Commands/MediaS3GarbageCollector.php +++ b/app/Console/Commands/MediaS3GarbageCollector.php @@ -45,7 +45,7 @@ class MediaS3GarbageCollector extends Command */ public function handle() { - $enabled = in_array(config_cache('pixelfed.cloud_storage'), ['1', true, 'true']); + $enabled = (bool) config_cache('pixelfed.cloud_storage'); if(!$enabled) { $this->error('Cloud storage not enabled. Exiting...'); return; diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index dcee73ee1..938696a1d 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -33,7 +33,7 @@ class Kernel extends ConsoleKernel $schedule->command('gc:passwordreset')->dailyAt('09:41')->onOneServer(); $schedule->command('gc:sessions')->twiceDaily(13, 23)->onOneServer(); - if (in_array(config_cache('pixelfed.cloud_storage'), ['1', true, 'true']) && config('media.delete_local_after_cloud')) { + if ((bool) config_cache('pixelfed.cloud_storage') && (bool) config_cache('media.delete_local_after_cloud')) { $schedule->command('media:s3gc')->hourlyAt(15); } diff --git a/app/Http/Controllers/ComposeController.php b/app/Http/Controllers/ComposeController.php index 341d56ea8..4c27aa18e 100644 --- a/app/Http/Controllers/ComposeController.php +++ b/app/Http/Controllers/ComposeController.php @@ -741,7 +741,7 @@ class ComposeController extends Controller case 'image/jpeg': case 'image/png': case 'video/mp4': - $finished = config_cache('pixelfed.cloud_storage') ? (bool) $media->cdn_url : (bool) $media->processed_at; + $finished = (bool) config_cache('pixelfed.cloud_storage') ? (bool) $media->cdn_url : (bool) $media->processed_at; break; default: diff --git a/app/Http/Controllers/MediaController.php b/app/Http/Controllers/MediaController.php index b10e75795..cbc08cb5a 100644 --- a/app/Http/Controllers/MediaController.php +++ b/app/Http/Controllers/MediaController.php @@ -2,30 +2,31 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; use App\Media; +use Illuminate\Http\Request; class MediaController extends Controller { - public function index(Request $request) - { - //return view('settings.drive.index'); - } + public function index(Request $request) + { + //return view('settings.drive.index'); + abort(404); + } - public function composeUpdate(Request $request, $id) - { + public function composeUpdate(Request $request, $id) + { abort(400, 'Endpoint deprecated'); - } + } - public function fallbackRedirect(Request $request, $pid, $mhash, $uhash, $f) - { - abort_if(!config_cache('pixelfed.cloud_storage'), 404); - $path = 'public/m/_v2/' . $pid . '/' . $mhash . '/' . $uhash . '/' . $f; - $media = Media::whereProfileId($pid) - ->whereMediaPath($path) - ->whereNotNull('cdn_url') - ->firstOrFail(); + public function fallbackRedirect(Request $request, $pid, $mhash, $uhash, $f) + { + abort_if(! (bool) config_cache('pixelfed.cloud_storage'), 404); + $path = 'public/m/_v2/'.$pid.'/'.$mhash.'/'.$uhash.'/'.$f; + $media = Media::whereProfileId($pid) + ->whereMediaPath($path) + ->whereNotNull('cdn_url') + ->firstOrFail(); - return redirect()->away($media->cdn_url); - } + return redirect()->away($media->cdn_url); + } } diff --git a/app/Http/Controllers/RemoteAuthController.php b/app/Http/Controllers/RemoteAuthController.php index e068f5d75..e0afd82ef 100644 --- a/app/Http/Controllers/RemoteAuthController.php +++ b/app/Http/Controllers/RemoteAuthController.php @@ -2,22 +2,20 @@ namespace App\Http\Controllers; -use Illuminate\Support\Str; -use Illuminate\Http\Request; -use App\Services\Account\RemoteAuthService; use App\Models\RemoteAuth; -use App\Profile; -use App\Instance; -use App\User; -use Purify; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; -use Illuminate\Auth\Events\Registered; -use App\Util\Lexer\RestrictedNames; +use App\Services\Account\RemoteAuthService; use App\Services\EmailService; use App\Services\MediaStorageService; +use App\User; use App\Util\ActivityPub\Helpers; +use App\Util\Lexer\RestrictedNames; +use Illuminate\Auth\Events\Registered; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Str; use InvalidArgumentException; +use Purify; class RemoteAuthController extends Controller { @@ -30,9 +28,10 @@ class RemoteAuthController extends Controller config('remote-auth.mastodon.ignore_closed_state') && config('remote-auth.mastodon.enabled') ), 404); - if($request->user()) { + if ($request->user()) { return redirect('/'); } + return view('auth.remote.start'); } @@ -51,25 +50,27 @@ class RemoteAuthController extends Controller config('remote-auth.mastodon.enabled') ), 404); - if(config('remote-auth.mastodon.domains.only_custom')) { + if (config('remote-auth.mastodon.domains.only_custom')) { $res = config('remote-auth.mastodon.domains.custom'); - if(!$res || !strlen($res)) { + if (! $res || ! strlen($res)) { return []; } $res = explode(',', $res); + return response()->json($res); } - if( config('remote-auth.mastodon.domains.custom') && - !config('remote-auth.mastodon.domains.only_default') && + if (config('remote-auth.mastodon.domains.custom') && + ! config('remote-auth.mastodon.domains.only_default') && strlen(config('remote-auth.mastodon.domains.custom')) > 3 && strpos(config('remote-auth.mastodon.domains.custom'), '.') > -1 ) { $res = config('remote-auth.mastodon.domains.custom'); - if(!$res || !strlen($res)) { + if (! $res || ! strlen($res)) { return []; } $res = explode(',', $res); + return response()->json($res); } @@ -93,57 +94,62 @@ class RemoteAuthController extends Controller $domain = $request->input('domain'); - if(str_starts_with(strtolower($domain), 'http')) { + if (str_starts_with(strtolower($domain), 'http')) { $res = [ 'domain' => $domain, 'ready' => false, - 'action' => 'incompatible_domain' + 'action' => 'incompatible_domain', ]; + return response()->json($res); } - $validateInstance = Helpers::validateUrl('https://' . $domain . '/?block-check=' . time()); + $validateInstance = Helpers::validateUrl('https://'.$domain.'/?block-check='.time()); - if(!$validateInstance) { - $res = [ + if (! $validateInstance) { + $res = [ 'domain' => $domain, 'ready' => false, - 'action' => 'blocked_domain' + 'action' => 'blocked_domain', ]; + return response()->json($res); } $compatible = RemoteAuthService::isDomainCompatible($domain); - if(!$compatible) { + if (! $compatible) { $res = [ 'domain' => $domain, 'ready' => false, - 'action' => 'incompatible_domain' + 'action' => 'incompatible_domain', ]; + return response()->json($res); } - if(config('remote-auth.mastodon.domains.only_default')) { + if (config('remote-auth.mastodon.domains.only_default')) { $defaultDomains = explode(',', config('remote-auth.mastodon.domains.default')); - if(!in_array($domain, $defaultDomains)) { + if (! in_array($domain, $defaultDomains)) { $res = [ 'domain' => $domain, 'ready' => false, - 'action' => 'incompatible_domain' + 'action' => 'incompatible_domain', ]; + return response()->json($res); } } - if(config('remote-auth.mastodon.domains.only_custom') && config('remote-auth.mastodon.domains.custom')) { + if (config('remote-auth.mastodon.domains.only_custom') && config('remote-auth.mastodon.domains.custom')) { $customDomains = explode(',', config('remote-auth.mastodon.domains.custom')); - if(!in_array($domain, $customDomains)) { + if (! in_array($domain, $customDomains)) { $res = [ 'domain' => $domain, 'ready' => false, - 'action' => 'incompatible_domain' + 'action' => 'incompatible_domain', ]; + return response()->json($res); } } @@ -163,13 +169,13 @@ class RemoteAuthController extends Controller 'state' => $state, ]); - $request->session()->put('oauth_redirect_to', 'https://' . $domain . '/oauth/authorize?' . $query); + $request->session()->put('oauth_redirect_to', 'https://'.$domain.'/oauth/authorize?'.$query); $dsh = Str::random(17); $res = [ 'domain' => $domain, 'ready' => true, - 'dsh' => $dsh + 'dsh' => $dsh, ]; return response()->json($res); @@ -185,7 +191,7 @@ class RemoteAuthController extends Controller config('remote-auth.mastodon.enabled') ), 404); - if(!$request->filled('d') || !$request->filled('dsh') || !$request->session()->exists('oauth_redirect_to')) { + if (! $request->filled('d') || ! $request->filled('dsh') || ! $request->session()->exists('oauth_redirect_to')) { return redirect('/login'); } @@ -204,7 +210,7 @@ class RemoteAuthController extends Controller $domain = $request->session()->get('oauth_domain'); - if($request->filled('code')) { + if ($request->filled('code')) { $code = $request->input('code'); $state = $request->session()->pull('state'); @@ -216,12 +222,14 @@ class RemoteAuthController extends Controller $res = RemoteAuthService::getToken($domain, $code); - if(!$res || !isset($res['access_token'])) { + if (! $res || ! isset($res['access_token'])) { $request->session()->regenerate(); + return redirect('/login'); } $request->session()->put('oauth_remote_session_token', $res['access_token']); + return redirect('/auth/mastodon/getting-started'); } @@ -237,9 +245,10 @@ class RemoteAuthController extends Controller config('remote-auth.mastodon.ignore_closed_state') && config('remote-auth.mastodon.enabled') ), 404); - if($request->user()) { + if ($request->user()) { return redirect('/'); } + return view('auth.remote.onboarding'); } @@ -261,36 +270,36 @@ class RemoteAuthController extends Controller $res = RemoteAuthService::getVerifyCredentials($domain, $token); - abort_if(!$res || !isset($res['acct']), 403, 'Invalid credentials'); + abort_if(! $res || ! isset($res['acct']), 403, 'Invalid credentials'); - $webfinger = strtolower('@' . $res['acct'] . '@' . $domain); + $webfinger = strtolower('@'.$res['acct'].'@'.$domain); $request->session()->put('oauth_masto_webfinger', $webfinger); - if(config('remote-auth.mastodon.max_uses.enabled')) { + if (config('remote-auth.mastodon.max_uses.enabled')) { $limit = config('remote-auth.mastodon.max_uses.limit'); $uses = RemoteAuthService::lookupWebfingerUses($webfinger); - if($uses >= $limit) { + if ($uses >= $limit) { return response()->json([ 'code' => 200, 'msg' => 'Success!', - 'action' => 'max_uses_reached' + 'action' => 'max_uses_reached', ]); } } $exists = RemoteAuth::whereDomain($domain)->where('webfinger', $webfinger)->whereNotNull('user_id')->first(); - if($exists && $exists->user_id) { + if ($exists && $exists->user_id) { return response()->json([ 'code' => 200, 'msg' => 'Success!', - 'action' => 'redirect_existing_user' + 'action' => 'redirect_existing_user', ]); } return response()->json([ 'code' => 200, 'msg' => 'Success!', - 'action' => 'onboard' + 'action' => 'onboard', ]); } @@ -311,7 +320,7 @@ class RemoteAuthController extends Controller $token = $request->session()->get('oauth_remote_session_token'); $res = RemoteAuthService::getVerifyCredentials($domain, $token); - $res['_webfinger'] = strtolower('@' . $res['acct'] . '@' . $domain); + $res['_webfinger'] = strtolower('@'.$res['acct'].'@'.$domain); $res['_domain'] = strtolower($domain); $request->session()->put('oauth_remasto_id', $res['id']); @@ -324,7 +333,7 @@ class RemoteAuthController extends Controller 'bearer_token' => $token, 'verify_credentials' => $res, 'last_verify_credentials_at' => now(), - 'last_successful_login_at' => now() + 'last_successful_login_at' => now(), ]); $request->session()->put('oauth_masto_raid', $ra->id); @@ -355,24 +364,24 @@ class RemoteAuthController extends Controller $underscore = substr_count($value, '_'); $period = substr_count($value, '.'); - if(ends_with($value, ['.php', '.js', '.css'])) { + if (ends_with($value, ['.php', '.js', '.css'])) { return $fail('Username is invalid.'); } - if(($dash + $underscore + $period) > 1) { + if (($dash + $underscore + $period) > 1) { return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).'); } - if (!ctype_alnum($value[0])) { + if (! ctype_alnum($value[0])) { return $fail('Username is invalid. Must start with a letter or number.'); } - if (!ctype_alnum($value[strlen($value) - 1])) { + if (! ctype_alnum($value[strlen($value) - 1])) { return $fail('Username is invalid. Must end with a letter or number.'); } $val = str_replace(['_', '.', '-'], '', $value); - if(!ctype_alnum($val)) { + if (! ctype_alnum($val)) { return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).'); } @@ -380,8 +389,8 @@ class RemoteAuthController extends Controller if (in_array(strtolower($value), array_map('strtolower', $restricted))) { return $fail('Username cannot be used.'); } - } - ] + }, + ], ]); $username = strtolower($request->input('username')); @@ -390,7 +399,7 @@ class RemoteAuthController extends Controller return response()->json([ 'code' => 200, 'username' => $username, - 'exists' => $exists + 'exists' => $exists, ]); } @@ -411,7 +420,7 @@ class RemoteAuthController extends Controller 'email' => [ 'required', 'email:strict,filter_unicode,dns,spoof', - ] + ], ]); $email = $request->input('email'); @@ -422,7 +431,7 @@ class RemoteAuthController extends Controller 'code' => 200, 'email' => $email, 'exists' => $exists, - 'banned' => $banned + 'banned' => $banned, ]); } @@ -445,18 +454,18 @@ class RemoteAuthController extends Controller $res = RemoteAuthService::getFollowing($domain, $token, $id); - if(!$res) { + if (! $res) { return response()->json([ 'code' => 200, - 'following' => [] + 'following' => [], ]); } - $res = collect($res)->filter(fn($acct) => Helpers::validateUrl($acct['url']))->values()->toArray(); + $res = collect($res)->filter(fn ($acct) => Helpers::validateUrl($acct['url']))->values()->toArray(); return response()->json([ 'code' => 200, - 'following' => $res + 'following' => $res, ]); } @@ -487,24 +496,24 @@ class RemoteAuthController extends Controller $underscore = substr_count($value, '_'); $period = substr_count($value, '.'); - if(ends_with($value, ['.php', '.js', '.css'])) { + if (ends_with($value, ['.php', '.js', '.css'])) { return $fail('Username is invalid.'); } - if(($dash + $underscore + $period) > 1) { + if (($dash + $underscore + $period) > 1) { return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).'); } - if (!ctype_alnum($value[0])) { + if (! ctype_alnum($value[0])) { return $fail('Username is invalid. Must start with a letter or number.'); } - if (!ctype_alnum($value[strlen($value) - 1])) { + if (! ctype_alnum($value[strlen($value) - 1])) { return $fail('Username is invalid. Must end with a letter or number.'); } $val = str_replace(['_', '.', '-'], '', $value); - if(!ctype_alnum($val)) { + if (! ctype_alnum($val)) { return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).'); } @@ -512,10 +521,10 @@ class RemoteAuthController extends Controller if (in_array(strtolower($value), array_map('strtolower', $restricted))) { return $fail('Username cannot be used.'); } - } + }, ], 'password' => 'required|string|min:8|confirmed', - 'name' => 'nullable|max:30' + 'name' => 'nullable|max:30', ]); $email = $request->input('email'); @@ -527,7 +536,7 @@ class RemoteAuthController extends Controller 'name' => $name, 'username' => $username, 'password' => $password, - 'email' => $email + 'email' => $email, ]); $raid = $request->session()->pull('oauth_masto_raid'); @@ -541,7 +550,7 @@ class RemoteAuthController extends Controller return [ 'code' => 200, 'msg' => 'Success', - 'token' => $token + 'token' => $token, ]; } @@ -585,7 +594,7 @@ class RemoteAuthController extends Controller abort_unless($request->session()->exists('oauth_remasto_id'), 403); $this->validate($request, [ - 'account' => 'required|url' + 'account' => 'required|url', ]); $account = $request->input('account'); @@ -594,10 +603,10 @@ class RemoteAuthController extends Controller $host = strtolower(config('pixelfed.domain.app')); $domain = strtolower(parse_url($account, PHP_URL_HOST)); - if($domain == $host) { + if ($domain == $host) { $username = Str::of($account)->explode('/')->last(); $user = User::where('username', $username)->first(); - if($user) { + if ($user) { return ['id' => (string) $user->profile_id]; } else { return []; @@ -605,7 +614,7 @@ class RemoteAuthController extends Controller } else { try { $profile = Helpers::profileFetch($account); - if($profile) { + if ($profile) { return ['id' => (string) $profile->id]; } else { return []; @@ -635,13 +644,13 @@ class RemoteAuthController extends Controller $user = $request->user(); $profile = $user->profile; - abort_if(!$profile->avatar, 404, 'Missing avatar'); + abort_if(! $profile->avatar, 404, 'Missing avatar'); $avatar = $profile->avatar; $avatar->remote_url = $request->input('avatar_url'); $avatar->save(); - MediaStorageService::avatar($avatar, config_cache('pixelfed.cloud_storage') == false); + MediaStorageService::avatar($avatar, (bool) config_cache('pixelfed.cloud_storage') == false); return [200]; } @@ -657,7 +666,7 @@ class RemoteAuthController extends Controller ), 404); abort_unless($request->user(), 404); - $currentWebfinger = '@' . $request->user()->username . '@' . config('pixelfed.domain.app'); + $currentWebfinger = '@'.$request->user()->username.'@'.config('pixelfed.domain.app'); $ra = RemoteAuth::where('user_id', $request->user()->id)->firstOrFail(); RemoteAuthService::submitToBeagle( $ra->webfinger, @@ -691,19 +700,20 @@ class RemoteAuthController extends Controller $user = User::findOrFail($ra->user_id); abort_if($user->is_admin || $user->status != null, 422, 'Invalid auth action'); Auth::loginUsingId($ra->user_id); + return [200]; } protected function createUser($data) { event(new Registered($user = User::create([ - 'name' => Purify::clean($data['name']), + 'name' => Purify::clean($data['name']), 'username' => $data['username'], - 'email' => $data['email'], + 'email' => $data['email'], 'password' => Hash::make($data['password']), 'email_verified_at' => config('remote-auth.mastodon.contraints.skip_email_verification') ? now() : null, 'app_register_ip' => request()->ip(), - 'register_source' => 'mastodon' + 'register_source' => 'mastodon', ]))); $this->guarder()->login($user); diff --git a/app/Jobs/AvatarPipeline/AvatarOptimize.php b/app/Jobs/AvatarPipeline/AvatarOptimize.php index 4464dff4e..8b50d8330 100644 --- a/app/Jobs/AvatarPipeline/AvatarOptimize.php +++ b/app/Jobs/AvatarPipeline/AvatarOptimize.php @@ -2,9 +2,9 @@ namespace App\Jobs\AvatarPipeline; -use Cache; use App\Avatar; use App\Profile; +use Cache; use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -17,88 +17,88 @@ use Storage; class AvatarOptimize implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $profile; - protected $current; + protected $profile; - /** - * Delete the job if its models no longer exist. - * - * @var bool - */ - public $deleteWhenMissingModels = true; + protected $current; - /** - * Create a new job instance. - * - * @return void - */ - public function __construct(Profile $profile, $current) - { - $this->profile = $profile; - $this->current = $current; - } + /** + * Delete the job if its models no longer exist. + * + * @var bool + */ + public $deleteWhenMissingModels = true; - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $avatar = $this->profile->avatar; - $file = storage_path("app/$avatar->media_path"); + /** + * Create a new job instance. + * + * @return void + */ + public function __construct(Profile $profile, $current) + { + $this->profile = $profile; + $this->current = $current; + } - try { - $img = Intervention::make($file)->orientate(); - $img->fit(200, 200, function ($constraint) { - $constraint->upsize(); - }); - $quality = config_cache('pixelfed.image_quality'); - $img->save($file, $quality); + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $avatar = $this->profile->avatar; + $file = storage_path("app/$avatar->media_path"); - $avatar = Avatar::whereProfileId($this->profile->id)->firstOrFail(); - $avatar->change_count = ++$avatar->change_count; - $avatar->last_processed_at = Carbon::now(); - $avatar->save(); - Cache::forget('avatar:' . $avatar->profile_id); - $this->deleteOldAvatar($avatar->media_path, $this->current); + try { + $img = Intervention::make($file)->orientate(); + $img->fit(200, 200, function ($constraint) { + $constraint->upsize(); + }); + $quality = config_cache('pixelfed.image_quality'); + $img->save($file, $quality); - if(config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud')) { - $this->uploadToCloud($avatar); - } else { - $avatar->cdn_url = null; - $avatar->save(); - } - } catch (Exception $e) { - } - } + $avatar = Avatar::whereProfileId($this->profile->id)->firstOrFail(); + $avatar->change_count = ++$avatar->change_count; + $avatar->last_processed_at = Carbon::now(); + $avatar->save(); + Cache::forget('avatar:'.$avatar->profile_id); + $this->deleteOldAvatar($avatar->media_path, $this->current); - protected function deleteOldAvatar($new, $current) - { - if ( storage_path('app/'.$new) == $current || - Str::endsWith($current, 'avatars/default.png') || - Str::endsWith($current, 'avatars/default.jpg')) - { - return; - } - if (is_file($current)) { - @unlink($current); - } - } + if ((bool) config_cache('pixelfed.cloud_storage') && (bool) config_cache('instance.avatar.local_to_cloud')) { + $this->uploadToCloud($avatar); + } else { + $avatar->cdn_url = null; + $avatar->save(); + } + } catch (Exception $e) { + } + } - protected function uploadToCloud($avatar) - { - $base = 'cache/avatars/' . $avatar->profile_id; - $disk = Storage::disk(config('filesystems.cloud')); - $disk->deleteDirectory($base); - $path = $base . '/' . 'avatar_' . strtolower(Str::random(random_int(3,6))) . $avatar->change_count . '.' . pathinfo($avatar->media_path, PATHINFO_EXTENSION); - $url = $disk->put($path, Storage::get($avatar->media_path)); - $avatar->media_path = $path; - $avatar->cdn_url = $disk->url($path); - $avatar->save(); - Storage::delete($avatar->media_path); - Cache::forget('avatar:' . $avatar->profile_id); - } + protected function deleteOldAvatar($new, $current) + { + if (storage_path('app/'.$new) == $current || + Str::endsWith($current, 'avatars/default.png') || + Str::endsWith($current, 'avatars/default.jpg')) { + return; + } + if (is_file($current)) { + @unlink($current); + } + } + + protected function uploadToCloud($avatar) + { + $base = 'cache/avatars/'.$avatar->profile_id; + $disk = Storage::disk(config('filesystems.cloud')); + $disk->deleteDirectory($base); + $path = $base.'/'.'avatar_'.strtolower(Str::random(random_int(3, 6))).$avatar->change_count.'.'.pathinfo($avatar->media_path, PATHINFO_EXTENSION); + $url = $disk->put($path, Storage::get($avatar->media_path)); + $avatar->media_path = $path; + $avatar->cdn_url = $disk->url($path); + $avatar->save(); + Storage::delete($avatar->media_path); + Cache::forget('avatar:'.$avatar->profile_id); + } } diff --git a/app/Jobs/AvatarPipeline/RemoteAvatarFetch.php b/app/Jobs/AvatarPipeline/RemoteAvatarFetch.php index 4e4a1b2ec..c2a2b2a16 100644 --- a/app/Jobs/AvatarPipeline/RemoteAvatarFetch.php +++ b/app/Jobs/AvatarPipeline/RemoteAvatarFetch.php @@ -4,112 +4,107 @@ namespace App\Jobs\AvatarPipeline; use App\Avatar; use App\Profile; +use App\Services\MediaStorageService; +use App\Util\ActivityPub\Helpers; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use App\Util\ActivityPub\Helpers; -use Illuminate\Support\Str; -use Zttp\Zttp; -use App\Http\Controllers\AvatarController; -use Storage; -use Log; -use Illuminate\Http\File; -use App\Services\MediaStorageService; -use App\Services\ActivityPubFetchService; class RemoteAvatarFetch implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $profile; + protected $profile; - /** - * Delete the job if its models no longer exist. - * - * @var bool - */ - public $deleteWhenMissingModels = true; + /** + * Delete the job if its models no longer exist. + * + * @var bool + */ + public $deleteWhenMissingModels = true; - /** - * The number of times the job may be attempted. - * - * @var int - */ - public $tries = 1; - public $timeout = 300; - public $maxExceptions = 1; + /** + * The number of times the job may be attempted. + * + * @var int + */ + public $tries = 1; - /** - * Create a new job instance. - * - * @return void - */ - public function __construct(Profile $profile) - { - $this->profile = $profile; - } + public $timeout = 300; - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $profile = $this->profile; + public $maxExceptions = 1; - if(boolval(config_cache('pixelfed.cloud_storage')) == false && boolval(config_cache('federation.avatars.store_local')) == false) { - return 1; - } + /** + * Create a new job instance. + * + * @return void + */ + public function __construct(Profile $profile) + { + $this->profile = $profile; + } - if($profile->domain == null || $profile->private_key) { - return 1; - } + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $profile = $this->profile; - $avatar = Avatar::whereProfileId($profile->id)->first(); + if ((bool) config_cache('pixelfed.cloud_storage') == false && (bool) config_cache('federation.avatars.store_local') == false) { + return 1; + } - if(!$avatar) { - $avatar = new Avatar; - $avatar->profile_id = $profile->id; - $avatar->save(); - } + if ($profile->domain == null || $profile->private_key) { + return 1; + } - if($avatar->media_path == null && $avatar->remote_url == null) { - $avatar->media_path = 'public/avatars/default.jpg'; - $avatar->is_remote = true; - $avatar->save(); - } + $avatar = Avatar::whereProfileId($profile->id)->first(); - $person = Helpers::fetchFromUrl($profile->remote_url); + if (! $avatar) { + $avatar = new Avatar; + $avatar->profile_id = $profile->id; + $avatar->save(); + } - if(!$person || !isset($person['@context'])) { - return 1; - } + if ($avatar->media_path == null && $avatar->remote_url == null) { + $avatar->media_path = 'public/avatars/default.jpg'; + $avatar->is_remote = true; + $avatar->save(); + } - if( !isset($person['icon']) || - !isset($person['icon']['type']) || - !isset($person['icon']['url']) - ) { - return 1; - } + $person = Helpers::fetchFromUrl($profile->remote_url); - if($person['icon']['type'] !== 'Image') { - return 1; - } + if (! $person || ! isset($person['@context'])) { + return 1; + } - if(!Helpers::validateUrl($person['icon']['url'])) { - return 1; - } + if (! isset($person['icon']) || + ! isset($person['icon']['type']) || + ! isset($person['icon']['url']) + ) { + return 1; + } - $icon = $person['icon']; + if ($person['icon']['type'] !== 'Image') { + return 1; + } - $avatar->remote_url = $icon['url']; - $avatar->save(); + if (! Helpers::validateUrl($person['icon']['url'])) { + return 1; + } - MediaStorageService::avatar($avatar, boolval(config_cache('pixelfed.cloud_storage')) == false, true); + $icon = $person['icon']; - return 1; - } + $avatar->remote_url = $icon['url']; + $avatar->save(); + + MediaStorageService::avatar($avatar, (bool) config_cache('pixelfed.cloud_storage') == false, true); + + return 1; + } } diff --git a/app/Jobs/AvatarPipeline/RemoteAvatarFetchFromUrl.php b/app/Jobs/AvatarPipeline/RemoteAvatarFetchFromUrl.php index c8c6820e4..f8a63c5bd 100644 --- a/app/Jobs/AvatarPipeline/RemoteAvatarFetchFromUrl.php +++ b/app/Jobs/AvatarPipeline/RemoteAvatarFetchFromUrl.php @@ -4,93 +4,88 @@ namespace App\Jobs\AvatarPipeline; use App\Avatar; use App\Profile; +use App\Services\AccountService; +use App\Services\MediaStorageService; +use Cache; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use App\Util\ActivityPub\Helpers; -use Illuminate\Support\Str; -use Zttp\Zttp; -use App\Http\Controllers\AvatarController; -use Cache; -use Storage; -use Log; -use Illuminate\Http\File; -use App\Services\AccountService; -use App\Services\MediaStorageService; -use App\Services\ActivityPubFetchService; class RemoteAvatarFetchFromUrl implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $profile; - protected $url; + protected $profile; - /** - * Delete the job if its models no longer exist. - * - * @var bool - */ - public $deleteWhenMissingModels = true; + protected $url; - /** - * The number of times the job may be attempted. - * - * @var int - */ - public $tries = 1; - public $timeout = 300; - public $maxExceptions = 1; + /** + * Delete the job if its models no longer exist. + * + * @var bool + */ + public $deleteWhenMissingModels = true; - /** - * Create a new job instance. - * - * @return void - */ - public function __construct(Profile $profile, $url) - { - $this->profile = $profile; - $this->url = $url; - } + /** + * The number of times the job may be attempted. + * + * @var int + */ + public $tries = 1; - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $profile = $this->profile; + public $timeout = 300; - Cache::forget('avatar:' . $profile->id); - AccountService::del($profile->id); + public $maxExceptions = 1; - if(boolval(config_cache('pixelfed.cloud_storage')) == false && boolval(config_cache('federation.avatars.store_local')) == false) { - return 1; - } + /** + * Create a new job instance. + * + * @return void + */ + public function __construct(Profile $profile, $url) + { + $this->profile = $profile; + $this->url = $url; + } - if($profile->domain == null || $profile->private_key) { - return 1; - } + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $profile = $this->profile; - $avatar = Avatar::whereProfileId($profile->id)->first(); + Cache::forget('avatar:'.$profile->id); + AccountService::del($profile->id); - if(!$avatar) { - $avatar = new Avatar; - $avatar->profile_id = $profile->id; - $avatar->is_remote = true; - $avatar->remote_url = $this->url; - $avatar->save(); - } else { - $avatar->remote_url = $this->url; - $avatar->is_remote = true; - $avatar->save(); - } + if ((bool) config_cache('pixelfed.cloud_storage') == false && (bool) config_cache('federation.avatars.store_local') == false) { + return 1; + } - MediaStorageService::avatar($avatar, boolval(config_cache('pixelfed.cloud_storage')) == false, true); + if ($profile->domain == null || $profile->private_key) { + return 1; + } - return 1; - } + $avatar = Avatar::whereProfileId($profile->id)->first(); + + if (! $avatar) { + $avatar = new Avatar; + $avatar->profile_id = $profile->id; + $avatar->is_remote = true; + $avatar->remote_url = $this->url; + $avatar->save(); + } else { + $avatar->remote_url = $this->url; + $avatar->is_remote = true; + $avatar->save(); + } + + MediaStorageService::avatar($avatar, (bool) config_cache('pixelfed.cloud_storage') == false, true); + + return 1; + } } diff --git a/app/Jobs/MediaPipeline/MediaDeletePipeline.php b/app/Jobs/MediaPipeline/MediaDeletePipeline.php index 55df84948..df16a42d5 100644 --- a/app/Jobs/MediaPipeline/MediaDeletePipeline.php +++ b/app/Jobs/MediaPipeline/MediaDeletePipeline.php @@ -3,27 +3,30 @@ namespace App\Jobs\MediaPipeline; use App\Media; +use App\Services\Media\MediaHlsService; use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Redis; -use Illuminate\Support\Facades\Storage; -use App\Services\Media\MediaHlsService; use Illuminate\Queue\Middleware\WithoutOverlapping; -use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing; +use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Storage; -class MediaDeletePipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing +class MediaDeletePipeline implements ShouldBeUniqueUntilProcessing, ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $media; + protected $media; public $timeout = 300; + public $tries = 3; + public $maxExceptions = 1; + public $failOnTimeout = true; + public $deleteWhenMissingModels = true; /** @@ -38,7 +41,7 @@ class MediaDeletePipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing */ public function uniqueId(): string { - return 'media:purge-job:id-' . $this->media->id; + return 'media:purge-job:id-'.$this->media->id; } /** @@ -51,58 +54,58 @@ class MediaDeletePipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing return [(new WithoutOverlapping("media:purge-job:id-{$this->media->id}"))->shared()->dontRelease()]; } - public function __construct(Media $media) - { - $this->media = $media; - } + public function __construct(Media $media) + { + $this->media = $media; + } - public function handle() - { - $media = $this->media; - $path = $media->media_path; - $thumb = $media->thumbnail_path; + public function handle() + { + $media = $this->media; + $path = $media->media_path; + $thumb = $media->thumbnail_path; - if(!$path) { - return 1; - } + if (! $path) { + return 1; + } - $e = explode('/', $path); - array_pop($e); - $i = implode('/', $e); + $e = explode('/', $path); + array_pop($e); + $i = implode('/', $e); - if(config_cache('pixelfed.cloud_storage') == true) { - $disk = Storage::disk(config('filesystems.cloud')); + if ((bool) config_cache('pixelfed.cloud_storage') == true) { + $disk = Storage::disk(config('filesystems.cloud')); - if($path && $disk->exists($path)) { - $disk->delete($path); - } + if ($path && $disk->exists($path)) { + $disk->delete($path); + } - if($thumb && $disk->exists($thumb)) { - $disk->delete($thumb); - } - } + if ($thumb && $disk->exists($thumb)) { + $disk->delete($thumb); + } + } - $disk = Storage::disk(config('filesystems.local')); + $disk = Storage::disk(config('filesystems.local')); - if($path && $disk->exists($path)) { - $disk->delete($path); - } + if ($path && $disk->exists($path)) { + $disk->delete($path); + } - if($thumb && $disk->exists($thumb)) { - $disk->delete($thumb); - } + if ($thumb && $disk->exists($thumb)) { + $disk->delete($thumb); + } - if($media->hls_path != null) { + if ($media->hls_path != null) { $files = MediaHlsService::allFiles($media); - if($files && count($files)) { - foreach($files as $file) { + if ($files && count($files)) { + foreach ($files as $file) { $disk->delete($file); } } - } + } - $media->delete(); + $media->delete(); - return 1; - } + return 1; + } } diff --git a/app/Jobs/MediaPipeline/MediaFixLocalFilesystemCleanupPipeline.php b/app/Jobs/MediaPipeline/MediaFixLocalFilesystemCleanupPipeline.php index bbd3851b9..a972f1f86 100644 --- a/app/Jobs/MediaPipeline/MediaFixLocalFilesystemCleanupPipeline.php +++ b/app/Jobs/MediaPipeline/MediaFixLocalFilesystemCleanupPipeline.php @@ -8,68 +8,69 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Storage; class MediaFixLocalFilesystemCleanupPipeline implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public $timeout = 1800; - public $tries = 5; - public $maxExceptions = 1; + public $timeout = 1800; - public function handle() - { - if(config_cache('pixelfed.cloud_storage') == false) { - // Only run if cloud storage is enabled - return; - } + public $tries = 5; - $disk = Storage::disk('local'); - $cloud = Storage::disk(config('filesystems.cloud')); + public $maxExceptions = 1; - Media::whereNotNull(['status_id', 'cdn_url', 'replicated_at']) - ->chunk(20, function ($medias) use($disk, $cloud) { - foreach($medias as $media) { - if(!str_starts_with($media->media_path, 'public')) { - continue; - } + public function handle() + { + if ((bool) config_cache('pixelfed.cloud_storage') == false) { + // Only run if cloud storage is enabled + return; + } - if($disk->exists($media->media_path) && $cloud->exists($media->media_path)) { - $disk->delete($media->media_path); - } + $disk = Storage::disk('local'); + $cloud = Storage::disk(config('filesystems.cloud')); - if($media->thumbnail_path) { - if($disk->exists($media->thumbnail_path)) { - $disk->delete($media->thumbnail_path); - } - } + Media::whereNotNull(['status_id', 'cdn_url', 'replicated_at']) + ->chunk(20, function ($medias) use ($disk, $cloud) { + foreach ($medias as $media) { + if (! str_starts_with($media->media_path, 'public')) { + continue; + } - $paths = explode('/', $media->media_path); - if(count($paths) === 7) { - array_pop($paths); - $baseDir = implode('/', $paths); + if ($disk->exists($media->media_path) && $cloud->exists($media->media_path)) { + $disk->delete($media->media_path); + } - if(count($disk->allFiles($baseDir)) === 0) { - $disk->deleteDirectory($baseDir); + if ($media->thumbnail_path) { + if ($disk->exists($media->thumbnail_path)) { + $disk->delete($media->thumbnail_path); + } + } - array_pop($paths); - $baseDir = implode('/', $paths); + $paths = explode('/', $media->media_path); + if (count($paths) === 7) { + array_pop($paths); + $baseDir = implode('/', $paths); - if(count($disk->allFiles($baseDir)) === 0) { - $disk->deleteDirectory($baseDir); + if (count($disk->allFiles($baseDir)) === 0) { + $disk->deleteDirectory($baseDir); - array_pop($paths); - $baseDir = implode('/', $paths); + array_pop($paths); + $baseDir = implode('/', $paths); - if(count($disk->allFiles($baseDir)) === 0) { - $disk->deleteDirectory($baseDir); - } - } - } - } - } - }); - } + if (count($disk->allFiles($baseDir)) === 0) { + $disk->deleteDirectory($baseDir); + + array_pop($paths); + $baseDir = implode('/', $paths); + + if (count($disk->allFiles($baseDir)) === 0) { + $disk->deleteDirectory($baseDir); + } + } + } + } + } + }); + } } diff --git a/app/Observers/AvatarObserver.php b/app/Observers/AvatarObserver.php index b7854e66f..557773ce0 100644 --- a/app/Observers/AvatarObserver.php +++ b/app/Observers/AvatarObserver.php @@ -3,9 +3,9 @@ namespace App\Observers; use App\Avatar; +use App\Services\AccountService; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; -use App\Services\AccountService; class AvatarObserver { @@ -19,7 +19,6 @@ class AvatarObserver /** * Handle the avatar "created" event. * - * @param \App\Avatar $avatar * @return void */ public function created(Avatar $avatar) @@ -30,7 +29,6 @@ class AvatarObserver /** * Handle the avatar "updated" event. * - * @param \App\Avatar $avatar * @return void */ public function updated(Avatar $avatar) @@ -41,7 +39,6 @@ class AvatarObserver /** * Handle the avatar "deleted" event. * - * @param \App\Avatar $avatar * @return void */ public function deleted(Avatar $avatar) @@ -52,23 +49,22 @@ class AvatarObserver /** * Handle the avatar "deleting" event. * - * @param \App\Avatar $avatar * @return void */ public function deleting(Avatar $avatar) { $path = storage_path('app/'.$avatar->media_path); - if( is_file($path) && + if (is_file($path) && $avatar->media_path != 'public/avatars/default.png' && $avatar->media_path != 'public/avatars/default.jpg' ) { @unlink($path); } - if(config_cache('pixelfed.cloud_storage')) { + if ((bool) config_cache('pixelfed.cloud_storage')) { $disk = Storage::disk(config('filesystems.cloud')); $base = Str::startsWith($avatar->media_path, 'cache/avatars/'); - if($base && $disk->exists($avatar->media_path)) { + if ($base && $disk->exists($avatar->media_path)) { $disk->delete($avatar->media_path); } } @@ -78,7 +74,6 @@ class AvatarObserver /** * Handle the avatar "restored" event. * - * @param \App\Avatar $avatar * @return void */ public function restored(Avatar $avatar) @@ -89,7 +84,6 @@ class AvatarObserver /** * Handle the avatar "force deleted" event. * - * @param \App\Avatar $avatar * @return void */ public function forceDeleted(Avatar $avatar) diff --git a/app/Services/ConfigCacheService.php b/app/Services/ConfigCacheService.php index c5bb9bca9..c17f6375f 100644 --- a/app/Services/ConfigCacheService.php +++ b/app/Services/ConfigCacheService.php @@ -102,6 +102,7 @@ class ConfigCacheService 'pixelfed.optimize_image', 'pixelfed.optimize_video', 'pixelfed.max_collection_length', + 'media.delete_local_after_cloud', // 'system.user_mode' ]; diff --git a/app/Services/MediaStorageService.php b/app/Services/MediaStorageService.php index 216e37497..87bb9a586 100644 --- a/app/Services/MediaStorageService.php +++ b/app/Services/MediaStorageService.php @@ -2,44 +2,38 @@ namespace App\Services; +use App\Jobs\AvatarPipeline\AvatarStorageCleanup; +use App\Jobs\MediaPipeline\MediaDeletePipeline; +use App\Media; use App\Util\ActivityPub\Helpers; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\RequestException; use Illuminate\Http\File; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; -use App\Media; -use App\Profile; -use App\User; -use GuzzleHttp\Client; -use App\Services\AccountService; -use App\Http\Controllers\AvatarController; -use GuzzleHttp\Exception\RequestException; -use App\Jobs\MediaPipeline\MediaDeletePipeline; -use Illuminate\Support\Arr; -use App\Jobs\AvatarPipeline\AvatarStorageCleanup; - -class MediaStorageService { +class MediaStorageService +{ public static function store(Media $media) { - if(config_cache('pixelfed.cloud_storage') == true) { + if ((bool) config_cache('pixelfed.cloud_storage') == true) { (new self())->cloudStore($media); } - return; } public static function move(Media $media) { - if($media->remote_media) { + if ($media->remote_media) { return; } - if(config_cache('pixelfed.cloud_storage') == true) { + if ((bool) config_cache('pixelfed.cloud_storage') == true) { return (new self())->cloudMove($media); } - return; + } public static function avatar($avatar, $local = false, $skipRecentCheck = false) @@ -56,31 +50,31 @@ class MediaStorageService { return false; } - $h = Arr::mapWithKeys($r->getHeaders(), function($item, $key) { + $h = Arr::mapWithKeys($r->getHeaders(), function ($item, $key) { return [strtolower($key) => last($item)]; }); - if(!isset($h['content-length'], $h['content-type'])) { + if (! isset($h['content-length'], $h['content-type'])) { return false; } $len = (int) $h['content-length']; $mime = $h['content-type']; - if($len < 10 || $len > ((config_cache('pixelfed.max_photo_size') * 1000))) { + if ($len < 10 || $len > ((config_cache('pixelfed.max_photo_size') * 1000))) { return false; } return [ 'length' => $len, - 'mime' => $mime + 'mime' => $mime, ]; } protected function cloudStore($media) { - if($media->remote_media == true) { - if(config('media.storage.remote.cloud')) { + if ($media->remote_media == true) { + if (config('media.storage.remote.cloud')) { (new self())->remoteToCloud($media); } } else { @@ -100,7 +94,7 @@ class MediaStorageService { $storagePath = implode('/', $p); $url = ResilientMediaStorageService::store($storagePath, $path, $name); - if($thumb) { + if ($thumb) { $thumbUrl = ResilientMediaStorageService::store($storagePath, $thumb, $thumbname); $media->thumbnail_url = $thumbUrl; } @@ -108,8 +102,8 @@ class MediaStorageService { $media->optimized_url = $url; $media->replicated_at = now(); $media->save(); - if($media->status_id) { - Cache::forget('status:transformer:media:attachments:' . $media->status_id); + if ($media->status_id) { + Cache::forget('status:transformer:media:attachments:'.$media->status_id); MediaService::del($media->status_id); StatusService::del($media->status_id, false); } @@ -119,20 +113,20 @@ class MediaStorageService { { $url = $media->remote_url; - if(!Helpers::validateUrl($url)) { + if (! Helpers::validateUrl($url)) { return; } $head = $this->head($media->remote_url); - if(!$head) { + if (! $head) { return; } $mimes = [ 'image/jpeg', 'image/png', - 'video/mp4' + 'video/mp4', ]; $mime = $head['mime']; @@ -141,11 +135,11 @@ class MediaStorageService { $media->remote_media = true; $media->save(); - if(!in_array($mime, $mimes)) { + if (! in_array($mime, $mimes)) { return; } - if($head['length'] >= $max_size) { + if ($head['length'] >= $max_size) { return; } @@ -168,10 +162,10 @@ class MediaStorageService { } $base = MediaPathService::get($media->profile); - $path = Str::random(40) . $ext; + $path = Str::random(40).$ext; $tmpBase = storage_path('app/remcache/'); - $tmpPath = $media->profile_id . '-' . $path; - $tmpName = $tmpBase . $tmpPath; + $tmpPath = $media->profile_id.'-'.$path; + $tmpName = $tmpBase.$tmpPath; $data = file_get_contents($url, false, null, 0, $head['length']); file_put_contents($tmpName, $data); $hash = hash_file('sha256', $tmpName); @@ -186,8 +180,8 @@ class MediaStorageService { $media->replicated_at = now(); $media->save(); - if($media->status_id) { - Cache::forget('status:transformer:media:attachments:' . $media->status_id); + if ($media->status_id) { + Cache::forget('status:transformer:media:attachments:'.$media->status_id); } unlink($tmpName); @@ -199,13 +193,13 @@ class MediaStorageService { $url = $avatar->remote_url; $driver = $local ? 'local' : config('filesystems.cloud'); - if(empty($url) || Helpers::validateUrl($url) == false) { + if (empty($url) || Helpers::validateUrl($url) == false) { return; } $head = $this->head($url); - if($head == false) { + if ($head == false) { return; } @@ -218,46 +212,47 @@ class MediaStorageService { $mime = $head['mime']; $max_size = (int) config('pixelfed.max_avatar_size') * 1000; - if(!$skipRecentCheck) { - if($avatar->last_fetched_at && $avatar->last_fetched_at->gt(now()->subMonths(3))) { + if (! $skipRecentCheck) { + if ($avatar->last_fetched_at && $avatar->last_fetched_at->gt(now()->subMonths(3))) { return; } } - Cache::forget('avatar:' . $avatar->profile_id); + Cache::forget('avatar:'.$avatar->profile_id); AccountService::del($avatar->profile_id); // handle pleroma edge case - if(Str::endsWith($mime, '; charset=utf-8')) { + if (Str::endsWith($mime, '; charset=utf-8')) { $mime = str_replace('; charset=utf-8', '', $mime); } - if(!in_array($mime, $mimes)) { + if (! in_array($mime, $mimes)) { return; } - if($head['length'] >= $max_size) { + if ($head['length'] >= $max_size) { return; } - $base = ($local ? 'public/cache/' : 'cache/') . 'avatars/' . $avatar->profile_id; + $base = ($local ? 'public/cache/' : 'cache/').'avatars/'.$avatar->profile_id; $ext = $head['mime'] == 'image/jpeg' ? 'jpg' : 'png'; - $path = 'avatar_' . strtolower(Str::random(random_int(3,6))) . '.' . $ext; + $path = 'avatar_'.strtolower(Str::random(random_int(3, 6))).'.'.$ext; $tmpBase = storage_path('app/remcache/'); - $tmpPath = 'avatar_' . $avatar->profile_id . '-' . $path; - $tmpName = $tmpBase . $tmpPath; + $tmpPath = 'avatar_'.$avatar->profile_id.'-'.$path; + $tmpName = $tmpBase.$tmpPath; $data = @file_get_contents($url, false, null, 0, $head['length']); - if(!$data) { + if (! $data) { return; } file_put_contents($tmpName, $data); - $mimeCheck = Storage::mimeType('remcache/' . $tmpPath); + $mimeCheck = Storage::mimeType('remcache/'.$tmpPath); - if(!$mimeCheck || !in_array($mimeCheck, ['image/png', 'image/jpeg'])) { + if (! $mimeCheck || ! in_array($mimeCheck, ['image/png', 'image/jpeg'])) { $avatar->last_fetched_at = now(); $avatar->save(); unlink($tmpName); + return; } @@ -265,15 +260,15 @@ class MediaStorageService { $file = $disk->putFileAs($base, new File($tmpName), $path, 'public'); $permalink = $disk->url($file); - $avatar->media_path = $base . '/' . $path; + $avatar->media_path = $base.'/'.$path; $avatar->is_remote = true; - $avatar->cdn_url = $local ? config('app.url') . $permalink : $permalink; + $avatar->cdn_url = $local ? config('app.url').$permalink : $permalink; $avatar->size = $head['length']; $avatar->change_count = $avatar->change_count + 1; $avatar->last_fetched_at = now(); $avatar->save(); - Cache::forget('avatar:' . $avatar->profile_id); + Cache::forget('avatar:'.$avatar->profile_id); AccountService::del($avatar->profile_id); AvatarStorageCleanup::dispatch($avatar)->onQueue($queue)->delay(now()->addMinutes(random_int(3, 15))); @@ -282,7 +277,7 @@ class MediaStorageService { public static function delete(Media $media, $confirm = false) { - if(!$confirm) { + if (! $confirm) { return; } MediaDeletePipeline::dispatch($media)->onQueue('mmo'); @@ -290,13 +285,13 @@ class MediaStorageService { protected function cloudMove($media) { - if(!Storage::exists($media->media_path)) { + if (! Storage::exists($media->media_path)) { return 'invalid file'; } $path = storage_path('app/'.$media->media_path); $thumb = false; - if($media->thumbnail_path) { + if ($media->thumbnail_path) { $thumb = storage_path('app/'.$media->thumbnail_path); $pt = explode('/', $media->thumbnail_path); $thumbname = array_pop($pt); @@ -307,7 +302,7 @@ class MediaStorageService { $storagePath = implode('/', $p); $url = ResilientMediaStorageService::store($storagePath, $path, $name); - if($thumb) { + if ($thumb) { $thumbUrl = ResilientMediaStorageService::store($storagePath, $thumb, $thumbname); $media->thumbnail_url = $thumbUrl; } @@ -316,8 +311,8 @@ class MediaStorageService { $media->replicated_at = now(); $media->save(); - if($media->status_id) { - Cache::forget('status:transformer:media:attachments:' . $media->status_id); + if ($media->status_id) { + Cache::forget('status:transformer:media:attachments:'.$media->status_id); MediaService::del($media->status_id); StatusService::del($media->status_id, false); } diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php index bcf4f359c..2002a8967 100644 --- a/app/Util/ActivityPub/Helpers.php +++ b/app/Util/ActivityPub/Helpers.php @@ -2,49 +2,34 @@ namespace App\Util\ActivityPub; -use DB, Cache, Purify, Storage, Request, Validator; -use App\{ - Activity, - Follower, - Instance, - Like, - Media, - Notification, - Profile, - Status -}; -use Zttp\Zttp; -use Carbon\Carbon; -use GuzzleHttp\Client; -use Illuminate\Http\File; -use Illuminate\Validation\Rule; -use App\Jobs\AvatarPipeline\CreateAvatar; -use App\Jobs\RemoteFollowPipeline\RemoteFollowImportRecent; -use App\Jobs\ImageOptimizePipeline\{ImageOptimize,ImageThumbnail}; -use App\Jobs\StatusPipeline\NewStatusPipeline; -use App\Jobs\StatusPipeline\StatusReplyPipeline; -use App\Jobs\StatusPipeline\StatusTagsPipeline; -use App\Util\ActivityPub\HttpSignature; -use Illuminate\Support\Str; -use App\Services\ActivityPubFetchService; -use App\Services\ActivityPubDeliveryService; -use App\Services\CustomEmojiService; -use App\Services\InstanceService; -use App\Services\MediaPathService; -use App\Services\MediaStorageService; -use App\Services\NetworkTimelineService; -use App\Jobs\MediaPipeline\MediaStoragePipeline; +use App\Instance; use App\Jobs\AvatarPipeline\RemoteAvatarFetch; use App\Jobs\HomeFeedPipeline\FeedInsertRemotePipeline; -use App\Util\Media\License; +use App\Jobs\MediaPipeline\MediaStoragePipeline; +use App\Jobs\StatusPipeline\StatusReplyPipeline; +use App\Jobs\StatusPipeline\StatusTagsPipeline; +use App\Media; use App\Models\Poll; -use Illuminate\Contracts\Cache\LockTimeoutException; -use App\Services\DomainService; -use App\Services\UserFilterService; +use App\Profile; use App\Services\Account\AccountStatService; +use App\Services\ActivityPubDeliveryService; +use App\Services\ActivityPubFetchService; +use App\Services\DomainService; +use App\Services\InstanceService; +use App\Services\MediaPathService; +use App\Services\NetworkTimelineService; +use App\Services\UserFilterService; +use App\Status; +use App\Util\Media\License; +use Cache; +use Carbon\Carbon; +use Illuminate\Support\Str; +use Illuminate\Validation\Rule; +use Purify; +use Validator; -class Helpers { - +class Helpers +{ public static function validateObject($data) { $verbs = ['Create', 'Announce', 'Like', 'Follow', 'Delete', 'Accept', 'Reject', 'Undo', 'Tombstone']; @@ -53,14 +38,14 @@ class Helpers { 'type' => [ 'required', 'string', - Rule::in($verbs) + Rule::in($verbs), ], 'id' => 'required|string', 'actor' => 'required|string|url', 'object' => 'required', 'object.type' => 'required_if:type,Create', 'object.attributedTo' => 'required_if:type,Create|url', - 'published' => 'required_if:type,Create|date' + 'published' => 'required_if:type,Create|date', ])->passes(); return $valid; @@ -68,8 +53,8 @@ class Helpers { public static function verifyAttachments($data) { - if(!isset($data['object']) || empty($data['object'])) { - $data = ['object'=>$data]; + if (! isset($data['object']) || empty($data['object'])) { + $data = ['object' => $data]; } $activity = $data['object']; @@ -80,7 +65,7 @@ class Helpers { // Peertube // $mediaTypes = in_array('video/mp4', $mimeTypes) ? ['Document', 'Image', 'Video', 'Link'] : ['Document', 'Image']; - if(!isset($activity['attachment']) || empty($activity['attachment'])) { + if (! isset($activity['attachment']) || empty($activity['attachment'])) { return false; } @@ -100,13 +85,13 @@ class Helpers { '*.type' => [ 'required', 'string', - Rule::in($mediaTypes) + Rule::in($mediaTypes), ], '*.url' => 'required|url', - '*.mediaType' => [ + '*.mediaType' => [ 'required', 'string', - Rule::in($mimeTypes) + Rule::in($mimeTypes), ], '*.name' => 'sometimes|nullable|string', '*.blurhash' => 'sometimes|nullable|string|min:6|max:164', @@ -119,7 +104,7 @@ class Helpers { public static function normalizeAudience($data, $localOnly = true) { - if(!isset($data['to'])) { + if (! isset($data['to'])) { return; } @@ -128,32 +113,35 @@ class Helpers { $audience['cc'] = []; $scope = 'private'; - if(is_array($data['to']) && !empty($data['to'])) { + if (is_array($data['to']) && ! empty($data['to'])) { foreach ($data['to'] as $to) { - if($to == 'https://www.w3.org/ns/activitystreams#Public') { + if ($to == 'https://www.w3.org/ns/activitystreams#Public') { $scope = 'public'; + continue; } $url = $localOnly ? self::validateLocalUrl($to) : self::validateUrl($to); - if($url != false) { + if ($url != false) { array_push($audience['to'], $url); } } } - if(is_array($data['cc']) && !empty($data['cc'])) { + if (is_array($data['cc']) && ! empty($data['cc'])) { foreach ($data['cc'] as $cc) { - if($cc == 'https://www.w3.org/ns/activitystreams#Public') { + if ($cc == 'https://www.w3.org/ns/activitystreams#Public') { $scope = 'unlisted'; + continue; } $url = $localOnly ? self::validateLocalUrl($cc) : self::validateUrl($cc); - if($url != false) { + if ($url != false) { array_push($audience['cc'], $url); } } } $audience['scope'] = $scope; + return $audience; } @@ -161,56 +149,57 @@ class Helpers { { $audience = self::normalizeAudience($data); $url = $profile->permalink(); + return in_array($url, $audience['to']) || in_array($url, $audience['cc']); } public static function validateUrl($url) { - if(is_array($url)) { + if (is_array($url)) { $url = $url[0]; } $hash = hash('sha256', $url); $key = "helpers:url:valid:sha256-{$hash}"; - $valid = Cache::remember($key, 900, function() use($url) { + $valid = Cache::remember($key, 900, function () use ($url) { $localhosts = [ - '127.0.0.1', 'localhost', '::1' + '127.0.0.1', 'localhost', '::1', ]; - if(strtolower(mb_substr($url, 0, 8)) !== 'https://') { + if (strtolower(mb_substr($url, 0, 8)) !== 'https://') { return false; } - if(substr_count($url, '://') !== 1) { + if (substr_count($url, '://') !== 1) { return false; } - if(mb_substr($url, 0, 8) !== 'https://') { - $url = 'https://' . substr($url, 8); + if (mb_substr($url, 0, 8) !== 'https://') { + $url = 'https://'.substr($url, 8); } $valid = filter_var($url, FILTER_VALIDATE_URL); - if(!$valid) { + if (! $valid) { return false; } $host = parse_url($valid, PHP_URL_HOST); - if(in_array($host, $localhosts)) { + if (in_array($host, $localhosts)) { return false; } - if(config('security.url.verify_dns')) { - if(DomainService::hasValidDns($host) === false) { + if (config('security.url.verify_dns')) { + if (DomainService::hasValidDns($host) === false) { return false; } } - if(app()->environment() === 'production') { + if (app()->environment() === 'production') { $bannedInstances = InstanceService::getBannedDomains(); - if(in_array($host, $bannedInstances)) { + if (in_array($host, $bannedInstances)) { return false; } } @@ -224,12 +213,14 @@ class Helpers { public static function validateLocalUrl($url) { $url = self::validateUrl($url); - if($url == true) { + if ($url == true) { $domain = config('pixelfed.domain.app'); $host = parse_url($url, PHP_URL_HOST); $url = strtolower($domain) === strtolower($host) ? $url : false; + return $url; } + return false; } @@ -237,15 +228,16 @@ class Helpers { { $version = config('pixelfed.version'); $url = config('app.url'); + return [ - 'Accept' => 'application/activity+json', + 'Accept' => 'application/activity+json', 'User-Agent' => "(Pixelfed/{$version}; +{$url})", ]; } public static function fetchFromUrl($url = false) { - if(self::validateUrl($url) == false) { + if (self::validateUrl($url) == false) { return; } @@ -253,13 +245,13 @@ class Helpers { $key = "helpers:url:fetcher:sha256-{$hash}"; $ttl = now()->addMinutes(15); - return Cache::remember($key, $ttl, function() use($url) { + return Cache::remember($key, $ttl, function () use ($url) { $res = ActivityPubFetchService::get($url); - if(!$res || empty($res)) { + if (! $res || empty($res)) { return false; } $res = json_decode($res, true, 8); - if(json_last_error() == JSON_ERROR_NONE) { + if (json_last_error() == JSON_ERROR_NONE) { return $res; } else { return false; @@ -274,12 +266,12 @@ class Helpers { public static function pluckval($val) { - if(is_string($val)) { + if (is_string($val)) { return $val; } - if(is_array($val)) { - return !empty($val) ? head($val) : null; + if (is_array($val)) { + return ! empty($val) ? head($val) : null; } return null; @@ -288,51 +280,52 @@ class Helpers { public static function statusFirstOrFetch($url, $replyTo = false) { $url = self::validateUrl($url); - if($url == false) { + if ($url == false) { return; } $host = parse_url($url, PHP_URL_HOST); $local = config('pixelfed.domain.app') == $host ? true : false; - if($local) { + if ($local) { $id = (int) last(explode('/', $url)); - return Status::whereNotIn('scope', ['draft','archived'])->findOrFail($id); + + return Status::whereNotIn('scope', ['draft', 'archived'])->findOrFail($id); } - $cached = Status::whereNotIn('scope', ['draft','archived']) + $cached = Status::whereNotIn('scope', ['draft', 'archived']) ->whereUri($url) ->orWhere('object_url', $url) ->first(); - if($cached) { + if ($cached) { return $cached; } $res = self::fetchFromUrl($url); - if(!$res || empty($res) || isset($res['error']) || !isset($res['@context']) || !isset($res['published']) ) { + if (! $res || empty($res) || isset($res['error']) || ! isset($res['@context']) || ! isset($res['published'])) { return; } - if(config('autospam.live_filters.enabled')) { + if (config('autospam.live_filters.enabled')) { $filters = config('autospam.live_filters.filters'); - if(!empty($filters) && isset($res['content']) && !empty($res['content']) && strlen($filters) > 3) { + 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) { + foreach ($filters as $filter) { $filter = trim(strtolower($filter)); - if(!$filter || !strlen($filter)) { + if (! $filter || ! strlen($filter)) { continue; } - if(str_contains(strtolower($content), $filter)) { + if (str_contains(strtolower($content), $filter)) { return; } } } } - if(isset($res['object'])) { + if (isset($res['object'])) { $activity = $res; } else { $activity = ['object' => $res]; @@ -342,37 +335,37 @@ class Helpers { $cw = isset($res['sensitive']) ? (bool) $res['sensitive'] : false; - if(isset($res['to']) == true) { - if(is_array($res['to']) && in_array('https://www.w3.org/ns/activitystreams#Public', $res['to'])) { + if (isset($res['to']) == true) { + if (is_array($res['to']) && in_array('https://www.w3.org/ns/activitystreams#Public', $res['to'])) { $scope = 'public'; } - if(is_string($res['to']) && 'https://www.w3.org/ns/activitystreams#Public' == $res['to']) { + if (is_string($res['to']) && $res['to'] == 'https://www.w3.org/ns/activitystreams#Public') { $scope = 'public'; } } - if(isset($res['cc']) == true) { - if(is_array($res['cc']) && in_array('https://www.w3.org/ns/activitystreams#Public', $res['cc'])) { + if (isset($res['cc']) == true) { + if (is_array($res['cc']) && in_array('https://www.w3.org/ns/activitystreams#Public', $res['cc'])) { $scope = 'unlisted'; } - if(is_string($res['cc']) && 'https://www.w3.org/ns/activitystreams#Public' == $res['cc']) { + if (is_string($res['cc']) && $res['cc'] == 'https://www.w3.org/ns/activitystreams#Public') { $scope = 'unlisted'; } } - if(config('costar.enabled') == true) { + if (config('costar.enabled') == true) { $blockedKeywords = config('costar.keyword.block'); - if($blockedKeywords !== null) { + if ($blockedKeywords !== null) { $keywords = config('costar.keyword.block'); - foreach($keywords as $kw) { - if(Str::contains($res['content'], $kw) == true) { + foreach ($keywords as $kw) { + if (Str::contains($res['content'], $kw) == true) { return; } } } $unlisted = config('costar.domain.unlisted'); - if(in_array(parse_url($url, PHP_URL_HOST), $unlisted) == true) { + if (in_array(parse_url($url, PHP_URL_HOST), $unlisted) == true) { $unlisted = true; $scope = 'unlisted'; } else { @@ -380,7 +373,7 @@ class Helpers { } $cwDomains = config('costar.domain.cw'); - if(in_array(parse_url($url, PHP_URL_HOST), $cwDomains) == true) { + if (in_array(parse_url($url, PHP_URL_HOST), $cwDomains) == true) { $cw = true; } } @@ -389,15 +382,15 @@ class Helpers { $idDomain = parse_url($id, PHP_URL_HOST); $urlDomain = parse_url($url, PHP_URL_HOST); - if($idDomain && $urlDomain && strtolower($idDomain) !== strtolower($urlDomain)) { + if ($idDomain && $urlDomain && strtolower($idDomain) !== strtolower($urlDomain)) { return; } - if(!self::validateUrl($id)) { + if (! self::validateUrl($id)) { return; } - if(!isset($activity['object']['attributedTo'])) { + if (! isset($activity['object']['attributedTo'])) { return; } @@ -405,39 +398,38 @@ class Helpers { $activity['object']['attributedTo'] : (is_array($activity['object']['attributedTo']) ? collect($activity['object']['attributedTo']) - ->filter(function($o) { + ->filter(function ($o) { return $o && isset($o['type']) && $o['type'] == 'Person'; }) ->pluck('id') ->first() : null ); - if($attributedTo) { + if ($attributedTo) { $actorDomain = parse_url($attributedTo, PHP_URL_HOST); - if(!self::validateUrl($attributedTo) || + if (! self::validateUrl($attributedTo) || $idDomain !== $actorDomain || $actorDomain !== $urlDomain - ) - { + ) { return; } } - if($idDomain !== $urlDomain) { + if ($idDomain !== $urlDomain) { return; } $profile = self::profileFirstOrNew($attributedTo); - if(!$profile) { + if (! $profile) { return; } - if(isset($activity['object']['inReplyTo']) && !empty($activity['object']['inReplyTo']) || $replyTo == true) { + if (isset($activity['object']['inReplyTo']) && ! empty($activity['object']['inReplyTo']) || $replyTo == true) { $reply_to = self::statusFirstOrFetch(self::pluckval($activity['object']['inReplyTo']), false); - if($reply_to) { + if ($reply_to) { $blocks = UserFilterService::blocks($reply_to->profile_id); - if(in_array($profile->id, $blocks)) { + if (in_array($profile->id, $blocks)) { return; } } @@ -447,15 +439,15 @@ class Helpers { } $ts = self::pluckval($res['published']); - if($scope == 'public' && in_array($urlDomain, InstanceService::getUnlistedDomains())) { + if ($scope == 'public' && in_array($urlDomain, InstanceService::getUnlistedDomains())) { $scope = 'unlisted'; } - if(in_array($urlDomain, InstanceService::getNsfwDomains())) { + if (in_array($urlDomain, InstanceService::getNsfwDomains())) { $cw = true; } - if($res['type'] === 'Question') { + if ($res['type'] === 'Question') { $status = self::storePoll( $profile, $res, @@ -466,6 +458,7 @@ class Helpers { $scope, $id ); + return $status; } else { $status = self::storeStatus($url, $profile, $res); @@ -482,12 +475,12 @@ class Helpers { $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)) { + if (! self::validateUrl($id) || ! self::validateUrl($url)) { return; } - if( strtolower($originalUrlDomain) !== strtolower($idDomain) || - strtolower($originalUrlDomain) !== strtolower($urlDomain) ) { + if (strtolower($originalUrlDomain) !== strtolower($idDomain) || + strtolower($originalUrlDomain) !== strtolower($urlDomain)) { return; } @@ -498,21 +491,21 @@ class Helpers { $cw = self::getSensitive($activity, $url); $pid = is_object($profile) ? $profile->id : (is_array($profile) ? $profile['id'] : null); $isUnlisted = is_object($profile) ? $profile->unlisted : (is_array($profile) ? $profile['unlisted'] : false); - $commentsDisabled = isset($activity['commentsEnabled']) ? !boolval($activity['commentsEnabled']) : false; + $commentsDisabled = isset($activity['commentsEnabled']) ? ! boolval($activity['commentsEnabled']) : false; - if(!$pid) { + if (! $pid) { return; } - if($scope == 'public') { - if($isUnlisted == true) { + if ($scope == 'public') { + if ($isUnlisted == true) { $scope = 'unlisted'; } } $status = Status::updateOrCreate( [ - 'uri' => $url + 'uri' => $url, ], [ 'profile_id' => $pid, 'url' => $url, @@ -527,24 +520,24 @@ class Helpers { 'visibility' => $scope, 'cw_summary' => ($cw == true && isset($activity['summary']) ? Purify::clean(strip_tags($activity['summary'])) : null), - 'comments_disabled' => $commentsDisabled + 'comments_disabled' => $commentsDisabled, ] ); - if($reply_to == null) { + if ($reply_to == null) { self::importNoteAttachment($activity, $status); } else { - if(isset($activity['attachment']) && !empty($activity['attachment'])) { + if (isset($activity['attachment']) && ! empty($activity['attachment'])) { self::importNoteAttachment($activity, $status); } StatusReplyPipeline::dispatch($status); } - if(isset($activity['tag']) && is_array($activity['tag']) && !empty($activity['tag'])) { + if (isset($activity['tag']) && is_array($activity['tag']) && ! empty($activity['tag'])) { StatusTagsPipeline::dispatch($activity, $status); } - if( config('instance.timeline.network.cached') && + if (config('instance.timeline.network.cached') && $status->in_reply_to_id === null && $status->reblog_of_id === null && in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) && @@ -556,8 +549,8 @@ class Helpers { ->unique() ->values() ->toArray(); - if(!in_array($urlDomain, $filteredDomains)) { - if(!$isUnlisted) { + if (! in_array($urlDomain, $filteredDomains)) { + if (! $isUnlisted) { NetworkTimelineService::add($status->id); } } @@ -565,7 +558,7 @@ class Helpers { AccountStatService::incrementPostCount($pid); - if( $status->in_reply_to_id === null && + if ($status->in_reply_to_id === null && in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) ) { FeedInsertRemotePipeline::dispatch($status->id, $pid)->onQueue('feed'); @@ -576,14 +569,14 @@ class Helpers { public static function getSensitive($activity, $url) { - if(!$url || !strlen($url)) { + if (! $url || ! strlen($url)) { return true; } $urlDomain = parse_url($url, PHP_URL_HOST); $cw = isset($activity['sensitive']) ? (bool) $activity['sensitive'] : false; - if(in_array($urlDomain, InstanceService::getNsfwDomains())) { + if (in_array($urlDomain, InstanceService::getNsfwDomains())) { $cw = true; } @@ -593,13 +586,13 @@ class Helpers { public static function getReplyTo($activity) { $reply_to = null; - $inReplyTo = isset($activity['inReplyTo']) && !empty($activity['inReplyTo']) ? + $inReplyTo = isset($activity['inReplyTo']) && ! empty($activity['inReplyTo']) ? self::pluckval($activity['inReplyTo']) : false; - if($inReplyTo) { + if ($inReplyTo) { $reply_to = self::statusFirstOrFetch($inReplyTo); - if($reply_to) { + if ($reply_to) { $reply_to = optional($reply_to)->id; } } else { @@ -616,25 +609,25 @@ class Helpers { $urlDomain = parse_url(self::pluckval($url), PHP_URL_HOST); $scope = 'private'; - if(isset($activity['to']) == true) { - if(is_array($activity['to']) && in_array('https://www.w3.org/ns/activitystreams#Public', $activity['to'])) { + if (isset($activity['to']) == true) { + if (is_array($activity['to']) && in_array('https://www.w3.org/ns/activitystreams#Public', $activity['to'])) { $scope = 'public'; } - if(is_string($activity['to']) && 'https://www.w3.org/ns/activitystreams#Public' == $activity['to']) { + if (is_string($activity['to']) && $activity['to'] == 'https://www.w3.org/ns/activitystreams#Public') { $scope = 'public'; } } - if(isset($activity['cc']) == true) { - if(is_array($activity['cc']) && in_array('https://www.w3.org/ns/activitystreams#Public', $activity['cc'])) { + if (isset($activity['cc']) == true) { + if (is_array($activity['cc']) && in_array('https://www.w3.org/ns/activitystreams#Public', $activity['cc'])) { $scope = 'unlisted'; } - if(is_string($activity['cc']) && 'https://www.w3.org/ns/activitystreams#Public' == $activity['cc']) { + if (is_string($activity['cc']) && $activity['cc'] == 'https://www.w3.org/ns/activitystreams#Public') { $scope = 'unlisted'; } } - if($scope == 'public' && in_array($urlDomain, InstanceService::getUnlistedDomains())) { + if ($scope == 'public' && in_array($urlDomain, InstanceService::getUnlistedDomains())) { $scope = 'unlisted'; } @@ -643,15 +636,15 @@ class Helpers { private static function storePoll($profile, $res, $url, $ts, $reply_to, $cw, $scope, $id) { - if(!isset($res['endTime']) || !isset($res['oneOf']) || !is_array($res['oneOf']) || count($res['oneOf']) > 4) { + if (! isset($res['endTime']) || ! isset($res['oneOf']) || ! is_array($res['oneOf']) || count($res['oneOf']) > 4) { return; } - $options = collect($res['oneOf'])->map(function($option) { + $options = collect($res['oneOf'])->map(function ($option) { return $option['name']; })->toArray(); - $cachedTallies = collect($res['oneOf'])->map(function($option) { + $cachedTallies = collect($res['oneOf'])->map(function ($option) { return $option['replies']['totalItems'] ?? 0; })->toArray(); @@ -697,9 +690,10 @@ class Helpers { public static function importNoteAttachment($data, Status $status) { - if(self::verifyAttachments($data) == false) { + if (self::verifyAttachments($data) == false) { // \Log::info('importNoteAttachment::failedVerification.', [$data['id']]); $status->viewType(); + return; } $attachments = isset($data['object']) ? $data['object']['attachment'] : $data['attachment']; @@ -712,11 +706,11 @@ class Helpers { $storagePath = MediaPathService::get($user, 2); $allowed = explode(',', config_cache('pixelfed.media_types')); - foreach($attachments as $key => $media) { + foreach ($attachments as $key => $media) { $type = $media['mediaType']; $url = $media['url']; $valid = self::validateUrl($url); - if(in_array($type, $allowed) == false || $valid == false) { + if (in_array($type, $allowed) == false || $valid == false) { continue; } $blurhash = isset($media['blurhash']) ? $media['blurhash'] : null; @@ -735,50 +729,52 @@ class Helpers { $media->remote_url = $url; $media->caption = $caption; $media->order = $key + 1; - if($width) { + if ($width) { $media->width = $width; } - if($height) { + if ($height) { $media->height = $height; } - if($license) { + if ($license) { $media->license = $license; } $media->mime = $type; $media->version = 3; $media->save(); - if(config_cache('pixelfed.cloud_storage') == true) { + if ((bool) config_cache('pixelfed.cloud_storage') == true) { MediaStoragePipeline::dispatch($media); } } $status->viewType(); - return; + } public static function profileFirstOrNew($url) { $url = self::validateUrl($url); - if($url == false) { + if ($url == false) { return; } $host = parse_url($url, PHP_URL_HOST); $local = config('pixelfed.domain.app') == $host ? true : false; - if($local == true) { + if ($local == true) { $id = last(explode('/', $url)); + return Profile::whereNull('status') ->whereNull('domain') ->whereUsername($id) ->firstOrFail(); } - if($profile = Profile::whereRemoteUrl($url)->first()) { - if($profile->last_fetched_at && $profile->last_fetched_at->lt(now()->subHours(24))) { + if ($profile = Profile::whereRemoteUrl($url)->first()) { + if ($profile->last_fetched_at && $profile->last_fetched_at->lt(now()->subHours(24))) { return self::profileUpdateOrCreate($url); } + return $profile; } @@ -788,42 +784,42 @@ class Helpers { public static function profileUpdateOrCreate($url) { $res = self::fetchProfileFromUrl($url); - if(!$res || isset($res['id']) == false) { + 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)) { + if (strtolower($urlDomain) !== strtolower($domain)) { return; } - if(!isset($res['preferredUsername']) && !isset($res['nickname'])) { + if (! isset($res['preferredUsername']) && ! isset($res['nickname'])) { return; } // skip invalid usernames - if(!ctype_alnum($res['preferredUsername'])) { + if (! ctype_alnum($res['preferredUsername'])) { $tmpUsername = str_replace(['_', '.', '-'], '', $res['preferredUsername']); - if(!ctype_alnum($tmpUsername)) { + if (! ctype_alnum($tmpUsername)) { return; } } $username = (string) Purify::clean($res['preferredUsername'] ?? $res['nickname']); - if(empty($username)) { + if (empty($username)) { return; } $remoteUsername = $username; $webfinger = "@{$username}@{$domain}"; - if(!self::validateUrl($res['inbox'])) { + if (! self::validateUrl($res['inbox'])) { return; } - if(!self::validateUrl($res['id'])) { + if (! self::validateUrl($res['id'])) { return; } $instance = Instance::updateOrCreate([ - 'domain' => $domain + 'domain' => $domain, ]); - if($instance->wasRecentlyCreated == true) { + if ($instance->wasRecentlyCreated == true) { \App\Jobs\InstancePipeline\FetchNodeinfoPipeline::dispatch($instance)->onQueue('low'); } @@ -846,13 +842,14 @@ class Helpers { ] ); - if( $profile->last_fetched_at == null || + if ($profile->last_fetched_at == null || $profile->last_fetched_at->lt(now()->subMonths(3)) ) { RemoteAvatarFetch::dispatch($profile); } $profile->last_fetched_at = now(); $profile->save(); + return $profile; } @@ -863,7 +860,7 @@ class Helpers { public static function sendSignedObject($profile, $url, $body) { - if(app()->environment() !== 'production') { + if (app()->environment() !== 'production') { return; } ActivityPubDeliveryService::queue() diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index 74f6100f0..0d8b21e47 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -740,7 +740,7 @@ PIXELFED PF_ENABLE_CLOUD - {{config_cache('pixelfed.cloud_storage') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('pixelfed.cloud_storage') ? '✅ true' : '❌ false' }} PIXELFED From fecbe1897bac91dcb510db7d1898a278e31ad74b Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 01:20:24 -0600 Subject: [PATCH 11/41] Update pixelfed.max_album_length, use config_cache --- app/Http/Controllers/Api/ApiV1Controller.php | 8 +-- .../Status/StoreStatusEditRequest.php | 63 ++++++++++--------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index 2da53762a..2041e341c 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -1665,7 +1665,7 @@ class ApiV1Controller extends Controller 'statuses' => [ 'characters_reserved_per_url' => 23, 'max_characters' => (int) config_cache('pixelfed.max_caption_length'), - 'max_media_attachments' => (int) config('pixelfed.max_album_length'), + 'max_media_attachments' => (int) config_cache('pixelfed.max_album_length'), ], ], ]; @@ -3308,9 +3308,9 @@ class ApiV1Controller extends Controller abort_unless($request->user()->tokenCan('write'), 403); $this->validate($request, [ - 'status' => 'nullable|string|max:' . config_cache('pixelfed.max_caption_length'), + 'status' => 'nullable|string|max:'.(int) config_cache('pixelfed.max_caption_length'), 'in_reply_to_id' => 'nullable', - 'media_ids' => 'sometimes|array|max:'.config_cache('pixelfed.max_album_length'), + 'media_ids' => 'sometimes|array|max:'.(int) config_cache('pixelfed.max_album_length'), 'sensitive' => 'nullable', 'visibility' => 'string|in:private,unlisted,public', 'spoiler_text' => 'sometimes|max:140', @@ -3436,7 +3436,7 @@ class ApiV1Controller extends Controller $mimes = []; foreach ($ids as $k => $v) { - if ($k + 1 > config_cache('pixelfed.max_album_length')) { + if ($k + 1 > (int) config_cache('pixelfed.max_album_length')) { continue; } $m = Media::whereUserId($user->id)->whereNull('status_id')->findOrFail($v); diff --git a/app/Http/Requests/Status/StoreStatusEditRequest.php b/app/Http/Requests/Status/StoreStatusEditRequest.php index aa9364ca6..e8e2d22f5 100644 --- a/app/Http/Requests/Status/StoreStatusEditRequest.php +++ b/app/Http/Requests/Status/StoreStatusEditRequest.php @@ -2,10 +2,10 @@ namespace App\Http\Requests\Status; -use Illuminate\Foundation\Http\FormRequest; use App\Media; use App\Status; use Closure; +use Illuminate\Foundation\Http\FormRequest; class StoreStatusEditRequest extends FormRequest { @@ -14,24 +14,25 @@ class StoreStatusEditRequest extends FormRequest */ public function authorize(): bool { - $profile = $this->user()->profile; - if($profile->status != null) { - return false; - } - if($profile->unlisted == true && $profile->cw == true) { - return false; - } - $types = [ - "photo", - "photo:album", - "photo:video:album", - "reply", - "text", - "video", - "video:album" - ]; - $scopes = ['public', 'unlisted', 'private']; - $status = Status::whereNull('reblog_of_id')->whereIn('type', $types)->whereIn('scope', $scopes)->find($this->route('id')); + $profile = $this->user()->profile; + if ($profile->status != null) { + return false; + } + if ($profile->unlisted == true && $profile->cw == true) { + return false; + } + $types = [ + 'photo', + 'photo:album', + 'photo:video:album', + 'reply', + 'text', + 'video', + 'video:album', + ]; + $scopes = ['public', 'unlisted', 'private']; + $status = Status::whereNull('reblog_of_id')->whereIn('type', $types)->whereIn('scope', $scopes)->find($this->route('id')); + return $status && $this->user()->profile_id === $status->profile_id; } @@ -47,18 +48,18 @@ class StoreStatusEditRequest extends FormRequest 'spoiler_text' => 'nullable|string|max:140', 'sensitive' => 'sometimes|boolean', 'media_ids' => [ - 'nullable', - 'required_without:status', - 'array', - 'max:' . config('pixelfed.max_album_length'), - function (string $attribute, mixed $value, Closure $fail) { - Media::whereProfileId($this->user()->profile_id) - ->where(function($query) { - return $query->whereNull('status_id') - ->orWhere('status_id', '=', $this->route('id')); - }) - ->findOrFail($value); - }, + 'nullable', + 'required_without:status', + 'array', + 'max:'.(int) config_cache('pixelfed.max_album_length'), + function (string $attribute, mixed $value, Closure $fail) { + Media::whereProfileId($this->user()->profile_id) + ->where(function ($query) { + return $query->whereNull('status_id') + ->orWhere('status_id', '=', $this->route('id')); + }) + ->findOrFail($value); + }, ], 'location' => 'sometimes|nullable', 'location.id' => 'sometimes|integer|min:1|max:128769', From d670de175e8f80bfb34943d7600665745548ad0d Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 01:28:08 -0600 Subject: [PATCH 12/41] Update media_types, use config_cache --- .../Commands/FetchMissingMediaMimeType.php | 14 +- app/Http/Controllers/Api/ApiV1Controller.php | 2 +- .../StatusRemoteUpdatePipeline.php | 279 +++++++++--------- 3 files changed, 147 insertions(+), 148 deletions(-) diff --git a/app/Console/Commands/FetchMissingMediaMimeType.php b/app/Console/Commands/FetchMissingMediaMimeType.php index 16aeb5f59..27ae23e4c 100644 --- a/app/Console/Commands/FetchMissingMediaMimeType.php +++ b/app/Console/Commands/FetchMissingMediaMimeType.php @@ -2,11 +2,11 @@ namespace App\Console\Commands; -use Illuminate\Console\Command; use App\Media; -use Illuminate\Support\Facades\Http; use App\Services\MediaService; use App\Services\StatusService; +use Illuminate\Console\Command; +use Illuminate\Support\Facades\Http; class FetchMissingMediaMimeType extends Command { @@ -29,20 +29,20 @@ class FetchMissingMediaMimeType extends Command */ public function handle() { - foreach(Media::whereNotNull(['remote_url', 'status_id'])->whereNull('mime')->lazyByIdDesc(50, 'id') as $media) { + foreach (Media::whereNotNull(['remote_url', 'status_id'])->whereNull('mime')->lazyByIdDesc(50, 'id') as $media) { $res = Http::retry(2, 100, throw: false)->head($media->remote_url); - if(!$res->successful()) { + if (! $res->successful()) { continue; } - if(!in_array($res->header('content-type'), explode(',',config('pixelfed.media_types')))) { + if (! in_array($res->header('content-type'), explode(',', config_cache('pixelfed.media_types')))) { continue; } $media->mime = $res->header('content-type'); - if($res->hasHeader('content-length')) { + if ($res->hasHeader('content-length')) { $media->size = $res->header('content-length'); } @@ -50,7 +50,7 @@ class FetchMissingMediaMimeType extends Command MediaService::del($media->status_id); StatusService::del($media->status_id); - $this->info('mid:'.$media->id . ' (' . $res->header('content-type') . ':' . $res->header('content-length') . ' bytes)'); + $this->info('mid:'.$media->id.' ('.$res->header('content-type').':'.$res->header('content-length').' bytes)'); } } } diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index 2041e341c..c88542541 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -1651,7 +1651,7 @@ class ApiV1Controller extends Controller 'media_attachments' => [ 'image_matrix_limit' => 16777216, 'image_size_limit' => config_cache('pixelfed.max_photo_size') * 1024, - 'supported_mime_types' => explode(',', config('pixelfed.media_types')), + 'supported_mime_types' => explode(',', config_cache('pixelfed.media_types')), 'video_frame_rate_limit' => 120, 'video_matrix_limit' => 2304000, 'video_size_limit' => config_cache('pixelfed.max_photo_size') * 1024, diff --git a/app/Jobs/StatusPipeline/StatusRemoteUpdatePipeline.php b/app/Jobs/StatusPipeline/StatusRemoteUpdatePipeline.php index 23b8716c1..7ef7a3366 100644 --- a/app/Jobs/StatusPipeline/StatusRemoteUpdatePipeline.php +++ b/app/Jobs/StatusPipeline/StatusRemoteUpdatePipeline.php @@ -2,172 +2,171 @@ namespace App\Jobs\StatusPipeline; +use App\Media; +use App\Models\StatusEdit; +use App\ModLog; +use App\Profile; +use App\Services\StatusService; +use App\Status; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use App\Media; -use App\ModLog; -use App\Profile; -use App\Status; -use App\Models\StatusEdit; -use App\Services\StatusService; -use Purify; use Illuminate\Support\Facades\Http; +use Purify; class StatusRemoteUpdatePipeline implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public $activity; + public $activity; - /** - * Create a new job instance. - */ - public function __construct($activity) - { - $this->activity = $activity; - } + /** + * Create a new job instance. + */ + public function __construct($activity) + { + $this->activity = $activity; + } - /** - * Execute the job. - */ - public function handle(): void - { - $activity = $this->activity; - $status = Status::with('media')->whereObjectUrl($activity['id'])->first(); - if(!$status) { - return; - } - $this->createPreviousEdit($status); - $this->updateMedia($status, $activity); - $this->updateImmediateAttributes($status, $activity); - $this->createEdit($status, $activity); - } + /** + * Execute the job. + */ + public function handle(): void + { + $activity = $this->activity; + $status = Status::with('media')->whereObjectUrl($activity['id'])->first(); + if (! $status) { + return; + } + $this->createPreviousEdit($status); + $this->updateMedia($status, $activity); + $this->updateImmediateAttributes($status, $activity); + $this->createEdit($status, $activity); + } - protected function createPreviousEdit($status) - { - if(!$status->edits()->count()) { - StatusEdit::create([ - 'status_id' => $status->id, - 'profile_id' => $status->profile_id, - 'caption' => $status->caption, - 'spoiler_text' => $status->cw_summary, - 'is_nsfw' => $status->is_nsfw, - 'ordered_media_attachment_ids' => $status->media()->orderBy('order')->pluck('id')->toArray(), - 'created_at' => $status->created_at - ]); - } - } + protected function createPreviousEdit($status) + { + if (! $status->edits()->count()) { + StatusEdit::create([ + 'status_id' => $status->id, + 'profile_id' => $status->profile_id, + 'caption' => $status->caption, + 'spoiler_text' => $status->cw_summary, + 'is_nsfw' => $status->is_nsfw, + 'ordered_media_attachment_ids' => $status->media()->orderBy('order')->pluck('id')->toArray(), + 'created_at' => $status->created_at, + ]); + } + } - protected function updateMedia($status, $activity) - { - if(!isset($activity['attachment'])) { - return; - } - $ogm = $status->media->count() ? $status->media()->orderBy('order')->get() : collect([]); - $nm = collect($activity['attachment'])->filter(function($nm) { - return isset( - $nm['type'], - $nm['mediaType'], - $nm['url'] - ) && - in_array($nm['type'], ['Document', 'Image', 'Video']) && - in_array($nm['mediaType'], explode(',', config('pixelfed.media_types'))); - }); + protected function updateMedia($status, $activity) + { + if (! isset($activity['attachment'])) { + return; + } + $ogm = $status->media->count() ? $status->media()->orderBy('order')->get() : collect([]); + $nm = collect($activity['attachment'])->filter(function ($nm) { + return isset( + $nm['type'], + $nm['mediaType'], + $nm['url'] + ) && + in_array($nm['type'], ['Document', 'Image', 'Video']) && + in_array($nm['mediaType'], explode(',', config_cache('pixelfed.media_types'))); + }); - // Skip when no media - if(!$ogm->count() && !$nm->count()) { - return; - } + // Skip when no media + if (! $ogm->count() && ! $nm->count()) { + return; + } - Media::whereProfileId($status->profile_id) - ->whereStatusId($status->id) - ->update([ - 'status_id' => null - ]); + Media::whereProfileId($status->profile_id) + ->whereStatusId($status->id) + ->update([ + 'status_id' => null, + ]); - $nm->each(function($n, $key) use($status) { - $res = Http::withOptions(['allow_redirects' => false])->retry(3, 100, throw: false)->head($n['url']); + $nm->each(function ($n, $key) use ($status) { + $res = Http::withOptions(['allow_redirects' => false])->retry(3, 100, throw: false)->head($n['url']); - if(!$res->successful()) { - return; - } + if (! $res->successful()) { + return; + } - if(!in_array($res->header('content-type'), explode(',',config('pixelfed.media_types')))) { - return; - } + if (! in_array($res->header('content-type'), explode(',', config_cache('pixelfed.media_types')))) { + return; + } - $m = new Media; - $m->status_id = $status->id; - $m->profile_id = $status->profile_id; - $m->remote_media = true; - $m->media_path = $n['url']; + $m = new Media; + $m->status_id = $status->id; + $m->profile_id = $status->profile_id; + $m->remote_media = true; + $m->media_path = $n['url']; $m->mime = $res->header('content-type'); $m->size = $res->hasHeader('content-length') ? $res->header('content-length') : null; - $m->caption = isset($n['name']) && !empty($n['name']) ? Purify::clean($n['name']) : null; - $m->remote_url = $n['url']; + $m->caption = isset($n['name']) && ! empty($n['name']) ? Purify::clean($n['name']) : null; + $m->remote_url = $n['url']; $m->blurhash = isset($n['blurhash']) && (strlen($n['blurhash']) < 50) ? $n['blurhash'] : null; - $m->width = isset($n['width']) && !empty($n['width']) ? $n['width'] : null; - $m->height = isset($n['height']) && !empty($n['height']) ? $n['height'] : null; - $m->skip_optimize = true; - $m->order = $key + 1; - $m->save(); - }); - } + $m->width = isset($n['width']) && ! empty($n['width']) ? $n['width'] : null; + $m->height = isset($n['height']) && ! empty($n['height']) ? $n['height'] : null; + $m->skip_optimize = true; + $m->order = $key + 1; + $m->save(); + }); + } - protected function updateImmediateAttributes($status, $activity) - { - if(isset($activity['content'])) { - $status->caption = strip_tags($activity['content']); - $status->rendered = Purify::clean($activity['content']); - } + protected function updateImmediateAttributes($status, $activity) + { + if (isset($activity['content'])) { + $status->caption = strip_tags($activity['content']); + $status->rendered = Purify::clean($activity['content']); + } - if(isset($activity['sensitive'])) { - if((bool) $activity['sensitive'] == false) { - $status->is_nsfw = false; - $exists = ModLog::whereObjectType('App\Status::class') - ->whereObjectId($status->id) - ->whereAction('admin.status.moderate') - ->exists(); - if($exists == true) { - $status->is_nsfw = true; - } - $profile = Profile::find($status->profile_id); - if(!$profile || $profile->cw == true) { - $status->is_nsfw = true; - } - } else { - $status->is_nsfw = true; - } - } + if (isset($activity['sensitive'])) { + if ((bool) $activity['sensitive'] == false) { + $status->is_nsfw = false; + $exists = ModLog::whereObjectType('App\Status::class') + ->whereObjectId($status->id) + ->whereAction('admin.status.moderate') + ->exists(); + if ($exists == true) { + $status->is_nsfw = true; + } + $profile = Profile::find($status->profile_id); + if (! $profile || $profile->cw == true) { + $status->is_nsfw = true; + } + } else { + $status->is_nsfw = true; + } + } - if(isset($activity['summary'])) { - $status->cw_summary = Purify::clean($activity['summary']); - } else { - $status->cw_summary = null; - } + if (isset($activity['summary'])) { + $status->cw_summary = Purify::clean($activity['summary']); + } else { + $status->cw_summary = null; + } - $status->edited_at = now(); - $status->save(); - StatusService::del($status->id); - } + $status->edited_at = now(); + $status->save(); + StatusService::del($status->id); + } - protected function createEdit($status, $activity) - { - $cleaned = isset($activity['content']) ? Purify::clean($activity['content']) : null; - $spoiler_text = isset($activity['summary']) ? Purify::clean($activity['summary']) : null; - $sensitive = isset($activity['sensitive']) ? $activity['sensitive'] : null; - $mids = $status->media()->count() ? $status->media()->orderBy('order')->pluck('id')->toArray() : null; - StatusEdit::create([ - 'status_id' => $status->id, - 'profile_id' => $status->profile_id, - 'caption' => $cleaned, - 'spoiler_text' => $spoiler_text, - 'is_nsfw' => $sensitive, - 'ordered_media_attachment_ids' => $mids - ]); - } + protected function createEdit($status, $activity) + { + $cleaned = isset($activity['content']) ? Purify::clean($activity['content']) : null; + $spoiler_text = isset($activity['summary']) ? Purify::clean($activity['summary']) : null; + $sensitive = isset($activity['sensitive']) ? $activity['sensitive'] : null; + $mids = $status->media()->count() ? $status->media()->orderBy('order')->pluck('id')->toArray() : null; + StatusEdit::create([ + 'status_id' => $status->id, + 'profile_id' => $status->profile_id, + 'caption' => $cleaned, + 'spoiler_text' => $spoiler_text, + 'is_nsfw' => $sensitive, + 'ordered_media_attachment_ids' => $mids, + ]); + } } From 40478f258aa60c8672d5e48aec347cd88cd07ba6 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 02:05:22 -0600 Subject: [PATCH 13/41] Update landing settings, use config_cache --- app/Http/Controllers/LandingController.php | 39 +++++++++++----------- app/Services/LandingService.php | 4 +-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/app/Http/Controllers/LandingController.php b/app/Http/Controllers/LandingController.php index 5f9f0bba1..f90d84bc2 100644 --- a/app/Http/Controllers/LandingController.php +++ b/app/Http/Controllers/LandingController.php @@ -2,44 +2,43 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; -use App\Profile; -use App\Services\AccountService; use App\Http\Resources\DirectoryProfile; +use App\Profile; +use Illuminate\Http\Request; class LandingController extends Controller { public function directoryRedirect(Request $request) { - if($request->user()) { - return redirect('/'); - } + if ($request->user()) { + return redirect('/'); + } - abort_if(config_cache('instance.landing.show_directory') == false, 404); + abort_if((bool) config_cache('instance.landing.show_directory') == false, 404); - return view('site.index'); + return view('site.index'); } public function exploreRedirect(Request $request) { - if($request->user()) { - return redirect('/'); - } + if ($request->user()) { + return redirect('/'); + } - abort_if(config_cache('instance.landing.show_explore') == false, 404); + abort_if((bool) config_cache('instance.landing.show_explore') == false, 404); - return view('site.index'); + return view('site.index'); } public function getDirectoryApi(Request $request) { - abort_if(config_cache('instance.landing.show_directory') == false, 404); + abort_if((bool) config_cache('instance.landing.show_directory') == false, 404); - return DirectoryProfile::collection( - Profile::whereNull('domain') - ->whereIsSuggestable(true) - ->orderByDesc('updated_at') - ->cursorPaginate(20) - ); + return DirectoryProfile::collection( + Profile::whereNull('domain') + ->whereIsSuggestable(true) + ->orderByDesc('updated_at') + ->cursorPaginate(20) + ); } } diff --git a/app/Services/LandingService.php b/app/Services/LandingService.php index 20759ecf4..213e63910 100644 --- a/app/Services/LandingService.php +++ b/app/Services/LandingService.php @@ -53,8 +53,8 @@ class LandingService 'name' => config_cache('app.name'), 'url' => config_cache('app.url'), 'domain' => config('pixelfed.domain.app'), - 'show_directory' => config_cache('instance.landing.show_directory'), - 'show_explore_feed' => config_cache('instance.landing.show_explore'), + 'show_directory' => (bool) config_cache('instance.landing.show_directory'), + 'show_explore_feed' => (bool) config_cache('instance.landing.show_explore'), 'open_registration' => (bool) $openReg, 'curated_onboarding' => (bool) config_cache('instance.curated_registration.enabled'), 'version' => config('pixelfed.version'), From 5071aaf4086715f5a6c45b86897e4ae2d066e894 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 02:20:37 -0600 Subject: [PATCH 14/41] Update activitpub setting, use config_cache() --- app/Http/Controllers/FederationController.php | 184 +++-- app/Http/Controllers/SearchController.php | 649 +++++++++--------- app/Http/Controllers/StatusController.php | 2 +- app/Jobs/SharePipeline/SharePipeline.php | 225 +++--- app/Jobs/SharePipeline/UndoSharePipeline.php | 203 +++--- app/Jobs/StatusPipeline/StatusDelete.php | 312 +++++---- app/Jobs/StatusPipeline/StatusEntityLexer.php | 43 +- app/Services/LandingService.php | 2 +- app/Util/ActivityPub/Outbox.php | 50 +- .../views/admin/diagnostics/home.blade.php | 2 +- 10 files changed, 833 insertions(+), 839 deletions(-) diff --git a/app/Http/Controllers/FederationController.php b/app/Http/Controllers/FederationController.php index 55c7b4393..54ad03227 100644 --- a/app/Http/Controllers/FederationController.php +++ b/app/Http/Controllers/FederationController.php @@ -2,57 +2,42 @@ namespace App\Http\Controllers; -use App\Jobs\InboxPipeline\{ - DeleteWorker, - InboxWorker, - InboxValidator -}; -use App\Jobs\RemoteFollowPipeline\RemoteFollowPipeline; -use App\{ - AccountLog, - Like, - Profile, - Status, - User -}; -use App\Util\Lexer\Nickname; -use App\Util\Webfinger\Webfinger; -use Auth; -use Cache; -use Carbon\Carbon; -use Illuminate\Http\Request; -use League\Fractal; -use App\Util\Site\Nodeinfo; -use App\Util\ActivityPub\{ - Helpers, - HttpSignature, - Outbox -}; -use Zttp\Zttp; -use App\Services\InstanceService; +use App\Jobs\InboxPipeline\DeleteWorker; +use App\Jobs\InboxPipeline\InboxValidator; +use App\Jobs\InboxPipeline\InboxWorker; +use App\Profile; use App\Services\AccountService; +use App\Services\InstanceService; +use App\Status; +use App\Util\Lexer\Nickname; +use App\Util\Site\Nodeinfo; +use App\Util\Webfinger\Webfinger; +use Cache; +use Illuminate\Http\Request; class FederationController extends Controller { public function nodeinfoWellKnown() { - abort_if(!config('federation.nodeinfo.enabled'), 404); + abort_if(! config('federation.nodeinfo.enabled'), 404); + return response()->json(Nodeinfo::wellKnown(), 200, [], JSON_UNESCAPED_SLASHES) - ->header('Access-Control-Allow-Origin','*'); + ->header('Access-Control-Allow-Origin', '*'); } public function nodeinfo() { - abort_if(!config('federation.nodeinfo.enabled'), 404); + abort_if(! config('federation.nodeinfo.enabled'), 404); + return response()->json(Nodeinfo::get(), 200, [], JSON_UNESCAPED_SLASHES) - ->header('Access-Control-Allow-Origin','*'); + ->header('Access-Control-Allow-Origin', '*'); } public function webfinger(Request $request) { - if (!config('federation.webfinger.enabled') || - !$request->has('resource') || - !$request->filled('resource') + if (! config('federation.webfinger.enabled') || + ! $request->has('resource') || + ! $request->filled('resource') ) { return response('', 400); } @@ -60,55 +45,56 @@ class FederationController extends Controller $resource = $request->input('resource'); $domain = config('pixelfed.domain.app'); - if(config('federation.activitypub.sharedInbox') && - $resource == 'acct:' . $domain . '@' . $domain) { + if (config('federation.activitypub.sharedInbox') && + $resource == 'acct:'.$domain.'@'.$domain) { $res = [ - 'subject' => 'acct:' . $domain . '@' . $domain, + 'subject' => 'acct:'.$domain.'@'.$domain, 'aliases' => [ - 'https://' . $domain . '/i/actor' + 'https://'.$domain.'/i/actor', ], 'links' => [ [ 'rel' => 'http://webfinger.net/rel/profile-page', 'type' => 'text/html', - 'href' => 'https://' . $domain . '/site/kb/instance-actor' + 'href' => 'https://'.$domain.'/site/kb/instance-actor', ], [ 'rel' => 'self', 'type' => 'application/activity+json', - 'href' => 'https://' . $domain . '/i/actor' - ] - ] + 'href' => 'https://'.$domain.'/i/actor', + ], + ], ]; + return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES); } $hash = hash('sha256', $resource); - $key = 'federation:webfinger:sha256:' . $hash; - if($cached = Cache::get($key)) { + $key = 'federation:webfinger:sha256:'.$hash; + if ($cached = Cache::get($key)) { return response()->json($cached, 200, [], JSON_UNESCAPED_SLASHES); } - if(strpos($resource, $domain) == false) { + if (strpos($resource, $domain) == false) { return response('', 400); } $parsed = Nickname::normalizeProfileUrl($resource); - if(empty($parsed) || $parsed['domain'] !== $domain) { + if (empty($parsed) || $parsed['domain'] !== $domain) { return response('', 400); } $username = $parsed['username']; $profile = Profile::whereNull('domain')->whereUsername($username)->first(); - if(!$profile || $profile->status !== null) { + if (! $profile || $profile->status !== null) { return response('', 400); } $webfinger = (new Webfinger($profile))->generate(); Cache::put($key, $webfinger, 1209600); return response()->json($webfinger, 200, [], JSON_UNESCAPED_SLASHES) - ->header('Access-Control-Allow-Origin','*'); + ->header('Access-Control-Allow-Origin', '*'); } public function hostMeta(Request $request) { - abort_if(!config('federation.webfinger.enabled'), 404); + abort_if(! config('federation.webfinger.enabled'), 404); $path = route('well-known.webfinger'); $xml = ''; @@ -118,19 +104,19 @@ class FederationController extends Controller public function userOutbox(Request $request, $username) { - abort_if(!config_cache('federation.activitypub.enabled'), 404); + abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404); - if(!$request->wantsJson()) { - return redirect('/' . $username); + if (! $request->wantsJson()) { + return redirect('/'.$username); } $id = AccountService::usernameToId($username); - abort_if(!$id, 404); + abort_if(! $id, 404); $account = AccountService::get($id); - abort_if(!$account || !isset($account['statuses_count']), 404); + abort_if(! $account || ! isset($account['statuses_count']), 404); $res = [ '@context' => 'https://www.w3.org/ns/activitystreams', - 'id' => 'https://' . config('pixelfed.domain.app') . '/users/' . $username . '/outbox', + 'id' => 'https://'.config('pixelfed.domain.app').'/users/'.$username.'/outbox', 'type' => 'OrderedCollection', 'totalItems' => $account['statuses_count'] ?? 0, ]; @@ -140,135 +126,145 @@ class FederationController extends Controller public function userInbox(Request $request, $username) { - abort_if(!config_cache('federation.activitypub.enabled'), 404); - abort_if(!config('federation.activitypub.inbox'), 404); + abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404); + abort_if(! config('federation.activitypub.inbox'), 404); $headers = $request->headers->all(); $payload = $request->getContent(); - if(!$payload || empty($payload)) { + if (! $payload || empty($payload)) { return; } $obj = json_decode($payload, true, 8); - if(!isset($obj['id'])) { + if (! isset($obj['id'])) { return; } $domain = parse_url($obj['id'], PHP_URL_HOST); - if(in_array($domain, InstanceService::getBannedDomains())) { + if (in_array($domain, InstanceService::getBannedDomains())) { return; } - if(isset($obj['type']) && $obj['type'] === 'Delete') { - if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) { - if($obj['object']['type'] === 'Person') { - if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) { + if (isset($obj['type']) && $obj['type'] === 'Delete') { + if (isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) { + if ($obj['object']['type'] === 'Person') { + if (Profile::whereRemoteUrl($obj['object']['id'])->exists()) { dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox'); + return; } } - if($obj['object']['type'] === 'Tombstone') { - if(Status::whereObjectUrl($obj['object']['id'])->exists()) { + if ($obj['object']['type'] === 'Tombstone') { + if (Status::whereObjectUrl($obj['object']['id'])->exists()) { dispatch(new DeleteWorker($headers, $payload))->onQueue('delete'); + return; } } - if($obj['object']['type'] === 'Story') { + if ($obj['object']['type'] === 'Story') { dispatch(new DeleteWorker($headers, $payload))->onQueue('story'); + return; } } + return; - } else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) { + } elseif (isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) { dispatch(new InboxValidator($username, $headers, $payload))->onQueue('follow'); } else { dispatch(new InboxValidator($username, $headers, $payload))->onQueue('high'); } - return; + } public function sharedInbox(Request $request) { - abort_if(!config_cache('federation.activitypub.enabled'), 404); - abort_if(!config('federation.activitypub.sharedInbox'), 404); + abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404); + abort_if(! config('federation.activitypub.sharedInbox'), 404); $headers = $request->headers->all(); $payload = $request->getContent(); - if(!$payload || empty($payload)) { + if (! $payload || empty($payload)) { return; } $obj = json_decode($payload, true, 8); - if(!isset($obj['id'])) { + if (! isset($obj['id'])) { return; } $domain = parse_url($obj['id'], PHP_URL_HOST); - if(in_array($domain, InstanceService::getBannedDomains())) { + if (in_array($domain, InstanceService::getBannedDomains())) { return; } - if(isset($obj['type']) && $obj['type'] === 'Delete') { - if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) { - if($obj['object']['type'] === 'Person') { - if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) { + if (isset($obj['type']) && $obj['type'] === 'Delete') { + if (isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) { + if ($obj['object']['type'] === 'Person') { + if (Profile::whereRemoteUrl($obj['object']['id'])->exists()) { dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox'); + return; } } - if($obj['object']['type'] === 'Tombstone') { - if(Status::whereObjectUrl($obj['object']['id'])->exists()) { + if ($obj['object']['type'] === 'Tombstone') { + if (Status::whereObjectUrl($obj['object']['id'])->exists()) { dispatch(new DeleteWorker($headers, $payload))->onQueue('delete'); + return; } } - if($obj['object']['type'] === 'Story') { + if ($obj['object']['type'] === 'Story') { dispatch(new DeleteWorker($headers, $payload))->onQueue('story'); + return; } } + return; - } else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) { + } elseif (isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) { dispatch(new InboxWorker($headers, $payload))->onQueue('follow'); } else { dispatch(new InboxWorker($headers, $payload))->onQueue('shared'); } - return; + } public function userFollowing(Request $request, $username) { - abort_if(!config_cache('federation.activitypub.enabled'), 404); + abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404); $id = AccountService::usernameToId($username); - abort_if(!$id, 404); + abort_if(! $id, 404); $account = AccountService::get($id); - abort_if(!$account || !isset($account['following_count']), 404); + abort_if(! $account || ! isset($account['following_count']), 404); $obj = [ '@context' => 'https://www.w3.org/ns/activitystreams', - 'id' => $request->getUri(), - 'type' => 'OrderedCollection', + 'id' => $request->getUri(), + 'type' => 'OrderedCollection', 'totalItems' => $account['following_count'] ?? 0, ]; + return response()->json($obj)->header('Content-Type', 'application/activity+json'); } public function userFollowers(Request $request, $username) { - abort_if(!config_cache('federation.activitypub.enabled'), 404); + abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404); $id = AccountService::usernameToId($username); - abort_if(!$id, 404); + abort_if(! $id, 404); $account = AccountService::get($id); - abort_if(!$account || !isset($account['followers_count']), 404); + abort_if(! $account || ! isset($account['followers_count']), 404); $obj = [ '@context' => 'https://www.w3.org/ns/activitystreams', - 'id' => $request->getUri(), - 'type' => 'OrderedCollection', + 'id' => $request->getUri(), + 'type' => 'OrderedCollection', 'totalItems' => $account['followers_count'] ?? 0, ]; + return response()->json($obj)->header('Content-Type', 'application/activity+json'); } } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index cbf21518b..9388d3abd 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -2,368 +2,367 @@ namespace App\Http\Controllers; -use Auth; use App\Hashtag; use App\Place; use App\Profile; +use App\Services\WebfingerService; use App\Status; -use Illuminate\Http\Request; use App\Util\ActivityPub\Helpers; +use Auth; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; -use App\Transformer\Api\{ - AccountTransformer, - HashtagTransformer, - StatusTransformer, -}; -use App\Services\WebfingerService; class SearchController extends Controller { - public $tokens = []; - public $term = ''; - public $hash = ''; - public $cacheKey = 'api:search:tag:'; + public $tokens = []; - public function __construct() - { - $this->middleware('auth'); - } + public $term = ''; - public function searchAPI(Request $request) - { - $this->validate($request, [ - 'q' => 'required|string|min:3|max:120', - 'src' => 'required|string|in:metro', - 'v' => 'required|integer|in:2', - 'scope' => 'required|in:all,hashtag,profile,remote,webfinger' - ]); + public $hash = ''; - $scope = $request->input('scope') ?? 'all'; - $this->term = e(urldecode($request->input('q'))); - $this->hash = hash('sha256', $this->term); + public $cacheKey = 'api:search:tag:'; - switch ($scope) { - case 'all': - $this->getHashtags(); - $this->getPosts(); - $this->getProfiles(); - // $this->getPlaces(); - break; + public function __construct() + { + $this->middleware('auth'); + } - case 'hashtag': - $this->getHashtags(); - break; + public function searchAPI(Request $request) + { + $this->validate($request, [ + 'q' => 'required|string|min:3|max:120', + 'src' => 'required|string|in:metro', + 'v' => 'required|integer|in:2', + 'scope' => 'required|in:all,hashtag,profile,remote,webfinger', + ]); - case 'profile': - $this->getProfiles(); - break; + $scope = $request->input('scope') ?? 'all'; + $this->term = e(urldecode($request->input('q'))); + $this->hash = hash('sha256', $this->term); - case 'webfinger': - $this->webfingerSearch(); - break; + switch ($scope) { + case 'all': + $this->getHashtags(); + $this->getPosts(); + $this->getProfiles(); + // $this->getPlaces(); + break; - case 'remote': - $this->remoteLookupSearch(); - break; + case 'hashtag': + $this->getHashtags(); + break; - case 'place': - $this->getPlaces(); - break; + case 'profile': + $this->getProfiles(); + break; - default: - break; - } + case 'webfinger': + $this->webfingerSearch(); + break; - return response()->json($this->tokens, 200, [], JSON_PRETTY_PRINT); - } + case 'remote': + $this->remoteLookupSearch(); + break; - protected function getPosts() - { - $tag = $this->term; - $hash = hash('sha256', $tag); - if( Helpers::validateUrl($tag) != false && - Helpers::validateLocalUrl($tag) != true && - config_cache('federation.activitypub.enabled') == true && - config('federation.activitypub.remoteFollow') == true - ) { - $remote = Helpers::fetchFromUrl($tag); - if( isset($remote['type']) && - $remote['type'] == 'Note') { - $item = Helpers::statusFetch($tag); - $this->tokens['posts'] = [[ - 'count' => 0, - 'url' => $item->url(), - 'type' => 'status', - 'value' => "by {$item->profile->username} {$item->created_at->diffForHumans(null, true, true)}", - 'tokens' => [$item->caption], - 'name' => $item->caption, - 'thumb' => $item->thumb(), - ]]; - } - } else { - $posts = Status::select('id', 'profile_id', 'caption', 'created_at') - ->whereHas('media') - ->whereNull('in_reply_to_id') - ->whereNull('reblog_of_id') - ->whereProfileId(Auth::user()->profile_id) - ->where('caption', 'like', '%'.$tag.'%') - ->latest() - ->limit(10) - ->get(); + case 'place': + $this->getPlaces(); + break; - if($posts->count() > 0) { - $posts = $posts->map(function($item, $key) { - return [ - 'count' => 0, - 'url' => $item->url(), - 'type' => 'status', - 'value' => "by {$item->profile->username} {$item->created_at->diffForHumans(null, true, true)}", - 'tokens' => [$item->caption], - 'name' => $item->caption, - 'thumb' => $item->thumb(), - 'filter' => $item->firstMedia()->filter_class - ]; - }); - $this->tokens['posts'] = $posts; - } - } - } + default: + break; + } - protected function getHashtags() - { - $tag = $this->term; - $key = $this->cacheKey . 'hashtags:' . $this->hash; - $ttl = now()->addMinutes(1); - $tokens = Cache::remember($key, $ttl, function() use($tag) { - $htag = Str::startsWith($tag, '#') == true ? mb_substr($tag, 1) : $tag; - $hashtags = Hashtag::select('id', 'name', 'slug') - ->where('slug', 'like', '%'.$htag.'%') - ->whereHas('posts') - ->limit(20) - ->get(); - if($hashtags->count() > 0) { - $tags = $hashtags->map(function ($item, $key) { - return [ - 'count' => $item->posts()->count(), - 'url' => $item->url(), - 'type' => 'hashtag', - 'value' => $item->name, - 'tokens' => '', - 'name' => null, - ]; - }); - return $tags; - } - }); - $this->tokens['hashtags'] = $tokens; - } + return response()->json($this->tokens, 200, [], JSON_PRETTY_PRINT); + } - protected function getPlaces() - { - $tag = $this->term; - // $key = $this->cacheKey . 'places:' . $this->hash; - // $ttl = now()->addHours(12); - // $tokens = Cache::remember($key, $ttl, function() use($tag) { - $htag = Str::contains($tag, ',') == true ? explode(',', $tag) : [$tag]; - $hashtags = Place::select('id', 'name', 'slug', 'country') - ->where('name', 'like', '%'.$htag[0].'%') - ->paginate(20); - $tags = []; - if($hashtags->count() > 0) { - $tags = $hashtags->map(function ($item, $key) { - return [ - 'count' => null, - 'url' => $item->url(), - 'type' => 'place', - 'value' => $item->name . ', ' . $item->country, - 'tokens' => '', - 'name' => null, - 'city' => $item->name, - 'country' => $item->country - ]; - }); - // return $tags; - } - // }); - $this->tokens['places'] = $tags; - $this->tokens['placesPagination'] = [ - 'total' => $hashtags->total(), - 'current_page' => $hashtags->currentPage(), - 'last_page' => $hashtags->lastPage() - ]; - } + protected function getPosts() + { + $tag = $this->term; + $hash = hash('sha256', $tag); + if (Helpers::validateUrl($tag) != false && + Helpers::validateLocalUrl($tag) != true && + (bool) config_cache('federation.activitypub.enabled') == true && + config('federation.activitypub.remoteFollow') == true + ) { + $remote = Helpers::fetchFromUrl($tag); + if (isset($remote['type']) && + in_array($remote['type'], ['Note', 'Question']) + ) { + $item = Helpers::statusFetch($tag); + $this->tokens['posts'] = [[ + 'count' => 0, + 'url' => $item->url(), + 'type' => 'status', + 'value' => "by {$item->profile->username} {$item->created_at->diffForHumans(null, true, true)}", + 'tokens' => [$item->caption], + 'name' => $item->caption, + 'thumb' => $item->thumb(), + ]]; + } + } else { + $posts = Status::select('id', 'profile_id', 'caption', 'created_at') + ->whereHas('media') + ->whereNull('in_reply_to_id') + ->whereNull('reblog_of_id') + ->whereProfileId(Auth::user()->profile_id) + ->where('caption', 'like', '%'.$tag.'%') + ->latest() + ->limit(10) + ->get(); - protected function getProfiles() - { - $tag = $this->term; - $remoteKey = $this->cacheKey . 'profiles:remote:' . $this->hash; - $key = $this->cacheKey . 'profiles:' . $this->hash; - $remoteTtl = now()->addMinutes(15); - $ttl = now()->addHours(2); - if( Helpers::validateUrl($tag) != false && - Helpers::validateLocalUrl($tag) != true && - config_cache('federation.activitypub.enabled') == true && - config('federation.activitypub.remoteFollow') == true - ) { - $remote = Helpers::fetchFromUrl($tag); - if( isset($remote['type']) && - $remote['type'] == 'Person' - ) { - $this->tokens['profiles'] = Cache::remember($remoteKey, $remoteTtl, function() use($tag) { - $item = Helpers::profileFirstOrNew($tag); - $tokens = [[ - 'count' => 1, - 'url' => $item->url(), - 'type' => 'profile', - 'value' => $item->username, - 'tokens' => [$item->username], - 'name' => $item->name, - 'entity' => [ - 'id' => (string) $item->id, - 'following' => $item->followedBy(Auth::user()->profile), - 'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id), - 'thumb' => $item->avatarUrl(), - 'local' => (bool) !$item->domain, - 'post_count' => $item->statuses()->count() - ] - ]]; - return $tokens; - }); - } - } + if ($posts->count() > 0) { + $posts = $posts->map(function ($item, $key) { + return [ + 'count' => 0, + 'url' => $item->url(), + 'type' => 'status', + 'value' => "by {$item->profile->username} {$item->created_at->diffForHumans(null, true, true)}", + 'tokens' => [$item->caption], + 'name' => $item->caption, + 'thumb' => $item->thumb(), + 'filter' => $item->firstMedia()->filter_class, + ]; + }); + $this->tokens['posts'] = $posts; + } + } + } - else { - $this->tokens['profiles'] = Cache::remember($key, $ttl, function() use($tag) { - if(Str::startsWith($tag, '@')) { - $tag = substr($tag, 1); - } - $users = Profile::select('status', 'domain', 'username', 'name', 'id') - ->whereNull('status') - ->where('username', 'like', '%'.$tag.'%') - ->limit(20) - ->orderBy('domain') - ->get(); + protected function getHashtags() + { + $tag = $this->term; + $key = $this->cacheKey.'hashtags:'.$this->hash; + $ttl = now()->addMinutes(1); + $tokens = Cache::remember($key, $ttl, function () use ($tag) { + $htag = Str::startsWith($tag, '#') == true ? mb_substr($tag, 1) : $tag; + $hashtags = Hashtag::select('id', 'name', 'slug') + ->where('slug', 'like', '%'.$htag.'%') + ->whereHas('posts') + ->limit(20) + ->get(); + if ($hashtags->count() > 0) { + $tags = $hashtags->map(function ($item, $key) { + return [ + 'count' => $item->posts()->count(), + 'url' => $item->url(), + 'type' => 'hashtag', + 'value' => $item->name, + 'tokens' => '', + 'name' => null, + ]; + }); - if($users->count() > 0) { - return $users->map(function ($item, $key) { - return [ - 'count' => 0, - 'url' => $item->url(), - 'type' => 'profile', - 'value' => $item->username, - 'tokens' => [$item->username], - 'name' => $item->name, - 'avatar' => $item->avatarUrl(), - 'id' => (string) $item->id, - 'entity' => [ - 'id' => (string) $item->id, - 'following' => $item->followedBy(Auth::user()->profile), - 'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id), - 'thumb' => $item->avatarUrl(), - 'local' => (bool) !$item->domain, - 'post_count' => $item->statuses()->count() - ] - ]; - }); - } - }); - } - } + return $tags; + } + }); + $this->tokens['hashtags'] = $tokens; + } - public function results(Request $request) - { - $this->validate($request, [ - 'q' => 'required|string|min:1', - ]); + protected function getPlaces() + { + $tag = $this->term; + // $key = $this->cacheKey . 'places:' . $this->hash; + // $ttl = now()->addHours(12); + // $tokens = Cache::remember($key, $ttl, function() use($tag) { + $htag = Str::contains($tag, ',') == true ? explode(',', $tag) : [$tag]; + $hashtags = Place::select('id', 'name', 'slug', 'country') + ->where('name', 'like', '%'.$htag[0].'%') + ->paginate(20); + $tags = []; + if ($hashtags->count() > 0) { + $tags = $hashtags->map(function ($item, $key) { + return [ + 'count' => null, + 'url' => $item->url(), + 'type' => 'place', + 'value' => $item->name.', '.$item->country, + 'tokens' => '', + 'name' => null, + 'city' => $item->name, + 'country' => $item->country, + ]; + }); + // return $tags; + } + // }); + $this->tokens['places'] = $tags; + $this->tokens['placesPagination'] = [ + 'total' => $hashtags->total(), + 'current_page' => $hashtags->currentPage(), + 'last_page' => $hashtags->lastPage(), + ]; + } - return view('search.results'); - } + protected function getProfiles() + { + $tag = $this->term; + $remoteKey = $this->cacheKey.'profiles:remote:'.$this->hash; + $key = $this->cacheKey.'profiles:'.$this->hash; + $remoteTtl = now()->addMinutes(15); + $ttl = now()->addHours(2); + if (Helpers::validateUrl($tag) != false && + Helpers::validateLocalUrl($tag) != true && + (bool) config_cache('federation.activitypub.enabled') == true && + config('federation.activitypub.remoteFollow') == true + ) { + $remote = Helpers::fetchFromUrl($tag); + if (isset($remote['type']) && + $remote['type'] == 'Person' + ) { + $this->tokens['profiles'] = Cache::remember($remoteKey, $remoteTtl, function () use ($tag) { + $item = Helpers::profileFirstOrNew($tag); + $tokens = [[ + 'count' => 1, + 'url' => $item->url(), + 'type' => 'profile', + 'value' => $item->username, + 'tokens' => [$item->username], + 'name' => $item->name, + 'entity' => [ + 'id' => (string) $item->id, + 'following' => $item->followedBy(Auth::user()->profile), + 'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id), + 'thumb' => $item->avatarUrl(), + 'local' => (bool) ! $item->domain, + 'post_count' => $item->statuses()->count(), + ], + ]]; - protected function webfingerSearch() - { - $wfs = WebfingerService::lookup($this->term); + return $tokens; + }); + } + } else { + $this->tokens['profiles'] = Cache::remember($key, $ttl, function () use ($tag) { + if (Str::startsWith($tag, '@')) { + $tag = substr($tag, 1); + } + $users = Profile::select('status', 'domain', 'username', 'name', 'id') + ->whereNull('status') + ->where('username', 'like', '%'.$tag.'%') + ->limit(20) + ->orderBy('domain') + ->get(); - if(empty($wfs)) { - return; - } + if ($users->count() > 0) { + return $users->map(function ($item, $key) { + return [ + 'count' => 0, + 'url' => $item->url(), + 'type' => 'profile', + 'value' => $item->username, + 'tokens' => [$item->username], + 'name' => $item->name, + 'avatar' => $item->avatarUrl(), + 'id' => (string) $item->id, + 'entity' => [ + 'id' => (string) $item->id, + 'following' => $item->followedBy(Auth::user()->profile), + 'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id), + 'thumb' => $item->avatarUrl(), + 'local' => (bool) ! $item->domain, + 'post_count' => $item->statuses()->count(), + ], + ]; + }); + } + }); + } + } - $this->tokens['profiles'] = [ - [ - 'count' => 1, - 'url' => $wfs['url'], - 'type' => 'profile', - 'value' => $wfs['username'], - 'tokens' => [$wfs['username']], - 'name' => $wfs['display_name'], - 'entity' => [ - 'id' => (string) $wfs['id'], - 'following' => null, - 'follow_request' => null, - 'thumb' => $wfs['avatar'], - 'local' => (bool) $wfs['local'] - ] - ] - ]; - return; - } + public function results(Request $request) + { + $this->validate($request, [ + 'q' => 'required|string|min:1', + ]); - protected function remotePostLookup() - { - $tag = $this->term; - $hash = hash('sha256', $tag); - $local = Helpers::validateLocalUrl($tag); - $valid = Helpers::validateUrl($tag); + return view('search.results'); + } - if($valid == false || $local == true) { - return; - } + protected function webfingerSearch() + { + $wfs = WebfingerService::lookup($this->term); - if(Status::whereUri($tag)->whereLocal(false)->exists()) { - $item = Status::whereUri($tag)->first(); - $media = $item->firstMedia(); - $url = null; - if($media) { - $url = $media->remote_url; - } - $this->tokens['posts'] = [[ - 'count' => 0, - 'url' => "/i/web/post/_/$item->profile_id/$item->id", - 'type' => 'status', - 'username' => $item->profile->username, - 'caption' => $item->rendered ?? $item->caption, - 'thumb' => $url, - 'timestamp' => $item->created_at->diffForHumans() - ]]; - } + if (empty($wfs)) { + return; + } - $remote = Helpers::fetchFromUrl($tag); + $this->tokens['profiles'] = [ + [ + 'count' => 1, + 'url' => $wfs['url'], + 'type' => 'profile', + 'value' => $wfs['username'], + 'tokens' => [$wfs['username']], + 'name' => $wfs['display_name'], + 'entity' => [ + 'id' => (string) $wfs['id'], + 'following' => null, + 'follow_request' => null, + 'thumb' => $wfs['avatar'], + 'local' => (bool) $wfs['local'], + ], + ], + ]; - if(isset($remote['type']) && $remote['type'] == 'Note') { - $item = Helpers::statusFetch($tag); - $media = $item->firstMedia(); - $url = null; - if($media) { - $url = $media->remote_url; - } - $this->tokens['posts'] = [[ - 'count' => 0, - 'url' => "/i/web/post/_/$item->profile_id/$item->id", - 'type' => 'status', - 'username' => $item->profile->username, - 'caption' => $item->rendered ?? $item->caption, - 'thumb' => $url, - 'timestamp' => $item->created_at->diffForHumans() - ]]; - } - } + } - protected function remoteLookupSearch() - { - if(!Helpers::validateUrl($this->term)) { - return; - } - $this->getProfiles(); - $this->remotePostLookup(); - } + protected function remotePostLookup() + { + $tag = $this->term; + $hash = hash('sha256', $tag); + $local = Helpers::validateLocalUrl($tag); + $valid = Helpers::validateUrl($tag); + + if ($valid == false || $local == true) { + return; + } + + if (Status::whereUri($tag)->whereLocal(false)->exists()) { + $item = Status::whereUri($tag)->first(); + $media = $item->firstMedia(); + $url = null; + if ($media) { + $url = $media->remote_url; + } + $this->tokens['posts'] = [[ + 'count' => 0, + 'url' => "/i/web/post/_/$item->profile_id/$item->id", + 'type' => 'status', + 'username' => $item->profile->username, + 'caption' => $item->rendered ?? $item->caption, + 'thumb' => $url, + 'timestamp' => $item->created_at->diffForHumans(), + ]]; + } + + $remote = Helpers::fetchFromUrl($tag); + + if (isset($remote['type']) && $remote['type'] == 'Note') { + $item = Helpers::statusFetch($tag); + $media = $item->firstMedia(); + $url = null; + if ($media) { + $url = $media->remote_url; + } + $this->tokens['posts'] = [[ + 'count' => 0, + 'url' => "/i/web/post/_/$item->profile_id/$item->id", + 'type' => 'status', + 'username' => $item->profile->username, + 'caption' => $item->rendered ?? $item->caption, + 'thumb' => $url, + 'timestamp' => $item->created_at->diffForHumans(), + ]]; + } + } + + protected function remoteLookupSearch() + { + if (! Helpers::validateUrl($this->term)) { + return; + } + $this->getProfiles(); + $this->remotePostLookup(); + } } diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index 4a3b3552d..7f77f9a81 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -78,7 +78,7 @@ class StatusController extends Controller ]); } - if ($request->wantsJson() && config_cache('federation.activitypub.enabled')) { + if ($request->wantsJson() && (bool) config_cache('federation.activitypub.enabled')) { return $this->showActivityPub($request, $status); } diff --git a/app/Jobs/SharePipeline/SharePipeline.php b/app/Jobs/SharePipeline/SharePipeline.php index 4eca4e1ab..734c44231 100644 --- a/app/Jobs/SharePipeline/SharePipeline.php +++ b/app/Jobs/SharePipeline/SharePipeline.php @@ -2,9 +2,15 @@ namespace App\Jobs\SharePipeline; -use Cache, Log; -use Illuminate\Support\Facades\Redis; -use App\{Status, Notification}; +use App\Jobs\HomeFeedPipeline\FeedInsertPipeline; +use App\Notification; +use App\Services\ReblogService; +use App\Services\StatusService; +use App\Status; +use App\Transformer\ActivityPub\Verb\Announce; +use App\Util\ActivityPub\HttpSignature; +use GuzzleHttp\Client; +use GuzzleHttp\Pool; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -12,141 +18,136 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use League\Fractal; use League\Fractal\Serializer\ArraySerializer; -use App\Transformer\ActivityPub\Verb\Announce; -use GuzzleHttp\{Pool, Client, Promise}; -use App\Util\ActivityPub\HttpSignature; -use App\Services\ReblogService; -use App\Services\StatusService; -use App\Jobs\HomeFeedPipeline\FeedInsertPipeline; class SharePipeline implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $status; + protected $status; - /** - * Delete the job if its models no longer exist. - * - * @var bool - */ - public $deleteWhenMissingModels = true; + /** + * Delete the job if its models no longer exist. + * + * @var bool + */ + public $deleteWhenMissingModels = true; - /** - * Create a new job instance. - * - * @return void - */ - public function __construct(Status $status) - { - $this->status = $status; - } + /** + * Create a new job instance. + * + * @return void + */ + public function __construct(Status $status) + { + $this->status = $status; + } - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $status = $this->status; - $parent = Status::find($this->status->reblog_of_id); - if(!$parent) { + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $status = $this->status; + $parent = Status::find($this->status->reblog_of_id); + if (! $parent) { return; } - $actor = $status->profile; - $target = $parent->profile; + $actor = $status->profile; + $target = $parent->profile; - if ($status->uri !== null) { - // Ignore notifications to remote statuses - return; - } + if ($status->uri !== null) { + // Ignore notifications to remote statuses + return; + } - if($target->id === $status->profile_id) { - $this->remoteAnnounceDeliver(); - return true; - } + if ($target->id === $status->profile_id) { + $this->remoteAnnounceDeliver(); - ReblogService::addPostReblog($parent->profile_id, $status->id); + return true; + } - $parent->reblogs_count = $parent->reblogs_count + 1; - $parent->save(); - StatusService::del($parent->id); + ReblogService::addPostReblog($parent->profile_id, $status->id); - Notification::firstOrCreate( - [ - 'profile_id' => $target->id, - 'actor_id' => $actor->id, - 'action' => 'share', - 'item_type' => 'App\Status', - 'item_id' => $status->reblog_of_id ?? $status->id, - ] - ); + $parent->reblogs_count = $parent->reblogs_count + 1; + $parent->save(); + StatusService::del($parent->id); - FeedInsertPipeline::dispatch($status->id, $status->profile_id)->onQueue('feed'); + Notification::firstOrCreate( + [ + 'profile_id' => $target->id, + 'actor_id' => $actor->id, + 'action' => 'share', + 'item_type' => 'App\Status', + 'item_id' => $status->reblog_of_id ?? $status->id, + ] + ); - return $this->remoteAnnounceDeliver(); - } + FeedInsertPipeline::dispatch($status->id, $status->profile_id)->onQueue('feed'); - public function remoteAnnounceDeliver() - { - if(config('app.env') !== 'production' || config_cache('federation.activitypub.enabled') == false) { - return true; - } - $status = $this->status; - $profile = $status->profile; + return $this->remoteAnnounceDeliver(); + } - $fractal = new Fractal\Manager(); - $fractal->setSerializer(new ArraySerializer()); - $resource = new Fractal\Resource\Item($status, new Announce()); - $activity = $fractal->createData($resource)->toArray(); + public function remoteAnnounceDeliver() + { + if (config('app.env') !== 'production' || (bool) config_cache('federation.activitypub.enabled') == false) { + return true; + } + $status = $this->status; + $profile = $status->profile; - $audience = $status->profile->getAudienceInbox(); + $fractal = new Fractal\Manager(); + $fractal->setSerializer(new ArraySerializer()); + $resource = new Fractal\Resource\Item($status, new Announce()); + $activity = $fractal->createData($resource)->toArray(); - if(empty($audience) || $status->scope != 'public') { - // Return on profiles with no remote followers - return; - } + $audience = $status->profile->getAudienceInbox(); - $payload = json_encode($activity); + if (empty($audience) || $status->scope != 'public') { + // Return on profiles with no remote followers + return; + } - $client = new Client([ - 'timeout' => config('federation.activitypub.delivery.timeout') - ]); + $payload = json_encode($activity); - $version = config('pixelfed.version'); - $appUrl = config('app.url'); - $userAgent = "(Pixelfed/{$version}; +{$appUrl})"; + $client = new Client([ + 'timeout' => config('federation.activitypub.delivery.timeout'), + ]); - $requests = function($audience) use ($client, $activity, $profile, $payload, $userAgent) { - foreach($audience as $url) { - $headers = HttpSignature::sign($profile, $url, $activity, [ - 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', - 'User-Agent' => $userAgent, - ]); - yield function() use ($client, $url, $headers, $payload) { - return $client->postAsync($url, [ - 'curl' => [ - CURLOPT_HTTPHEADER => $headers, - CURLOPT_POSTFIELDS => $payload, - CURLOPT_HEADER => true - ] - ]); - }; - } - }; + $version = config('pixelfed.version'); + $appUrl = config('app.url'); + $userAgent = "(Pixelfed/{$version}; +{$appUrl})"; - $pool = new Pool($client, $requests($audience), [ - 'concurrency' => config('federation.activitypub.delivery.concurrency'), - 'fulfilled' => function ($response, $index) { - }, - 'rejected' => function ($reason, $index) { - } - ]); + $requests = function ($audience) use ($client, $activity, $profile, $payload, $userAgent) { + foreach ($audience as $url) { + $headers = HttpSignature::sign($profile, $url, $activity, [ + 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'User-Agent' => $userAgent, + ]); + yield function () use ($client, $url, $headers, $payload) { + return $client->postAsync($url, [ + 'curl' => [ + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => $payload, + CURLOPT_HEADER => true, + ], + ]); + }; + } + }; - $promise = $pool->promise(); + $pool = new Pool($client, $requests($audience), [ + 'concurrency' => config('federation.activitypub.delivery.concurrency'), + 'fulfilled' => function ($response, $index) { + }, + 'rejected' => function ($reason, $index) { + }, + ]); - $promise->wait(); + $promise = $pool->promise(); - } + $promise->wait(); + + } } diff --git a/app/Jobs/SharePipeline/UndoSharePipeline.php b/app/Jobs/SharePipeline/UndoSharePipeline.php index 1435688d9..af3239953 100644 --- a/app/Jobs/SharePipeline/UndoSharePipeline.php +++ b/app/Jobs/SharePipeline/UndoSharePipeline.php @@ -2,9 +2,15 @@ namespace App\Jobs\SharePipeline; -use Cache, Log; -use Illuminate\Support\Facades\Redis; -use App\{Status, Notification}; +use App\Jobs\HomeFeedPipeline\FeedRemovePipeline; +use App\Notification; +use App\Services\ReblogService; +use App\Services\StatusService; +use App\Status; +use App\Transformer\ActivityPub\Verb\UndoAnnounce; +use App\Util\ActivityPub\HttpSignature; +use GuzzleHttp\Client; +use GuzzleHttp\Pool; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -12,128 +18,125 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use League\Fractal; use League\Fractal\Serializer\ArraySerializer; -use App\Transformer\ActivityPub\Verb\UndoAnnounce; -use GuzzleHttp\{Pool, Client, Promise}; -use App\Util\ActivityPub\HttpSignature; -use App\Services\ReblogService; -use App\Services\StatusService; -use App\Jobs\HomeFeedPipeline\FeedRemovePipeline; class UndoSharePipeline implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $status; - public $deleteWhenMissingModels = true; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public function __construct(Status $status) - { - $this->status = $status; - } + protected $status; - public function handle() - { - $status = $this->status; - $actor = $status->profile; - $parent = Status::find($status->reblog_of_id); + public $deleteWhenMissingModels = true; - FeedRemovePipeline::dispatch($status->id, $status->profile_id)->onQueue('feed'); + public function __construct(Status $status) + { + $this->status = $status; + } - if($parent) { - $target = $parent->profile_id; - ReblogService::removePostReblog($parent->profile_id, $status->id); + public function handle() + { + $status = $this->status; + $actor = $status->profile; + $parent = Status::find($status->reblog_of_id); - if($parent->reblogs_count > 0) { - $parent->reblogs_count = $parent->reblogs_count - 1; - $parent->save(); - StatusService::del($parent->id); - } + FeedRemovePipeline::dispatch($status->id, $status->profile_id)->onQueue('feed'); - $notification = Notification::whereProfileId($target) - ->whereActorId($status->profile_id) - ->whereAction('share') - ->whereItemId($status->reblog_of_id) - ->whereItemType('App\Status') - ->first(); + if ($parent) { + $target = $parent->profile_id; + ReblogService::removePostReblog($parent->profile_id, $status->id); - if($notification) { - $notification->forceDelete(); - } - } + if ($parent->reblogs_count > 0) { + $parent->reblogs_count = $parent->reblogs_count - 1; + $parent->save(); + StatusService::del($parent->id); + } - if ($status->uri != null) { - return; - } + $notification = Notification::whereProfileId($target) + ->whereActorId($status->profile_id) + ->whereAction('share') + ->whereItemId($status->reblog_of_id) + ->whereItemType('App\Status') + ->first(); - if(config('app.env') !== 'production' || config_cache('federation.activitypub.enabled') == false) { - return $status->delete(); - } else { - return $this->remoteAnnounceDeliver(); - } - } + if ($notification) { + $notification->forceDelete(); + } + } - public function remoteAnnounceDeliver() - { - if(config('app.env') !== 'production' || config_cache('federation.activitypub.enabled') == false) { + if ($status->uri != null) { + return; + } + + if (config('app.env') !== 'production' || (bool) config_cache('federation.activitypub.enabled') == false) { + return $status->delete(); + } else { + return $this->remoteAnnounceDeliver(); + } + } + + public function remoteAnnounceDeliver() + { + if (config('app.env') !== 'production' || (bool) config_cache('federation.activitypub.enabled') == false) { $status->delete(); - return 1; - } - $status = $this->status; - $profile = $status->profile; + return 1; + } - $fractal = new Fractal\Manager(); - $fractal->setSerializer(new ArraySerializer()); - $resource = new Fractal\Resource\Item($status, new UndoAnnounce()); - $activity = $fractal->createData($resource)->toArray(); + $status = $this->status; + $profile = $status->profile; - $audience = $status->profile->getAudienceInbox(); + $fractal = new Fractal\Manager(); + $fractal->setSerializer(new ArraySerializer()); + $resource = new Fractal\Resource\Item($status, new UndoAnnounce()); + $activity = $fractal->createData($resource)->toArray(); - if(empty($audience) || $status->scope != 'public') { - return 1; - } + $audience = $status->profile->getAudienceInbox(); - $payload = json_encode($activity); + if (empty($audience) || $status->scope != 'public') { + return 1; + } - $client = new Client([ - 'timeout' => config('federation.activitypub.delivery.timeout') - ]); + $payload = json_encode($activity); - $version = config('pixelfed.version'); - $appUrl = config('app.url'); - $userAgent = "(Pixelfed/{$version}; +{$appUrl})"; + $client = new Client([ + 'timeout' => config('federation.activitypub.delivery.timeout'), + ]); - $requests = function($audience) use ($client, $activity, $profile, $payload, $userAgent) { - foreach($audience as $url) { - $headers = HttpSignature::sign($profile, $url, $activity, [ - 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', - 'User-Agent' => $userAgent, - ]); - yield function() use ($client, $url, $headers, $payload) { - return $client->postAsync($url, [ - 'curl' => [ - CURLOPT_HTTPHEADER => $headers, - CURLOPT_POSTFIELDS => $payload, - CURLOPT_HEADER => true - ] - ]); - }; - } - }; + $version = config('pixelfed.version'); + $appUrl = config('app.url'); + $userAgent = "(Pixelfed/{$version}; +{$appUrl})"; - $pool = new Pool($client, $requests($audience), [ - 'concurrency' => config('federation.activitypub.delivery.concurrency'), - 'fulfilled' => function ($response, $index) { - }, - 'rejected' => function ($reason, $index) { - } - ]); + $requests = function ($audience) use ($client, $activity, $profile, $payload, $userAgent) { + foreach ($audience as $url) { + $headers = HttpSignature::sign($profile, $url, $activity, [ + 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'User-Agent' => $userAgent, + ]); + yield function () use ($client, $url, $headers, $payload) { + return $client->postAsync($url, [ + 'curl' => [ + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => $payload, + CURLOPT_HEADER => true, + ], + ]); + }; + } + }; - $promise = $pool->promise(); + $pool = new Pool($client, $requests($audience), [ + 'concurrency' => config('federation.activitypub.delivery.concurrency'), + 'fulfilled' => function ($response, $index) { + }, + 'rejected' => function ($reason, $index) { + }, + ]); - $promise->wait(); + $promise = $pool->promise(); - $status->delete(); + $promise->wait(); - return 1; - } + $status->delete(); + + return 1; + } } diff --git a/app/Jobs/StatusPipeline/StatusDelete.php b/app/Jobs/StatusPipeline/StatusDelete.php index dbbfad5ac..d85ebdc4a 100644 --- a/app/Jobs/StatusPipeline/StatusDelete.php +++ b/app/Jobs/StatusPipeline/StatusDelete.php @@ -2,126 +2,122 @@ namespace App\Jobs\StatusPipeline; -use DB, Cache, Storage; -use App\{ - AccountInterstitial, - Bookmark, - CollectionItem, - DirectMessage, - Like, - Media, - MediaTag, - Mention, - Notification, - Report, - Status, - StatusArchived, - StatusHashtag, - StatusView -}; +use App\AccountInterstitial; +use App\Bookmark; +use App\CollectionItem; +use App\DirectMessage; +use App\Jobs\MediaPipeline\MediaDeletePipeline; +use App\Like; +use App\Media; +use App\MediaTag; +use App\Mention; +use App\Notification; +use App\Report; +use App\Services\CollectionService; +use App\Services\NotificationService; +use App\Services\StatusService; +use App\Status; +use App\StatusArchived; +use App\StatusHashtag; +use App\StatusView; +use App\Transformer\ActivityPub\Verb\DeleteNote; +use App\Util\ActivityPub\HttpSignature; +use Cache; +use GuzzleHttp\Client; +use GuzzleHttp\Pool; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use League\Fractal; -use Illuminate\Support\Str; use League\Fractal\Serializer\ArraySerializer; -use App\Transformer\ActivityPub\Verb\DeleteNote; -use App\Util\ActivityPub\Helpers; -use GuzzleHttp\Pool; -use GuzzleHttp\Client; -use GuzzleHttp\Promise; -use App\Util\ActivityPub\HttpSignature; -use App\Services\CollectionService; -use App\Services\StatusService; -use App\Services\NotificationService; -use App\Jobs\MediaPipeline\MediaDeletePipeline; class StatusDelete implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected $status; + protected $status; - /** - * Delete the job if its models no longer exist. - * - * @var bool - */ - public $deleteWhenMissingModels = true; + /** + * Delete the job if its models no longer exist. + * + * @var bool + */ + public $deleteWhenMissingModels = true; public $timeout = 900; + public $tries = 2; - /** - * Create a new job instance. - * - * @return void - */ - public function __construct(Status $status) - { - $this->status = $status; - } + /** + * Create a new job instance. + * + * @return void + */ + public function __construct(Status $status) + { + $this->status = $status; + } - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $status = $this->status; - $profile = $this->status->profile; + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $status = $this->status; + $profile = $this->status->profile; - StatusService::del($status->id, true); - if($profile) { - if(in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])) { - $profile->status_count = $profile->status_count - 1; - $profile->save(); - } - } + StatusService::del($status->id, true); + if ($profile) { + if (in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])) { + $profile->status_count = $profile->status_count - 1; + $profile->save(); + } + } - Cache::forget('pf:atom:user-feed:by-id:' . $status->profile_id); + Cache::forget('pf:atom:user-feed:by-id:'.$status->profile_id); - if(config_cache('federation.activitypub.enabled') == true) { - return $this->fanoutDelete($status); - } else { - return $this->unlinkRemoveMedia($status); - } - } + if ((bool) config_cache('federation.activitypub.enabled') == true) { + return $this->fanoutDelete($status); + } else { + return $this->unlinkRemoveMedia($status); + } + } - public function unlinkRemoveMedia($status) - { + public function unlinkRemoveMedia($status) + { Media::whereStatusId($status->id) - ->get() - ->each(function($media) { - MediaDeletePipeline::dispatch($media); - }); + ->get() + ->each(function ($media) { + MediaDeletePipeline::dispatch($media); + }); - if($status->in_reply_to_id) { - $parent = Status::findOrFail($status->in_reply_to_id); - --$parent->reply_count; - $parent->save(); - StatusService::del($parent->id); - } + if ($status->in_reply_to_id) { + $parent = Status::findOrFail($status->in_reply_to_id); + $parent->reply_count--; + $parent->save(); + StatusService::del($parent->id); + } Bookmark::whereStatusId($status->id)->delete(); CollectionItem::whereObjectType('App\Status') ->whereObjectId($status->id) ->get() - ->each(function($col) { + ->each(function ($col) { CollectionService::removeItem($col->collection_id, $col->object_id); $col->delete(); - }); + }); $dms = DirectMessage::whereStatusId($status->id)->get(); - foreach($dms as $dm) { + foreach ($dms as $dm) { $not = Notification::whereItemType('App\DirectMessage') ->whereItemId($dm->id) ->first(); - if($not) { + if ($not) { NotificationService::del($not->profile_id, $not->id); $not->forceDeleteQuietly(); } @@ -130,11 +126,11 @@ class StatusDelete implements ShouldQueue Like::whereStatusId($status->id)->delete(); $mediaTags = MediaTag::where('status_id', $status->id)->get(); - foreach($mediaTags as $mtag) { + foreach ($mediaTags as $mtag) { $not = Notification::whereItemType('App\MediaTag') ->whereItemId($mtag->id) ->first(); - if($not) { + if ($not) { NotificationService::del($not->profile_id, $not->id); $not->forceDeleteQuietly(); } @@ -142,85 +138,85 @@ class StatusDelete implements ShouldQueue } Mention::whereStatusId($status->id)->forceDelete(); - Notification::whereItemType('App\Status') - ->whereItemId($status->id) - ->forceDelete(); + Notification::whereItemType('App\Status') + ->whereItemId($status->id) + ->forceDelete(); - Report::whereObjectType('App\Status') - ->whereObjectId($status->id) - ->delete(); + Report::whereObjectType('App\Status') + ->whereObjectId($status->id) + ->delete(); StatusArchived::whereStatusId($status->id)->delete(); StatusHashtag::whereStatusId($status->id)->delete(); StatusView::whereStatusId($status->id)->delete(); - Status::whereInReplyToId($status->id)->update(['in_reply_to_id' => null]); + Status::whereInReplyToId($status->id)->update(['in_reply_to_id' => null]); - AccountInterstitial::where('item_type', 'App\Status') - ->where('item_id', $status->id) - ->delete(); + AccountInterstitial::where('item_type', 'App\Status') + ->where('item_id', $status->id) + ->delete(); - $status->delete(); - - return 1; - } - - public function fanoutDelete($status) - { - $profile = $status->profile; - - if(!$profile) { - return; - } - - $audience = $status->profile->getAudienceInbox(); - - $fractal = new Fractal\Manager(); - $fractal->setSerializer(new ArraySerializer()); - $resource = new Fractal\Resource\Item($status, new DeleteNote()); - $activity = $fractal->createData($resource)->toArray(); - - $this->unlinkRemoveMedia($status); - - $payload = json_encode($activity); - - $client = new Client([ - 'timeout' => config('federation.activitypub.delivery.timeout') - ]); - - $version = config('pixelfed.version'); - $appUrl = config('app.url'); - $userAgent = "(Pixelfed/{$version}; +{$appUrl})"; - - $requests = function($audience) use ($client, $activity, $profile, $payload, $userAgent) { - foreach($audience as $url) { - $headers = HttpSignature::sign($profile, $url, $activity, [ - 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', - 'User-Agent' => $userAgent, - ]); - yield function() use ($client, $url, $headers, $payload) { - return $client->postAsync($url, [ - 'curl' => [ - CURLOPT_HTTPHEADER => $headers, - CURLOPT_POSTFIELDS => $payload, - CURLOPT_HEADER => true - ] - ]); - }; - } - }; - - $pool = new Pool($client, $requests($audience), [ - 'concurrency' => config('federation.activitypub.delivery.concurrency'), - 'fulfilled' => function ($response, $index) { - }, - 'rejected' => function ($reason, $index) { - } - ]); - - $promise = $pool->promise(); - - $promise->wait(); + $status->delete(); return 1; - } + } + + public function fanoutDelete($status) + { + $profile = $status->profile; + + if (! $profile) { + return; + } + + $audience = $status->profile->getAudienceInbox(); + + $fractal = new Fractal\Manager(); + $fractal->setSerializer(new ArraySerializer()); + $resource = new Fractal\Resource\Item($status, new DeleteNote()); + $activity = $fractal->createData($resource)->toArray(); + + $this->unlinkRemoveMedia($status); + + $payload = json_encode($activity); + + $client = new Client([ + 'timeout' => config('federation.activitypub.delivery.timeout'), + ]); + + $version = config('pixelfed.version'); + $appUrl = config('app.url'); + $userAgent = "(Pixelfed/{$version}; +{$appUrl})"; + + $requests = function ($audience) use ($client, $activity, $profile, $payload, $userAgent) { + foreach ($audience as $url) { + $headers = HttpSignature::sign($profile, $url, $activity, [ + 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'User-Agent' => $userAgent, + ]); + yield function () use ($client, $url, $headers, $payload) { + return $client->postAsync($url, [ + 'curl' => [ + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POSTFIELDS => $payload, + CURLOPT_HEADER => true, + ], + ]); + }; + } + }; + + $pool = new Pool($client, $requests($audience), [ + 'concurrency' => config('federation.activitypub.delivery.concurrency'), + 'fulfilled' => function ($response, $index) { + }, + 'rejected' => function ($reason, $index) { + }, + ]); + + $promise = $pool->promise(); + + $promise->wait(); + + return 1; + } } diff --git a/app/Jobs/StatusPipeline/StatusEntityLexer.php b/app/Jobs/StatusPipeline/StatusEntityLexer.php index 872594a96..5c37838dc 100644 --- a/app/Jobs/StatusPipeline/StatusEntityLexer.php +++ b/app/Jobs/StatusPipeline/StatusEntityLexer.php @@ -3,12 +3,16 @@ namespace App\Jobs\StatusPipeline; use App\Hashtag; +use App\Jobs\HomeFeedPipeline\FeedInsertPipeline; use App\Jobs\MentionPipeline\MentionPipeline; use App\Mention; use App\Profile; +use App\Services\AdminShadowFilterService; +use App\Services\PublicTimelineService; +use App\Services\StatusService; +use App\Services\UserFilterService; use App\Status; use App\StatusHashtag; -use App\Services\PublicTimelineService; use App\Util\Lexer\Autolink; use App\Util\Lexer\Extractor; use App\Util\Sentiment\Bouncer; @@ -19,18 +23,15 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use App\Services\StatusService; -use App\Services\UserFilterService; -use App\Services\AdminShadowFilterService; -use App\Jobs\HomeFeedPipeline\FeedInsertPipeline; -use App\Jobs\HomeFeedPipeline\HashtagInsertFanoutPipeline; class StatusEntityLexer implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $status; + protected $entities; + protected $autolink; /** @@ -60,12 +61,12 @@ class StatusEntityLexer implements ShouldQueue $profile = $this->status->profile; $status = $this->status; - if(in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])) { + if (in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])) { $profile->status_count = $profile->status_count + 1; $profile->save(); } - if($profile->no_autolink == false) { + if ($profile->no_autolink == false) { $this->parseEntities(); } } @@ -103,16 +104,16 @@ class StatusEntityLexer implements ShouldQueue $status = $this->status; foreach ($tags as $tag) { - if(mb_strlen($tag) > 124) { + if (mb_strlen($tag) > 124) { continue; } DB::transaction(function () use ($status, $tag) { $slug = str_slug($tag, '-', false); $hashtag = Hashtag::firstOrCreate([ - 'slug' => $slug + 'slug' => $slug, ], [ - 'name' => $tag + 'name' => $tag, ]); StatusHashtag::firstOrCreate( @@ -136,11 +137,11 @@ class StatusEntityLexer implements ShouldQueue foreach ($mentions as $mention) { $mentioned = Profile::whereUsername($mention)->first(); - if (empty($mentioned) || !isset($mentioned->id)) { + if (empty($mentioned) || ! isset($mentioned->id)) { continue; } $blocks = UserFilterService::blocks($mentioned->id); - if($blocks && in_array($status->profile_id, $blocks)) { + if ($blocks && in_array($status->profile_id, $blocks)) { continue; } @@ -161,8 +162,8 @@ class StatusEntityLexer implements ShouldQueue $status = $this->status; StatusService::refresh($status->id); - if(config('exp.cached_home_timeline')) { - if( $status->in_reply_to_id === null && + if (config('exp.cached_home_timeline')) { + if ($status->in_reply_to_id === null && in_array($status->scope, ['public', 'unlisted', 'private']) ) { FeedInsertPipeline::dispatch($status->id, $status->profile_id)->onQueue('feed'); @@ -179,28 +180,28 @@ class StatusEntityLexer implements ShouldQueue 'photo:album', 'video', 'video:album', - 'photo:video:album' + 'photo:video:album', ]; - if(config_cache('pixelfed.bouncer.enabled')) { + if (config_cache('pixelfed.bouncer.enabled')) { Bouncer::get($status); } - Cache::forget('pf:atom:user-feed:by-id:' . $status->profile_id); + Cache::forget('pf:atom:user-feed:by-id:'.$status->profile_id); $hideNsfw = config('instance.hide_nsfw_on_public_feeds'); - if( $status->uri == null && + if ($status->uri == null && $status->scope == 'public' && in_array($status->type, $types) && $status->in_reply_to_id === null && $status->reblog_of_id === null && ($hideNsfw ? $status->is_nsfw == false : true) ) { - if(AdminShadowFilterService::canAddToPublicFeedByProfileId($status->profile_id)) { + if (AdminShadowFilterService::canAddToPublicFeedByProfileId($status->profile_id)) { PublicTimelineService::add($status->id); } } - if(config_cache('federation.activitypub.enabled') == true && config('app.env') == 'production') { + if ((bool) config_cache('federation.activitypub.enabled') == true && config('app.env') == 'production') { StatusActivityPubDeliver::dispatch($status); } } diff --git a/app/Services/LandingService.php b/app/Services/LandingService.php index 213e63910..f51822df2 100644 --- a/app/Services/LandingService.php +++ b/app/Services/LandingService.php @@ -85,7 +85,7 @@ class LandingService 'media_types' => config_cache('pixelfed.media_types'), ], 'features' => [ - 'federation' => config_cache('federation.activitypub.enabled'), + 'federation' => (bool) config_cache('federation.activitypub.enabled'), 'timelines' => [ 'local' => true, 'network' => (bool) config_cache('federation.network_timeline'), diff --git a/app/Util/ActivityPub/Outbox.php b/app/Util/ActivityPub/Outbox.php index 43adb36e3..aba34955e 100644 --- a/app/Util/ActivityPub/Outbox.php +++ b/app/Util/ActivityPub/Outbox.php @@ -2,34 +2,32 @@ namespace App\Util\ActivityPub; -use App\Profile; -use App\Status; -use League\Fractal; use App\Http\Controllers\ProfileController; -use App\Transformer\ActivityPub\ProfileOutbox; +use App\Status; use App\Transformer\ActivityPub\Verb\CreateNote; +use League\Fractal; -class Outbox { +class Outbox +{ + public static function get($profile) + { + abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404); + abort_if(! config('federation.activitypub.outbox'), 404); - public static function get($profile) - { - abort_if(!config_cache('federation.activitypub.enabled'), 404); - abort_if(!config('federation.activitypub.outbox'), 404); - - if($profile->status != null) { + if ($profile->status != null) { return ProfileController::accountCheck($profile); } - if($profile->is_private) { - return ['error'=>'403', 'msg' => 'private profile']; + if ($profile->is_private) { + return ['error' => '403', 'msg' => 'private profile']; } $timeline = $profile - ->statuses() - ->whereScope('public') - ->orderBy('created_at', 'desc') - ->take(10) - ->get(); + ->statuses() + ->whereScope('public') + ->orderBy('created_at', 'desc') + ->take(10) + ->get(); $count = Status::whereProfileId($profile->id)->count(); @@ -38,14 +36,14 @@ class Outbox { $res = $fractal->createData($resource)->toArray(); $outbox = [ - '@context' => 'https://www.w3.org/ns/activitystreams', - '_debug' => 'Outbox only supports latest 10 objects, pagination is not supported', - 'id' => $profile->permalink('/outbox'), - 'type' => 'OrderedCollection', - 'totalItems' => $count, - 'orderedItems' => $res['data'] + '@context' => 'https://www.w3.org/ns/activitystreams', + '_debug' => 'Outbox only supports latest 10 objects, pagination is not supported', + 'id' => $profile->permalink('/outbox'), + 'type' => 'OrderedCollection', + 'totalItems' => $count, + 'orderedItems' => $res['data'], ]; - return $outbox; - } + return $outbox; + } } diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index 0d8b21e47..2f592b1cc 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -298,7 +298,7 @@ FEDERATION ACTIVITY_PUB - {{config_cache('federation.activitypub.enabled') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('federation.activitypub.enabled') ? '✅ true' : '❌ false' }} FEDERATION From ce228f7fa494db1dead8e3172ef8ed8099dc6394 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 03:55:51 -0600 Subject: [PATCH 15/41] Update oauth setting, use config_cache --- .../Admin/AdminDirectoryController.php | 184 +++++++++--------- app/Http/Controllers/Api/ApiV1Controller.php | 2 +- app/Providers/AuthServiceProvider.php | 8 +- .../views/admin/diagnostics/home.blade.php | 4 +- .../views/settings/applications.blade.php | 2 +- resources/views/settings/developers.blade.php | 2 +- .../views/settings/partial/sidebar.blade.php | 2 +- 7 files changed, 101 insertions(+), 103 deletions(-) diff --git a/app/Http/Controllers/Admin/AdminDirectoryController.php b/app/Http/Controllers/Admin/AdminDirectoryController.php index 2a4cc5ce8..ce53ea560 100644 --- a/app/Http/Controllers/Admin/AdminDirectoryController.php +++ b/app/Http/Controllers/Admin/AdminDirectoryController.php @@ -2,30 +2,20 @@ namespace App\Http\Controllers\Admin; -use DB, Cache; -use App\{ - DiscoverCategory, - DiscoverCategoryHashtag, - Hashtag, - Media, - Profile, - Status, - StatusHashtag, - User -}; +use App\Http\Controllers\PixelfedDirectoryController; use App\Models\ConfigCache; use App\Services\AccountService; use App\Services\ConfigCacheService; use App\Services\StatusService; -use Carbon\Carbon; +use App\Status; +use App\User; +use Cache; use Illuminate\Http\Request; -use Illuminate\Validation\Rule; -use League\ISO3166\ISO3166; -use Illuminate\Support\Str; +use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; -use Illuminate\Support\Facades\Http; -use App\Http\Controllers\PixelfedDirectoryController; +use Illuminate\Support\Str; +use League\ISO3166\ISO3166; trait AdminDirectoryController { @@ -41,37 +31,37 @@ trait AdminDirectoryController $res['countries'] = collect((new ISO3166)->all())->pluck('name'); $res['admins'] = User::whereIsAdmin(true) ->where('2fa_enabled', true) - ->get()->map(function($user) { - return [ - 'uid' => (string) $user->id, - 'pid' => (string) $user->profile_id, - 'username' => $user->username, - 'created_at' => $user->created_at - ]; - }); + ->get()->map(function ($user) { + return [ + 'uid' => (string) $user->id, + 'pid' => (string) $user->profile_id, + 'username' => $user->username, + 'created_at' => $user->created_at, + ]; + }); $config = ConfigCache::whereK('pixelfed.directory')->first(); - if($config) { + if ($config) { $data = $config->v ? json_decode($config->v, true) : []; $res = array_merge($res, $data); } - if(empty($res['summary'])) { + if (empty($res['summary'])) { $summary = ConfigCache::whereK('app.short_description')->pluck('v'); $res['summary'] = $summary ? $summary[0] : null; } - if(isset($res['banner_image']) && !empty($res['banner_image'])) { + if (isset($res['banner_image']) && ! empty($res['banner_image'])) { $res['banner_image'] = url(Storage::url($res['banner_image'])); } - if(isset($res['favourite_posts'])) { - $res['favourite_posts'] = collect($res['favourite_posts'])->map(function($id) { + if (isset($res['favourite_posts'])) { + $res['favourite_posts'] = collect($res['favourite_posts'])->map(function ($id) { return StatusService::get($id); }) - ->filter(function($post) { - return $post && isset($post['account']); - }) - ->values(); + ->filter(function ($post) { + return $post && isset($post['account']); + }) + ->values(); } $res['community_guidelines'] = config_cache('app.rules') ? json_decode(config_cache('app.rules'), true) : []; @@ -94,12 +84,12 @@ trait AdminDirectoryController 'account_deletion' => (bool) config_cache('pixelfed.account_deletion'), ]; - if(config_cache('pixelfed.directory.testimonials')) { - $testimonials = collect(json_decode(config_cache('pixelfed.directory.testimonials'),true)) - ->map(function($t) { + if (config_cache('pixelfed.directory.testimonials')) { + $testimonials = collect(json_decode(config_cache('pixelfed.directory.testimonials'), true)) + ->map(function ($t) { return [ 'profile' => AccountService::get($t['profile_id']), - 'body' => $t['body'] + 'body' => $t['body'], ]; }); $res['testimonials'] = $testimonials; @@ -108,8 +98,8 @@ trait AdminDirectoryController $validator = Validator::make($res['feature_config'], [ 'media_types' => [ 'required', - function ($attribute, $value, $fail) { - if (!in_array('image/jpeg', $value->toArray()) || !in_array('image/png', $value->toArray())) { + function ($attribute, $value, $fail) { + if (! in_array('image/jpeg', $value->toArray()) || ! in_array('image/png', $value->toArray())) { $fail('You must enable image/jpeg and image/png support.'); } }, @@ -120,7 +110,7 @@ trait AdminDirectoryController 'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000', 'max_album_length' => 'required|integer|min:4|max:20', 'account_deletion' => 'required|accepted', - 'max_caption_length' => 'required|integer|min:500|max:10000' + 'max_caption_length' => 'required|integer|min:500|max:10000', ]); $res['requirements_validator'] = $validator->errors(); @@ -146,11 +136,11 @@ trait AdminDirectoryController foreach (new \DirectoryIterator($path) as $io) { $name = $io->getFilename(); $skip = ['vendor']; - if($io->isDot() || in_array($name, $skip)) { + if ($io->isDot() || in_array($name, $skip)) { continue; } - if($io->isDir()) { + if ($io->isDir()) { $langs->push(['code' => $name, 'name' => locale_get_display_name($name)]); } } @@ -159,25 +149,26 @@ trait AdminDirectoryController $res['primary_locale'] = config('app.locale'); $submissionState = Http::withoutVerifying() - ->post('https://pixelfed.org/api/v1/directory/check-submission', [ - 'domain' => config('pixelfed.domain.app') - ]); + ->post('https://pixelfed.org/api/v1/directory/check-submission', [ + 'domain' => config('pixelfed.domain.app'), + ]); $res['submission_state'] = $submissionState->json(); + return $res; } protected function validVal($res, $val, $count = false, $minLen = false) { - if(!isset($res[$val])) { + if (! isset($res[$val])) { return false; } - if($count) { + if ($count) { return count($res[$val]) >= $count; } - if($minLen) { + if ($minLen) { return strlen($res[$val]) >= $minLen; } @@ -194,11 +185,11 @@ trait AdminDirectoryController 'favourite_posts' => 'array|max:12', 'favourite_posts.*' => 'distinct', 'privacy_pledge' => 'sometimes', - 'banner_image' => 'sometimes|mimes:jpg,png|dimensions:width=1920,height:1080|max:5000' + 'banner_image' => 'sometimes|mimes:jpg,png|dimensions:width=1920,height:1080|max:5000', ]); $config = ConfigCache::firstOrNew([ - 'k' => 'pixelfed.directory' + 'k' => 'pixelfed.directory', ]); $res = $config->v ? json_decode($config->v, true) : []; @@ -208,26 +199,27 @@ trait AdminDirectoryController $res['contact_email'] = $request->input('contact_email'); $res['privacy_pledge'] = (bool) $request->input('privacy_pledge'); - if($request->filled('location')) { + if ($request->filled('location')) { $exists = (new ISO3166)->name($request->location); - if($exists) { + if ($exists) { $res['location'] = $request->input('location'); } } - if($request->hasFile('banner_image')) { + if ($request->hasFile('banner_image')) { collect(Storage::files('public/headers')) - ->filter(function($name) { - $protected = [ - 'public/headers/.gitignore', - 'public/headers/default.jpg', - 'public/headers/missing.png' - ]; - return !in_array($name, $protected); - }) - ->each(function($name) { - Storage::delete($name); - }); + ->filter(function ($name) { + $protected = [ + 'public/headers/.gitignore', + 'public/headers/default.jpg', + 'public/headers/missing.png', + ]; + + return ! in_array($name, $protected); + }) + ->each(function ($name) { + Storage::delete($name); + }); $path = $request->file('banner_image')->storePublicly('public/headers'); $res['banner_image'] = $path; ConfigCacheService::put('app.banner_image', url(Storage::url($path))); @@ -240,9 +232,10 @@ trait AdminDirectoryController ConfigCacheService::put('pixelfed.directory', $config->v); $updated = json_decode($config->v, true); - if(isset($updated['banner_image'])) { + if (isset($updated['banner_image'])) { $updated['banner_image'] = url(Storage::url($updated['banner_image'])); } + return $updated; } @@ -253,7 +246,7 @@ trait AdminDirectoryController 'open_registration' => (bool) config_cache('pixelfed.open_registration'), 'curated_onboarding' => (bool) config_cache('instance.curated_registration.enabled'), 'activitypub_enabled' => config_cache('federation.activitypub.enabled'), - 'oauth_enabled' => config_cache('pixelfed.oauth_enabled'), + 'oauth_enabled' => (bool) config_cache('pixelfed.oauth_enabled'), 'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','), 'image_quality' => config_cache('pixelfed.image_quality'), 'optimize_image' => config_cache('pixelfed.optimize_image'), @@ -273,8 +266,8 @@ trait AdminDirectoryController 'oauth_enabled' => 'required|accepted', 'media_types' => [ 'required', - function ($attribute, $value, $fail) { - if (!in_array('image/jpeg', $value->toArray()) || !in_array('image/png', $value->toArray())) { + function ($attribute, $value, $fail) { + if (! in_array('image/jpeg', $value->toArray()) || ! in_array('image/png', $value->toArray())) { $fail('You must enable image/jpeg and image/png support.'); } }, @@ -285,10 +278,10 @@ trait AdminDirectoryController 'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000', 'max_album_length' => 'required|integer|min:4|max:20', 'account_deletion' => 'required|accepted', - 'max_caption_length' => 'required|integer|min:500|max:10000' + 'max_caption_length' => 'required|integer|min:500|max:10000', ]); - if(!$validator->validate()) { + if (! $validator->validate()) { return response()->json($validator->errors(), 422); } @@ -297,6 +290,7 @@ trait AdminDirectoryController $data = (new PixelfedDirectoryController())->buildListing(); $res = Http::withoutVerifying()->post('https://pixelfed.org/api/v1/directory/submission', $data); + return 200; } @@ -304,7 +298,7 @@ trait AdminDirectoryController { $bannerImage = ConfigCache::whereK('app.banner_image')->first(); $directory = ConfigCache::whereK('pixelfed.directory')->first(); - if(!$bannerImage && !$directory || empty($directory->v)) { + if (! $bannerImage && ! $directory || empty($directory->v)) { return; } $directoryArr = json_decode($directory->v, true); @@ -312,12 +306,12 @@ trait AdminDirectoryController $protected = [ 'public/headers/.gitignore', 'public/headers/default.jpg', - 'public/headers/missing.png' + 'public/headers/missing.png', ]; - if(!$path || in_array($path, $protected)) { + if (! $path || in_array($path, $protected)) { return; } - if(Storage::exists($directoryArr['banner_image'])) { + if (Storage::exists($directoryArr['banner_image'])) { Storage::delete($directoryArr['banner_image']); } @@ -328,12 +322,13 @@ trait AdminDirectoryController $bannerImage->save(); Cache::forget('api:v1:instance-data-response-v1'); ConfigCacheService::put('pixelfed.directory', $directory); + return $bannerImage->v; } public function directoryGetPopularPosts(Request $request) { - $ids = Cache::remember('admin:api:popular_posts', 86400, function() { + $ids = Cache::remember('admin:api:popular_posts', 86400, function () { return Status::whereLocal(true) ->whereScope('public') ->whereType('photo') @@ -343,21 +338,21 @@ trait AdminDirectoryController ->pluck('id'); }); - $res = $ids->map(function($id) { + $res = $ids->map(function ($id) { return StatusService::get($id); }) - ->filter(function($post) { - return $post && isset($post['account']); - }) - ->values(); + ->filter(function ($post) { + return $post && isset($post['account']); + }) + ->values(); - return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + return response()->json($res, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } public function directoryGetAddPostByIdSearch(Request $request) { $this->validate($request, [ - 'q' => 'required|integer' + 'q' => 'required|integer', ]); $id = $request->input('q'); @@ -380,11 +375,12 @@ trait AdminDirectoryController $profile_id = $request->input('profile_id'); $testimonials = ConfigCache::whereK('pixelfed.directory.testimonials')->firstOrFail(); $existing = collect(json_decode($testimonials->v, true)) - ->filter(function($t) use($profile_id) { + ->filter(function ($t) use ($profile_id) { return $t['profile_id'] !== $profile_id; }) ->values(); ConfigCacheService::put('pixelfed.directory.testimonials', $existing); + return $existing; } @@ -392,13 +388,13 @@ trait AdminDirectoryController { $this->validate($request, [ 'username' => 'required', - 'body' => 'required|string|min:5|max:500' + 'body' => 'required|string|min:5|max:500', ]); $user = User::whereUsername($request->input('username'))->whereNull('status')->firstOrFail(); $configCache = ConfigCache::firstOrCreate([ - 'k' => 'pixelfed.directory.testimonials' + 'k' => 'pixelfed.directory.testimonials', ]); $testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]); @@ -409,7 +405,7 @@ trait AdminDirectoryController $testimonials->push([ 'profile_id' => (string) $user->profile_id, 'username' => $request->input('username'), - 'body' => $request->input('body') + 'body' => $request->input('body'), ]); $configCache->v = json_encode($testimonials->toArray()); @@ -417,8 +413,9 @@ trait AdminDirectoryController ConfigCacheService::put('pixelfed.directory.testimonials', $configCache->v); $res = [ 'profile' => AccountService::get($user->profile_id), - 'body' => $request->input('body') + 'body' => $request->input('body'), ]; + return $res; } @@ -426,7 +423,7 @@ trait AdminDirectoryController { $this->validate($request, [ 'profile_id' => 'required', - 'body' => 'required|string|min:5|max:500' + 'body' => 'required|string|min:5|max:500', ]); $profile_id = $request->input('profile_id'); @@ -434,18 +431,19 @@ trait AdminDirectoryController $user = User::whereProfileId($profile_id)->firstOrFail(); $configCache = ConfigCache::firstOrCreate([ - 'k' => 'pixelfed.directory.testimonials' + 'k' => 'pixelfed.directory.testimonials', ]); $testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]); - $updated = $testimonials->map(function($t) use($profile_id, $body) { - if($t['profile_id'] == $profile_id) { + $updated = $testimonials->map(function ($t) use ($profile_id, $body) { + if ($t['profile_id'] == $profile_id) { $t['body'] = $body; } + return $t; }) - ->values(); + ->values(); $configCache->v = json_encode($updated); $configCache->save(); diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index c88542541..cdb488ab4 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -131,7 +131,7 @@ class ApiV1Controller extends Controller */ public function apps(Request $request) { - abort_if(! config_cache('pixelfed.oauth_enabled'), 404); + abort_if(! (bool) config_cache('pixelfed.oauth_enabled'), 404); $this->validate($request, [ 'client_name' => 'required', diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 52e992ce0..43d12b592 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,9 +2,9 @@ namespace App\Providers; +use Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Laravel\Passport\Passport; -use Gate; class AuthServiceProvider extends ServiceProvider { @@ -24,11 +24,11 @@ class AuthServiceProvider extends ServiceProvider */ public function boot() { - if(config('app.env') === 'production' && config('pixelfed.oauth_enabled') == true) { + if (config('app.env') === 'production' && (bool) config_cache('pixelfed.oauth_enabled') == true) { Passport::tokensExpireIn(now()->addDays(config('instance.oauth.token_expiration', 356))); Passport::refreshTokensExpireIn(now()->addDays(config('instance.oauth.refresh_expiration', 400))); Passport::enableImplicitGrant(); - if(config('instance.oauth.pat.enabled')) { + if (config('instance.oauth.pat.enabled')) { Passport::personalAccessClientId(config('instance.oauth.pat.id')); } @@ -38,7 +38,7 @@ class AuthServiceProvider extends ServiceProvider 'follow' => 'Ability to follow other profiles', 'admin:read' => 'Read all data on the server', 'admin:write' => 'Modify all data on the server', - 'push' => 'Receive your push notifications' + 'push' => 'Receive your push notifications', ]); Passport::setDefaultScope([ diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index 2f592b1cc..63e830b49 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -66,7 +66,7 @@
  • OAUTH enabled: - {{ config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }} + {{ (bool) config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }}
  • OAUTH token_expiration @@ -810,7 +810,7 @@ PIXELFED OAUTH_ENABLED - {{config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }} + {{ (bool) config_cache('pixelfed.oauth_enabled') ? '✅ true' : '❌ false' }} PIXELFED diff --git a/resources/views/settings/applications.blade.php b/resources/views/settings/applications.blade.php index 691ab0c81..97270fb62 100644 --- a/resources/views/settings/applications.blade.php +++ b/resources/views/settings/applications.blade.php @@ -6,7 +6,7 @@

    Applications


    -@if(config_cache('pixelfed.oauth_enabled') == true) +@if((bool) config_cache('pixelfed.oauth_enabled') == true) @else diff --git a/resources/views/settings/developers.blade.php b/resources/views/settings/developers.blade.php index 8b4c94471..22d869580 100644 --- a/resources/views/settings/developers.blade.php +++ b/resources/views/settings/developers.blade.php @@ -6,7 +6,7 @@

    Developers


    -@if(config_cache('pixelfed.oauth_enabled') == true) +@if((bool) config_cache('pixelfed.oauth_enabled') == true) @else

    OAuth has not been enabled on this instance.

    diff --git a/resources/views/settings/partial/sidebar.blade.php b/resources/views/settings/partial/sidebar.blade.php index b971e1f5d..0eadb9773 100644 --- a/resources/views/settings/partial/sidebar.blade.php +++ b/resources/views/settings/partial/sidebar.blade.php @@ -39,7 +39,7 @@
  • - @if(config_cache('pixelfed.oauth_enabled') == true) + @if((bool) config_cache('pixelfed.oauth_enabled') == true) From d1adb109de06c942e2845420ce86c29985167bea Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 04:15:05 -0600 Subject: [PATCH 16/41] Update stories config, use config_cache --- .../Stories/StoryApiV1Controller.php | 252 +++++++++--------- .../Controllers/StoryComposeController.php | 184 +++++++------ .../views/admin/diagnostics/home.blade.php | 2 +- resources/views/layouts/partial/nav.blade.php | 2 +- 4 files changed, 223 insertions(+), 217 deletions(-) diff --git a/app/Http/Controllers/Stories/StoryApiV1Controller.php b/app/Http/Controllers/Stories/StoryApiV1Controller.php index ca6a24791..5d0a15160 100644 --- a/app/Http/Controllers/Stories/StoryApiV1Controller.php +++ b/app/Http/Controllers/Stories/StoryApiV1Controller.php @@ -2,54 +2,56 @@ namespace App\Http\Controllers\Stories; -use App\Http\Controllers\Controller; -use Illuminate\Http\Request; -use Illuminate\Support\Str; -use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Storage; -use App\Models\Conversation; use App\DirectMessage; -use App\Notification; -use App\Story; -use App\Status; -use App\StoryView; +use App\Http\Controllers\Controller; +use App\Http\Resources\StoryView as StoryViewResource; use App\Jobs\StoryPipeline\StoryDelete; use App\Jobs\StoryPipeline\StoryFanout; use App\Jobs\StoryPipeline\StoryReplyDeliver; use App\Jobs\StoryPipeline\StoryViewDeliver; +use App\Models\Conversation; +use App\Notification; use App\Services\AccountService; use App\Services\MediaPathService; use App\Services\StoryService; -use App\Http\Resources\StoryView as StoryViewResource; +use App\Status; +use App\Story; +use App\StoryView; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; class StoryApiV1Controller extends Controller { const RECENT_KEY = 'pf:stories:recent-by-id:'; + const RECENT_TTL = 300; public function carousel(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $pid = $request->user()->profile_id; - if(config('database.default') == 'pgsql') { - $s = Cache::remember(self::RECENT_KEY . $pid, self::RECENT_TTL, function() use($pid) { + if (config('database.default') == 'pgsql') { + $s = Cache::remember(self::RECENT_KEY.$pid, self::RECENT_TTL, function () use ($pid) { return Story::select('stories.*', 'followers.following_id') ->leftJoin('followers', 'followers.following_id', 'stories.profile_id') ->where('followers.profile_id', $pid) ->where('stories.active', true) - ->map(function($s) { - $r = new \StdClass; + ->map(function ($s) { + $r = new \StdClass; $r->id = $s->id; $r->profile_id = $s->profile_id; $r->type = $s->type; $r->path = $s->path; + return $r; }) ->unique('profile_id'); }); } else { - $s = Cache::remember(self::RECENT_KEY . $pid, self::RECENT_TTL, function() use($pid) { + $s = Cache::remember(self::RECENT_KEY.$pid, self::RECENT_TTL, function () use ($pid) { return Story::select('stories.*', 'followers.following_id') ->leftJoin('followers', 'followers.following_id', 'stories.profile_id') ->where('followers.profile_id', $pid) @@ -59,9 +61,9 @@ class StoryApiV1Controller extends Controller }); } - $nodes = $s->map(function($s) use($pid) { + $nodes = $s->map(function ($s) use ($pid) { $profile = AccountService::get($s->profile_id, true); - if(!$profile || !isset($profile['id'])) { + if (! $profile || ! isset($profile['id'])) { return false; } @@ -72,50 +74,51 @@ class StoryApiV1Controller extends Controller 'src' => url(Storage::url($s->path)), 'duration' => $s->duration ?? 3, 'seen' => StoryService::hasSeen($pid, $s->id), - 'created_at' => $s->created_at->format('c') + 'created_at' => $s->created_at->format('c'), ]; }) - ->filter() - ->groupBy('pid') - ->map(function($item) use($pid) { - $profile = AccountService::get($item[0]['pid'], true); - $url = $profile['local'] ? url("/stories/{$profile['username']}") : - url("/i/rs/{$profile['id']}"); - return [ - 'id' => 'pfs:' . $profile['id'], - 'user' => [ - 'id' => (string) $profile['id'], - 'username' => $profile['username'], - 'username_acct' => $profile['acct'], - 'avatar' => $profile['avatar'], - 'local' => $profile['local'], - 'is_author' => $profile['id'] == $pid - ], - 'nodes' => $item, - 'url' => $url, - 'seen' => StoryService::hasSeen($pid, StoryService::latest($profile['id'])), - ]; - }) - ->sortBy('seen') - ->values(); + ->filter() + ->groupBy('pid') + ->map(function ($item) use ($pid) { + $profile = AccountService::get($item[0]['pid'], true); + $url = $profile['local'] ? url("/stories/{$profile['username']}") : + url("/i/rs/{$profile['id']}"); + + return [ + 'id' => 'pfs:'.$profile['id'], + 'user' => [ + 'id' => (string) $profile['id'], + 'username' => $profile['username'], + 'username_acct' => $profile['acct'], + 'avatar' => $profile['avatar'], + 'local' => $profile['local'], + 'is_author' => $profile['id'] == $pid, + ], + 'nodes' => $item, + 'url' => $url, + 'seen' => StoryService::hasSeen($pid, StoryService::latest($profile['id'])), + ]; + }) + ->sortBy('seen') + ->values(); $res = [ 'self' => [], 'nodes' => $nodes, ]; - if(Story::whereProfileId($pid)->whereActive(true)->exists()) { + if (Story::whereProfileId($pid)->whereActive(true)->exists()) { $selfStories = Story::whereProfileId($pid) ->whereActive(true) ->get() - ->map(function($s) use($pid) { + ->map(function ($s) { return [ 'id' => (string) $s->id, 'type' => $s->type, 'src' => url(Storage::url($s->path)), 'duration' => $s->duration, 'seen' => true, - 'created_at' => $s->created_at->format('c') + 'created_at' => $s->created_at->format('c'), ]; }) ->sortBy('id') @@ -127,38 +130,40 @@ class StoryApiV1Controller extends Controller 'username' => $selfProfile['acct'], 'avatar' => $selfProfile['avatar'], 'local' => $selfProfile['local'], - 'is_author' => true + 'is_author' => true, ], 'nodes' => $selfStories, ]; } - return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + + return response()->json($res, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } public function selfCarousel(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $pid = $request->user()->profile_id; - if(config('database.default') == 'pgsql') { - $s = Cache::remember(self::RECENT_KEY . $pid, self::RECENT_TTL, function() use($pid) { + if (config('database.default') == 'pgsql') { + $s = Cache::remember(self::RECENT_KEY.$pid, self::RECENT_TTL, function () use ($pid) { return Story::select('stories.*', 'followers.following_id') ->leftJoin('followers', 'followers.following_id', 'stories.profile_id') ->where('followers.profile_id', $pid) ->where('stories.active', true) - ->map(function($s) { - $r = new \StdClass; + ->map(function ($s) { + $r = new \StdClass; $r->id = $s->id; $r->profile_id = $s->profile_id; $r->type = $s->type; $r->path = $s->path; + return $r; }) ->unique('profile_id'); }); } else { - $s = Cache::remember(self::RECENT_KEY . $pid, self::RECENT_TTL, function() use($pid) { + $s = Cache::remember(self::RECENT_KEY.$pid, self::RECENT_TTL, function () use ($pid) { return Story::select('stories.*', 'followers.following_id') ->leftJoin('followers', 'followers.following_id', 'stories.profile_id') ->where('followers.profile_id', $pid) @@ -168,9 +173,9 @@ class StoryApiV1Controller extends Controller }); } - $nodes = $s->map(function($s) use($pid) { + $nodes = $s->map(function ($s) use ($pid) { $profile = AccountService::get($s->profile_id, true); - if(!$profile || !isset($profile['id'])) { + if (! $profile || ! isset($profile['id'])) { return false; } @@ -181,32 +186,33 @@ class StoryApiV1Controller extends Controller 'src' => url(Storage::url($s->path)), 'duration' => $s->duration ?? 3, 'seen' => StoryService::hasSeen($pid, $s->id), - 'created_at' => $s->created_at->format('c') + 'created_at' => $s->created_at->format('c'), ]; }) - ->filter() - ->groupBy('pid') - ->map(function($item) use($pid) { - $profile = AccountService::get($item[0]['pid'], true); - $url = $profile['local'] ? url("/stories/{$profile['username']}") : - url("/i/rs/{$profile['id']}"); - return [ - 'id' => 'pfs:' . $profile['id'], - 'user' => [ - 'id' => (string) $profile['id'], - 'username' => $profile['username'], - 'username_acct' => $profile['acct'], - 'avatar' => $profile['avatar'], - 'local' => $profile['local'], - 'is_author' => $profile['id'] == $pid - ], - 'nodes' => $item, - 'url' => $url, - 'seen' => StoryService::hasSeen($pid, StoryService::latest($profile['id'])), - ]; - }) - ->sortBy('seen') - ->values(); + ->filter() + ->groupBy('pid') + ->map(function ($item) use ($pid) { + $profile = AccountService::get($item[0]['pid'], true); + $url = $profile['local'] ? url("/stories/{$profile['username']}") : + url("/i/rs/{$profile['id']}"); + + return [ + 'id' => 'pfs:'.$profile['id'], + 'user' => [ + 'id' => (string) $profile['id'], + 'username' => $profile['username'], + 'username_acct' => $profile['acct'], + 'avatar' => $profile['avatar'], + 'local' => $profile['local'], + 'is_author' => $profile['id'] == $pid, + ], + 'nodes' => $item, + 'url' => $url, + 'seen' => StoryService::hasSeen($pid, StoryService::latest($profile['id'])), + ]; + }) + ->sortBy('seen') + ->values(); $selfProfile = AccountService::get($pid, true); $res = [ @@ -216,7 +222,7 @@ class StoryApiV1Controller extends Controller 'username' => $selfProfile['acct'], 'avatar' => $selfProfile['avatar'], 'local' => $selfProfile['local'], - 'is_author' => true + 'is_author' => true, ], 'nodes' => [], @@ -224,40 +230,41 @@ class StoryApiV1Controller extends Controller 'nodes' => $nodes, ]; - if(Story::whereProfileId($pid)->whereActive(true)->exists()) { + if (Story::whereProfileId($pid)->whereActive(true)->exists()) { $selfStories = Story::whereProfileId($pid) ->whereActive(true) ->get() - ->map(function($s) use($pid) { + ->map(function ($s) { return [ 'id' => (string) $s->id, 'type' => $s->type, 'src' => url(Storage::url($s->path)), 'duration' => $s->duration, 'seen' => true, - 'created_at' => $s->created_at->format('c') + 'created_at' => $s->created_at->format('c'), ]; }) ->sortBy('id') ->values(); $res['self']['nodes'] = $selfStories; } - return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); + + return response()->json($res, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } public function add(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ - 'file' => function() { + 'file' => function () { return [ 'required', 'mimetypes:image/jpeg,image/png,video/mp4', - 'max:' . config_cache('pixelfed.max_photo_size'), + 'max:'.config_cache('pixelfed.max_photo_size'), ]; }, - 'duration' => 'sometimes|integer|min:0|max:30' + 'duration' => 'sometimes|integer|min:0|max:30', ]); $user = $request->user(); @@ -267,7 +274,7 @@ class StoryApiV1Controller extends Controller ->where('expires_at', '>', now()) ->count(); - if($count >= Story::MAX_PER_DAY) { + if ($count >= Story::MAX_PER_DAY) { abort(418, 'You have reached your limit for new Stories today.'); } @@ -277,7 +284,7 @@ class StoryApiV1Controller extends Controller $story = new Story(); $story->duration = $request->input('duration', 3); $story->profile_id = $user->profile_id; - $story->type = Str::endsWith($photo->getMimeType(), 'mp4') ? 'video' :'photo'; + $story->type = Str::endsWith($photo->getMimeType(), 'mp4') ? 'video' : 'photo'; $story->mime = $photo->getMimeType(); $story->path = $path; $story->local = true; @@ -290,10 +297,10 @@ class StoryApiV1Controller extends Controller $res = [ 'code' => 200, - 'msg' => 'Successfully added', + 'msg' => 'Successfully added', 'media_id' => (string) $story->id, - 'media_url' => url(Storage::url($url)) . '?v=' . time(), - 'media_type' => $story->type + 'media_url' => url(Storage::url($url)).'?v='.time(), + 'media_type' => $story->type, ]; return $res; @@ -301,13 +308,13 @@ class StoryApiV1Controller extends Controller public function publish(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'media_id' => 'required', 'duration' => 'required|integer|min:0|max:30', 'can_reply' => 'required|boolean', - 'can_react' => 'required|boolean' + 'can_react' => 'required|boolean', ]); $id = $request->input('media_id'); @@ -327,13 +334,13 @@ class StoryApiV1Controller extends Controller return [ 'code' => 200, - 'msg' => 'Successfully published', + 'msg' => 'Successfully published', ]; } public function delete(Request $request, $id) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $user = $request->user(); @@ -346,16 +353,16 @@ class StoryApiV1Controller extends Controller return [ 'code' => 200, - 'msg' => 'Successfully deleted' + 'msg' => 'Successfully deleted', ]; } public function viewed(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ - 'id' => 'required|min:1', + 'id' => 'required|min:1', ]); $id = $request->input('id'); @@ -367,44 +374,45 @@ class StoryApiV1Controller extends Controller $profile = $story->profile; - if($story->profile_id == $authed->id) { + if ($story->profile_id == $authed->id) { return []; } $publicOnly = (bool) $profile->followedBy($authed); - abort_if(!$publicOnly, 403); + abort_if(! $publicOnly, 403); $v = StoryView::firstOrCreate([ 'story_id' => $id, - 'profile_id' => $authed->id + 'profile_id' => $authed->id, ]); - if($v->wasRecentlyCreated) { + if ($v->wasRecentlyCreated) { Story::findOrFail($story->id)->increment('view_count'); - if($story->local == false) { + if ($story->local == false) { StoryViewDeliver::dispatch($story, $authed)->onQueue('story'); } } - Cache::forget('stories:recent:by_id:' . $authed->id); + Cache::forget('stories:recent:by_id:'.$authed->id); StoryService::addSeen($authed->id, $story->id); + return ['code' => 200]; } public function comment(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'sid' => 'required', - 'caption' => 'required|string' + 'caption' => 'required|string', ]); $pid = $request->user()->profile_id; $text = $request->input('caption'); $story = Story::findOrFail($request->input('sid')); - abort_if(!$story->can_reply, 422); + abort_if(! $story->can_reply, 422); $status = new Status; $status->type = 'story:reply'; @@ -415,7 +423,7 @@ class StoryApiV1Controller extends Controller $status->visibility = 'direct'; $status->in_reply_to_profile_id = $story->profile_id; $status->entities = json_encode([ - 'story_id' => $story->id + 'story_id' => $story->id, ]); $status->save(); @@ -429,24 +437,24 @@ class StoryApiV1Controller extends Controller 'story_actor_username' => $request->user()->username, 'story_id' => $story->id, 'story_media_url' => url(Storage::url($story->path)), - 'caption' => $text + 'caption' => $text, ]); $dm->save(); Conversation::updateOrInsert( [ 'to_id' => $story->profile_id, - 'from_id' => $pid + 'from_id' => $pid, ], [ 'type' => 'story:comment', 'status_id' => $status->id, 'dm_id' => $dm->id, - 'is_hidden' => false + 'is_hidden' => false, ] ); - if($story->local) { + if ($story->local) { $n = new Notification; $n->profile_id = $dm->to_id; $n->actor_id = $dm->from_id; @@ -460,33 +468,35 @@ class StoryApiV1Controller extends Controller return [ 'code' => 200, - 'msg' => 'Sent!' + 'msg' => 'Sent!', ]; } protected function storeMedia($photo, $user) { $mimes = explode(',', config_cache('pixelfed.media_types')); - if(in_array($photo->getMimeType(), [ + if (in_array($photo->getMimeType(), [ 'image/jpeg', 'image/png', - 'video/mp4' + 'video/mp4', ]) == false) { abort(400, 'Invalid media type'); + return; } $storagePath = MediaPathService::story($user->profile); - $path = $photo->storePubliclyAs($storagePath, Str::random(random_int(2, 12)) . '_' . Str::random(random_int(32, 35)) . '_' . Str::random(random_int(1, 14)) . '.' . $photo->extension()); + $path = $photo->storePubliclyAs($storagePath, Str::random(random_int(2, 12)).'_'.Str::random(random_int(32, 35)).'_'.Str::random(random_int(1, 14)).'.'.$photo->extension()); + return $path; } public function viewers(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ - 'sid' => 'required|string|min:1|max:50' + 'sid' => 'required|string|min:1|max:50', ]); $pid = $request->user()->profile_id; diff --git a/app/Http/Controllers/StoryComposeController.php b/app/Http/Controllers/StoryComposeController.php index eb2d859c0..c8b0599a6 100644 --- a/app/Http/Controllers/StoryComposeController.php +++ b/app/Http/Controllers/StoryComposeController.php @@ -2,59 +2,52 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; -use Illuminate\Support\Str; -use App\Media; -use App\Profile; -use App\Report; use App\DirectMessage; -use App\Notification; -use App\Status; -use App\Story; -use App\StoryView; -use App\Models\Poll; -use App\Models\PollVote; -use App\Services\ProfileService; -use App\Services\StoryService; -use Cache, Storage; -use Image as Intervention; -use App\Services\FollowerService; -use App\Services\MediaPathService; -use FFMpeg; -use FFMpeg\Coordinate\Dimension; -use FFMpeg\Format\Video\X264; +use App\Jobs\StoryPipeline\StoryDelete; +use App\Jobs\StoryPipeline\StoryFanout; use App\Jobs\StoryPipeline\StoryReactionDeliver; use App\Jobs\StoryPipeline\StoryReplyDeliver; -use App\Jobs\StoryPipeline\StoryFanout; -use App\Jobs\StoryPipeline\StoryDelete; -use ImageOptimizer; use App\Models\Conversation; +use App\Models\Poll; +use App\Models\PollVote; +use App\Notification; +use App\Report; +use App\Services\FollowerService; +use App\Services\MediaPathService; +use App\Services\StoryService; use App\Services\UserRoleService; +use App\Status; +use App\Story; +use FFMpeg; +use Illuminate\Http\Request; +use Illuminate\Support\Str; +use Image as Intervention; +use Storage; class StoryComposeController extends Controller { public function apiV1Add(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ - 'file' => function() { + 'file' => function () { return [ 'required', 'mimetypes:image/jpeg,image/png,video/mp4', - 'max:' . config_cache('pixelfed.max_photo_size'), + 'max:'.config_cache('pixelfed.max_photo_size'), ]; }, ]); $user = $request->user(); - abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); + abort_if($user->has_roles && ! UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); $count = Story::whereProfileId($user->profile_id) ->whereActive(true) ->where('expires_at', '>', now()) ->count(); - if($count >= Story::MAX_PER_DAY) { + if ($count >= Story::MAX_PER_DAY) { abort(418, 'You have reached your limit for new Stories today.'); } @@ -64,7 +57,7 @@ class StoryComposeController extends Controller $story = new Story(); $story->duration = 3; $story->profile_id = $user->profile_id; - $story->type = Str::endsWith($photo->getMimeType(), 'mp4') ? 'video' :'photo'; + $story->type = Str::endsWith($photo->getMimeType(), 'mp4') ? 'video' : 'photo'; $story->mime = $photo->getMimeType(); $story->path = $path; $story->local = true; @@ -77,21 +70,22 @@ class StoryComposeController extends Controller $res = [ 'code' => 200, - 'msg' => 'Successfully added', + 'msg' => 'Successfully added', 'media_id' => (string) $story->id, - 'media_url' => url(Storage::url($url)) . '?v=' . time(), - 'media_type' => $story->type + 'media_url' => url(Storage::url($url)).'?v='.time(), + 'media_type' => $story->type, ]; - if($story->type === 'video') { + if ($story->type === 'video') { $video = FFMpeg::open($path); $duration = $video->getDurationInSeconds(); $res['media_duration'] = $duration; - if($duration > 500) { + if ($duration > 500) { Storage::delete($story->path); $story->delete(); + return response()->json([ - 'message' => 'Video duration cannot exceed 60 seconds' + 'message' => 'Video duration cannot exceed 60 seconds', ], 422); } } @@ -102,37 +96,39 @@ class StoryComposeController extends Controller protected function storePhoto($photo, $user) { $mimes = explode(',', config_cache('pixelfed.media_types')); - if(in_array($photo->getMimeType(), [ + if (in_array($photo->getMimeType(), [ 'image/jpeg', 'image/png', - 'video/mp4' + 'video/mp4', ]) == false) { abort(400, 'Invalid media type'); + return; } $storagePath = MediaPathService::story($user->profile); - $path = $photo->storePubliclyAs($storagePath, Str::random(random_int(2, 12)) . '_' . Str::random(random_int(32, 35)) . '_' . Str::random(random_int(1, 14)) . '.' . $photo->extension()); - if(in_array($photo->getMimeType(), ['image/jpeg','image/png'])) { - $fpath = storage_path('app/' . $path); + $path = $photo->storePubliclyAs($storagePath, Str::random(random_int(2, 12)).'_'.Str::random(random_int(32, 35)).'_'.Str::random(random_int(1, 14)).'.'.$photo->extension()); + if (in_array($photo->getMimeType(), ['image/jpeg', 'image/png'])) { + $fpath = storage_path('app/'.$path); $img = Intervention::make($fpath); $img->orientate(); $img->save($fpath, config_cache('pixelfed.image_quality')); $img->destroy(); } + return $path; } public function cropPhoto(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'media_id' => 'required|integer|min:1', 'width' => 'required', 'height' => 'required', 'x' => 'required', - 'y' => 'required' + 'y' => 'required', ]); $user = $request->user(); @@ -144,13 +140,13 @@ class StoryComposeController extends Controller $story = Story::whereProfileId($user->profile_id)->findOrFail($id); - $path = storage_path('app/' . $story->path); + $path = storage_path('app/'.$story->path); - if(!is_file($path)) { + if (! is_file($path)) { abort(400, 'Invalid or missing media.'); } - if($story->type === 'photo') { + if ($story->type === 'photo') { $img = Intervention::make($path); $img->crop($width, $height, $x, $y); $img->resize(1080, 1920, function ($constraint) { @@ -161,24 +157,24 @@ class StoryComposeController extends Controller return [ 'code' => 200, - 'msg' => 'Successfully cropped', + 'msg' => 'Successfully cropped', ]; } public function publishStory(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'media_id' => 'required', 'duration' => 'required|integer|min:3|max:120', 'can_reply' => 'required|boolean', - 'can_react' => 'required|boolean' + 'can_react' => 'required|boolean', ]); $id = $request->input('media_id'); $user = $request->user(); - abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); + abort_if($user->has_roles && ! UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); $story = Story::whereProfileId($user->profile_id) ->findOrFail($id); @@ -194,13 +190,13 @@ class StoryComposeController extends Controller return [ 'code' => 200, - 'msg' => 'Successfully published', + 'msg' => 'Successfully published', ]; } public function apiV1Delete(Request $request, $id) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $user = $request->user(); @@ -213,40 +209,40 @@ class StoryComposeController extends Controller return [ 'code' => 200, - 'msg' => 'Successfully deleted' + 'msg' => 'Successfully deleted', ]; } public function compose(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $user = $request->user(); - abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); + abort_if($user->has_roles && ! UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); return view('stories.compose'); } public function createPoll(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); - abort_if(!config_cache('instance.polls.enabled'), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); + abort_if(! config_cache('instance.polls.enabled'), 404); return $request->all(); } public function publishStoryPoll(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'question' => 'required|string|min:6|max:140', 'options' => 'required|array|min:2|max:4', 'can_reply' => 'required|boolean', - 'can_react' => 'required|boolean' + 'can_react' => 'required|boolean', ]); $user = $request->user(); - abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); + abort_if($user->has_roles && ! UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); $pid = $request->user()->profile_id; $count = Story::whereProfileId($pid) @@ -254,7 +250,7 @@ class StoryComposeController extends Controller ->where('expires_at', '>', now()) ->count(); - if($count >= Story::MAX_PER_DAY) { + if ($count >= Story::MAX_PER_DAY) { abort(418, 'You have reached your limit for new Stories today.'); } @@ -262,7 +258,7 @@ class StoryComposeController extends Controller $story->type = 'poll'; $story->story = json_encode([ 'question' => $request->input('question'), - 'options' => $request->input('options') + 'options' => $request->input('options'), ]); $story->public = false; $story->local = true; @@ -278,7 +274,7 @@ class StoryComposeController extends Controller $poll->profile_id = $pid; $poll->poll_options = $request->input('options'); $poll->expires_at = $story->expires_at; - $poll->cached_tallies = collect($poll->poll_options)->map(function($o) { + $poll->cached_tallies = collect($poll->poll_options)->map(function ($o) { return 0; })->toArray(); $poll->save(); @@ -290,23 +286,23 @@ class StoryComposeController extends Controller return [ 'code' => 200, - 'msg' => 'Successfully published', + 'msg' => 'Successfully published', ]; } public function storyPollVote(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'sid' => 'required', - 'ci' => 'required|integer|min:0|max:3' + 'ci' => 'required|integer|min:0|max:3', ]); $pid = $request->user()->profile_id; $ci = $request->input('ci'); $story = Story::findOrFail($request->input('sid')); - abort_if(!FollowerService::follows($pid, $story->profile_id), 403); + abort_if(! FollowerService::follows($pid, $story->profile_id), 403); $poll = Poll::whereStoryId($story->id)->firstOrFail(); $vote = new PollVote; @@ -318,7 +314,7 @@ class StoryComposeController extends Controller $vote->save(); $poll->votes_count = $poll->votes_count + 1; - $poll->cached_tallies = collect($poll->getTallies())->map(function($tally, $key) use($ci) { + $poll->cached_tallies = collect($poll->getTallies())->map(function ($tally, $key) use ($ci) { return $ci == $key ? $tally + 1 : $tally; })->toArray(); $poll->save(); @@ -328,15 +324,15 @@ class StoryComposeController extends Controller public function storeReport(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ - 'type' => 'required|alpha_dash', - 'id' => 'required|integer|min:1', + 'type' => 'required|alpha_dash', + 'id' => 'required|integer|min:1', ]); $user = $request->user(); - abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); + abort_if($user->has_roles && ! UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); $pid = $request->user()->profile_id; $sid = $request->input('id'); @@ -353,24 +349,24 @@ class StoryComposeController extends Controller 'copyright', 'impersonation', 'scam', - 'terrorism' + 'terrorism', ]; - abort_if(!in_array($type, $types), 422, 'Invalid story report type'); + abort_if(! in_array($type, $types), 422, 'Invalid story report type'); $story = Story::findOrFail($sid); abort_if($story->profile_id == $pid, 422, 'Cannot report your own story'); - abort_if(!FollowerService::follows($pid, $story->profile_id), 422, 'Cannot report a story from an account you do not follow'); + abort_if(! FollowerService::follows($pid, $story->profile_id), 422, 'Cannot report a story from an account you do not follow'); - if( Report::whereProfileId($pid) + if (Report::whereProfileId($pid) ->whereObjectType('App\Story') ->whereObjectId($story->id) ->exists() ) { return response()->json(['error' => [ 'code' => 409, - 'message' => 'Cannot report the same story again' + 'message' => 'Cannot report the same story again', ]], 409); } @@ -389,18 +385,18 @@ class StoryComposeController extends Controller public function react(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'sid' => 'required', - 'reaction' => 'required|string' + 'reaction' => 'required|string', ]); $pid = $request->user()->profile_id; $text = $request->input('reaction'); $user = $request->user(); - abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); + abort_if($user->has_roles && ! UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); $story = Story::findOrFail($request->input('sid')); - abort_if(!$story->can_react, 422); + abort_if(! $story->can_react, 422); abort_if(StoryService::reactCounter($story->id, $pid) >= 5, 422, 'You have already reacted to this story'); $status = new Status; @@ -413,7 +409,7 @@ class StoryComposeController extends Controller $status->in_reply_to_profile_id = $story->profile_id; $status->entities = json_encode([ 'story_id' => $story->id, - 'reaction' => $text + 'reaction' => $text, ]); $status->save(); @@ -427,24 +423,24 @@ class StoryComposeController extends Controller 'story_actor_username' => $request->user()->username, 'story_id' => $story->id, 'story_media_url' => url(Storage::url($story->path)), - 'reaction' => $text + 'reaction' => $text, ]); $dm->save(); Conversation::updateOrInsert( [ 'to_id' => $story->profile_id, - 'from_id' => $pid + 'from_id' => $pid, ], [ 'type' => 'story:react', 'status_id' => $status->id, 'dm_id' => $dm->id, - 'is_hidden' => false + 'is_hidden' => false, ] ); - if($story->local) { + if ($story->local) { // generate notification $n = new Notification; $n->profile_id = $dm->to_id; @@ -464,18 +460,18 @@ class StoryComposeController extends Controller public function comment(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $this->validate($request, [ 'sid' => 'required', - 'caption' => 'required|string' + 'caption' => 'required|string', ]); $pid = $request->user()->profile_id; $text = $request->input('caption'); $user = $request->user(); - abort_if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); + abort_if($user->has_roles && ! UserRoleService::can('can-use-stories', $user->id), 403, 'Invalid permissions for this action'); $story = Story::findOrFail($request->input('sid')); - abort_if(!$story->can_reply, 422); + abort_if(! $story->can_reply, 422); $status = new Status; $status->type = 'story:reply'; @@ -486,7 +482,7 @@ class StoryComposeController extends Controller $status->visibility = 'direct'; $status->in_reply_to_profile_id = $story->profile_id; $status->entities = json_encode([ - 'story_id' => $story->id + 'story_id' => $story->id, ]); $status->save(); @@ -500,24 +496,24 @@ class StoryComposeController extends Controller 'story_actor_username' => $request->user()->username, 'story_id' => $story->id, 'story_media_url' => url(Storage::url($story->path)), - 'caption' => $text + 'caption' => $text, ]); $dm->save(); Conversation::updateOrInsert( [ 'to_id' => $story->profile_id, - 'from_id' => $pid + 'from_id' => $pid, ], [ 'type' => 'story:comment', 'status_id' => $status->id, 'dm_id' => $dm->id, - 'is_hidden' => false + 'is_hidden' => false, ] ); - if($story->local) { + if ($story->local) { // generate notification $n = new Notification; $n->profile_id = $dm->to_id; diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index 63e830b49..21251d167 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -545,7 +545,7 @@ INSTANCE STORIES_ENABLED - {{config_cache('instance.stories.enabled') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('instance.stories.enabled') ? '✅ true' : '❌ false' }} INSTANCE diff --git a/resources/views/layouts/partial/nav.blade.php b/resources/views/layouts/partial/nav.blade.php index 1d89902bf..56fe7da4a 100644 --- a/resources/views/layouts/partial/nav.blade.php +++ b/resources/views/layouts/partial/nav.blade.php @@ -105,7 +105,7 @@ {{__('navmenu.discover')}} - @if(config_cache('instance.stories.enabled')) + @if((bool) config_cache('instance.stories.enabled')) From 2d113de536b4e761bda7137ec288e672c5922b33 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 04:27:24 -0600 Subject: [PATCH 17/41] Update config_cache, fix type casting --- app/Http/Controllers/ProfileController.php | 2 +- app/Http/Controllers/StoryController.php | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 1dbb9a046..65a756eaf 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -373,7 +373,7 @@ class ProfileController extends Controller public function stories(Request $request, $username) { - abort_if(! config_cache('instance.stories.enabled') || ! $request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || ! $request->user(), 404); $profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail(); $pid = $profile->id; $authed = Auth::user()->profile_id; diff --git a/app/Http/Controllers/StoryController.php b/app/Http/Controllers/StoryController.php index 692e27961..fede7c6d9 100644 --- a/app/Http/Controllers/StoryController.php +++ b/app/Http/Controllers/StoryController.php @@ -34,7 +34,7 @@ class StoryController extends StoryComposeController { public function recent(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $user = $request->user(); if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id)) { return []; @@ -117,7 +117,7 @@ class StoryController extends StoryComposeController public function profile(Request $request, $id) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $user = $request->user(); if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id)) { @@ -176,7 +176,7 @@ class StoryController extends StoryComposeController public function viewed(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $this->validate($request, [ 'id' => 'required|min:1', @@ -221,7 +221,7 @@ class StoryController extends StoryComposeController public function exists(Request $request, $id) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $user = $request->user(); if($user->has_roles && !UserRoleService::can('can-use-stories', $user->id)) { return response()->json(false); @@ -233,7 +233,7 @@ class StoryController extends StoryComposeController public function iRedirect(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $user = $request->user(); abort_if(!$user, 404); @@ -243,7 +243,7 @@ class StoryController extends StoryComposeController public function viewers(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $this->validate($request, [ 'sid' => 'required|string' @@ -274,7 +274,7 @@ class StoryController extends StoryComposeController public function remoteStory(Request $request, $id) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $profile = Profile::findOrFail($id); if($profile->user_id != null || $profile->domain == null) { @@ -286,7 +286,7 @@ class StoryController extends StoryComposeController public function pollResults(Request $request) { - abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404); + abort_if(!(bool) config_cache('instance.stories.enabled') || !$request->user(), 404); $this->validate($request, [ 'sid' => 'required|string' @@ -304,7 +304,7 @@ class StoryController extends StoryComposeController public function getActivityObject(Request $request, $username, $id) { - abort_if(!config_cache('instance.stories.enabled'), 404); + abort_if(!(bool) config_cache('instance.stories.enabled'), 404); if(!$request->wantsJson()) { return redirect('/stories/' . $username); From da0e0ffabfe0d4772d3b55f40bb31271160a0549 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 06:11:20 -0600 Subject: [PATCH 18/41] Update ig import, use config_cache --- app/Http/Controllers/Import/Instagram.php | 26 ++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Import/Instagram.php b/app/Http/Controllers/Import/Instagram.php index 95d290f61..f1b886d52 100644 --- a/app/Http/Controllers/Import/Instagram.php +++ b/app/Http/Controllers/Import/Instagram.php @@ -17,7 +17,7 @@ trait Instagram { public function instagram() { - if(config_cache('pixelfed.import.instagram.enabled') != true) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { abort(404, 'Feature not enabled'); } return view('settings.import.instagram.home'); @@ -25,6 +25,9 @@ trait Instagram public function instagramStart(Request $request) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $completed = ImportJob::whereProfileId(Auth::user()->profile->id) ->whereService('instagram') ->whereNotNull('completed_at') @@ -38,6 +41,9 @@ trait Instagram protected function instagramRedirectOrNew() { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $profile = Auth::user()->profile; $exists = ImportJob::whereProfileId($profile->id) ->whereService('instagram') @@ -61,6 +67,9 @@ trait Instagram public function instagramStepOne(Request $request, $uuid) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $profile = Auth::user()->profile; $job = ImportJob::whereProfileId($profile->id) ->whereNull('completed_at') @@ -72,6 +81,9 @@ trait Instagram public function instagramStepOneStore(Request $request, $uuid) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $max = 'max:' . config('pixelfed.import.instagram.limits.size'); $this->validate($request, [ 'media.*' => 'required|mimes:bin,jpeg,png,gif|'.$max, @@ -114,6 +126,9 @@ trait Instagram public function instagramStepTwo(Request $request, $uuid) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $profile = Auth::user()->profile; $job = ImportJob::whereProfileId($profile->id) ->whereNull('completed_at') @@ -125,6 +140,9 @@ trait Instagram public function instagramStepTwoStore(Request $request, $uuid) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $this->validate($request, [ 'media' => 'required|file|max:1000' ]); @@ -150,6 +168,9 @@ trait Instagram public function instagramStepThree(Request $request, $uuid) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $profile = Auth::user()->profile; $job = ImportJob::whereProfileId($profile->id) ->whereService('instagram') @@ -162,6 +183,9 @@ trait Instagram public function instagramStepThreeStore(Request $request, $uuid) { + if((bool) config_cache('pixelfed.import.instagram.enabled') != true) { + abort(404, 'Feature not enabled'); + } $profile = Auth::user()->profile; try { From a76cb5f4f889be31de92e13e02f45958adcce9d3 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 06:20:26 -0600 Subject: [PATCH 19/41] Update autospam config, use config_cache --- app/Jobs/StatusPipeline/StatusEntityLexer.php | 2 +- app/Services/AutospamService.php | 121 +++++++++--------- .../views/admin/diagnostics/home.blade.php | 2 +- 3 files changed, 65 insertions(+), 60 deletions(-) diff --git a/app/Jobs/StatusPipeline/StatusEntityLexer.php b/app/Jobs/StatusPipeline/StatusEntityLexer.php index 5c37838dc..4d19c7d8a 100644 --- a/app/Jobs/StatusPipeline/StatusEntityLexer.php +++ b/app/Jobs/StatusPipeline/StatusEntityLexer.php @@ -183,7 +183,7 @@ class StatusEntityLexer implements ShouldQueue 'photo:video:album', ]; - if (config_cache('pixelfed.bouncer.enabled')) { + if ((bool) config_cache('pixelfed.bouncer.enabled')) { Bouncer::get($status); } diff --git a/app/Services/AutospamService.php b/app/Services/AutospamService.php index 6986e81e4..3164d14d0 100644 --- a/app/Services/AutospamService.php +++ b/app/Services/AutospamService.php @@ -2,77 +2,82 @@ namespace App\Services; +use App\Util\Lexer\Classifier; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Storage; -use App\Util\Lexer\Classifier; class AutospamService { - const CHCKD_CACHE_KEY = 'pf:services:autospam:nlp:checked'; - const MODEL_CACHE_KEY = 'pf:services:autospam:nlp:model-cache'; - const MODEL_FILE_PATH = 'nlp/active-training-data.json'; - const MODEL_SPAM_PATH = 'nlp/spam.json'; - const MODEL_HAM_PATH = 'nlp/ham.json'; + const CHCKD_CACHE_KEY = 'pf:services:autospam:nlp:checked'; - public static function check($text) - { - if(!$text || strlen($text) == 0) { - false; - } - if(!self::active()) { - return null; - } - $model = self::getCachedModel(); - $classifier = new Classifier; - $classifier->import($model['documents'], $model['words']); - return $classifier->most($text) === 'spam'; - } + const MODEL_CACHE_KEY = 'pf:services:autospam:nlp:model-cache'; - public static function eligible() - { - return Cache::remember(self::CHCKD_CACHE_KEY, 86400, function() { - if(!config_cache('pixelfed.bouncer.enabled') || !config('autospam.enabled')) { - return false; - } + const MODEL_FILE_PATH = 'nlp/active-training-data.json'; - if(!Storage::exists(self::MODEL_SPAM_PATH)) { - return false; - } + const MODEL_SPAM_PATH = 'nlp/spam.json'; - if(!Storage::exists(self::MODEL_HAM_PATH)) { - return false; - } + const MODEL_HAM_PATH = 'nlp/ham.json'; - if(!Storage::exists(self::MODEL_FILE_PATH)) { - return false; - } else { - if(Storage::size(self::MODEL_FILE_PATH) < 1000) { - return false; - } - } + public static function check($text) + { + if (! $text || strlen($text) == 0) { - return true; - }); - } + } + if (! self::active()) { + return null; + } + $model = self::getCachedModel(); + $classifier = new Classifier; + $classifier->import($model['documents'], $model['words']); - public static function active() - { - return config_cache('autospam.nlp.enabled') && self::eligible(); - } + return $classifier->most($text) === 'spam'; + } - public static function getCachedModel() - { - if(!self::active()) { - return null; - } + public static function eligible() + { + return Cache::remember(self::CHCKD_CACHE_KEY, 86400, function () { + if (! (bool) config_cache('pixelfed.bouncer.enabled') || ! (bool) config_cache('autospam.enabled')) { + return false; + } - return Cache::remember(self::MODEL_CACHE_KEY, 86400, function() { - $res = Storage::get(self::MODEL_FILE_PATH); - if(!$res || empty($res)) { - return null; - } + if (! Storage::exists(self::MODEL_SPAM_PATH)) { + return false; + } - return json_decode($res, true); - }); - } + if (! Storage::exists(self::MODEL_HAM_PATH)) { + return false; + } + + if (! Storage::exists(self::MODEL_FILE_PATH)) { + return false; + } else { + if (Storage::size(self::MODEL_FILE_PATH) < 1000) { + return false; + } + } + + return true; + }); + } + + public static function active() + { + return config_cache('autospam.nlp.enabled') && self::eligible(); + } + + public static function getCachedModel() + { + if (! self::active()) { + return null; + } + + return Cache::remember(self::MODEL_CACHE_KEY, 86400, function () { + $res = Storage::get(self::MODEL_FILE_PATH); + if (! $res || empty($res)) { + return null; + } + + return json_decode($res, true); + }); + } } diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index 21251d167..b23652b51 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -815,7 +815,7 @@ PIXELFED PF_BOUNCER_ENABLED - {{config_cache('pixelfed.bouncer.enabled') ? '✅ true' : '❌ false' }} + {{(bool) config_cache('pixelfed.bouncer.enabled') ? '✅ true' : '❌ false' }} PIXELFED From 911446c03ec92be4d08674d83e004968f6392ca8 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 12 Mar 2024 06:42:12 -0600 Subject: [PATCH 20/41] Update app.name config, use config_cache --- app/Http/Controllers/Api/ApiV1Controller.php | 2 +- resources/views/account/moderation/post/autospam.blade.php | 4 ++-- resources/views/account/moderation/post/cw.blade.php | 4 ++-- resources/views/account/moderation/post/removed.blade.php | 4 ++-- resources/views/account/moderation/post/unlist.blade.php | 4 ++-- resources/views/home.blade.php | 4 ++-- resources/views/layouts/app-guest.blade.php | 6 +++--- resources/views/layouts/app.blade.php | 4 ++-- resources/views/layouts/blank.blade.php | 4 ++-- resources/views/layouts/bundle.blade.php | 6 +++--- resources/views/layouts/partial/noauthnav.blade.php | 2 +- resources/views/portfolio/layout.blade.php | 4 ++-- resources/views/profile/embed-removed.blade.php | 4 ++-- resources/views/profile/embed.blade.php | 4 ++-- resources/views/profile/private.blade.php | 2 +- resources/views/site/index.blade.php | 2 +- resources/views/status/embed-removed.blade.php | 4 ++-- resources/views/vendor/mail/html/message.blade.php | 4 ++-- resources/views/vendor/mail/text/message.blade.php | 4 ++-- resources/views/vendor/passport/authorize.blade.php | 2 +- 20 files changed, 37 insertions(+), 37 deletions(-) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index cdb488ab4..313890c28 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -1632,7 +1632,7 @@ class ApiV1Controller extends Controller return [ 'uri' => config('pixelfed.domain.app'), - 'title' => config('app.name'), + 'title' => config_cache('app.name'), 'short_description' => config_cache('app.short_description'), 'description' => config_cache('app.description'), 'email' => config('instance.email'), diff --git a/resources/views/account/moderation/post/autospam.blade.php b/resources/views/account/moderation/post/autospam.blade.php index 91296759d..d16d85f3e 100644 --- a/resources/views/account/moderation/post/autospam.blade.php +++ b/resources/views/account/moderation/post/autospam.blade.php @@ -69,7 +69,7 @@

    Review the Community Guidelines

    -

    We want to keep {{config('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    +

    We want to keep {{config_cache('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    @@ -100,4 +100,4 @@ ctx.putImageData(imageData, 0, 0); @endif -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/account/moderation/post/cw.blade.php b/resources/views/account/moderation/post/cw.blade.php index d1e1d3537..bffa7a186 100644 --- a/resources/views/account/moderation/post/cw.blade.php +++ b/resources/views/account/moderation/post/cw.blade.php @@ -70,7 +70,7 @@

    Review the Community Guidelines

    -

    We want to keep {{config('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    +

    We want to keep {{config_cache('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    @@ -127,4 +127,4 @@ ctx.putImageData(imageData, 0, 0); @endif -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/account/moderation/post/removed.blade.php b/resources/views/account/moderation/post/removed.blade.php index 123863489..4b8a1fee4 100644 --- a/resources/views/account/moderation/post/removed.blade.php +++ b/resources/views/account/moderation/post/removed.blade.php @@ -62,7 +62,7 @@

    Review the Community Guidelines

    -

    We want to keep {{config('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    +

    We want to keep {{config_cache('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    @@ -96,4 +96,4 @@ ctx.putImageData(imageData, 0, 0); @endif -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/account/moderation/post/unlist.blade.php b/resources/views/account/moderation/post/unlist.blade.php index 3c86acb76..4f62a10bb 100644 --- a/resources/views/account/moderation/post/unlist.blade.php +++ b/resources/views/account/moderation/post/unlist.blade.php @@ -69,7 +69,7 @@

    Review the Community Guidelines

    -

    We want to keep {{config('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    +

    We want to keep {{config_cache('app.name')}} a safe place for everyone, and we created these Community Guidelines to support and protect our community.

    @@ -125,4 +125,4 @@ ctx.putImageData(imageData, 0, 0); @endif -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 2f7b05cd3..e5aed6dad 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -1,4 +1,4 @@ -@extends('layouts.app',['title' => 'Welcome to ' . config('app.name')]) +@extends('layouts.app',['title' => 'Welcome to ' . config_cache('app.name')]) @section('content')
    @@ -14,7 +14,7 @@
    @endif -

    Welcome to {{config('app.name')}}!

    +

    Welcome to {{config_cache('app.name')}}!

    diff --git a/resources/views/layouts/app-guest.blade.php b/resources/views/layouts/app-guest.blade.php index 6adcffac4..7d8dbd201 100644 --- a/resources/views/layouts/app-guest.blade.php +++ b/resources/views/layouts/app-guest.blade.php @@ -5,11 +5,11 @@ - {{ $title ?? config('app.name', 'Pixelfed') }} + {{ $title ?? config_cache('app.name', 'Pixelfed') }} - - + + @stack('meta') diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 0136842bb..168992aaf 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -70,11 +70,11 @@ - {{ $title ?? config('app.name', 'Pixelfed') }} + {{ $title ?? config_cache('app.name', 'Pixelfed') }} - + @stack('meta') diff --git a/resources/views/layouts/blank.blade.php b/resources/views/layouts/blank.blade.php index 00042315d..deaf71d62 100644 --- a/resources/views/layouts/blank.blade.php +++ b/resources/views/layouts/blank.blade.php @@ -11,8 +11,8 @@ {{ $title ?? config_cache('app.name') }} - - + + @stack('meta') diff --git a/resources/views/layouts/bundle.blade.php b/resources/views/layouts/bundle.blade.php index 1050a39d6..94e0c2c00 100644 --- a/resources/views/layouts/bundle.blade.php +++ b/resources/views/layouts/bundle.blade.php @@ -9,11 +9,11 @@ - {{ $title ?? config('app.name', 'Laravel') }} + {{ $title ?? config_cache('app.name', 'Pixelfed') }} - - + + @stack('meta') diff --git a/resources/views/layouts/partial/noauthnav.blade.php b/resources/views/layouts/partial/noauthnav.blade.php index 465b51354..004c8497f 100644 --- a/resources/views/layouts/partial/noauthnav.blade.php +++ b/resources/views/layouts/partial/noauthnav.blade.php @@ -2,7 +2,7 @@ diff --git a/resources/views/portfolio/layout.blade.php b/resources/views/portfolio/layout.blade.php index 14158fb37..89e909284 100644 --- a/resources/views/portfolio/layout.blade.php +++ b/resources/views/portfolio/layout.blade.php @@ -11,8 +11,8 @@ {!! $title ?? config_cache('app.name') !!} - - + + @stack('meta') diff --git a/resources/views/profile/embed-removed.blade.php b/resources/views/profile/embed-removed.blade.php index 7a49d2e79..c236eb790 100644 --- a/resources/views/profile/embed-removed.blade.php +++ b/resources/views/profile/embed-removed.blade.php @@ -9,8 +9,8 @@ Pixelfed | 404 Embed Not Found - - + + diff --git a/resources/views/profile/embed.blade.php b/resources/views/profile/embed.blade.php index cc6097e3a..aeb6a5b99 100644 --- a/resources/views/profile/embed.blade.php +++ b/resources/views/profile/embed.blade.php @@ -9,8 +9,8 @@ {{ $title ?? config('app.name', 'Pixelfed') }} - - + + diff --git a/resources/views/profile/private.blade.php b/resources/views/profile/private.blade.php index ffff37d49..118ef643e 100644 --- a/resources/views/profile/private.blade.php +++ b/resources/views/profile/private.blade.php @@ -1,4 +1,4 @@ -@extends('layouts.app-guest',['title' => $user->username . " on " . config('app.name')]) +@extends('layouts.app-guest',['title' => $user->username . " on " . config_cache('app.name')]) @section('content') @if (session('error')) diff --git a/resources/views/site/index.blade.php b/resources/views/site/index.blade.php index e6753a727..b7d3befaa 100644 --- a/resources/views/site/index.blade.php +++ b/resources/views/site/index.blade.php @@ -8,7 +8,7 @@ - {{ config('app.name', 'Pixelfed') }} + {{ config_cache('app.name', 'Pixelfed') }} diff --git a/resources/views/status/embed-removed.blade.php b/resources/views/status/embed-removed.blade.php index e5f94525b..b9f0a2df6 100644 --- a/resources/views/status/embed-removed.blade.php +++ b/resources/views/status/embed-removed.blade.php @@ -9,8 +9,8 @@ Pixelfed | 404 Embed Not Found - - + + diff --git a/resources/views/vendor/mail/html/message.blade.php b/resources/views/vendor/mail/html/message.blade.php index deec4a1f4..26c1f7d80 100644 --- a/resources/views/vendor/mail/html/message.blade.php +++ b/resources/views/vendor/mail/html/message.blade.php @@ -2,7 +2,7 @@ {{-- Header --}} @slot('header') @component('mail::header', ['url' => config('app.url')]) -{{ config('app.name') }} +{{ config_cache('app.name') }} @endcomponent @endslot @@ -21,7 +21,7 @@ {{-- Footer --}} @slot('footer') @component('mail::footer') -© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.') +© {{ date('Y') }} {{ config_cache('app.name') }}. @lang('All rights reserved.') @endcomponent @endslot @endcomponent diff --git a/resources/views/vendor/mail/text/message.blade.php b/resources/views/vendor/mail/text/message.blade.php index 1ae9ed8f1..3416a9bd4 100644 --- a/resources/views/vendor/mail/text/message.blade.php +++ b/resources/views/vendor/mail/text/message.blade.php @@ -2,7 +2,7 @@ {{-- Header --}} @slot('header') @component('mail::header', ['url' => config('app.url')]) - {{ config('app.name') }} + {{ config_cache('app.name') }} @endcomponent @endslot @@ -21,7 +21,7 @@ {{-- Footer --}} @slot('footer') @component('mail::footer') - © {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.') + © {{ date('Y') }} {{ config_cache('app.name') }}. @lang('All rights reserved.') @endcomponent @endslot @endcomponent diff --git a/resources/views/vendor/passport/authorize.blade.php b/resources/views/vendor/passport/authorize.blade.php index 986f76801..a644289ca 100644 --- a/resources/views/vendor/passport/authorize.blade.php +++ b/resources/views/vendor/passport/authorize.blade.php @@ -4,7 +4,7 @@ - {{ config('app.name') }} - Authorization + {{ config_cache('app.name') }} - Authorization diff --git a/resources/assets/components/admin/partial/AdminSettingsTabHeader.vue b/resources/assets/components/admin/partial/AdminSettingsTabHeader.vue new file mode 100644 index 000000000..ac75d3f37 --- /dev/null +++ b/resources/assets/components/admin/partial/AdminSettingsTabHeader.vue @@ -0,0 +1,63 @@ + + + From 674e560f0497785fe917099db093303ddf97bcb3 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 14 Mar 2024 05:17:18 -0600 Subject: [PATCH 28/41] Update admin settings, refactor to vue component --- .../assets/components/admin/AdminSettings.vue | 1535 +++++++++++++++++ resources/assets/js/admin.js | 10 + resources/views/admin/settings/home.blade.php | 413 +---- 3 files changed, 1547 insertions(+), 411 deletions(-) create mode 100644 resources/assets/components/admin/AdminSettings.vue diff --git a/resources/assets/components/admin/AdminSettings.vue b/resources/assets/components/admin/AdminSettings.vue new file mode 100644 index 000000000..9993ab1a0 --- /dev/null +++ b/resources/assets/components/admin/AdminSettings.vue @@ -0,0 +1,1535 @@ + + + + + diff --git a/resources/assets/js/admin.js b/resources/assets/js/admin.js index 8d2b82ca1..e5a74d8e6 100644 --- a/resources/assets/js/admin.js +++ b/resources/assets/js/admin.js @@ -36,11 +36,21 @@ Vue.component( require('./../components/admin/AdminReports.vue').default ); +Vue.component( + 'admin-settings', + require('./../components/admin/AdminSettings.vue').default +); + Vue.component( 'instances-component', require('./../components/admin/AdminInstances.vue').default ); +// Vue.component( +// 'instance-details-component', +// require('./../components/admin/AdminInstanceDetails.vue').default +// ); + Vue.component( 'hashtag-component', require('./../components/admin/AdminHashtags.vue').default diff --git a/resources/views/admin/settings/home.blade.php b/resources/views/admin/settings/home.blade.php index c2254f700..d78780878 100644 --- a/resources/views/admin/settings/home.blade.php +++ b/resources/views/admin/settings/home.blade.php @@ -1,421 +1,12 @@ @extends('admin.partial.template-full') @section('section') -
    -

    Settings

    -@if(config('instance.enable_cc')) -

    Manage instance settings

    -
    - @csrf - -
    - -
    - {{--
    - -
      -
    • - Max Upload Size: - {{$system['max_upload_size']}} -
    • -
    • - Image Driver: - {{$system['image_driver']}} -
    • -
    • - Image Driver Loaded: - - @if($system['image_driver_loaded']) - - @else - - @endif - -
    • -
    • - File Permissions: - - @if($system['permissions']) - - @else - - @endif - -
    • -
    • - - -
    • -
    -
    --}} -
    -
    - - -
    - -
    - -
    -
    - - @if($cloud_ready) -
    - - -
    -

    Store photos & videos on S3 compatible object storage providers.

    - @endif - -
    - - -
    -

    ActivityPub federation, compatible with Pixelfed, Mastodon and other projects.

    - -
    - - -
    - @if((bool) config_cache('federation.activitypub.enabled')) -

    Allow local accounts to migrate to other local or remote accounts.

    - @else -

    ActivityPub Required Allow local accounts to migrate to other local or remote accounts.

    - @endif - - {{--
    - - -
    -

    Allow new user registrations.

    --}} - - - {{--
    - - -
    -

    Manually review new account registration applications.

    --}} - -
    - - -
    -

    Enable apis required for mobile app support.

    - -
    - - -
    -

    Allow users to share ephemeral Stories.

    - -
    - - -
    -

    Allow experimental Instagram Import support.

    - -
    - - -
    -

    Detect and remove spam from timelines.

    -
    -
    - {{--
    -
    - - -

    The instance name used in titles, metadata and apis.

    -
    -
    -
    -
    - - -

    Short description of instance used on various pages and apis.

    -
    -
    -
    -
    - - -

    Longer description of instance used on about page.

    -
    -
    --}} -
    - -
    -
    -
    -

    Configure your landing page

    -
    -
    -
    -
    -

    Discovery

    - -
    -
    - - -
    -
    - -
    -
    - - -
    -
    -
    -
    -
    -
    -

    Admin Account

    - -
    - -
    -
    -
    -
    - -
    -
    -
    - - -

    The instance name used in titles, metadata and apis.

    -
    -
    -
    -
    - - -

    Short description of instance used on various pages and apis.

    -
    -
    -
    -
    - - -

    Longer description of instance used on about page.

    -
    -
    -
    -
    - - -

    The header title used on the about page.

    -
    -
    -
    - -
    -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -

    Set a storage limit per user account.

    -
    - - -

    Account limit size in KB.

    -

    {{config_cache('pixelfed.max_account_size')}} KB = {{floor(config_cache('pixelfed.max_account_size') / 1024)}} MB

    -
    -
    - -
    -
    -
    - - -

    Enable auto follow accounts, new accounts will follow accounts you set.

    -
    - - -

    Add account usernames to follow separated by commas.

    -
    -
    -
    - -
    -
    -
    - - -

    Maximum file upload size in KB

    -

    {{config_cache('pixelfed.max_photo_size')}} KB = {{number_format(config_cache('pixelfed.max_photo_size') / 1024)}} MB

    -
    -
    -
    -
    - - -

    The maximum number of photos or videos per album

    -
    -
    -
    -
    - - -

    Image optimization quality from 0-100%. Set to 0 to disable image optimization.

    -
    -
    -
    -
    - -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -

    Allowed media types.

    -
    -
    -
    - -
    -
    -

    Add rules that explain what is acceptable use.

    -
    -
    -

    Active Rules

    -
      - @if($rules) - @foreach($rules as $rule) -
    1. -

      - {{$rule}} -

      -

      - -

      -
    2. - @endforeach - @endif -
    -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    - -
    - - -
    - -

    Add custom CSS, will be used on all pages

    -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -@else - -
    -

    Not enabled

    -

    Add ENABLE_CONFIG_CACHE=true in your .env file
    and run php artisan config:cache

    -
    -@endif + @endsection @push('scripts') @endpush From 3628b4625c8887df4cc2d7eab420200f1f0b65a8 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Thu, 14 Mar 2024 05:49:02 -0600 Subject: [PATCH 29/41] Update ConfigCacheService, encrypt keys at rest --- .../Admin/AdminSettingsController.php | 4 +- app/Services/ConfigCacheService.php | 37 +++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Admin/AdminSettingsController.php b/app/Http/Controllers/Admin/AdminSettingsController.php index 8f29765ee..f889f2be6 100644 --- a/app/Http/Controllers/Admin/AdminSettingsController.php +++ b/app/Http/Controllers/Admin/AdminSettingsController.php @@ -685,10 +685,10 @@ trait AdminSettingsController if($captcha) { $secret = $request->input('captcha_secret'); $sitekey = $request->input('captcha_sitekey'); - if(config_cache('captcha.secret') !== $secret && strpos('*', $secret) === false) { + if(config_cache('captcha.secret') != $secret && strpos($secret, '*') === false) { ConfigCacheService::put('captcha.secret', $secret); } - if(config_cache('captcha.sitekey') !== $sitekey && strpos('*', $sitekey) === false) { + if(config_cache('captcha.sitekey') != $sitekey && strpos($sitekey, '*') === false) { ConfigCacheService::put('captcha.sitekey', $sitekey); } ConfigCacheService::put('captcha.active.login', $request->boolean('captcha_on_login')); diff --git a/app/Services/ConfigCacheService.php b/app/Services/ConfigCacheService.php index 4abea8b28..626982781 100644 --- a/app/Services/ConfigCacheService.php +++ b/app/Services/ConfigCacheService.php @@ -8,6 +8,14 @@ use Cache; class ConfigCacheService { const CACHE_KEY = 'config_cache:_v0-key:'; + const PROTECTED_KEYS = [ + 'filesystems.disks.s3.key', + 'filesystems.disks.s3.secret', + 'filesystems.disks.spaces.key', + 'filesystems.disks.spaces.secret', + 'captcha.secret', + 'captcha.sitekey', + ]; public static function get($key) { @@ -135,20 +143,34 @@ class ConfigCacheService return config($key); } + $protect = false; + $protected = null; + if(in_array($key, self::PROTECTED_KEYS)) { + $protect = true; + } + $v = config($key); $c = ConfigCacheModel::where('k', $key)->first(); if ($c) { - return $c->v ?? config($key); + if($protect) { + return decrypt($c->v) ?? config($key); + } else { + return $c->v ?? config($key); + } } if (! $v) { return; } + if($protect && $v) { + $protected = encrypt($v); + } + $cc = new ConfigCacheModel; $cc->k = $key; - $cc->v = $v; + $cc->v = $protect ? $protected : $v; $cc->save(); return $v; @@ -159,8 +181,15 @@ class ConfigCacheService { $exists = ConfigCacheModel::whereK($key)->first(); + $protect = false; + $protected = null; + if(in_array($key, self::PROTECTED_KEYS)) { + $protect = true; + $protected = encrypt($val); + } + if ($exists) { - $exists->v = $val; + $exists->v = $protect ? $protected : $val; $exists->save(); Cache::put(self::CACHE_KEY.$key, $val, now()->addHours(12)); @@ -169,7 +198,7 @@ class ConfigCacheService $cc = new ConfigCacheModel; $cc->k = $key; - $cc->v = $val; + $cc->v = $protect ? $protected : $val; $cc->save(); Cache::put(self::CACHE_KEY.$key, $val, now()->addHours(12)); From 5162c0704a3ea148f6cdb0c53a61457ad3d1939b Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Mar 2024 03:58:24 -0600 Subject: [PATCH 30/41] Update RemoteFollowImportRecent, use MediaPathService --- app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php b/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php index 5b413ecc1..394c2cfb8 100644 --- a/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php +++ b/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php @@ -17,6 +17,7 @@ use Log; use Storage; use Zttp\Zttp; use App\Util\ActivityPub\Helpers; +use App\Services\MediaPathService; class RemoteFollowImportRecent implements ShouldQueue { @@ -45,7 +46,6 @@ class RemoteFollowImportRecent implements ShouldQueue 'image/jpg', 'image/jpeg', 'image/png', - 'image/gif', ]; } @@ -208,9 +208,7 @@ class RemoteFollowImportRecent implements ShouldQueue public function importMedia($url, $mime, $status) { $user = $this->profile; - $monthHash = hash('sha1', date('Y').date('m')); - $userHash = hash('sha1', $user->id.(string) $user->created_at); - $storagePath = "public/m/{$monthHash}/{$userHash}"; + $storagePath = MediaPathService::get($user, 2); try { $info = pathinfo($url); From ac1f0748892240f05c77698d49a478b424fcfc4a Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 16 Mar 2024 05:13:06 -0600 Subject: [PATCH 31/41] Update AdminSettingsController, add user filter max limit settings --- .../Controllers/Admin/AdminSettingsController.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/AdminSettingsController.php b/app/Http/Controllers/Admin/AdminSettingsController.php index f889f2be6..b1f83440b 100644 --- a/app/Http/Controllers/Admin/AdminSettingsController.php +++ b/app/Http/Controllers/Admin/AdminSettingsController.php @@ -725,16 +725,25 @@ trait AdminSettingsController $this->validate($request, [ 'require_email_verification' => 'required', 'enforce_account_limit' => 'required', - 'admin_autofollow' => 'required' + 'admin_autofollow' => 'required', + 'max_user_blocks' => 'required', + 'max_user_mutes' => 'required', + 'max_domain_blocks' => 'required', ]); ConfigCacheService::put('pixelfed.enforce_email_verification', $request->boolean('require_email_verification')); ConfigCacheService::put('pixelfed.enforce_account_limit', $request->boolean('enforce_account_limit')); ConfigCacheService::put('account.autofollow', $request->boolean('admin_autofollow')); + ConfigCacheService::put('instance.user_filters.max_user_blocks', (int) $request->input('max_user_blocks')); + ConfigCacheService::put('instance.user_filters.max_user_mutes', (int) $request->input('max_user_mutes')); + ConfigCacheService::put('instance.user_filters.max_domain_blocks', (int) $request->input('max_domain_blocks')); $res = [ 'require_email_verification' => $request->boolean('require_email_verification'), 'enforce_account_limit' => $request->boolean('enforce_account_limit'), 'admin_autofollow' => $request->boolean('admin_autofollow'), + 'max_user_blocks' => $request->input('max_user_blocks'), + 'max_user_mutes' => $request->input('max_user_mutes'), + 'max_domain_blocks' => $request->input('max_domain_blocks'), ]; Cache::forget('api:v1:instance-data:rules'); Cache::forget('api:v1:instance-data-response-v1'); From dcc5f416efcc3bd785b7ce28dac75c89e88c7bac Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Mar 2024 05:55:38 -0600 Subject: [PATCH 32/41] Update AdminSettingsController, add AdminSettingsService --- .../Admin/AdminSettingsController.php | 117 ++++++++---- app/Services/AdminSettingsService.php | 166 ++++++++++++++++++ 2 files changed, 249 insertions(+), 34 deletions(-) create mode 100644 app/Services/AdminSettingsService.php diff --git a/app/Http/Controllers/Admin/AdminSettingsController.php b/app/Http/Controllers/Admin/AdminSettingsController.php index b1f83440b..e141fc81b 100644 --- a/app/Http/Controllers/Admin/AdminSettingsController.php +++ b/app/Http/Controllers/Admin/AdminSettingsController.php @@ -9,14 +9,13 @@ use App\Profile; use App\Services\AccountService; use App\Services\AdminSettingsService; use App\Services\ConfigCacheService; +use App\Services\FilesystemService; use App\User; use App\Util\Site\Config; use Artisan; use Cache; use DB; use Illuminate\Http\Request; -use App\Services\Internal\BeagleService; -use App\Services\FilesystemService; trait AdminSettingsController { @@ -74,7 +73,7 @@ trait AdminSettingsController 'admin_account_id' => 'nullable', 'regs' => 'required|in:open,filtered,closed', 'account_migration' => 'nullable', - 'rule_delete' => 'sometimes' + 'rule_delete' => 'sometimes', ]); $orb = false; @@ -335,7 +334,7 @@ trait AdminSettingsController $regState = $openReg ? 'open' : ($curOnboarding ? 'filtered' : 'closed'); $accountMigration = (bool) config_cache('federation.migration'); $autoFollow = config_cache('account.autofollow_usernames'); - if(strlen($autoFollow) > 3) { + if (strlen($autoFollow) > 3) { $autoFollow = explode(',', $autoFollow); } @@ -347,7 +346,7 @@ trait AdminSettingsController public function settingsApiRulesAdd(Request $request) { $this->validate($request, [ - 'rule' => 'required|string|min:5|max:1000' + 'rule' => 'required|string|min:5|max:1000', ]); $rules = ConfigCacheService::get('app.rules'); @@ -357,7 +356,7 @@ trait AdminSettingsController } else { $json = json_decode($rules, true); $count = count($json); - if($count >= 30) { + if ($count >= 30) { return response()->json(['message' => 'Max rules limit reached, you can set up to 30 rules at a time.'], 400); } $json[] = $val; @@ -367,6 +366,7 @@ trait AdminSettingsController Cache::forget('api:v1:instance-data-response-v1'); Cache::forget('api:v2:instance-data-response-v2'); Config::refresh(); + return [$val]; } @@ -384,7 +384,7 @@ trait AdminSettingsController } else { $json = json_decode($rules, true); $idx = array_search($val, $json); - if($idx !== false) { + if ($idx !== false) { unset($json[$idx]); $json = array_values($json); } @@ -426,17 +426,18 @@ trait AdminSettingsController $username = $request->input('username'); $names = []; $existing = config_cache('account.autofollow_usernames'); - if($existing) { + if ($existing) { $names = explode(',', $existing); } - if(in_array($username, $names)) { + if (in_array($username, $names)) { $key = array_search($username, $names); - if($key !== false) { + if ($key !== false) { unset($names[$key]); } } ConfigCacheService::put('account.autofollow_usernames', implode(',', $names)); + return response()->json(['accounts' => array_values($names)]); } @@ -449,16 +450,26 @@ trait AdminSettingsController $username = $request->input('username'); $names = []; $existing = config_cache('account.autofollow_usernames'); - if($existing) { + if ($existing) { $names = explode(',', $existing); } - $p = Profile::whereUsername($username)->whereNotNull('user_id')->first(); + if ($existing && count($names)) { + if (count($names) >= 5) { + return response()->json(['message' => 'You can only add up to 5 accounts to be autofollowed.'], 400); + } + if (in_array(strtolower($username), array_map('strtolower', $names))) { + return response()->json(['message' => 'User already exists, please try again.'], 400); + } + } + + $p = User::whereUsername($username)->whereNull('status')->first(); if (! $p || in_array($p->username, $names)) { abort(404); } - array_push($names, strtolower($p->username)); + array_push($names, $p->username); ConfigCacheService::put('account.autofollow_usernames', implode(',', $names)); + return response()->json(['accounts' => array_values($names)]); } @@ -478,11 +489,11 @@ trait AdminSettingsController switch ($type) { case 'home': return $this->settingsApiUpdateHomeType($request); - break; + break; case 'landing': return $this->settingsApiUpdateLandingType($request); - break; + break; case 'posts': return $this->settingsApiUpdatePostsType($request); @@ -531,13 +542,13 @@ trait AdminSettingsController ConfigCacheService::put('pixelfed.open_registration', $regStatus === 'open'); ConfigCacheService::put('instance.curated_registration.enabled', $regStatus === 'filtered'); $cloudStorage = $request->boolean('cloud_storage'); - if($cloudStorage !== (bool) config_cache('pixelfed.cloud_storage')) { - if(!$cloudStorage) { + if ($cloudStorage !== (bool) config_cache('pixelfed.cloud_storage')) { + if (! $cloudStorage) { ConfigCacheService::put('pixelfed.cloud_storage', false); } else { $cloud_disk = config('filesystems.cloud'); $cloud_ready = ! empty(config('filesystems.disks.'.$cloud_disk.'.key')) && ! empty(config('filesystems.disks.'.$cloud_disk.'.secret')); - if(!$cloud_ready) { + if (! $cloud_ready) { return redirect()->back()->withErrors(['cloud_storage' => 'Must configure cloud storage before enabling!']); } else { ConfigCacheService::put('pixelfed.cloud_storage', true); @@ -555,6 +566,7 @@ trait AdminSettingsController Cache::forget('api:v2:instance-data-response-v2'); Cache::forget('api:v1:instance-data:contact'); Config::refresh(); + return $request->all(); } @@ -593,7 +605,7 @@ trait AdminSettingsController $mediaTypes = $request->input('media_types'); $mediaArray = explode(',', $mediaTypes); foreach ($mediaArray as $mediaType) { - if(!in_array($mediaType, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4'])) { + if (! in_array($mediaType, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4'])) { return redirect()->back()->withErrors(['media_types' => 'Invalid media type']); } } @@ -652,6 +664,7 @@ trait AdminSettingsController Cache::forget('api:v1:instance-data-response-v1'); Cache::forget('api:v2:instance-data-response-v2'); Config::refresh(); + return $res; } @@ -682,13 +695,13 @@ trait AdminSettingsController ConfigCacheService::put('instance.embed.profile', $request->boolean('allow_profile_embeds')); ConfigCacheService::put('federation.custom_emoji.enabled', $request->boolean('custom_emoji_enabled')); $captcha = $request->boolean('captcha_enabled'); - if($captcha) { + if ($captcha) { $secret = $request->input('captcha_secret'); $sitekey = $request->input('captcha_sitekey'); - if(config_cache('captcha.secret') != $secret && strpos($secret, '*') === false) { + if (config_cache('captcha.secret') != $secret && strpos($secret, '*') === false) { ConfigCacheService::put('captcha.secret', $secret); } - if(config_cache('captcha.sitekey') != $sitekey && strpos($sitekey, '*') === false) { + if (config_cache('captcha.sitekey') != $sitekey && strpos($sitekey, '*') === false) { ConfigCacheService::put('captcha.sitekey', $sitekey); } ConfigCacheService::put('captcha.active.login', $request->boolean('captcha_on_login')); @@ -717,6 +730,7 @@ trait AdminSettingsController Cache::forget('api:v1:instance-data-response-v1'); Cache::forget('api:v2:instance-data-response-v2'); Config::refresh(); + return $res; } @@ -726,11 +740,43 @@ trait AdminSettingsController 'require_email_verification' => 'required', 'enforce_account_limit' => 'required', 'admin_autofollow' => 'required', + 'admin_autofollow_accounts' => 'sometimes', 'max_user_blocks' => 'required', 'max_user_mutes' => 'required', 'max_domain_blocks' => 'required', ]); + $adminAutofollow = $request->boolean('admin_autofollow'); + $adminAutofollowAccounts = $request->input('admin_autofollow_accounts'); + if ($adminAutofollow) { + if ($request->filled('admin_autofollow_accounts')) { + $names = []; + $existing = config_cache('account.autofollow_usernames'); + if ($existing) { + $names = explode(',', $existing); + foreach (array_map('strtolower', $adminAutofollowAccounts) as $afc) { + if (in_array(strtolower($afc), array_map('strtolower', $names))) { + continue; + } + $names[] = $afc; + } + } else { + $names = $adminAutofollowAccounts; + } + if (! $names || count($names) == 0) { + return response()->json(['message' => 'You need to assign autofollow accounts before you can enable it.'], 400); + } + if (count($names) > 5) { + return response()->json(['message' => 'You can only add up to 5 accounts to be autofollowed.'.json_encode($names)], 400); + } + $autofollows = User::whereIn('username', $names)->whereNull('status')->pluck('username'); + $adminAutofollowAccounts = $autofollows->implode(','); + ConfigCacheService::put('account.autofollow_usernames', $adminAutofollowAccounts); + } else { + return response()->json(['message' => 'You need to assign autofollow accounts before you can enable it.'], 400); + } + } + ConfigCacheService::put('pixelfed.enforce_email_verification', $request->boolean('require_email_verification')); ConfigCacheService::put('pixelfed.enforce_account_limit', $request->boolean('enforce_account_limit')); ConfigCacheService::put('account.autofollow', $request->boolean('admin_autofollow')); @@ -741,6 +787,7 @@ trait AdminSettingsController 'require_email_verification' => $request->boolean('require_email_verification'), 'enforce_account_limit' => $request->boolean('enforce_account_limit'), 'admin_autofollow' => $request->boolean('admin_autofollow'), + 'admin_autofollow_accounts' => $adminAutofollowAccounts, 'max_user_blocks' => $request->input('max_user_blocks'), 'max_user_mutes' => $request->input('max_user_mutes'), 'max_domain_blocks' => $request->input('max_domain_blocks'), @@ -749,6 +796,7 @@ trait AdminSettingsController Cache::forget('api:v1:instance-data-response-v1'); Cache::forget('api:v2:instance-data-response-v2'); Config::refresh(); + return $res; } @@ -772,7 +820,7 @@ trait AdminSettingsController $res = [ 'primary_disk' => $request->input('primary_disk'), ]; - if($request->has('update_disk')) { + if ($request->has('update_disk')) { $res['disk_config'] = $request->input('disk_config'); $changes = []; $dkey = $request->input('disk_config.driver') === 's3' ? 'filesystems.disks.s3.' : 'filesystems.disks.spaces.'; @@ -785,33 +833,33 @@ trait AdminSettingsController $visibility = $request->input('disk_config.visibility'); $url = $request->input('disk_config.url'); $endpoint = $request->input('disk_config.endpoint'); - if(strpos($key, '*') === false && $key != config_cache($dkey . 'key')) { + if (strpos($key, '*') === false && $key != config_cache($dkey.'key')) { array_push($changes, 'key'); } else { - $ckey = config_cache($dkey . 'key'); + $ckey = config_cache($dkey.'key'); } - if(strpos($secret, '*') === false && $secret != config_cache($dkey . 'secret')) { + if (strpos($secret, '*') === false && $secret != config_cache($dkey.'secret')) { array_push($changes, 'secret'); } else { - $csecret = config_cache($dkey . 'secret'); + $csecret = config_cache($dkey.'secret'); } - if($region != config_cache($dkey . 'region')) { + if ($region != config_cache($dkey.'region')) { array_push($changes, 'region'); } - if($bucket != config_cache($dkey . 'bucket')) { + if ($bucket != config_cache($dkey.'bucket')) { array_push($changes, 'bucket'); } - if($visibility != config_cache($dkey . 'visibility')) { + if ($visibility != config_cache($dkey.'visibility')) { array_push($changes, 'visibility'); } - if($url != config_cache($dkey . 'url')) { + if ($url != config_cache($dkey.'url')) { array_push($changes, 'url'); } - if($endpoint != config_cache($dkey . 'endpoint')) { + if ($endpoint != config_cache($dkey.'endpoint')) { array_push($changes, 'endpoint'); } - if($changes && count($changes)) { + if ($changes && count($changes)) { $isValid = FilesystemService::getVerifyCredentials( $ckey ?? $key, $csecret ?? $secret, @@ -819,7 +867,7 @@ trait AdminSettingsController $bucket, $endpoint, ); - if(!$isValid) { + if (! $isValid) { return response()->json(['error' => true, 's3_vce' => true, 'message' => "
    The S3/Spaces credentials you provided are invalid, or the bucket does not have the proper permissions.

    Please check all fields and try again.

    Any cloud storage configuration changes you made have NOT been saved due to invalid credentials."], 400); } } @@ -829,6 +877,7 @@ trait AdminSettingsController Cache::forget('api:v1:instance-data-response-v1'); Cache::forget('api:v2:instance-data-response-v2'); Config::refresh(); + return $res; } } diff --git a/app/Services/AdminSettingsService.php b/app/Services/AdminSettingsService.php new file mode 100644 index 000000000..57fb6e96f --- /dev/null +++ b/app/Services/AdminSettingsService.php @@ -0,0 +1,166 @@ + self::getFeatures(), + 'landing' => self::getLanding(), + 'branding' => self::getBranding(), + 'media' => self::getMedia(), + 'rules' => self::getRules(), + 'suggested_rules' => self::getSuggestedRules(), + 'users' => self::getUsers(), + 'posts' => self::getPosts(), + 'platform' => self::getPlatform(), + 'storage' => self::getStorage(), + ]; + } + + public static function getFeatures() + { + $cloud_storage = (bool) config_cache('pixelfed.cloud_storage'); + $cloud_disk = config('filesystems.cloud'); + $cloud_ready = ! empty(config('filesystems.disks.'.$cloud_disk.'.key')) && ! empty(config('filesystems.disks.'.$cloud_disk.'.secret')); + $openReg = (bool) config_cache('pixelfed.open_registration'); + $curOnboarding = (bool) config_cache('instance.curated_registration.enabled'); + $regState = $openReg ? 'open' : ($curOnboarding ? 'filtered' : 'closed'); + + return [ + 'registration_status' => $regState, + 'cloud_storage' => $cloud_ready && $cloud_storage, + 'activitypub_enabled' => (bool) config_cache('federation.activitypub.enabled'), + 'account_migration' => (bool) config_cache('federation.migration'), + 'mobile_apis' => (bool) config_cache('pixelfed.oauth_enabled'), + 'stories' => (bool) config_cache('instance.stories.enabled'), + 'instagram_import' => (bool) config_cache('pixelfed.import.instagram.enabled'), + 'autospam_enabled' => (bool) config_cache('pixelfed.bouncer.enabled'), + ]; + } + + public static function getLanding() + { + $availableAdmins = User::whereIsAdmin(true)->get(); + $currentAdmin = config_cache('instance.admin.pid'); + + return [ + 'admins' => $availableAdmins, + 'current_admin' => $currentAdmin, + 'show_directory' => (bool) config_cache('instance.landing.show_directory'), + 'show_explore' => (bool) config_cache('instance.landing.show_explore'), + ]; + } + + public static function getBranding() + { + return [ + 'name' => config_cache('app.name'), + 'short_description' => config_cache('app.short_description'), + 'long_description' => config_cache('app.description'), + ]; + } + + public static function getMedia() + { + return [ + 'max_photo_size' => config_cache('pixelfed.max_photo_size'), + 'max_album_length' => config_cache('pixelfed.max_album_length'), + 'image_quality' => config_cache('pixelfed.image_quality'), + 'media_types' => config_cache('pixelfed.media_types'), + 'optimize_image' => (bool) config_cache('pixelfed.optimize_image'), + 'optimize_video' => (bool) config_cache('pixelfed.optimize_video'), + ]; + } + + public static function getRules() + { + return config_cache('app.rules') ? json_decode(config_cache('app.rules'), true) : []; + } + + public static function getSuggestedRules() + { + return BeagleService::getDefaultRules(); + } + + public static function getUsers() + { + $autoFollow = config_cache('account.autofollow_usernames'); + if (strlen($autoFollow) >= 2) { + $autoFollow = explode(',', $autoFollow); + } else { + $autoFollow = []; + } + + return [ + 'require_email_verification' => (bool) config_cache('pixelfed.enforce_email_verification'), + 'enforce_account_limit' => (bool) config_cache('pixelfed.enforce_account_limit'), + 'max_account_size' => config_cache('pixelfed.max_account_size'), + 'admin_autofollow' => (bool) config_cache('account.autofollow'), + 'admin_autofollow_accounts' => $autoFollow, + 'max_user_blocks' => (int) config_cache('instance.user_filters.max_user_blocks'), + 'max_user_mutes' => (int) config_cache('instance.user_filters.max_user_mutes'), + 'max_domain_blocks' => (int) config_cache('instance.user_filters.max_domain_blocks'), + ]; + } + + public static function getPosts() + { + return [ + 'max_caption_length' => config_cache('pixelfed.max_caption_length'), + 'max_altext_length' => config_cache('pixelfed.max_altext_length'), + ]; + } + + public static function getPlatform() + { + return [ + 'allow_app_registration' => (bool) config_cache('pixelfed.allow_app_registration'), + 'app_registration_rate_limit_attempts' => config_cache('pixelfed.app_registration_rate_limit_attempts'), + 'app_registration_rate_limit_decay' => config_cache('pixelfed.app_registration_rate_limit_decay'), + 'app_registration_confirm_rate_limit_attempts' => config_cache('pixelfed.app_registration_confirm_rate_limit_attempts'), + 'app_registration_confirm_rate_limit_decay' => config_cache('pixelfed.app_registration_confirm_rate_limit_decay'), + 'allow_post_embeds' => (bool) config_cache('instance.embed.post'), + 'allow_profile_embeds' => (bool) config_cache('instance.embed.profile'), + 'captcha_enabled' => (bool) config_cache('captcha.enabled'), + 'captcha_on_login' => (bool) config_cache('captcha.active.login'), + 'captcha_on_register' => (bool) config_cache('captcha.active.register'), + 'captcha_secret' => Str::of(config_cache('captcha.secret'))->mask('*', 4, -4), + 'captcha_sitekey' => Str::of(config_cache('captcha.sitekey'))->mask('*', 4, -4), + 'custom_emoji_enabled' => (bool) config_cache('federation.custom_emoji.enabled'), + ]; + } + + public static function getStorage() + { + $cloud_storage = (bool) config_cache('pixelfed.cloud_storage'); + $cloud_disk = config('filesystems.cloud'); + $cloud_ready = ! empty(config('filesystems.disks.'.$cloud_disk.'.key')) && ! empty(config('filesystems.disks.'.$cloud_disk.'.secret')); + $primaryDisk = (bool) $cloud_ready && $cloud_storage; + $pkey = 'filesystems.disks.'.$cloud_disk.'.'; + $disk = [ + 'driver' => $cloud_disk, + 'key' => Str::of(config_cache($pkey.'key'))->mask('*', 0, -2), + 'secret' => Str::of(config_cache($pkey.'secret'))->mask('*', 0, -2), + 'region' => config_cache($pkey.'region'), + 'bucket' => config_cache($pkey.'bucket'), + 'visibility' => config_cache($pkey.'visibility'), + 'endpoint' => config_cache($pkey.'endpoint'), + 'url' => config_cache($pkey.'url'), + 'use_path_style_endpoint' => config_cache($pkey.'use_path_style_endpoint'), + ]; + + return [ + 'primary_disk' => $primaryDisk ? 'cloud' : 'local', + 'cloud_ready' => (bool) $cloud_ready, + 'cloud_disk' => $cloud_disk, + 'disk_config' => $disk, + ]; + } +} From aba1e13d43fc05a2f481d34633e2fe766792fb88 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 18 Mar 2024 05:57:21 -0600 Subject: [PATCH 33/41] Update AdminSettings component, fix user settings --- .../assets/components/admin/AdminSettings.vue | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/resources/assets/components/admin/AdminSettings.vue b/resources/assets/components/admin/AdminSettings.vue index 9993ab1a0..9721baa62 100644 --- a/resources/assets/components/admin/AdminSettings.vue +++ b/resources/assets/components/admin/AdminSettings.vue @@ -111,7 +111,7 @@
    - @@ -729,21 +729,21 @@ @@ -794,19 +794,21 @@
    -
    -
    -

    @{{ user }}

    - +
    +
    +
    +

    @{{ user }}

    + +
    -
    +

    No autofollow accounts active.

    -