Merge pull request #5720 from pixelfed/staging

Update DirectMessageController, fix performance issue
pull/5729/head
daniel 4 months ago committed by GitHub
commit 2f2a361804
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

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

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

Loading…
Cancel
Save