pixelfed/app/Http/Controllers/PublicApiController.php

901 lines
35 KiB
PHP
Raw Normal View History

2018-11-09 05:23:36 +00:00
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\{
Hashtag,
2018-11-29 08:32:27 +00:00
Follower,
2018-11-09 05:23:36 +00:00
Like,
Media,
Notification,
Profile,
StatusHashtag,
Status,
StatusView,
2018-12-09 23:03:53 +00:00
UserFilter
2018-11-09 05:23:36 +00:00
};
use Auth, Cache, DB;
use Illuminate\Support\Facades\Redis;
2018-11-09 05:23:36 +00:00
use Carbon\Carbon;
use League\Fractal;
use App\Transformer\Api\{
AccountTransformer,
2019-02-10 20:42:26 +00:00
RelationshipTransformer,
StatusTransformer,
StatusStatelessTransformer
2018-11-09 05:23:36 +00:00
};
use App\Services\{
AccountService,
BookmarkService,
2021-12-05 03:41:46 +00:00
FollowerService,
LikeService,
PublicTimelineService,
ProfileService,
2022-06-09 10:15:23 +00:00
NetworkTimelineService,
ReblogService,
RelationshipService,
StatusService,
SnowflakeService,
UserFilterService
};
2018-11-09 05:23:36 +00:00
use App\Jobs\StatusPipeline\NewStatusPipeline;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
class PublicApiController extends Controller
{
protected $fractal;
public function __construct()
{
$this->fractal = new Fractal\Manager();
$this->fractal->setSerializer(new ArraySerializer());
}
protected function getUserData($user)
2018-11-09 05:23:36 +00:00
{
if(!$user) {
return [];
} else {
return AccountService::get($user->profile_id);
}
2018-11-09 05:23:36 +00:00
}
2018-12-02 04:01:15 +00:00
protected function getLikes($status)
{
if(false == Auth::check()) {
return [];
} else {
$profile = Auth::user()->profile;
2018-12-24 04:50:35 +00:00
if($profile->status) {
return [];
}
2018-12-02 04:01:15 +00:00
$likes = $status->likedBy()->orderBy('created_at','desc')->paginate(10);
$collection = new Fractal\Resource\Collection($likes, new AccountTransformer());
return $this->fractal->createData($collection)->toArray();
}
}
protected function getShares($status)
{
if(false == Auth::check()) {
return [];
} else {
$profile = Auth::user()->profile;
2018-12-25 06:52:42 +00:00
if($profile->status) {
return [];
}
2018-12-02 04:01:15 +00:00
$shares = $status->sharedBy()->orderBy('created_at','desc')->paginate(10);
$collection = new Fractal\Resource\Collection($shares, new AccountTransformer());
return $this->fractal->createData($collection)->toArray();
}
}
2022-01-03 06:46:17 +00:00
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);
}
2018-11-09 05:23:36 +00:00
public function status(Request $request, $username, int $postid)
{
2018-12-24 04:50:35 +00:00
$profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
$status = Status::whereProfileId($profile->id)->findOrFail($postid);
2018-11-29 08:32:27 +00:00
$this->scopeCheck($profile, $status);
2021-08-05 02:29:21 +00:00
if(!$request->user()) {
$cached = StatusService::get($status->id, false);
abort_if(!in_array($cached['visibility'], ['public', 'unlisted']), 403);
$res = ['status' => $cached];
2021-08-05 02:29:21 +00:00
} else {
$item = new Fractal\Resource\Item($status, new StatusStatelessTransformer());
$res = [
'status' => $this->fractal->createData($item)->toArray(),
];
}
2021-08-05 02:29:21 +00:00
return response()->json($res);
}
public function statusState(Request $request, $username, int $postid)
{
$profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
$status = Status::whereProfileId($profile->id)->findOrFail($postid);
$this->scopeCheck($profile, $status);
if(!Auth::check()) {
$res = [
'user' => [],
'likes' => [],
'shares' => [],
'reactions' => [
'liked' => false,
'shared' => false,
'bookmarked' => false,
],
];
return response()->json($res);
}
$res = [
'user' => $this->getUserData($request->user()),
'likes' => [],
'shares' => [],
2018-11-17 19:58:58 +00:00
'reactions' => [
'liked' => (bool) $status->liked(),
'shared' => (bool) $status->shared(),
'bookmarked' => (bool) $status->bookmarked(),
2018-11-17 19:58:58 +00:00
],
2018-11-09 05:23:36 +00:00
];
return response()->json($res);
2018-11-09 05:23:36 +00:00
}
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'
]);
2019-04-14 03:25:34 +00:00
2018-11-09 05:23:36 +00:00
$limit = $request->limit ?? 10;
2020-07-22 01:14:48 +00:00
$profile = Profile::whereNull('status')->findOrFail($username);
2019-04-01 03:30:16 +00:00
$status = Status::whereProfileId($profile->id)->whereCommentsDisabled(false)->findOrFail($postId);
2018-11-29 08:32:27 +00:00
$this->scopeCheck($profile, $status);
2019-07-21 02:09:46 +00:00
if(Auth::check()) {
$p = Auth::user()->profile;
2021-12-05 03:41:46 +00:00
$scope = $p->id == $status->profile_id || FollowerService::follows($p->id, $profile->id) ? ['public', 'private', 'unlisted'] : ['public','unlisted'];
2019-07-21 02:09:46 +00:00
} else {
$scope = ['public', 'unlisted'];
2019-07-21 02:09:46 +00:00
}
2018-11-09 05:23:36 +00:00
if($request->filled('min_id') || $request->filled('max_id')) {
if($request->filled('min_id')) {
$replies = $status->comments()
2018-12-25 06:52:42 +00:00
->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')
2018-11-09 05:23:36 +00:00
->where('id', '>=', $request->min_id)
->orderBy('id', 'desc')
->paginate($limit);
}
if($request->filled('max_id')) {
$replies = $status->comments()
2018-12-25 06:52:42 +00:00
->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')
2018-11-09 05:23:36 +00:00
->where('id', '<=', $request->max_id)
->orderBy('id', 'desc')
->paginate($limit);
}
} else {
2021-12-05 03:41:46 +00:00
$replies = Status::whereInReplyToId($status->id)
2018-12-25 06:55:11 +00:00
->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')
2018-11-09 05:23:36 +00:00
->orderBy('id', 'desc')
->paginate($limit);
}
2022-02-16 11:41:36 +00:00
$resource = new Fractal\Resource\Collection($replies, new StatusStatelessTransformer(), 'data');
2018-11-09 05:23:36 +00:00
$resource->setPaginator(new IlluminatePaginatorAdapter($replies));
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
}
2018-11-29 08:32:27 +00:00
2018-12-02 04:01:15 +00:00
public function statusLikes(Request $request, $username, $id)
{
abort_if(!$request->user(), 404);
$status = Status::findOrFail($id);
$this->scopeCheck($status->profile, $status);
$page = $request->input('page');
if($page && $page >= 3 && $request->user()->profile_id != $status->profile_id) {
return response()->json([
'data' => []
]);
}
2018-12-02 04:01:15 +00:00
$likes = $this->getLikes($status);
return response()->json([
'data' => $likes
]);
}
public function statusShares(Request $request, $username, $id)
{
abort_if(!$request->user(), 404);
2018-12-24 04:50:35 +00:00
$profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
$status = Status::whereProfileId($profile->id)->findOrFail($id);
2018-12-02 04:01:15 +00:00
$this->scopeCheck($profile, $status);
$page = $request->input('page');
if($page && $page >= 3 && $request->user()->profile_id != $status->profile_id) {
return response()->json([
'data' => []
]);
}
2018-12-02 04:01:15 +00:00
$shares = $this->getShares($status);
return response()->json([
'data' => $shares
]);
}
2018-11-29 08:32:27 +00:00
protected function scopeCheck(Profile $profile, Status $status)
{
if($profile->is_private == true && Auth::check() == false) {
abort(404);
2021-04-07 03:17:42 +00:00
}
2018-11-29 08:32:27 +00:00
switch ($status->scope) {
case 'public':
case 'unlisted':
2019-02-15 19:58:27 +00:00
break;
2018-12-08 12:43:17 +00:00
case 'private':
2018-11-29 08:32:27 +00:00
$user = Auth::check() ? Auth::user() : false;
2019-02-15 19:58:27 +00:00
if(!$user) {
abort(403);
} else {
2019-04-13 05:28:23 +00:00
$follows = $profile->followedBy($user->profile);
if($follows == false && $profile->id !== $user->profile->id && $user->is_admin == false) {
2018-11-29 08:32:27 +00:00
abort(404);
}
}
break;
case 'direct':
abort(404);
break;
case 'draft':
abort(404);
break;
2021-04-07 03:17:42 +00:00
2018-11-29 08:32:27 +00:00
default:
abort(404);
break;
}
}
2018-12-09 23:03:53 +00:00
public function publicTimelineApi(Request $request)
{
$this->validate($request,[
'page' => 'nullable|integer|max:40',
2019-03-04 02:31:19 +00:00
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
2019-09-21 04:01:36 +00:00
'limit' => 'nullable|integer|max:30'
2018-12-09 23:03:53 +00:00
]);
if(!$request->user()) {
return response('', 403);
}
2018-12-09 23:03:53 +00:00
$page = $request->input('page');
$min = $request->input('min_id');
$max = $request->input('max_id');
2019-02-25 07:11:52 +00:00
$limit = $request->input('limit') ?? 3;
$user = $request->user();
$filtered = $user ? UserFilterService::filters($user->profile_id) : [];
$hideNsfw = config('instance.hide_nsfw_on_public_feeds');
if(config('exp.cached_public_timeline') == false) {
2021-12-05 03:41:46 +00:00
if($min || $max) {
$dir = $min ? '>' : '<';
$id = $min ?? $max;
$timeline = Status::select(
'id',
'profile_id',
'type',
'scope',
'local'
)
->where('id', $dir, $id)
2021-12-22 07:59:03 +00:00
->whereNull(['in_reply_to_id', 'reblog_of_id'])
2021-12-05 03:41:46 +00:00
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->whereLocal(true)
->when($hideNsfw, function($q, $hideNsfw) {
return $q->where('is_nsfw', false);
})
2021-12-05 03:41:46 +00:00
->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;
}
2021-12-05 03:41:46 +00:00
$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);
2021-12-05 03:41:46 +00:00
return $status;
})
->filter(function($s) use($filtered) {
2022-01-08 02:47:21 +00:00
return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false;
2021-12-22 07:59:03 +00:00
})
->values();
2021-12-05 03:41:46 +00:00
$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'
)
2021-12-22 07:59:03 +00:00
->whereNull(['in_reply_to_id', 'reblog_of_id'])
2021-12-05 03:41:46 +00:00
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->whereLocal(true)
->when($hideNsfw, function($q, $hideNsfw) {
return $q->where('is_nsfw', false);
})
2021-12-05 03:41:46 +00:00
->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;
}
2021-12-05 03:41:46 +00:00
$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);
2021-12-05 03:41:46 +00:00
return $status;
})
->filter(function($s) use($filtered) {
return $s && isset($s['account']) && in_array($s['account']['id'], $filtered) == false;
2021-12-22 07:59:03 +00:00
})
->values();
2021-12-05 03:41:46 +00:00
$res = $timeline->toArray();
}
} else {
2021-12-05 03:41:46 +00:00
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) {
$feed = PublicTimelineService::getRankedMinId($min, $limit);
} else {
$feed = PublicTimelineService::get(0, $limit);
}
$res = collect($feed)
2022-06-19 13:05:07 +00:00
->take($limit)
2021-12-05 03:41:46 +00:00
->map(function($k) use($user) {
$status = StatusService::get($k);
if($status && isset($status['account']) && $user) {
2021-12-05 03:41:46 +00:00
$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);
2021-12-05 03:41:46 +00:00
$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;
2021-12-05 03:41:46 +00:00
})
->values()
->toArray();
}
2018-12-09 23:03:53 +00:00
return response()->json($res);
}
public function homeTimelineApi(Request $request)
{
if(!$request->user()) {
return response('', 403);
2018-12-09 23:03:53 +00:00
}
$this->validate($request,[
'page' => 'nullable|integer|max:40',
2019-03-04 02:31:19 +00:00
'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'
2018-12-09 23:03:53 +00:00
]);
$recentFeed = $request->input('recent_feed') == 'true';
$recentFeedMin = $request->input('recent_min');
2018-12-09 23:03:53 +00:00
$page = $request->input('page');
$min = $request->input('min_id');
$max = $request->input('max_id');
2019-02-25 07:11:52 +00:00
$limit = $request->input('limit') ?? 3;
$user = $request->user();
2021-04-07 03:17:42 +00:00
$key = 'user:last_active_at:id:'.$user->id;
2022-12-16 07:43:20 +00:00
if(Cache::get($key) == null) {
$user->last_active_at = now();
$user->save();
2022-12-16 07:43:20 +00:00
Cache::put($key, true, 43200);
}
2018-12-09 23:03:53 +00:00
$pid = $user->profile_id;
2018-12-09 23:03:53 +00:00
2022-12-16 07:43:20 +00:00
$following = Cache::remember('profile:following:'.$pid, 1209600, function() use($pid) {
2019-02-25 06:02:11 +00:00
$following = Follower::whereProfileId($pid)->pluck('following_id');
return $following->push($pid)->toArray();
});
2018-12-09 23:03:53 +00:00
$filtered = $user ? UserFilterService::filters($user->profile_id) : [];
2021-08-05 02:29:21 +00:00
$types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'];
2021-12-05 03:41:46 +00:00
// $types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'text'];
2021-08-05 02:29:21 +00:00
$textOnlyReplies = false;
if(config('exp.top')) {
$textOnlyReplies = (bool) Redis::zscore('pf:tl:replies', $pid);
$textOnlyPosts = (bool) Redis::zscore('pf:tl:top', $pid);
2021-08-05 02:29:21 +00:00
if($textOnlyPosts) {
array_push($types, 'text');
}
2021-08-05 02:29:21 +00:00
}
if(config('exp.polls') == true) {
array_push($types, 'poll');
2021-08-05 02:29:21 +00:00
}
2018-12-09 23:03:53 +00:00
2022-12-16 07:43:20 +00:00
if(config('instance.timeline.home.cached') && $limit == 6 && (!$min && !$max)) {
$ttl = config('instance.timeline.home.cache_ttl');
$res = Cache::remember(
'pf:timelines:home:' . $pid,
$ttl,
function() use(
$types,
$textOnlyReplies,
$following,
$limit,
$filtered,
$user
) {
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) {
$status = StatusService::get($s->id, false);
if(!$status) {
return false;
}
return $status;
})
->filter(function($s) use($filtered) {
return $s && in_array($s['account']['id'], $filtered) == false;
})
->values()
->toArray();
});
$res = collect($res)
->map(function($s) use ($user) {
$status = StatusService::get($s['id'], false);
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 && in_array($s['account']['id'], $filtered) == false;
})
->values()
->take($limit)
->toArray();
return $res;
}
2018-12-09 23:03:53 +00:00
if($min || $max) {
$dir = $min ? '>' : '<';
$id = $min ?? $max;
2022-01-29 03:29:46 +00:00
return Status::select(
2021-04-07 03:17:42 +00:00
'id',
2019-02-14 01:16:58 +00:00
'uri',
'caption',
'rendered',
2021-04-07 03:17:42 +00:00
'profile_id',
2019-02-14 01:16:58 +00:00
'type',
'in_reply_to_id',
'reblog_of_id',
'is_nsfw',
'scope',
'local',
2019-04-03 06:03:40 +00:00
'reply_count',
'comments_disabled',
2019-08-17 07:42:21 +00:00
'place_id',
'likes_count',
'reblogs_count',
2019-02-14 01:16:58 +00:00
'created_at',
'updated_at'
)
->whereIn('type', $types)
2023-02-06 05:50:12 +00:00
->when(!$textOnlyReplies, function($q, $textOnlyReplies) {
return $q->whereNull('in_reply_to_id');
})
2018-12-09 23:03:53 +00:00
->where('id', $dir, $id)
->whereIn('profile_id', $following)
2018-12-27 08:02:08 +00:00
->whereIn('visibility',['public', 'unlisted', 'private'])
2018-12-09 23:03:53 +00:00
->orderBy('created_at', 'desc')
->limit($limit)
2022-01-29 03:29:46 +00:00
->get()
->map(function($s) use ($user) {
$status = StatusService::get($s->id, false);
2022-01-29 03:29:46 +00:00
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);
2022-01-29 03:29:46 +00:00
return $status;
})
->filter(function($s) use($filtered) {
return $s && in_array($s['account']['id'], $filtered) == false;
})
->values()
->toArray();
2018-12-09 23:03:53 +00:00
} else {
2022-01-29 03:29:46 +00:00
return Status::select(
2021-04-07 03:17:42 +00:00
'id',
2019-02-25 06:28:35 +00:00
'uri',
'caption',
'rendered',
2021-04-07 03:17:42 +00:00
'profile_id',
2019-02-25 06:28:35 +00:00
'type',
'in_reply_to_id',
'reblog_of_id',
'is_nsfw',
'scope',
'local',
2019-04-03 06:03:40 +00:00
'reply_count',
'comments_disabled',
2019-08-17 07:42:21 +00:00
'place_id',
'likes_count',
'reblogs_count',
2019-02-25 06:28:35 +00:00
'created_at',
'updated_at'
)
->whereIn('type', $types)
->when(!$textOnlyReplies, function($q, $textOnlyReplies) {
return $q->whereNull('in_reply_to_id');
})
2018-12-09 23:03:53 +00:00
->whereIn('profile_id', $following)
2018-12-27 08:02:08 +00:00
->whereIn('visibility',['public', 'unlisted', 'private'])
2018-12-09 23:03:53 +00:00
->orderBy('created_at', 'desc')
2022-01-29 03:29:46 +00:00
->limit($limit)
->get()
->map(function($s) use ($user) {
$status = StatusService::get($s->id, false);
2022-01-29 03:29:46 +00:00
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);
2022-01-29 03:29:46 +00:00
return $status;
})
->filter(function($s) use($filtered) {
return $s && in_array($s['account']['id'], $filtered) == false;
})
->values()
->toArray();
2018-12-09 23:03:53 +00:00
}
}
2019-02-10 20:42:26 +00:00
2019-03-14 19:52:36 +00:00
public function networkTimelineApi(Request $request)
{
if(!$request->user()) {
return response('', 403);
}
2021-04-07 03:17:42 +00:00
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'
]);
$page = $request->input('page');
$min = $request->input('min_id');
$max = $request->input('max_id');
$limit = $request->input('limit') ?? 3;
$user = $request->user();
$amin = SnowflakeService::byDate(now()->subDays(config('federation.network_timeline_days_falloff')));
2021-04-07 03:17:42 +00:00
$filtered = $user ? UserFilterService::filters($user->profile_id) : [];
$hideNsfw = config('instance.hide_nsfw_on_public_feeds');
2022-06-09 10:15:23 +00:00
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);
})
2022-06-09 10:15:23 +00:00
->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);
})
2022-06-09 10:15:23 +00:00
->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) {
$feed = NetworkTimelineService::getRankedMinId($min, $limit);
} else {
$feed = NetworkTimelineService::get(0, $limit);
}
$res = collect($feed)
2022-06-19 13:05:07 +00:00
->take($limit)
2022-06-09 10:15:23 +00:00
->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();
2021-04-07 03:17:42 +00:00
}
return response()->json($res);
2019-03-14 19:52:36 +00:00
}
2019-02-10 20:42:26 +00:00
public function relationships(Request $request)
{
2019-08-17 07:42:21 +00:00
if(!Auth::check()) {
return response()->json([]);
}
2019-02-10 20:42:26 +00:00
$pid = $request->user()->profile_id;
2019-02-10 20:42:26 +00:00
$this->validate($request, [
2019-02-11 04:49:54 +00:00
'id' => 'required|array|min:1|max:20',
'id.*' => 'required|integer'
2019-02-10 20:42:26 +00:00
]);
$ids = collect($request->input('id'));
$res = $ids->filter(function($v) use($pid) {
return $v != $pid;
})
->map(function($id) use($pid) {
2021-12-05 03:41:46 +00:00
return RelationshipService::get($pid, $id);
2019-02-10 20:42:26 +00:00
});
2019-02-10 20:42:26 +00:00
return response()->json($res);
}
2019-02-11 04:49:54 +00:00
public function account(Request $request, $id)
{
$res = AccountService::get($id);
2019-02-11 04:49:54 +00:00
return response()->json($res);
}
public function accountStatuses(Request $request, $id)
{
$this->validate($request, [
'only_media' => 'nullable',
'pinned' => 'nullable',
'exclude_replies' => 'nullable',
2019-03-04 05:09:35 +00:00
'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,
2019-02-11 04:49:54 +00:00
'limit' => 'nullable|integer|min:1|max:24'
]);
2019-03-04 05:09:35 +00:00
$user = $request->user();
$profile = AccountService::get($id);
abort_if(!$profile, 404);
2019-03-04 05:09:35 +00:00
$limit = $request->limit ?? 9;
$max_id = $request->max_id;
$min_id = $request->min_id;
$scope = ['photo', 'photo:album', 'video', 'video:album'];
$onlyMedia = $request->input('only_media', true);
2021-04-07 03:17:42 +00:00
if(!$min_id && !$max_id) {
$min_id = 1;
}
if($profile['locked']) {
if(!$user) {
2019-03-04 05:09:35 +00:00
return response()->json([]);
}
$pid = $user->profile_id;
2019-03-04 05:09:35 +00:00
$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'] : [];
2019-02-11 04:49:54 +00:00
} else {
if($user) {
$pid = $user->profile_id;
2019-03-04 05:09:35 +00:00
$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'];
2019-03-04 05:09:35 +00:00
} else {
2019-04-01 03:30:16 +00:00
$visibility = ['public', 'unlisted'];
2019-03-04 05:09:35 +00:00
}
2019-02-11 04:49:54 +00:00
}
2019-03-04 05:09:35 +00:00
$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();
return response()->json($res);
2019-02-11 04:49:54 +00:00
}
2018-11-09 05:23:36 +00:00
}