1
0
Эх сурвалжийг харах

Merge pull request #5720 from pixelfed/staging

Update DirectMessageController, fix performance issue
daniel 4 сар өмнө
parent
commit
2f2a361804

+ 35 - 25
app/Http/Controllers/Api/ApiV1Controller.php

@@ -3007,39 +3007,49 @@ class ApiV1Controller extends Controller
             'scope' => 'nullable|in:inbox,sent,requests',
         ]);
 
-        return [];
-
         $limit = $request->input('limit', 20);
         $scope = $request->input('scope', 'inbox');
         $user = $request->user();
+
         if ($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id)) {
             return [];
         }
+
         $pid = $user->profile_id;
 
         if (config('database.default') == 'pgsql') {
-            $dms = DirectMessage::when($scope === 'inbox', function ($q, $scope) use ($pid) {
-                return $q->whereIsHidden(false)->where('to_id', $pid)->orWhere('from_id', $pid);
+            $dms = DirectMessage::when($scope === 'inbox', function ($q) use ($pid) {
+                return $q->whereIsHidden(false)
+                    ->where(function ($query) use ($pid) {
+                        $query->where('to_id', $pid)
+                            ->orWhere('from_id', $pid);
+                    });
             })
-                ->when($scope === 'sent', function ($q, $scope) use ($pid) {
-                    return $q->whereFromId($pid)->groupBy(['to_id', 'id']);
+                ->when($scope === 'sent', function ($q) use ($pid) {
+                    return $q->whereFromId($pid)
+                        ->groupBy(['to_id', 'id']);
                 })
-                ->when($scope === 'requests', function ($q, $scope) use ($pid) {
-                    return $q->whereToId($pid)->whereIsHidden(true);
+                ->when($scope === 'requests', function ($q) use ($pid) {
+                    return $q->whereToId($pid)
+                        ->whereIsHidden(true);
                 });
         } else {
-            $dms = Conversation::when($scope === 'inbox', function ($q, $scope) use ($pid) {
+            $dms = Conversation::when($scope === 'inbox', function ($q) use ($pid) {
                 return $q->whereIsHidden(false)
-                    ->where('to_id', $pid)
-                    ->orWhere('from_id', $pid)
+                    ->where(function ($query) use ($pid) {
+                        $query->where('to_id', $pid)
+                            ->orWhere('from_id', $pid);
+                    })
                     ->orderByDesc('status_id')
                     ->groupBy(['to_id', 'from_id']);
             })
-                ->when($scope === 'sent', function ($q, $scope) use ($pid) {
-                    return $q->whereFromId($pid)->groupBy('to_id');
+                ->when($scope === 'sent', function ($q) use ($pid) {
+                    return $q->whereFromId($pid)
+                        ->groupBy('to_id');
                 })
-                ->when($scope === 'requests', function ($q, $scope) use ($pid) {
-                    return $q->whereToId($pid)->whereIsHidden(true);
+                ->when($scope === 'requests', function ($q) use ($pid) {
+                    return $q->whereToId($pid)
+                        ->whereIsHidden(true);
                 });
         }
 
@@ -3047,7 +3057,8 @@ class ApiV1Controller extends Controller
             ->simplePaginate($limit)
             ->map(function ($dm) use ($pid) {
                 $from = $pid == $dm->to_id ? $dm->from_id : $dm->to_id;
-                $res = [
+
+                return [
                     'id' => $dm->id,
                     'unread' => false,
                     'accounts' => [
@@ -3055,17 +3066,16 @@ class ApiV1Controller extends Controller
                     ],
                     'last_status' => StatusService::getDirectMessage($dm->status_id),
                 ];
-
-                return $res;
             })
             ->filter(function ($dm) {
-                if (! $dm || empty($dm['last_status']) || ! isset($dm['accounts']) || ! count($dm['accounts']) || ! isset($dm['accounts'][0]) || ! isset($dm['accounts'][0]['id'])) {
-                    return false;
-                }
-
-                return true;
+                return $dm
+                    && ! empty($dm['last_status'])
+                    && isset($dm['accounts'])
+                    && count($dm['accounts'])
+                    && isset($dm['accounts'][0])
+                    && isset($dm['accounts'][0]['id']);
             })
-            ->unique(function ($item, $key) {
+            ->unique(function ($item) {
                 return $item['accounts'][0]['id'];
             })
             ->values();
@@ -3500,7 +3510,7 @@ class ApiV1Controller extends Controller
             return [];
         }
 
-        $defaultCaption = "";
+        $defaultCaption = '';
         $content = $request->filled('status') ? strip_tags($request->input('status')) : $defaultCaption;
         $cw = $user->profile->cw == true ? true : $request->boolean('sensitive', false);
         $spoilerText = $cw && $request->filled('spoiler_text') ? $request->input('spoiler_text') : null;

+ 141 - 299
app/Http/Controllers/DirectMessageController.php

@@ -30,7 +30,6 @@ class DirectMessageController extends Controller
 {
     public function __construct()
     {
-        abort(404);
         $this->middleware('auth');
     }
 
@@ -45,257 +44,93 @@ class DirectMessageController extends Controller
         if ($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id)) {
             return [];
         }
+
         $profile = $user->profile_id;
         $action = $request->input('a', 'inbox');
-        $page = $request->input('page');
+        $page = $request->input('page', 1);
+        $limit = 8;
+        $offset = ($page - 1) * $limit;
+
+        $baseQuery = DirectMessage::select(
+            'id', 'type', 'to_id', 'from_id', 'status_id',
+            'is_hidden', 'meta', 'created_at', 'read_at'
+        )->with(['author', 'status', 'recipient']);
 
         if (config('database.default') == 'pgsql') {
-            if ($action == 'inbox') {
-                $dms = DirectMessage::select('id', 'type', 'to_id', 'from_id', 'id', 'status_id', 'is_hidden', 'meta', 'created_at', 'read_at')
-                    ->whereToId($profile)
-                    ->with(['author', 'status'])
+            $query = match ($action) {
+                'inbox' => $baseQuery->whereToId($profile)
                     ->whereIsHidden(false)
-                    ->when($page, function ($q, $page) {
-                        if ($page > 1) {
-                            return $q->offset($page * 8 - 8);
-                        }
-                    })
-                    ->latest()
-                    ->get()
-                    ->unique('from_id')
-                    ->take(8)
-                    ->map(function ($r) use ($profile) {
-                        return $r->from_id !== $profile ? [
-                            'id' => (string) $r->from_id,
-                            'name' => $r->author->name,
-                            'username' => $r->author->username,
-                            'avatar' => $r->author->avatarUrl(),
-                            'url' => $r->author->url(),
-                            'isLocal' => (bool) ! $r->author->domain,
-                            'domain' => $r->author->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ] : [
-                            'id' => (string) $r->to_id,
-                            'name' => $r->recipient->name,
-                            'username' => $r->recipient->username,
-                            'avatar' => $r->recipient->avatarUrl(),
-                            'url' => $r->recipient->url(),
-                            'isLocal' => (bool) ! $r->recipient->domain,
-                            'domain' => $r->recipient->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ];
-                    })->values();
-            }
-
-            if ($action == 'sent') {
-                $dms = DirectMessage::select('id', 'type', 'to_id', 'from_id', 'id', 'status_id', 'is_hidden', 'meta', 'created_at', 'read_at')
-                    ->whereFromId($profile)
-                    ->with(['author', 'status'])
-                    ->orderBy('id', 'desc')
-                    ->when($page, function ($q, $page) {
-                        if ($page > 1) {
-                            return $q->offset($page * 8 - 8);
-                        }
-                    })
-                    ->get()
-                    ->unique('to_id')
-                    ->take(8)
-                    ->map(function ($r) use ($profile) {
-                        return $r->from_id !== $profile ? [
-                            'id' => (string) $r->from_id,
-                            'name' => $r->author->name,
-                            'username' => $r->author->username,
-                            'avatar' => $r->author->avatarUrl(),
-                            'url' => $r->author->url(),
-                            'isLocal' => (bool) ! $r->author->domain,
-                            'domain' => $r->author->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ] : [
-                            'id' => (string) $r->to_id,
-                            'name' => $r->recipient->name,
-                            'username' => $r->recipient->username,
-                            'avatar' => $r->recipient->avatarUrl(),
-                            'url' => $r->recipient->url(),
-                            'isLocal' => (bool) ! $r->recipient->domain,
-                            'domain' => $r->recipient->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ];
-                    });
-            }
-
-            if ($action == 'filtered') {
-                $dms = DirectMessage::select('id', 'type', 'to_id', 'from_id', 'id', 'status_id', 'is_hidden', 'meta', 'created_at', 'read_at')
-                    ->whereToId($profile)
-                    ->with(['author', 'status'])
+                    ->orderBy('created_at', 'desc'),
+                'sent' => $baseQuery->whereFromId($profile)
+                    ->orderBy('created_at', 'desc'),
+                'filtered' => $baseQuery->whereToId($profile)
                     ->whereIsHidden(true)
-                    ->orderBy('id', 'desc')
-                    ->when($page, function ($q, $page) {
-                        if ($page > 1) {
-                            return $q->offset($page * 8 - 8);
-                        }
-                    })
-                    ->get()
-                    ->unique('from_id')
-                    ->take(8)
-                    ->map(function ($r) use ($profile) {
-                        return $r->from_id !== $profile ? [
-                            'id' => (string) $r->from_id,
-                            'name' => $r->author->name,
-                            'username' => $r->author->username,
-                            'avatar' => $r->author->avatarUrl(),
-                            'url' => $r->author->url(),
-                            'isLocal' => (bool) ! $r->author->domain,
-                            'domain' => $r->author->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ] : [
-                            'id' => (string) $r->to_id,
-                            'name' => $r->recipient->name,
-                            'username' => $r->recipient->username,
-                            'avatar' => $r->recipient->avatarUrl(),
-                            'url' => $r->recipient->url(),
-                            'isLocal' => (bool) ! $r->recipient->domain,
-                            'domain' => $r->recipient->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ];
-                    });
-            }
-        } elseif (config('database.default') == 'mysql') {
-            if ($action == 'inbox') {
-                $dms = DirectMessage::selectRaw('*, max(created_at) as createdAt')
-                    ->whereToId($profile)
-                    ->with(['author', 'status'])
-                    ->whereIsHidden(false)
-                    ->groupBy('from_id')
-                    ->latest()
-                    ->when($page, function ($q, $page) {
-                        if ($page > 1) {
-                            return $q->offset($page * 8 - 8);
-                        }
-                    })
-                    ->limit(8)
-                    ->get()
-                    ->map(function ($r) use ($profile) {
-                        return $r->from_id !== $profile ? [
-                            'id' => (string) $r->from_id,
-                            'name' => $r->author->name,
-                            'username' => $r->author->username,
-                            'avatar' => $r->author->avatarUrl(),
-                            'url' => $r->author->url(),
-                            'isLocal' => (bool) ! $r->author->domain,
-                            'domain' => $r->author->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ] : [
-                            'id' => (string) $r->to_id,
-                            'name' => $r->recipient->name,
-                            'username' => $r->recipient->username,
-                            'avatar' => $r->recipient->avatarUrl(),
-                            'url' => $r->recipient->url(),
-                            'isLocal' => (bool) ! $r->recipient->domain,
-                            'domain' => $r->recipient->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ];
-                    });
-            }
+                    ->orderBy('created_at', 'desc'),
+                default => throw new \InvalidArgumentException('Invalid action')
+            };
 
-            if ($action == 'sent') {
-                $dms = DirectMessage::selectRaw('*, max(created_at) as createdAt')
-                    ->whereFromId($profile)
-                    ->with(['author', 'status'])
-                    ->groupBy('to_id')
-                    ->orderBy('createdAt', 'desc')
-                    ->when($page, function ($q, $page) {
-                        if ($page > 1) {
-                            return $q->offset($page * 8 - 8);
-                        }
-                    })
-                    ->limit(8)
-                    ->get()
-                    ->map(function ($r) use ($profile) {
-                        return $r->from_id !== $profile ? [
-                            'id' => (string) $r->from_id,
-                            'name' => $r->author->name,
-                            'username' => $r->author->username,
-                            'avatar' => $r->author->avatarUrl(),
-                            'url' => $r->author->url(),
-                            'isLocal' => (bool) ! $r->author->domain,
-                            'domain' => $r->author->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ] : [
-                            'id' => (string) $r->to_id,
-                            'name' => $r->recipient->name,
-                            'username' => $r->recipient->username,
-                            'avatar' => $r->recipient->avatarUrl(),
-                            'url' => $r->recipient->url(),
-                            'isLocal' => (bool) ! $r->recipient->domain,
-                            'domain' => $r->recipient->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ];
-                    });
-            }
+            $dms = $query->offset($offset)
+                ->limit($limit)
+                ->get();
 
-            if ($action == 'filtered') {
-                $dms = DirectMessage::selectRaw('*, max(created_at) as createdAt')
-                    ->whereToId($profile)
-                    ->with(['author', 'status'])
+            $dms = $action === 'sent' ?
+                   $dms->unique('to_id') :
+                   $dms->unique('from_id');
+        } else {
+            $query = match ($action) {
+                'inbox' => $baseQuery->whereToId($profile)
+                    ->whereIsHidden(false)
+                    ->groupBy('from_id', 'id', 'type', 'to_id', 'status_id',
+                        'is_hidden', 'meta', 'created_at', 'read_at')
+                    ->orderBy('created_at', 'desc'),
+                'sent' => $baseQuery->whereFromId($profile)
+                    ->groupBy('to_id', 'id', 'type', 'from_id', 'status_id',
+                        'is_hidden', 'meta', 'created_at', 'read_at')
+                    ->orderBy('created_at', 'desc'),
+                'filtered' => $baseQuery->whereToId($profile)
                     ->whereIsHidden(true)
-                    ->groupBy('from_id')
-                    ->orderBy('createdAt', 'desc')
-                    ->when($page, function ($q, $page) {
-                        if ($page > 1) {
-                            return $q->offset($page * 8 - 8);
-                        }
-                    })
-                    ->limit(8)
-                    ->get()
-                    ->map(function ($r) use ($profile) {
-                        return $r->from_id !== $profile ? [
-                            'id' => (string) $r->from_id,
-                            'name' => $r->author->name,
-                            'username' => $r->author->username,
-                            'avatar' => $r->author->avatarUrl(),
-                            'url' => $r->author->url(),
-                            'isLocal' => (bool) ! $r->author->domain,
-                            'domain' => $r->author->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ] : [
-                            'id' => (string) $r->to_id,
-                            'name' => $r->recipient->name,
-                            'username' => $r->recipient->username,
-                            'avatar' => $r->recipient->avatarUrl(),
-                            'url' => $r->recipient->url(),
-                            'isLocal' => (bool) ! $r->recipient->domain,
-                            'domain' => $r->recipient->domain,
-                            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
-                            'lastMessage' => $r->status->caption,
-                            'messages' => [],
-                        ];
-                    });
-            }
+                    ->groupBy('from_id', 'id', 'type', 'to_id', 'status_id',
+                        'is_hidden', 'meta', 'created_at', 'read_at')
+                    ->orderBy('created_at', 'desc'),
+                default => throw new \InvalidArgumentException('Invalid action')
+            };
+
+            $dms = $query->offset($offset)
+                ->limit($limit)
+                ->get();
         }
 
-        return response()->json($dms->all());
+        $mappedDms = $dms->map(function ($r) use ($action) {
+            if ($action === 'sent') {
+                return [
+                    'id' => (string) $r->to_id,
+                    'name' => $r->recipient->name,
+                    'username' => $r->recipient->username,
+                    'avatar' => $r->recipient->avatarUrl(),
+                    'url' => $r->recipient->url(),
+                    'isLocal' => (bool) ! $r->recipient->domain,
+                    'domain' => $r->recipient->domain,
+                    'timeAgo' => $r->created_at->diffForHumans(null, true, true),
+                    'lastMessage' => $r->status->caption,
+                    'messages' => [],
+                ];
+            }
+
+            return [
+                'id' => (string) $r->from_id,
+                'name' => $r->author->name,
+                'username' => $r->author->username,
+                'avatar' => $r->author->avatarUrl(),
+                'url' => $r->author->url(),
+                'isLocal' => (bool) ! $r->author->domain,
+                'domain' => $r->author->domain,
+                'timeAgo' => $r->created_at->diffForHumans(null, true, true),
+                'lastMessage' => $r->status->caption,
+                'messages' => [],
+            ];
+        });
+
+        return response()->json($mappedDms->values());
     }
 
     public function create(Request $request)
@@ -411,90 +246,97 @@ class DirectMessageController extends Controller
             'max_id' => 'sometimes|integer',
             'min_id' => 'sometimes|integer',
         ]);
+
         $user = $request->user();
-        abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
+        abort_if(
+            $user->has_roles && ! UserRoleService::can('can-direct-message', $user->id),
+            403,
+            'Invalid permissions for this action'
+        );
 
         $uid = $user->profile_id;
         $pid = $request->input('pid');
         $max_id = $request->input('max_id');
         $min_id = $request->input('min_id');
 
-        $r = Profile::findOrFail($pid);
+        $profile = Profile::findOrFail($pid);
+
+        $query = DirectMessage::select(
+            'id',
+            'is_hidden',
+            'from_id',
+            'to_id',
+            'type',
+            'status_id',
+            'meta',
+            'created_at',
+            'read_at'
+        )->with(['status' => function ($q) {
+            $q->select('id', 'caption', 'profile_id');
+        }])->where(function ($q) use ($pid, $uid) {
+            $q->where(function ($query) use ($pid, $uid) {
+                $query->where('from_id', $pid)
+                    ->where('to_id', $uid)
+                    ->where('is_hidden', false);
+            })->orWhere(function ($query) use ($pid, $uid) {
+                $query->where('from_id', $uid)
+                    ->where('to_id', $pid);
+            });
+        });
 
         if ($min_id) {
-            $res = DirectMessage::select('*')
-                ->where('id', '>', $min_id)
-                ->where(function ($query) use ($pid, $uid) {
-                    $query->where('from_id', $pid)->where('to_id', $uid);
-                })->orWhere(function ($query) use ($pid, $uid) {
-                    $query->where('from_id', $uid)->where('to_id', $pid);
-                })
+            $res = $query->where('id', '>', $min_id)
                 ->orderBy('id', 'asc')
                 ->take(8)
                 ->get()
                 ->reverse();
         } elseif ($max_id) {
-            $res = DirectMessage::select('*')
-                ->where('id', '<', $max_id)
-                ->where(function ($query) use ($pid, $uid) {
-                    $query->where('from_id', $pid)->where('to_id', $uid);
-                })->orWhere(function ($query) use ($pid, $uid) {
-                    $query->where('from_id', $uid)->where('to_id', $pid);
-                })
+            $res = $query->where('id', '<', $max_id)
                 ->orderBy('id', 'desc')
                 ->take(8)
                 ->get();
         } else {
-            $res = DirectMessage::where(function ($query) use ($pid, $uid) {
-                $query->where('from_id', $pid)->where('to_id', $uid);
-            })->orWhere(function ($query) use ($pid, $uid) {
-                $query->where('from_id', $uid)->where('to_id', $pid);
-            })
-                ->orderBy('id', 'desc')
+            $res = $query->orderBy('id', 'desc')
                 ->take(8)
                 ->get();
         }
 
-        $res = $res->filter(function ($s) {
-            return $s && $s->status;
-        })
-            ->map(function ($s) use ($uid) {
-                return [
-                    'id' => (string) $s->id,
-                    'hidden' => (bool) $s->is_hidden,
-                    'isAuthor' => $uid == $s->from_id,
-                    'type' => $s->type,
-                    'text' => $s->status->caption,
-                    'media' => $s->status->firstMedia() ? $s->status->firstMedia()->url() : null,
-                    'carousel' => MediaService::get($s->status_id),
-                    'created_at' => $s->created_at->format('c'),
-                    'timeAgo' => $s->created_at->diffForHumans(null, null, true),
-                    'seen' => $s->read_at != null,
-                    'reportId' => (string) $s->status_id,
-                    'meta' => json_decode($s->meta, true),
-                ];
-            })
-            ->values();
+        $messages = $res->filter(function ($message) {
+            return $message && $message->status;
+        })->map(function ($message) use ($uid) {
+            return [
+                'id' => (string) $message->id,
+                'hidden' => (bool) $message->is_hidden,
+                'isAuthor' => $uid == $message->from_id,
+                'type' => $message->type,
+                'text' => $message->status->caption,
+                'media' => $message->status->firstMedia() ? $message->status->firstMedia()->url() : null,
+                'carousel' => MediaService::get($message->status_id),
+                'created_at' => $message->created_at->format('c'),
+                'timeAgo' => $message->created_at->diffForHumans(null, null, true),
+                'seen' => $message->read_at != null,
+                'reportId' => (string) $message->status_id,
+                'meta' => is_string($message->meta) ? json_decode($message->meta, true) : $message->meta,
+            ];
+        })->values();
 
         $filters = UserFilterService::mutes($uid);
 
-        $w = [
-            'id' => (string) $r->id,
-            'name' => $r->name,
-            'username' => $r->username,
-            'avatar' => $r->avatarUrl(),
-            'url' => $r->url(),
-            'muted' => in_array($r->id, $filters),
-            'isLocal' => (bool) ! $r->domain,
-            'domain' => $r->domain,
-            'created_at' => $r->created_at->format('c'),
-            'updated_at' => $r->updated_at->format('c'),
-            'timeAgo' => $r->created_at->diffForHumans(null, true, true),
+        return response()->json([
+            'id' => (string) $profile->id,
+            'name' => $profile->name,
+            'username' => $profile->username,
+            'avatar' => $profile->avatarUrl(),
+            'url' => $profile->url(),
+            'muted' => in_array($profile->id, $filters),
+            'isLocal' => (bool) ! $profile->domain,
+            'domain' => $profile->domain,
+            'created_at' => $profile->created_at->format('c'),
+            'updated_at' => $profile->updated_at->format('c'),
+            'timeAgo' => $profile->created_at->diffForHumans(null, true, true),
             'lastMessage' => '',
-            'messages' => $res,
-        ];
-
-        return response()->json($w, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+            'messages' => $messages,
+        ], 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
     }
 
     public function delete(Request $request)