From 679ef677b7547d650f2f78341e942ae925db6179 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Sat, 8 Jun 2024 06:21:11 -0600 Subject: [PATCH] Update ApiController, add pe support to like/unlike endpoints --- app/Http/Controllers/Api/ApiV1Controller.php | 42 +- app/Http/Controllers/PublicApiController.php | 877 +++++++++---------- 2 files changed, 466 insertions(+), 453 deletions(-) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index 8c6b18202..193d3456b 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -1352,7 +1352,8 @@ class ApiV1Controller extends Controller $user = $request->user(); abort_if($user->has_roles && ! UserRoleService::can('can-like', $user->id), 403, 'Invalid permissions for this action'); - $status = StatusService::getMastodon($id, false); + $napi = $request->has(self::PF_API_ENTITY_KEY); + $status = $napi ? StatusService::get($id, false) : StatusService::getMastodon($id, false); abort_unless($status, 404); @@ -1420,34 +1421,47 @@ class ApiV1Controller extends Controller $user = $request->user(); abort_if($user->has_roles && ! UserRoleService::can('can-like', $user->id), 403, 'Invalid permissions for this action'); + $napi = $request->has(self::PF_API_ENTITY_KEY); + $status = $napi ? StatusService::get($id, false) : StatusService::getMastodon($id, false); + + abort_unless($status && isset($status['account']), 404); + + if ($status && isset($status['account'], $status['account']['acct']) && strpos($status['account']['acct'], '@') != -1) { + $domain = parse_url($status['account']['url'], PHP_URL_HOST); + abort_if(in_array($domain, InstanceService::getBannedDomains()), 404); + } + + $spid = $status['account']['id']; + AccountService::setLastActive($user->id); - $status = Status::findOrFail($id); - - if (intval($status->profile_id) !== intval($user->profile_id)) { - if ($status->scope == 'private') { - abort_if(! $status->profile->followedBy($user->profile), 403); + if (intval($spid) !== intval($user->profile_id)) { + if ($status['visibility'] == 'private') { + abort_if(! FollowerService::follows($user->profile_id, $spid), 403); } else { - abort_if(! in_array($status->scope, ['public', 'unlisted']), 403); + abort_if(! in_array($status['visibility'], ['public', 'unlisted']), 403); } } $like = Like::whereProfileId($user->profile_id) - ->whereStatusId($status->id) + ->whereStatusId($status['id']) ->first(); if ($like) { $like->forceDelete(); - $status->likes_count = $status->likes_count > 1 ? $status->likes_count - 1 : 0; - $status->save(); + $ogStatus = Status::find($status['id']); + if ($ogStatus) { + $ogStatus->likes_count = $ogStatus->likes_count > 1 ? $ogStatus->likes_count - 1 : 0; + $ogStatus->save(); + } } - StatusService::del($status->id); + StatusService::del($status['id']); - $res = StatusService::getMastodon($status->id, false); - $res['favourited'] = false; + $status['favourited'] = false; + $status['favourites_count'] = isset($ogStatus) ? $ogStatus->likes_count : $status['favourites_count'] - 1; - return $this->json($res); + return $this->json($status); } /** diff --git a/app/Http/Controllers/PublicApiController.php b/app/Http/Controllers/PublicApiController.php index 78008eda4..b0e2efc40 100644 --- a/app/Http/Controllers/PublicApiController.php +++ b/app/Http/Controllers/PublicApiController.php @@ -2,47 +2,28 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; -use App\{ - Hashtag, - Follower, - Like, - Media, - Notification, - Profile, - StatusHashtag, - Status, - StatusView, - UserFilter -}; -use Auth, Cache, DB; -use Illuminate\Support\Facades\Redis; -use Carbon\Carbon; -use League\Fractal; -use App\Transformer\Api\{ - AccountTransformer, - RelationshipTransformer, - StatusTransformer, - StatusStatelessTransformer -}; -use App\Services\{ - AccountService, - BookmarkService, - FollowerService, - LikeService, - PublicTimelineService, - ProfileService, - NetworkTimelineService, - ReblogService, - RelationshipService, - StatusService, - SnowflakeService, - UserFilterService -}; -use App\Jobs\StatusPipeline\NewStatusPipeline; -use League\Fractal\Serializer\ArraySerializer; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use App\Follower; +use App\Profile; +use App\Services\AccountService; +use App\Services\BookmarkService; +use App\Services\FollowerService; use App\Services\InstanceService; +use App\Services\LikeService; +use App\Services\NetworkTimelineService; +use App\Services\PublicTimelineService; +use App\Services\ReblogService; +use App\Services\RelationshipService; +use App\Services\SnowflakeService; +use App\Services\StatusService; +use App\Services\UserFilterService; +use App\Status; +use App\Transformer\Api\StatusStatelessTransformer; +use Auth; +use Cache; +use Illuminate\Http\Request; +use League\Fractal; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Serializer\ArraySerializer; class PublicApiController extends Controller { @@ -56,7 +37,7 @@ class PublicApiController extends Controller protected function getUserData($user) { - if(!$user) { + if (! $user) { return []; } else { return AccountService::get($user->profile_id); @@ -65,22 +46,22 @@ class PublicApiController extends Controller public function getStatus(Request $request, $id) { - abort_if(!$request->user(), 403); - $status = StatusService::get($id, false); - abort_if(!$status, 404); - if(in_array($status['visibility'], ['public', 'unlisted'])) { - return $status; - } - $pid = $request->user()->profile_id; - if($status['account']['id'] == $pid) { - return $status; - } - if($status['visibility'] == 'private') { - if(FollowerService::follows($pid, $status['account']['id'])) { - return $status; - } - } - abort(404); + abort_if(! $request->user(), 403); + $status = StatusService::get($id, false); + abort_if(! $status, 404); + if (in_array($status['visibility'], ['public', 'unlisted'])) { + return $status; + } + $pid = $request->user()->profile_id; + if ($status['account']['id'] == $pid) { + return $status; + } + if ($status['visibility'] == 'private') { + if (FollowerService::follows($pid, $status['account']['id'])) { + return $status; + } + } + abort(404); } public function status(Request $request, $username, int $postid) @@ -88,9 +69,9 @@ class PublicApiController extends Controller $profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail(); $status = Status::whereProfileId($profile->id)->findOrFail($postid); $this->scopeCheck($profile, $status); - if(!$request->user()) { + if (! $request->user()) { $cached = StatusService::get($status->id, false); - abort_if(!in_array($cached['visibility'], ['public', 'unlisted']), 403); + abort_if(! in_array($cached['visibility'], ['public', 'unlisted']), 403); $res = ['status' => $cached]; } else { $item = new Fractal\Resource\Item($status, new StatusStatelessTransformer()); @@ -107,7 +88,7 @@ class PublicApiController extends Controller $profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail(); $status = Status::whereProfileId($profile->id)->findOrFail($postid); $this->scopeCheck($profile, $status); - if(!Auth::check()) { + if (! Auth::check()) { $res = [ 'user' => [], 'likes' => [], @@ -118,6 +99,7 @@ class PublicApiController extends Controller 'bookmarked' => false, ], ]; + return response()->json($res); } $res = [ @@ -130,15 +112,16 @@ class PublicApiController extends Controller 'bookmarked' => (bool) $status->bookmarked(), ], ]; + return response()->json($res); } public function statusComments(Request $request, $username, int $postId) { $this->validate($request, [ - 'min_id' => 'nullable|integer|min:1', - 'max_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX, - 'limit' => 'nullable|integer|min:5|max:50' + 'min_id' => 'nullable|integer|min:1', + 'max_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX, + 'limit' => 'nullable|integer|min:5|max:50', ]); $limit = $request->limit ?? 10; @@ -146,50 +129,51 @@ class PublicApiController extends Controller $status = Status::whereProfileId($profile->id)->whereCommentsDisabled(false)->findOrFail($postId); $this->scopeCheck($profile, $status); - if(Auth::check()) { + if (Auth::check()) { $p = Auth::user()->profile; - $scope = $p->id == $status->profile_id || FollowerService::follows($p->id, $profile->id) ? ['public', 'private', 'unlisted'] : ['public','unlisted']; + $scope = $p->id == $status->profile_id || FollowerService::follows($p->id, $profile->id) ? ['public', 'private', 'unlisted'] : ['public', 'unlisted']; } else { $scope = ['public', 'unlisted']; } - if($request->filled('min_id') || $request->filled('max_id')) { - if($request->filled('min_id')) { + if ($request->filled('min_id') || $request->filled('max_id')) { + if ($request->filled('min_id')) { $replies = $status->comments() - ->whereNull('reblog_of_id') - ->whereIn('scope', $scope) - ->select('id', 'caption', 'local', 'visibility', 'scope', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at') - ->where('id', '>=', $request->min_id) - ->orderBy('id', 'desc') - ->paginate($limit); + ->whereNull('reblog_of_id') + ->whereIn('scope', $scope) + ->select('id', 'caption', 'local', 'visibility', 'scope', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at') + ->where('id', '>=', $request->min_id) + ->orderBy('id', 'desc') + ->paginate($limit); } - if($request->filled('max_id')) { + if ($request->filled('max_id')) { $replies = $status->comments() - ->whereNull('reblog_of_id') - ->whereIn('scope', $scope) - ->select('id', 'caption', 'local', 'visibility', 'scope', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at') - ->where('id', '<=', $request->max_id) - ->orderBy('id', 'desc') - ->paginate($limit); + ->whereNull('reblog_of_id') + ->whereIn('scope', $scope) + ->select('id', 'caption', 'local', 'visibility', 'scope', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at') + ->where('id', '<=', $request->max_id) + ->orderBy('id', 'desc') + ->paginate($limit); } } else { $replies = Status::whereInReplyToId($status->id) - ->whereNull('reblog_of_id') - ->whereIn('scope', $scope) - ->select('id', 'caption', 'local', 'visibility', 'scope', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at') - ->orderBy('id', 'desc') - ->paginate($limit); + ->whereNull('reblog_of_id') + ->whereIn('scope', $scope) + ->select('id', 'caption', 'local', 'visibility', 'scope', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at') + ->orderBy('id', 'desc') + ->paginate($limit); } $resource = new Fractal\Resource\Collection($replies, new StatusStatelessTransformer(), 'data'); $resource->setPaginator(new IlluminatePaginatorAdapter($replies)); $res = $this->fractal->createData($resource)->toArray(); + return response()->json($res, 200, [], JSON_PRETTY_PRINT); } protected function scopeCheck(Profile $profile, Status $status) { - if($profile->is_private == true && Auth::check() == false) { + if ($profile->is_private == true && Auth::check() == false) { abort(404); } @@ -199,11 +183,11 @@ class PublicApiController extends Controller break; case 'private': $user = Auth::check() ? Auth::user() : false; - if(!$user) { + if (! $user) { abort(403); } else { - $follows = $profile->followedBy($user->profile); - if($follows == false && $profile->id !== $user->profile->id && $user->is_admin == false) { + $follows = FollowerService::follows($profile->id, $user->profile_id); + if ($follows == false && $profile->id !== $user->profile_id && $user->is_admin == false) { abort(404); } } @@ -225,14 +209,14 @@ class PublicApiController extends Controller public function publicTimelineApi(Request $request) { - $this->validate($request,[ - 'page' => 'nullable|integer|max:40', - 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'limit' => 'nullable|integer|max:30' + $this->validate($request, [ + 'page' => 'nullable|integer|max:40', + 'min_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'max_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'limit' => 'nullable|integer|max:30', ]); - if(!$request->user()) { + if (! $request->user()) { return response('', 403); } @@ -244,123 +228,126 @@ class PublicApiController extends Controller $filtered = $user ? UserFilterService::filters($user->profile_id) : []; $hideNsfw = config('instance.hide_nsfw_on_public_feeds'); - if(config('exp.cached_public_timeline') == false) { - if($min || $max) { + if (config('exp.cached_public_timeline') == false) { + if ($min || $max) { $dir = $min ? '>' : '<'; $id = $min ?? $max; $timeline = Status::select( - 'id', - 'profile_id', - 'type', - 'scope', - 'local' - ) - ->where('id', $dir, $id) - ->whereNull(['in_reply_to_id', 'reblog_of_id']) - ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) - ->whereLocal(true) - ->when($hideNsfw, function($q, $hideNsfw) { - return $q->where('is_nsfw', false); - }) - ->whereScope('public') - ->orderBy('id', 'desc') - ->limit($limit) - ->get() - ->map(function($s) use ($user) { - $status = StatusService::getFull($s->id, $user->profile_id); - if(!$status) { - return false; - } - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); - return $status; - }) - ->filter(function($s) use($filtered) { - return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; - }) - ->values(); + 'id', + 'profile_id', + 'type', + 'scope', + 'local' + ) + ->where('id', $dir, $id) + ->whereNull(['in_reply_to_id', 'reblog_of_id']) + ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->whereLocal(true) + ->when($hideNsfw, function ($q, $hideNsfw) { + return $q->where('is_nsfw', false); + }) + ->whereScope('public') + ->orderBy('id', 'desc') + ->limit($limit) + ->get() + ->map(function ($s) use ($user) { + $status = StatusService::getFull($s->id, $user->profile_id); + if (! $status) { + return false; + } + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); + + return $status; + }) + ->filter(function ($s) use ($filtered) { + return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; + }) + ->values(); $res = $timeline->toArray(); } else { $timeline = Status::select( - 'id', - 'uri', - 'caption', - 'rendered', - 'profile_id', - 'type', - 'in_reply_to_id', - 'reblog_of_id', - 'is_nsfw', - 'scope', - 'local', - 'reply_count', - 'comments_disabled', - 'created_at', - 'place_id', - 'likes_count', - 'reblogs_count', - 'updated_at' - ) - ->whereNull(['in_reply_to_id', 'reblog_of_id']) - ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) - ->whereLocal(true) - ->when($hideNsfw, function($q, $hideNsfw) { - return $q->where('is_nsfw', false); - }) - ->whereScope('public') - ->orderBy('id', 'desc') - ->limit($limit) - ->get() - ->map(function($s) use ($user) { - $status = StatusService::getFull($s->id, $user->profile_id); - if(!$status) { - return false; - } - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); - return $status; - }) - ->filter(function($s) use($filtered) { - return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; - }) - ->values(); + 'id', + 'uri', + 'caption', + 'rendered', + 'profile_id', + 'type', + 'in_reply_to_id', + 'reblog_of_id', + 'is_nsfw', + 'scope', + 'local', + 'reply_count', + 'comments_disabled', + 'created_at', + 'place_id', + 'likes_count', + 'reblogs_count', + 'updated_at' + ) + ->whereNull(['in_reply_to_id', 'reblog_of_id']) + ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->whereLocal(true) + ->when($hideNsfw, function ($q, $hideNsfw) { + return $q->where('is_nsfw', false); + }) + ->whereScope('public') + ->orderBy('id', 'desc') + ->limit($limit) + ->get() + ->map(function ($s) use ($user) { + $status = StatusService::getFull($s->id, $user->profile_id); + if (! $status) { + return false; + } + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); + + return $status; + }) + ->filter(function ($s) use ($filtered) { + return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; + }) + ->values(); $res = $timeline->toArray(); } } else { - Cache::remember('api:v1:timelines:public:cache_check', 10368000, function() { - if(PublicTimelineService::count() == 0) { + Cache::remember('api:v1:timelines:public:cache_check', 10368000, function () { + if (PublicTimelineService::count() == 0) { PublicTimelineService::warmCache(true, 400); } }); if ($max) { $feed = PublicTimelineService::getRankedMaxId($max, $limit); - } else if ($min) { + } elseif ($min) { $feed = PublicTimelineService::getRankedMinId($min, $limit); } else { $feed = PublicTimelineService::get(0, $limit); } $res = collect($feed) - ->take($limit) - ->map(function($k) use($user) { - $status = StatusService::get($k); - if($status && isset($status['account']) && $user) { - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $k); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $k); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $k); - $status['relationship'] = RelationshipService::get($user->profile_id, $status['account']['id']); - } - return $status; - }) - ->filter(function($s) use($filtered) { - return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; - }) - ->values() - ->toArray(); + ->take($limit) + ->map(function ($k) use ($user) { + $status = StatusService::get($k); + if ($status && isset($status['account']) && $user) { + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $k); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $k); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $k); + $status['relationship'] = RelationshipService::get($user->profile_id, $status['account']['id']); + } + + return $status; + }) + ->filter(function ($s) use ($filtered) { + return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; + }) + ->values() + ->toArray(); } return response()->json($res); @@ -368,17 +355,17 @@ class PublicApiController extends Controller public function homeTimelineApi(Request $request) { - if(!$request->user()) { + if (! $request->user()) { return response('', 403); } - $this->validate($request,[ - 'page' => 'nullable|integer|max:40', - 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'limit' => 'nullable|integer|max:40', - 'recent_feed' => 'nullable', - 'recent_min' => 'nullable|integer' + $this->validate($request, [ + 'page' => 'nullable|integer|max:40', + 'min_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'max_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'limit' => 'nullable|integer|max:40', + 'recent_feed' => 'nullable', + 'recent_min' => 'nullable|integer', ]); $recentFeed = $request->input('recent_feed') == 'true'; @@ -390,7 +377,7 @@ class PublicApiController extends Controller $user = $request->user(); $key = 'user:last_active_at:id:'.$user->id; - if(Cache::get($key) == null) { + if (Cache::get($key) == null) { $user->last_active_at = now(); $user->save(); Cache::put($key, true, 43200); @@ -398,8 +385,9 @@ class PublicApiController extends Controller $pid = $user->profile_id; - $following = Cache::remember('profile:following:'.$pid, 1209600, function() use($pid) { + $following = Cache::remember('profile:following:'.$pid, 1209600, function () use ($pid) { $following = Follower::whereProfileId($pid)->pluck('following_id'); + return $following->push($pid)->toArray(); }); @@ -409,123 +397,126 @@ class PublicApiController extends Controller $textOnlyReplies = false; - if($min || $max) { + if ($min || $max) { $dir = $min ? '>' : '<'; $id = $min ?? $max; - return Status::select( - 'id', - 'uri', - 'caption', - 'rendered', - 'profile_id', - 'type', - 'in_reply_to_id', - 'reblog_of_id', - 'is_nsfw', - 'scope', - 'local', - 'reply_count', - 'comments_disabled', - 'place_id', - 'likes_count', - 'reblogs_count', - 'created_at', - 'updated_at' - ) - ->whereIn('type', $types) - ->when(!$textOnlyReplies, function($q, $textOnlyReplies) { - return $q->whereNull('in_reply_to_id'); - }) - ->where('id', $dir, $id) - ->whereIn('profile_id', $following) - ->whereIn('visibility',['public', 'unlisted', 'private']) - ->orderBy('created_at', 'desc') - ->limit($limit) - ->get() - ->map(function($s) use ($user) { - try { - $status = StatusService::get($s->id, false); - if(!$status) { - return false; - } - } catch(\Exception $e) { - return false; - } - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); - return $status; - }) - ->filter(function($s) use($filtered) { - return $s && in_array($s['account']['id'], $filtered) == false; - }) - ->values() - ->toArray(); + + return Status::select( + 'id', + 'uri', + 'caption', + 'rendered', + 'profile_id', + 'type', + 'in_reply_to_id', + 'reblog_of_id', + 'is_nsfw', + 'scope', + 'local', + 'reply_count', + 'comments_disabled', + 'place_id', + 'likes_count', + 'reblogs_count', + 'created_at', + 'updated_at' + ) + ->whereIn('type', $types) + ->when(! $textOnlyReplies, function ($q, $textOnlyReplies) { + return $q->whereNull('in_reply_to_id'); + }) + ->where('id', $dir, $id) + ->whereIn('profile_id', $following) + ->whereIn('visibility', ['public', 'unlisted', 'private']) + ->orderBy('created_at', 'desc') + ->limit($limit) + ->get() + ->map(function ($s) use ($user) { + try { + $status = StatusService::get($s->id, false); + if (! $status) { + return false; + } + } catch (\Exception $e) { + return false; + } + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); + + return $status; + }) + ->filter(function ($s) use ($filtered) { + return $s && in_array($s['account']['id'], $filtered) == false; + }) + ->values() + ->toArray(); } else { return Status::select( - 'id', - 'uri', - 'caption', - 'rendered', - 'profile_id', - 'type', - 'in_reply_to_id', - 'reblog_of_id', - 'is_nsfw', - 'scope', - 'local', - 'reply_count', - 'comments_disabled', - 'place_id', - 'likes_count', - 'reblogs_count', - 'created_at', - 'updated_at' - ) - ->whereIn('type', $types) - ->when(!$textOnlyReplies, function($q, $textOnlyReplies) { - return $q->whereNull('in_reply_to_id'); - }) - ->whereIn('profile_id', $following) - ->whereIn('visibility',['public', 'unlisted', 'private']) - ->orderBy('created_at', 'desc') - ->limit($limit) - ->get() - ->map(function($s) use ($user) { - try { - $status = StatusService::get($s->id, false); - if(!$status) { - return false; - } - } catch(\Exception $e) { - return false; - } - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); - return $status; - }) - ->filter(function($s) use($filtered) { - return $s && in_array($s['account']['id'], $filtered) == false; - }) - ->values() - ->toArray(); + 'id', + 'uri', + 'caption', + 'rendered', + 'profile_id', + 'type', + 'in_reply_to_id', + 'reblog_of_id', + 'is_nsfw', + 'scope', + 'local', + 'reply_count', + 'comments_disabled', + 'place_id', + 'likes_count', + 'reblogs_count', + 'created_at', + 'updated_at' + ) + ->whereIn('type', $types) + ->when(! $textOnlyReplies, function ($q, $textOnlyReplies) { + return $q->whereNull('in_reply_to_id'); + }) + ->whereIn('profile_id', $following) + ->whereIn('visibility', ['public', 'unlisted', 'private']) + ->orderBy('created_at', 'desc') + ->limit($limit) + ->get() + ->map(function ($s) use ($user) { + try { + $status = StatusService::get($s->id, false); + if (! $status) { + return false; + } + } catch (\Exception $e) { + return false; + } + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); + + return $status; + }) + ->filter(function ($s) use ($filtered) { + return $s && in_array($s['account']['id'], $filtered) == false; + }) + ->values() + ->toArray(); } } public function networkTimelineApi(Request $request) { - if(!$request->user()) { + if (! $request->user()) { return response('', 403); } abort_if(config('federation.network_timeline') == false, 404); - $this->validate($request,[ - 'page' => 'nullable|integer|max:40', - 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'limit' => 'nullable|integer|max:30' + $this->validate($request, [ + 'page' => 'nullable|integer|max:40', + 'min_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'max_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'limit' => 'nullable|integer|max:30', ]); $page = $request->input('page'); @@ -538,99 +529,102 @@ class PublicApiController extends Controller $filtered = $user ? UserFilterService::filters($user->profile_id) : []; $hideNsfw = config('instance.hide_nsfw_on_public_feeds'); - if(config('instance.timeline.network.cached') == false) { - if($min || $max) { - $dir = $min ? '>' : '<'; - $id = $min ?? $max; - $timeline = Status::select( - 'id', - 'uri', - 'type', - 'scope', - 'created_at', - ) - ->where('id', $dir, $id) - ->when($hideNsfw, function($q, $hideNsfw) { - return $q->where('is_nsfw', false); - }) - ->whereNull(['in_reply_to_id', 'reblog_of_id']) - ->whereNotIn('profile_id', $filtered) - ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) - ->whereNotNull('uri') - ->whereScope('public') - ->where('id', '>', $amin) - ->orderBy('created_at', 'desc') - ->limit($limit) - ->get() - ->map(function($s) use ($user) { - $status = StatusService::get($s->id); - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); - return $status; - }); - $res = $timeline->toArray(); - } else { - $timeline = Status::select( - 'id', - 'uri', - 'type', - 'scope', - 'created_at', - ) - ->whereNull(['in_reply_to_id', 'reblog_of_id']) - ->whereNotIn('profile_id', $filtered) - ->when($hideNsfw, function($q, $hideNsfw) { - return $q->where('is_nsfw', false); - }) - ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) - ->whereNotNull('uri') - ->whereScope('public') - ->where('id', '>', $amin) - ->orderBy('created_at', 'desc') - ->limit($limit) - ->get() - ->map(function($s) use ($user) { - $status = StatusService::get($s->id); - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); - return $status; - }); - $res = $timeline->toArray(); - } - } else { - Cache::remember('api:v1:timelines:network:cache_check', 10368000, function() { - if(NetworkTimelineService::count() == 0) { + if (config('instance.timeline.network.cached') == false) { + if ($min || $max) { + $dir = $min ? '>' : '<'; + $id = $min ?? $max; + $timeline = Status::select( + 'id', + 'uri', + 'type', + 'scope', + 'created_at', + ) + ->where('id', $dir, $id) + ->when($hideNsfw, function ($q, $hideNsfw) { + return $q->where('is_nsfw', false); + }) + ->whereNull(['in_reply_to_id', 'reblog_of_id']) + ->whereNotIn('profile_id', $filtered) + ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->whereNotNull('uri') + ->whereScope('public') + ->where('id', '>', $amin) + ->orderBy('created_at', 'desc') + ->limit($limit) + ->get() + ->map(function ($s) use ($user) { + $status = StatusService::get($s->id); + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); + + return $status; + }); + $res = $timeline->toArray(); + } else { + $timeline = Status::select( + 'id', + 'uri', + 'type', + 'scope', + 'created_at', + ) + ->whereNull(['in_reply_to_id', 'reblog_of_id']) + ->whereNotIn('profile_id', $filtered) + ->when($hideNsfw, function ($q, $hideNsfw) { + return $q->where('is_nsfw', false); + }) + ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->whereNotNull('uri') + ->whereScope('public') + ->where('id', '>', $amin) + ->orderBy('created_at', 'desc') + ->limit($limit) + ->get() + ->map(function ($s) use ($user) { + $status = StatusService::get($s->id); + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $s->id); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $s->id); + + return $status; + }); + $res = $timeline->toArray(); + } + } else { + Cache::remember('api:v1:timelines:network:cache_check', 10368000, function () { + if (NetworkTimelineService::count() == 0) { NetworkTimelineService::warmCache(true, 400); } }); if ($max) { $feed = NetworkTimelineService::getRankedMaxId($max, $limit); - } else if ($min) { + } elseif ($min) { $feed = NetworkTimelineService::getRankedMinId($min, $limit); } else { $feed = NetworkTimelineService::get(0, $limit); } $res = collect($feed) - ->take($limit) - ->map(function($k) use($user) { - $status = StatusService::get($k); - if($status && isset($status['account']) && $user) { - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $k); - $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $k); - $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $k); - $status['relationship'] = RelationshipService::get($user->profile_id, $status['account']['id']); - } - return $status; - }) - ->filter(function($s) use($filtered) { - return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; - }) - ->values() - ->toArray(); + ->take($limit) + ->map(function ($k) use ($user) { + $status = StatusService::get($k); + if ($status && isset($status['account']) && $user) { + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $k); + $status['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $k); + $status['reblogged'] = (bool) ReblogService::get($user->profile_id, $k); + $status['relationship'] = RelationshipService::get($user->profile_id, $status['account']['id']); + } + + return $status; + }) + ->filter(function ($s) use ($filtered) { + return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false; + }) + ->values() + ->toArray(); } return response()->json($res); @@ -638,23 +632,23 @@ class PublicApiController extends Controller public function relationships(Request $request) { - if(!Auth::check()) { + if (! Auth::check()) { return response()->json([]); } $pid = $request->user()->profile_id; $this->validate($request, [ - 'id' => 'required|array|min:1|max:20', - 'id.*' => 'required|integer' + 'id' => 'required|array|min:1|max:20', + 'id.*' => 'required|integer', ]); $ids = collect($request->input('id')); - $res = $ids->filter(function($v) use($pid) { + $res = $ids->filter(function ($v) use ($pid) { return $v != $pid; }) - ->map(function($id) use($pid) { - return RelationshipService::get($pid, $id); - }); + ->map(function ($id) use ($pid) { + return RelationshipService::get($pid, $id); + }); return response()->json($res); } @@ -662,10 +656,11 @@ class PublicApiController extends Controller public function account(Request $request, $id) { $res = AccountService::get($id); - if($res && isset($res['local'], $res['url']) && !$res['local']) { + if ($res && isset($res['local'], $res['url']) && ! $res['local']) { $domain = parse_url($res['url'], PHP_URL_HOST); abort_if(in_array($domain, InstanceService::getBannedDomains()), 404); } + return response()->json($res); } @@ -675,17 +670,17 @@ class PublicApiController extends Controller 'only_media' => 'nullable', 'pinned' => 'nullable', 'exclude_replies' => 'nullable', - 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'since_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, - 'limit' => 'nullable|integer|min:1|max:24' + 'max_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'since_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'min_id' => 'nullable|integer|min:0|max:'.PHP_INT_MAX, + 'limit' => 'nullable|integer|min:1|max:24', ]); $user = $request->user(); $profile = AccountService::get($id); - abort_if(!$profile, 404); + abort_if(! $profile, 404); - if($profile && isset($profile['local'], $profile['url']) && !$profile['local']) { + if ($profile && isset($profile['local'], $profile['url']) && ! $profile['local']) { $domain = parse_url($profile['url'], PHP_URL_HOST); abort_if(in_array($domain, InstanceService::getBannedDomains()), 404); } @@ -696,28 +691,30 @@ class PublicApiController extends Controller $scope = ['photo', 'photo:album', 'video', 'video:album']; $onlyMedia = $request->input('only_media', true); - if(!$min_id && !$max_id) { - $min_id = 1; + if (! $min_id && ! $max_id) { + $min_id = 1; } - if($profile['locked']) { - if(!$user) { + if ($profile['locked']) { + if (! $user) { return response()->json([]); } $pid = $user->profile_id; - $following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), function() use($pid) { + $following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), function () use ($pid) { $following = Follower::whereProfileId($pid)->pluck('following_id'); + return $following->push($pid)->toArray(); }); - $visibility = true == in_array($profile['id'], $following) ? ['public', 'unlisted', 'private'] : []; + $visibility = in_array($profile['id'], $following) == true ? ['public', 'unlisted', 'private'] : []; } else { - if($user) { + if ($user) { $pid = $user->profile_id; - $following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), function() use($pid) { + $following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), function () use ($pid) { $following = Follower::whereProfileId($pid)->pluck('following_id'); + return $following->push($pid)->toArray(); }); - $visibility = true == in_array($profile['id'], $following) ? ['public', 'unlisted', 'private'] : ['public', 'unlisted']; + $visibility = in_array($profile['id'], $following) == true ? ['public', 'unlisted', 'private'] : ['public', 'unlisted']; } else { $visibility = ['public', 'unlisted']; } @@ -725,38 +722,40 @@ class PublicApiController extends Controller $dir = $min_id ? '>' : '<'; $id = $min_id ?? $max_id; $res = Status::whereProfileId($profile['id']) - ->whereNull('in_reply_to_id') - ->whereNull('reblog_of_id') - ->whereIn('type', $scope) - ->where('id', $dir, $id) - ->whereIn('scope', $visibility) - ->limit($limit) - ->orderByDesc('id') - ->get() - ->map(function($s) use($user) { - try { - $status = StatusService::get($s->id, false); - } catch (\Exception $e) { - $status = false; - } - if($user && $status) { - $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); - } - return $status; - }) - ->filter(function($s) use($onlyMedia) { - if($onlyMedia) { - if( - !isset($s['media_attachments']) || - !is_array($s['media_attachments']) || - empty($s['media_attachments']) - ) { - return false; - } - } - return $s; - }) - ->values(); + ->whereNull('in_reply_to_id') + ->whereNull('reblog_of_id') + ->whereIn('type', $scope) + ->where('id', $dir, $id) + ->whereIn('scope', $visibility) + ->limit($limit) + ->orderByDesc('id') + ->get() + ->map(function ($s) use ($user) { + try { + $status = StatusService::get($s->id, false); + } catch (\Exception $e) { + $status = false; + } + if ($user && $status) { + $status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id); + } + + return $status; + }) + ->filter(function ($s) use ($onlyMedia) { + if ($onlyMedia) { + if ( + ! isset($s['media_attachments']) || + ! is_array($s['media_attachments']) || + empty($s['media_attachments']) + ) { + return false; + } + } + + return $s; + }) + ->values(); return response()->json($res); }