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

Merge pull request #3655 from pixelfed/staging

Staging
daniel 2 жил өмнө
parent
commit
d744200545

+ 4 - 0
CHANGELOG.md

@@ -76,6 +76,10 @@
 - Update ApiV1Controller, fix typo in statavouriteById method ([c91a6a75](https://github.com/pixelfed/pixelfed/commit/c91a6a75))
 - Update InboxPipeline, fix peertube attributedTo parsing ([99fb80bf](https://github.com/pixelfed/pixelfed/commit/99fb80bf))
 - Update Collection components, fix addId bug #3230 ([62c05665](https://github.com/pixelfed/pixelfed/commit/62c05665))
+- Update DirectMessageController, include account entity in lookup endpoint ([9e223a6b](https://github.com/pixelfed/pixelfed/commit/9e223a6b))
+- Update ApiV1Controller update_credentials endpoint to support app response ([61d26e85](https://github.com/pixelfed/pixelfed/commit/61d26e85))
+- Update PronounService, fix json_decode null parameter ([d72cd819](https://github.com/pixelfed/pixelfed/commit/d72cd819))
+- Update ApiV1Controller, normalize profile id comparison ([374bfdae](https://github.com/pixelfed/pixelfed/commit/374bfdae))
 -  ([](https://github.com/pixelfed/pixelfed/commit/))
 
 ## [v0.11.3 (2022-05-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.2...v0.11.3)

+ 58 - 27
app/Http/Controllers/Api/ApiV1Controller.php

@@ -65,6 +65,7 @@ use App\Services\{
 	NetworkTimelineService,
 	NotificationService,
 	MediaPathService,
+    ProfileStatusService,
 	PublicTimelineService,
 	ReblogService,
 	RelationshipService,
@@ -432,9 +433,13 @@ class ApiV1Controller extends Controller
 			MediaSyncLicensePipeline::dispatch($user->id, $request->input('license'));
 		}
 
-		$res = AccountService::getMastodon($user->profile_id);
-		$res['bio'] = strip_tags($res['note']);
-		$res = array_merge($res, $other);
+        if($request->has(self::PF_API_ENTITY_KEY)) {
+            $res = AccountService::get($user->profile_id, true);
+        } else {
+           $res = AccountService::getMastodon($user->profile_id, true);
+           $res['bio'] = strip_tags($res['note']);
+           $res = array_merge($res, $other);
+       }
 
 		return $this->json($res);
 	}
@@ -453,7 +458,7 @@ class ApiV1Controller extends Controller
 		abort_if(!$account, 404);
 		$pid = $request->user()->profile_id;
 
-		if($pid != $account['id']) {
+		if(intval($pid) !== intval($account['id'])) {
 			if($account['locked']) {
 				if(!FollowerService::follows($pid, $account['id'])) {
 					return [];
@@ -500,7 +505,7 @@ class ApiV1Controller extends Controller
 		abort_if(!$account, 404);
 		$pid = $request->user()->profile_id;
 
-		if($pid != $account['id']) {
+		if(intval($pid) !== intval($account['id'])) {
 			if($account['locked']) {
 				if(!FollowerService::follows($pid, $account['id'])) {
 					return [];
@@ -559,7 +564,7 @@ class ApiV1Controller extends Controller
 		$profile = $napi ? AccountService::get($id, true) : AccountService::getMastodon($id, true);
 
         if(!$profile || !isset($profile['id']) || !$user) {
-        	return response('', 404);
+        	return $this->json(['error' => 'Account not found'], 404);
         }
 
 		$limit = $request->limit ?? 20;
@@ -582,7 +587,7 @@ class ApiV1Controller extends Controller
 			}
 		}
 
-		if($pid == $profile['id']) {
+		if(intval($pid) === intval($profile['id'])) {
 			$visibility = ['public', 'unlisted', 'private'];
 		} else if($profile['locked']) {
 			$following = FollowerService::follows($pid, $profile['id']);
@@ -812,7 +817,7 @@ class ApiV1Controller extends Controller
 		$pid = $request->user()->profile_id ?? $request->user()->profile->id;
 		$res = collect($request->input('id'))
 			->filter(function($id) use($pid) {
-				return $id != $pid;
+				return intval($id) !== intval($pid);
 			})
 			->map(function($id) use($pid) {
 				return RelationshipService::get($pid, $id);
@@ -843,15 +848,21 @@ class ApiV1Controller extends Controller
 		$resolve = (bool) $request->input('resolve', false);
 		$q = '%' . $query . '%';
 
-		$profiles = Profile::whereNull('status')
-			->where('username', 'like', $q)
-			->orWhere('name', 'like', $q)
-			->limit($limit)
-			->get();
+		$profiles = Cache::remember('api:v1:accounts:search:' . sha1($query) . ':limit:' . $limit, 86400, function() use($q, $limit) {
+            return Profile::whereNull('status')
+    			->where('username', 'like', $q)
+    			->orWhere('name', 'like', $q)
+    			->limit($limit)
+    			->pluck('id')
+                ->map(function($id) {
+                    return AccountService::getMastodon($id);
+                })
+                ->filter(function($account) {
+                    return $account && isset($account['id']);
+                });
+        });
 
-		$resource = new Fractal\Resource\Collection($profiles, new AccountTransformer());
-		$res = $this->fractal->createData($resource)->toArray();
-		return $this->json($res);
+		return $this->json($profiles);
 	}
 
 	/**
@@ -903,7 +914,7 @@ class ApiV1Controller extends Controller
 		$user = $request->user();
 		$pid = $user->profile_id ?? $user->profile->id;
 
-		if($id == $pid) {
+		if(intval($id) === intval($pid)) {
 			abort(400, 'You cannot block yourself');
 		}
 
@@ -948,7 +959,7 @@ class ApiV1Controller extends Controller
 		$user = $request->user();
 		$pid = $user->profile_id ?? $user->profile->id;
 
-		if($id == $pid) {
+		if(intval($id) === intval($pid)) {
 			abort(400, 'You cannot unblock yourself');
 		}
 
@@ -1083,7 +1094,7 @@ class ApiV1Controller extends Controller
 
 		$spid = $status['account']['id'];
 
-		if($spid !== $user->profile_id) {
+		if(intval($spid) !== intval($user->profile_id)) {
 			if($status['visibility'] == 'private') {
 				abort_if(!FollowerService::follows($user->profile_id, $spid), 403);
 			} else {
@@ -1138,7 +1149,7 @@ class ApiV1Controller extends Controller
 
 		$status = Status::findOrFail($id);
 
-		if($status->profile_id !== $user->profile_id) {
+		if(intval($status->profile_id) !== intval($user->profile_id)) {
 			if($status->scope == 'private') {
 				abort_if(!$status->profile->followedBy($user->profile), 403);
 			} else {
@@ -1765,6 +1776,10 @@ class ApiV1Controller extends Controller
 		$user = $request->user();
 		$pid = $user->profile_id;
 
+        if(intval($pid) === intval($id)) {
+            return $this->json(['error' => 'You cannot mute yourself'], 500);
+        }
+
 		$account = Profile::findOrFail($id);
 
 		$filter = UserFilter::firstOrCreate([
@@ -1798,6 +1813,10 @@ class ApiV1Controller extends Controller
 		$user = $request->user();
 		$pid = $user->profile_id;
 
+        if(intval($pid) === intval($id)) {
+            return $this->json(['error' => 'You cannot unmute yourself'], 500);
+        }
+
 		$account = Profile::findOrFail($id);
 
 		$filter = UserFilter::whereUserId($pid)
@@ -2223,7 +2242,7 @@ class ApiV1Controller extends Controller
 		$scope = $res['visibility'];
 		if(!in_array($scope, ['public', 'unlisted'])) {
 			if($scope === 'private') {
-				if($res['account']['id'] != $user->profile_id) {
+				if(intval($res['account']['id']) !== intval($user->profile_id)) {
 					abort_unless(FollowerService::follows($user->profile_id, $res['account']['id']), 403);
 				}
 			} else {
@@ -2256,7 +2275,7 @@ class ApiV1Controller extends Controller
 			return response('', 404);
 		}
 
-		if($status['account']['id'] != $user->profile_id) {
+		if(intval($status['account']['id']) !== intval($user->profile_id)) {
 			if($status['visibility'] == 'private') {
 				if(!FollowerService::follows($user->profile_id, $status['account']['id'])) {
 					return response('', 404);
@@ -2336,7 +2355,7 @@ class ApiV1Controller extends Controller
 		$user = $request->user();
 		$status = Status::findOrFail($id);
 
-		if($status->profile_id !== $user->profile_id) {
+		if(intval($status->profile_id) !== intval($user->profile_id)) {
 			if($status->scope == 'private') {
 				abort_if(!FollowerService::follows($user->profile_id, $status->profile_id), 403);
 			} else {
@@ -2402,7 +2421,7 @@ class ApiV1Controller extends Controller
 			}
 		}
 
-		if($status->profile_id !== $user->profile_id) {
+		if(intval($status->profile_id) !== intval($user->profile_id)) {
 			if($status->scope == 'private') {
 				abort_if(!$status->profile->followedBy($user->profile), 403);
 			} else {
@@ -2639,7 +2658,7 @@ class ApiV1Controller extends Controller
 		$user = $request->user();
 		$status = Status::whereScope('public')->findOrFail($id);
 
-		if($status->profile_id !== $user->profile_id) {
+		if(intval($status->profile_id) !== intval($user->profile_id)) {
 			if($status->scope == 'private') {
 				abort_if(!FollowerService::follows($user->profile_id, $status->profile_id), 403);
 			} else {
@@ -2687,7 +2706,7 @@ class ApiV1Controller extends Controller
 		$user = $request->user();
 		$status = Status::whereScope('public')->findOrFail($id);
 
-		if($status->profile_id !== $user->profile_id) {
+		if(intval($status->profile_id) !== intval($user->profile_id)) {
 			if($status->scope == 'private') {
 				abort_if(!FollowerService::follows($user->profile_id, $status->profile_id), 403);
 			} else {
@@ -3026,7 +3045,7 @@ class ApiV1Controller extends Controller
 	}
 
    /**
-	* GET /api/v1/discover/accounts/popular
+	* GET /api/v1.1/discover/accounts/popular
 	*
 	*
 	* @return array
@@ -3054,6 +3073,18 @@ class ApiV1Controller extends Controller
 		->filter(function($profile) use($pid) {
 			return $profile['id'] != $pid;
 		})
+        ->map(function($profile) {
+            $ids = collect(ProfileStatusService::get($profile['id'], 0, 9))
+                ->map(function($id) {
+                    return StatusService::get($id, true);
+                })
+                ->filter(function($post) {
+                    return $post && isset($post['id']);
+                })
+                ->take(3);
+            $profile['recent_posts'] = $ids;
+            return $profile;
+        })
 		->take(6)
 		->values();
 

+ 3 - 1
app/Http/Controllers/DirectMessageController.php

@@ -704,12 +704,14 @@ class DirectMessageController extends Controller
 		->limit(8)
 		->get()
 		->map(function($r) {
+			$acct = AccountService::get($r->id);
 			return [
 				'local' => (bool) !$r->domain,
 				'id' => (string) $r->id,
 				'name' => $r->username,
 				'privacy' => true,
-				'avatar' => $r->avatarUrl()
+				'avatar' => $r->avatarUrl(),
+				'account' => $acct
 			];
 		});
 

+ 66 - 0
app/Observers/StatusObserver.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Observers;
+
+use App\Status;
+use App\Services\ProfileStatusService;
+
+class StatusObserver
+{
+    /**
+     * Handle the Status "created" event.
+     *
+     * @param  \App\Status  $status
+     * @return void
+     */
+    public function created(Status $status)
+    {
+        //
+    }
+
+    /**
+     * Handle the Status "updated" event.
+     *
+     * @param  \App\Status  $status
+     * @return void
+     */
+    public function updated(Status $status)
+    {
+        if(in_array($status->scope, ['public', 'unlisted']) && in_array($status->type, ['photo', 'photo:album', 'video'])) {
+            ProfileStatusService::add($status->profile_id, $status->id);
+        }
+    }
+
+    /**
+     * Handle the Status "deleted" event.
+     *
+     * @param  \App\Status  $status
+     * @return void
+     */
+    public function deleted(Status $status)
+    {
+        ProfileStatusService::delete($status->profile_id, $status->id);
+    }
+
+    /**
+     * Handle the Status "restored" event.
+     *
+     * @param  \App\Status  $status
+     * @return void
+     */
+    public function restored(Status $status)
+    {
+        //
+    }
+
+    /**
+     * Handle the Status "force deleted" event.
+     *
+     * @param  \App\Status  $status
+     * @return void
+     */
+    public function forceDeleted(Status $status)
+    {
+        //
+    }
+}

+ 4 - 1
app/Providers/AppServiceProvider.php

@@ -8,7 +8,8 @@ use App\Observers\{
 	NotificationObserver,
 	ModLogObserver,
 	ProfileObserver,
-	StatusHashtagObserver,
+    StatusHashtagObserver,
+    StatusObserver,
 	UserObserver,
 	UserFilterObserver,
 };
@@ -19,6 +20,7 @@ use App\{
 	ModLog,
 	Profile,
 	StatusHashtag,
+    Status,
 	User,
 	UserFilter
 };
@@ -47,6 +49,7 @@ class AppServiceProvider extends ServiceProvider
 		Profile::observe(ProfileObserver::class);
 		StatusHashtag::observe(StatusHashtagObserver::class);
 		User::observe(UserObserver::class);
+        Status::observe(StatusObserver::class);
 		UserFilter::observe(UserFilterObserver::class);
 		Horizon::auth(function ($request) {
 			return Auth::check() && $request->user()->is_admin;

+ 66 - 0
app/Services/ProfileStatusService.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Services;
+
+use DB;
+use Illuminate\Support\Facades\Redis;
+
+class ProfileStatusService
+{
+    const CACHE_KEY = 'pf:services:profile-statuses:ids:';
+    const COLD_CHECK_KEY = 'pf:services:profile-statuses:id-ttl:';
+    const FALLOFF_LIMIT = 40;
+
+    public static function get($id, $start = 0, $stop = 8)
+    {
+        $key = self::CACHE_KEY . $id;
+        if(!Redis::zscore(self::COLD_CHECK_KEY, $id)) {
+            $res = self::coldFetch($id);
+            if($res && count($res)) {
+                return array_slice($res, $start, $stop);
+            }
+        }
+        $ids = Redis::zrevrange($key, $start, $stop - 1);
+        return $ids;
+    }
+
+    public static function count($id)
+    {
+        return Redis::zcount(self::CACHE_KEY . $id, '-inf', '+inf');
+    }
+
+    public static function add($pid, $sid)
+    {
+        if(self::count($pid) > self::FALLOFF_LIMIT) {
+            Redis::zpopmin(self::CACHE_KEY . $pid);
+        }
+        return Redis::zadd(self::CACHE_KEY . $pid, $sid, $sid);
+    }
+
+    public static function delete($pid, $sid)
+    {
+        return Redis::zrem(self::CACHE_KEY . $pid, $sid);
+    }
+
+    public static function coldFetch($pid)
+    {
+        Redis::del(self::CACHE_KEY . $pid);
+        $ids = DB::table('statuses')
+            ->select('id', 'profile_id', 'type', 'scope')
+            ->whereIn('type', ['photo', 'photo:album', 'video'])
+            ->whereIn('scope', ['public', 'unlisted'])
+            ->whereProfileId($pid)
+            ->orderByDesc('id')
+            ->limit(self::FALLOFF_LIMIT)
+            ->pluck('id')
+            ->toArray();
+
+        if($ids && count($ids)) {
+            foreach($ids as $id) {
+                self::add($pid, $id);
+            }
+        }
+        Redis::zadd(self::COLD_CHECK_KEY, $pid, $pid);
+        return $ids;
+    }
+}

+ 1 - 1
app/Services/PronounService.php

@@ -15,7 +15,7 @@ class PronounService {
 
 		return Cache::remember($key, $ttl, function() use($id) {
 			$res = UserPronoun::whereProfileId($id)->first();
-			return $res ? json_decode($res->pronouns, true) : [];
+			return $res && $res->pronouns ? json_decode($res->pronouns, true) : [];
 		});
 	}
 

+ 11 - 2
routes/api.php

@@ -100,8 +100,17 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
 	Route::group(['prefix' => 'v1.1'], function() use($middleware) {
 		Route::post('report', 'Api\ApiV1Dot1Controller@report')->middleware($middleware);
 		Route::delete('accounts/avatar', 'Api\ApiV1Dot1Controller@deleteAvatar')->middleware($middleware);
-		Route::get('direct/thread', 'DirectMessageController@thread')->middleware($middleware);
-		Route::post('direct/thread/send', 'DirectMessageController@create')->middleware($middleware);
+
+		Route::group(['prefix' => 'direct'], function () use($middleware) {
+			Route::get('thread', 'DirectMessageController@thread')->middleware($middleware);
+			Route::post('thread/send', 'DirectMessageController@create')->middleware($middleware);
+			Route::delete('thread/message', 'DirectMessageController@delete')->middleware($middleware);
+			Route::post('thread/mute', 'DirectMessageController@mute')->middleware($middleware);
+			Route::post('thread/unmute', 'DirectMessageController@unmute')->middleware($middleware);
+			Route::post('thread/media', 'DirectMessageController@mediaUpload')->middleware($middleware);
+			Route::post('thread/read', 'DirectMessageController@read')->middleware($middleware);
+			Route::post('lookup', 'DirectMessageController@composeLookup')->middleware($middleware);
+		});
 
 		Route::group(['prefix' => 'stories'], function () use($middleware) {
 			Route::get('recent', 'StoryController@recent')->middleware($middleware);