Ver código fonte

Refactor following & relationship logic. Replace FollowerObserver with FollowerService and added RelationshipService to cache results. Removed NotificationTransformer includes and replaced with cached services to improve performance and reduce database queries.

Daniel Supernault 3 anos atrás
pai
commit
80d9b9399a

+ 4 - 6
app/Http/Controllers/Api/ApiV1Controller.php

@@ -55,6 +55,7 @@ use App\Services\{
 	MediaPathService,
 	PublicTimelineService,
 	ProfileService,
+	RelationshipService,
 	SearchApiV2Service,
 	StatusService,
 	MediaBlocklistService
@@ -551,7 +552,7 @@ class ApiV1Controller extends Controller
 	 *
 	 * @param  array|integer  $id
 	 *
-	 * @return \App\Transformer\Api\RelationshipTransformer
+	 * @return \App\Services\RelationshipService
 	 */
 	public function accountRelationshipsById(Request $request)
 	{
@@ -563,12 +564,9 @@ class ApiV1Controller extends Controller
 		]);
 		$pid = $request->user()->profile_id ?? $request->user()->profile->id;
 		$ids = collect($request->input('id'));
-		$filtered = $ids->filter(function($v) use($pid) {
-			return $v != $pid;
+		$res = $ids->map(function($id) use($pid) {
+			return RelationshipService::get($pid, $id);
 		});
-		$relations = Profile::whereNull('status')->findOrFail($filtered->values());
-		$fractal = new Fractal\Resource\Collection($relations, new RelationshipTransformer());
-		$res = $this->fractal->createData($fractal)->toArray();
 		return response()->json($res);
 	}
 

+ 6 - 1
app/Http/Controllers/FollowerController.php

@@ -12,6 +12,7 @@ use Auth, Cache;
 use Illuminate\Http\Request;
 use App\Jobs\FollowPipeline\FollowPipeline;
 use App\Util\ActivityPub\Helpers;
+use App\Services\FollowerService;
 
 class FollowerController extends Controller
 {
@@ -70,7 +71,9 @@ class FollowerController extends Controller
             ]);
             if($remote == true && config('federation.activitypub.remoteFollow') == true) {
                 $this->sendFollow($user, $target);
-            } 
+            }
+
+            FollowerService::add($user->id, $target->id);
         } elseif ($private == false && $isFollowing == 0) {
             if($user->following()->count() >= Follower::MAX_FOLLOWING) {
                 abort(400, 'You cannot follow more than ' . Follower::MAX_FOLLOWING . ' accounts');
@@ -87,6 +90,7 @@ class FollowerController extends Controller
             if($remote == true && config('federation.activitypub.remoteFollow') == true) {
                 $this->sendFollow($user, $target);
             } 
+            FollowerService::add($user->id, $target->id);
             FollowPipeline::dispatch($follower);
         } else {
             if($force == true) {
@@ -101,6 +105,7 @@ class FollowerController extends Controller
                 Follower::whereProfileId($user->id)
                     ->whereFollowingId($target->id)
                     ->delete();
+                FollowerService::remove($user->id, $target->id);
             }
         }
 

+ 0 - 64
app/Observers/FollowerObserver.php

@@ -1,64 +0,0 @@
-<?php
-
-namespace App\Observers;
-
-use App\Follower;
-use App\Services\FollowerService;
-
-class FollowerObserver
-{
-    /**
-     * Handle the Follower "created" event.
-     *
-     * @param  \App\Models\Follower  $follower
-     * @return void
-     */
-    public function created(Follower $follower)
-    {
-        FollowerService::add($follower->profile_id, $follower->following_id);
-    }
-
-    /**
-     * Handle the Follower "updated" event.
-     *
-     * @param  \App\Models\Follower  $follower
-     * @return void
-     */
-    public function updated(Follower $follower)
-    {
-        FollowerService::add($follower->profile_id, $follower->following_id);
-    }
-
-    /**
-     * Handle the Follower "deleted" event.
-     *
-     * @param  \App\Models\Follower  $follower
-     * @return void
-     */
-    public function deleted(Follower $follower)
-    {
-        FollowerService::remove($follower->profile_id, $follower->following_id);
-    }
-
-    /**
-     * Handle the Follower "restored" event.
-     *
-     * @param  \App\Models\Follower  $follower
-     * @return void
-     */
-    public function restored(Follower $follower)
-    {
-        FollowerService::add($follower->profile_id, $follower->following_id);
-    }
-
-    /**
-     * Handle the Follower "force deleted" event.
-     *
-     * @param  \App\Models\Follower  $follower
-     * @return void
-     */
-    public function forceDeleted(Follower $follower)
-    {
-        FollowerService::remove($follower->profile_id, $follower->following_id);
-    }
-}

+ 0 - 3
app/Providers/AppServiceProvider.php

@@ -4,7 +4,6 @@ namespace App\Providers;
 
 use App\Observers\{
 	AvatarObserver,
-	FollowerObserver,
 	LikeObserver,
 	NotificationObserver,
 	ModLogObserver,
@@ -15,7 +14,6 @@ use App\Observers\{
 };
 use App\{
 	Avatar,
-	Follower,
 	Like,
 	Notification,
 	ModLog,
@@ -50,7 +48,6 @@ class AppServiceProvider extends ServiceProvider
 		StatusHashtag::observe(StatusHashtagObserver::class);
 		User::observe(UserObserver::class);
 		UserFilter::observe(UserFilterObserver::class);
-		Follower::observe(FollowerObserver::class);
 		Horizon::auth(function ($request) {
 			return Auth::check() && $request->user()->is_admin;
 		});

+ 2 - 0
app/Services/FollowerService.php

@@ -17,12 +17,14 @@ class FollowerService
 
 	public static function add($actor, $target)
 	{
+		RelationshipService::refresh($actor, $target);
 		Redis::zadd(self::FOLLOWING_KEY . $actor, $target, $target);
 		Redis::zadd(self::FOLLOWERS_KEY . $target, $actor, $actor);
 	}
 
 	public static function remove($actor, $target)
 	{
+		RelationshipService::refresh($actor, $target);
 		Redis::zrem(self::FOLLOWING_KEY . $actor, $target);
 		Redis::zrem(self::FOLLOWERS_KEY . $target, $actor);
 		Cache::forget('pf:services:follow:audience:' . $actor);

+ 86 - 0
app/Services/RelationshipService.php

@@ -0,0 +1,86 @@
+<?php
+
+namespace App\Services;
+
+use Illuminate\Support\Facades\Cache;
+use App\Follower;
+use App\FollowRequest;
+use App\Profile;
+use App\UserFilter;
+
+class RelationshipService
+{
+	const CACHE_KEY = 'pf:services:urel:';
+
+	public static function get($aid, $tid)
+	{
+		$actor = AccountService::get($aid);
+		$target = AccountService::get($tid);
+		if(!$actor || !$target) {
+			return self::defaultRelation($tid);
+		}
+
+		if($actor['id'] === $target['id']) {
+			return self::defaultRelation($tid);
+		}
+
+		return Cache::remember(self::key("a_{$aid}:t_{$tid}"), 1209600, function() use($aid, $tid) {
+			return [
+				'id' => (string) $tid,
+				'following' => Follower::whereProfileId($aid)->whereFollowingId($tid)->exists(),
+				'followed_by' => Follower::whereProfileId($tid)->whereFollowingId($aid)->exists(),
+				'blocking' => UserFilter::whereUserId($aid)
+					->whereFilterableType('App\Profile')
+					->whereFilterableId($tid)
+					->whereFilterType('block')
+					->exists(),
+				'muting' => UserFilter::whereUserId($aid)
+					->whereFilterableType('App\Profile')
+					->whereFilterableId($tid)
+					->whereFilterType('mute')
+					->exists(),
+				'muting_notifications' => null,
+				'requested' => FollowRequest::whereFollowerId($aid)
+					->whereFollowingId($tid)
+					->exists(),
+				'domain_blocking' => null,
+				'showing_reblogs' => null,
+				'endorsed' => false
+			];
+		});
+	}
+
+	public static function delete($aid, $tid)
+	{
+		return Cache::forget(self::key("a_{$aid}:t_{$tid}"));
+	}
+
+	public static function refresh($aid, $tid)
+	{
+		self::delete($tid, $aid);
+		self::delete($aid, $tid);
+		self::get($tid, $aid);
+		return self::get($aid, $tid);
+	}
+
+	public static function defaultRelation($tid)
+	{
+		return [
+            'id' => (string) $tid,
+            'following' => false,
+            'followed_by' => false,
+            'blocking' => false,
+            'muting' => false,
+            'muting_notifications' => null,
+            'requested' => false,
+            'domain_blocking' => null,
+            'showing_reblogs' => null,
+            'endorsed' => false
+        ];
+	}
+
+	protected static function key($suffix)
+	{
+		return self::CACHE_KEY . $suffix;
+	}
+}

+ 50 - 65
app/Transformer/Api/NotificationTransformer.php

@@ -2,50 +2,65 @@
 
 namespace App\Transformer\Api;
 
-use App\{
-	Notification,
-	Status
-};
+use App\Notification;
+use App\Services\AccountService;
 use App\Services\HashidService;
+use App\Services\StatusService;
 use League\Fractal;
 
 class NotificationTransformer extends Fractal\TransformerAbstract
 {
 	protected $defaultIncludes = [
-		'account',
-		'status',
-		'relationship',
-		'modlog',
-		'tagged'
+		// 'relationship',
 	];
 
 	public function transform(Notification $notification)
 	{
-		return [
+		$res = [
 			'id'       		=> (string) $notification->id,
 			'type'       	=> $this->replaceTypeVerb($notification->action),
 			'created_at' 	=> (string) $notification->created_at->format('c'),
 		];
-	}
 
-	public function includeAccount(Notification $notification)
-	{
-		return $this->item($notification->actor, new AccountTransformer());
-	}
+		$n = $notification;
+		if($n->item_id && $n->item_type == 'App\Status' && in_array($n->action, ['group:comment'])) {
+			$status = $n->status;
+			$res['group_id'] = $status->group_id;
 
-	public function includeStatus(Notification $notification)
-	{
-		$item = $notification;
-		if($item->item_id && $item->item_type == 'App\Status') {
-			$status = Status::with('media')->find($item->item_id);
-			if($status) {
-				return $this->item($status, new StatusTransformer());
-			} else {
-				return null;
+			if($n->action == 'group:comment') {
+				$res['group_post_url'] = GroupPost::whereStatusId($status->id)->first()->url();
 			}
-		} else {
-			return null;
 		}
+
+		if(in_array($n->action, ['group.join.approved', 'group.join.rejected', 'group.like'])) {
+			$res['group'] = GroupService::get($n->item_id);
+		}
+
+		if($n->actor_id) {
+			$res['account'] = AccountService::get($n->actor_id);
+		}
+
+		if($n->item_id && $n->item_type == 'App\Status') {
+			$res['status'] = StatusService::get($n->item_id, false);
+		}
+
+		if($n->item_id && $n->item_type == 'App\ModLog') {
+			$ml = $n->item;
+			$res['modlog'] = [
+				'id' => $ml->object_uid,
+				'url' => url('/i/admin/users/modlogs/' . $ml->object_uid)
+			];
+		}
+
+		if($n->item_id && $n->item_type == 'App\MediaTag') {
+			$ml = $n->item;
+			$res['tagged'] = [
+				'username' => $ml->tagged_username,
+				'post_url' => '/p/'.HashidService::encode($ml->status_id)
+			];
+		}
+
+		return $res;
 	}
 
 	public function replaceTypeVerb($verb)
@@ -57,13 +72,21 @@ class NotificationTransformer extends Fractal\TransformerAbstract
 			'reblog' => 'share',
 			'share' => 'share',
 			'like' => 'favourite',
+			'group:like' => 'favourite',
 			'comment' => 'comment',
 			'admin.user.modlog.comment' => 'modlog',
 			'tagged' => 'tagged',
 			'group:comment' => 'group:comment',
 			'story:react' => 'story:react',
-			'story:comment' => 'story:comment'
+			'story:comment' => 'story:comment',
+			'group:join:approved' => 'group:join:approved',
+			'group:join:rejected' => 'group:join:rejected'
 		];
+
+		if(!isset($verbs[$verb])) {
+			return $verb;
+		}
+
 		return $verbs[$verb];
 	}
 
@@ -71,42 +94,4 @@ class NotificationTransformer extends Fractal\TransformerAbstract
 	{
 		return $this->item($notification->actor, new RelationshipTransformer());
 	}
-
-	public function includeModlog(Notification $notification)
-	{
-		$n = $notification;
-		if($n->item_id && $n->item_type == 'App\ModLog') {
-			$ml = $n->item;
-			if(!empty($ml)) {
-				$res = $this->item($ml, function($ml) {
-					return [
-						'id' => $ml->object_uid,
-						'url' => url('/i/admin/users/modlogs/' . $ml->object_uid)
-					];
-				});
-				return $res;
-			} else {
-				return null;
-			}
-		} else {
-			return null;
-		}
-	}
-
-	public function includeTagged(Notification $notification)
-	{
-		$n = $notification;
-		if($n->item_id && $n->item_type == 'App\MediaTag') {
-			$ml = $n->item;
-			$res = $this->item($ml, function($ml) {
-				return [
-					'username' => $ml->tagged_username,
-					'post_url' => '/p/'.HashidService::encode($ml->status_id)
-				];
-			});
-			return $res;
-		} else {
-			return null;
-		}
-	}
 }

+ 3 - 0
app/Util/ActivityPub/Inbox.php

@@ -455,6 +455,7 @@ class Inbox
 			Cache::forget('profile:follower_count:'.$actor->id);
 			Cache::forget('profile:following_count:'.$target->id);
 			Cache::forget('profile:following_count:'.$actor->id);
+			FollowerService::add($actor->id, $target->id);
 
 		} else {
 			$follower = new Follower;
@@ -464,6 +465,7 @@ class Inbox
 			$follower->save();
 
 			FollowPipeline::dispatch($follower);
+			FollowerService::add($actor->id, $target->id);
 
 			// send Accept to remote profile
 			$accept = [
@@ -722,6 +724,7 @@ class Inbox
 					->whereItemId($following->id)
 					->whereItemType('App\Profile')
 					->forceDelete();
+				FollowerService::remove($profile->id, $following->id);
 				break;
 
 			case 'Like':