diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index 7a0d63760..8b18efb77 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -61,6 +61,7 @@ use App\Jobs\VideoPipeline\{ use App\Services\{ AccountService, + BookmarkService, CollectionService, FollowerService, InstanceService, @@ -768,6 +769,10 @@ class ApiV1Controller extends Controller ->whereFollowingId($target->id) ->delete(); + if(config('instance.timeline.home.cached')) { + Cache::forget('pf:timelines:home:' . $user->profile_id); + } + FollowerService::remove($user->profile_id, $target->id); $target->decrement('followers_count'); @@ -1935,11 +1940,80 @@ class ApiV1Controller extends Controller $limit = $request->input('limit') ?? 20; $pid = $request->user()->profile_id; - $following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), 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(); }); + if(config('instance.timeline.home.cached') && (!$min && !$max)) { + $ttl = config('instance.timeline.home.cache_ttl'); + $res = Cache::remember( + 'pf:timelines:home:' . $pid, + $ttl, + function() use( + $following, + $limit, + $pid + ) { + 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', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->whereIn('profile_id', $following) + ->whereIn('visibility',['public', 'unlisted', 'private']) + ->orderBy('created_at', 'desc') + ->limit($limit) + ->get() + ->map(function($s) { + $status = StatusService::get($s->id, false); + if(!$status) { + return false; + } + return $status; + }) + ->filter(function($s) { + return $s && isset($s['account']['id']); + }) + ->values() + ->toArray(); + }); + + $res = collect($res) + ->map(function($s) use ($pid) { + $status = StatusService::get($s['id'], false); + if(!$status) { + return false; + } + $status['favourited'] = (bool) LikeService::liked($pid, $s['id']); + $status['bookmarked'] = (bool) BookmarkService::get($pid, $s['id']); + $status['reblogged'] = (bool) ReblogService::get($pid, $s['id']); + return $status; + }) + ->filter(function($s) { + return $s && isset($s['account']['id']); + }) + ->values() + ->take($limit) + ->toArray(); + } + if($min || $max) { $dir = $min ? '>' : '<'; $id = $min ?? $max; diff --git a/app/Http/Controllers/PublicApiController.php b/app/Http/Controllers/PublicApiController.php index 6c1c4cc4d..18bb7e0b7 100644 --- a/app/Http/Controllers/PublicApiController.php +++ b/app/Http/Controllers/PublicApiController.php @@ -454,29 +454,19 @@ class PublicApiController extends Controller $user = $request->user(); $key = 'user:last_active_at:id:'.$user->id; - $ttl = now()->addMinutes(20); - Cache::remember($key, $ttl, function() use($user) { + if(Cache::get($key) == null) { $user->last_active_at = now(); $user->save(); - return; - }); + Cache::put($key, true, 43200); + } $pid = $user->profile_id; - $following = Cache::remember('profile:following:'.$pid, now()->addMinutes(1440), 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(); }); - if($recentFeed == true) { - $key = 'profile:home-timeline-cursor:'.$user->id; - $ttl = now()->addMinutes(30); - $min = Cache::remember($key, $ttl, function() use($pid) { - $res = StatusView::whereProfileId($pid)->orderByDesc('status_id')->first(); - return $res ? $res->status_id : null; - }); - } - $filtered = $user ? UserFilterService::filters($user->profile_id) : []; $types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']; // $types = ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'text']; @@ -496,6 +486,83 @@ class PublicApiController extends Controller array_push($types, 'poll'); } + 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; + } + if($min || $max) { $dir = $min ? '>' : '<'; $id = $min ?? $max; diff --git a/app/Observers/FollowerObserver.php b/app/Observers/FollowerObserver.php index fd5f00880..f230bb79f 100644 --- a/app/Observers/FollowerObserver.php +++ b/app/Observers/FollowerObserver.php @@ -4,6 +4,7 @@ namespace App\Observers; use App\Follower; use App\Services\FollowerService; +use Cache; class FollowerObserver { @@ -15,6 +16,10 @@ class FollowerObserver */ public function created(Follower $follower) { + if(config('instance.timeline.home.cached')) { + Cache::forget('pf:timelines:home:' . $follower->profile_id); + } + FollowerService::add($follower->profile_id, $follower->following_id); } diff --git a/app/Observers/StatusObserver.php b/app/Observers/StatusObserver.php index 15d24cbcd..8d370b38e 100644 --- a/app/Observers/StatusObserver.php +++ b/app/Observers/StatusObserver.php @@ -4,6 +4,7 @@ namespace App\Observers; use App\Status; use App\Services\ProfileStatusService; +use Cache; class StatusObserver { @@ -33,6 +34,10 @@ class StatusObserver */ public function updated(Status $status) { + if(config('instance.timeline.home.cached')) { + Cache::forget('pf:timelines:home:' . $status->profile_id); + } + if(in_array($status->scope, ['public', 'unlisted']) && in_array($status->type, ['photo', 'photo:album', 'video'])) { ProfileStatusService::add($status->profile_id, $status->id); } @@ -46,6 +51,10 @@ class StatusObserver */ public function deleted(Status $status) { + if(config('instance.timeline.home.cached')) { + Cache::forget('pf:timelines:home:' . $status->profile_id); + } + ProfileStatusService::delete($status->profile_id, $status->id); } diff --git a/app/Services/FollowerService.php b/app/Services/FollowerService.php index 457819654..aacf71218 100644 --- a/app/Services/FollowerService.php +++ b/app/Services/FollowerService.php @@ -27,6 +27,7 @@ class FollowerService RelationshipService::refresh($actor, $target); Redis::zadd(self::FOLLOWING_KEY . $actor, $ts, $target); Redis::zadd(self::FOLLOWERS_KEY . $target, $ts, $actor); + Cache::forget('profile:following:' . $actor); } public static function remove($actor, $target) @@ -38,6 +39,7 @@ class FollowerService AccountService::del($actor); AccountService::del($target); RelationshipService::refresh($actor, $target); + Cache::forget('profile:following:' . $actor); } public static function followers($id, $start = 0, $stop = 10) diff --git a/config/instance.php b/config/instance.php index 31adf4916..062b3fb42 100644 --- a/config/instance.php +++ b/config/instance.php @@ -23,6 +23,11 @@ return [ 'email' => env('INSTANCE_CONTACT_EMAIL'), 'timeline' => [ + 'home' => [ + 'cached' => env('PF_HOME_TIMELINE_CACHE', false), + 'cache_ttl' => env('PF_HOME_TIMELINE_CACHE_TTL', 900) + ], + 'local' => [ 'is_public' => env('INSTANCE_PUBLIC_LOCAL_TIMELINE', false) ],