mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-22 13:03:16 +00:00
Update cloud storage, use config_cache
This commit is contained in:
parent
a72188a7db
commit
665581d80c
20 changed files with 669 additions and 673 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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'))
|
||||
];
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -102,6 +102,7 @@ class ConfigCacheService
|
|||
'pixelfed.optimize_image',
|
||||
'pixelfed.optimize_video',
|
||||
'pixelfed.max_collection_length',
|
||||
'media.delete_local_after_cloud',
|
||||
// 'system.user_mode'
|
||||
];
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -740,7 +740,7 @@
|
|||
<tr>
|
||||
<td><span class="badge badge-primary">PIXELFED</span></td>
|
||||
<td><strong>PF_ENABLE_CLOUD</strong></td>
|
||||
<td><span>{{config_cache('pixelfed.cloud_storage') ? '✅ true' : '❌ false' }}</span></td>
|
||||
<td><span>{{(bool) config_cache('pixelfed.cloud_storage') ? '✅ true' : '❌ false' }}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge badge-primary">PIXELFED</span></td>
|
||||
|
|
Loading…
Reference in a new issue