Merge pull request #3333 from pixelfed/staging

MastoAPI Compatibility fixes
This commit is contained in:
daniel 2022-03-25 02:39:38 -06:00 committed by GitHub
commit 160097c287
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 793 additions and 595 deletions

View file

@ -100,6 +100,15 @@
- Updated ApiV1Controller, fix search v2 entities. ([9dac861e](https://github.com/pixelfed/pixelfed/commit/9dac861e))
- Updated ApiV1Controller, fix apps endpoint. ([50baae52](https://github.com/pixelfed/pixelfed/commit/50baae52))
- Updated ApiV1Controller, add apps/verify_credentials endpoint. ([c4d38c20](https://github.com/pixelfed/pixelfed/commit/c4d38c20))
- Updated ApiV1Controller, increase max limion timelines. ([df22f2e4](https://github.com/pixelfed/pixelfed/commit/df22f2e4))
- Updated ApiV1Controller, add preferences endpoint. ([c3e56b87](https://github.com/pixelfed/pixelfed/commit/c3e56b87))
- Updated ApiV1Controller, fix tag timeline limits and remove has(media) constraint. ([8c65d60b](https://github.com/pixelfed/pixelfed/commit/8c65d60b))
- Updated ApiV1Controller, add trends endpoint. ([d40a8453](https://github.com/pixelfed/pixelfed/commit/d40a8453))
- Updated ApiV1Controller, add announcements endpoint. ([fbe07c51](https://github.com/pixelfed/pixelfed/commit/fbe07c51))
- Updated ApiV1Controller, add markers endpoint. ([93a9769e](https://github.com/pixelfed/pixelfed/commit/93a9769e))
- Updated ApiV1Controller, increase limits from 80 to 100. ([15eccd44](https://github.com/pixelfed/pixelfed/commit/15eccd44))
- Updated ApiV1Controller, fix accountStatusesById endpoint. ([db7b1af3](https://github.com/pixelfed/pixelfed/commit/db7b1af3))
- Updated ApiV1Controller, update statusCreate entity. ([a84ab6ea](https://github.com/pixelfed/pixelfed/commit/a84ab6ea))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.2 (2022-01-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.1...v0.11.2)

View file

@ -0,0 +1,25 @@
<?php
namespace App\Auth;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
class BearerTokenResponse extends \League\OAuth2\Server\ResponseTypes\BearerTokenResponse
{
/**
* Add custom fields to your Bearer Token response here, then override
* AuthorizationServer::getResponseType() to pull in your version of
* this class rather than the default.
*
* @param AccessTokenEntityInterface $accessToken
*
* @return array
*/
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
{
return [
'created_at' => time(),
'scope' => 'read write follow push'
];
}
}

View file

@ -80,6 +80,7 @@ use App\Util\Media\License;
use App\Jobs\MediaPipeline\MediaSyncLicensePipeline;
use App\Services\DiscoverService;
use App\Services\CustomEmojiService;
use App\Services\MarkerService;
class ApiV1Controller extends Controller
{
@ -536,11 +537,14 @@ class ApiV1Controller extends Controller
'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:80'
'limit' => 'nullable|integer|min:1|max:100'
]);
$profile = AccountService::getMastodon($id);
abort_if(!$profile, 404);
$profile = AccountService::getMastodon($id, true);
if(!$profile || !isset($profile['id']) || !$user) {
return response('', 404);
}
$limit = $request->limit ?? 20;
$max_id = $request->max_id;
@ -566,7 +570,9 @@ class ApiV1Controller extends Controller
$visibility = ['public', 'unlisted', 'private'];
} else if($profile['locked']) {
$following = FollowerService::follows($pid, $profile['id']);
abort_unless($following, 403);
if(!$following) {
return response('', 403);
}
$visibility = ['public', 'unlisted', 'private'];
} else {
$following = FollowerService::follows($pid, $profile['id']);
@ -585,11 +591,8 @@ class ApiV1Controller extends Controller
->orderByDesc('id')
->get()
->map(function($s) use($user) {
try {
$status = StatusService::getMastodon($s->id, false);
} catch (\Exception $e) {
$status = false;
}
if($user && $status) {
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id);
}
@ -1581,7 +1584,7 @@ class ApiV1Controller extends Controller
abort_if(!$request->user(), 403);
$this->validate($request, [
'limit' => 'nullable|integer|min:1|max:80',
'limit' => 'nullable|integer|min:1|max:100',
'min_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
'max_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
'since_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
@ -1656,7 +1659,7 @@ class ApiV1Controller extends Controller
'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:80'
'limit' => 'nullable|integer|max:100'
]);
$page = $request->input('page');
@ -1817,7 +1820,7 @@ class ApiV1Controller extends Controller
$this->validate($request,[
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'limit' => 'nullable|integer|max:80'
'limit' => 'nullable|integer|max:100'
]);
$min = $request->input('min_id');
@ -1983,7 +1986,7 @@ class ApiV1Controller extends Controller
$this->validate($request, [
'page' => 'nullable|integer|min:1|max:40',
'limit' => 'nullable|integer|min:1|max:80'
'limit' => 'nullable|integer|min:1|max:100'
]);
$limit = $request->input('limit') ?? 40;
@ -2042,7 +2045,7 @@ class ApiV1Controller extends Controller
$this->validate($request, [
'page' => 'nullable|integer|min:1|max:40',
'limit' => 'nullable|integer|min:1|max:80'
'limit' => 'nullable|integer|min:1|max:100'
]);
$page = $request->input('page', 1);
@ -2239,6 +2242,11 @@ class ApiV1Controller extends Controller
Cache::forget($limitKey);
$res = StatusService::getMastodon($status->id, false);
$res['favourited'] = false;
$res['language'] = 'en';
$res['bookmarked'] = false;
$res['pinned'] = false;
$res['card'] = null;
return $this->json($res);
}
@ -2365,7 +2373,7 @@ class ApiV1Controller extends Controller
'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'
'limit' => 'nullable|integer|max:100'
]);
$tag = Hashtag::whereName($hashtag)
@ -2390,18 +2398,18 @@ class ApiV1Controller extends Controller
$res = StatusHashtag::whereHashtagId($tag->id)
->whereStatusVisibility('public')
->whereHas('media')
->where('status_id', $dir, $id)
->latest()
->limit($limit)
->pluck('status_id')
->filter(function($i) {
return StatusService::getMastodon($i);
})
->map(function ($i) {
if($i) {
return StatusService::getMastodon($i);
}
})
->filter(function($i) {
return $i && isset($i['account']);
})
->filter()
->values()
->toArray();
@ -2514,7 +2522,7 @@ class ApiV1Controller extends Controller
abort_if(!$request->user(), 403);
$this->validate($request, [
'q' => 'required|string|min:1|max:80',
'q' => 'required|string|min:1|max:100',
'account_id' => 'nullable|string',
'max_id' => 'nullable|string',
'min_id' => 'nullable|string',
@ -2695,4 +2703,95 @@ class ApiV1Controller extends Controller
return $this->json($ids);
}
/**
* GET /api/v1/preferences
*
*
* @return array
*/
public function getPreferences(Request $request)
{
abort_if(!$request->user(), 403);
$pid = $request->user()->profile_id;
$account = AccountService::get($pid);
return $this->json([
'posting:default:visibility' => $account['locked'] ? 'private' : 'public',
'posting:default:sensitive' => false,
'posting:default:language' => null,
'reading:expand:media' => 'default',
'reading:expand:spoilers' => false
]);
}
/**
* GET /api/v1/trends
*
*
* @return array
*/
public function getTrends(Request $request)
{
abort_if(!$request->user(), 403);
return $this->json([]);
}
/**
* GET /api/v1/announcements
*
*
* @return array
*/
public function getAnnouncements(Request $request)
{
abort_if(!$request->user(), 403);
return $this->json([]);
}
/**
* GET /api/v1/markers
*
*
* @return array
*/
public function getMarkers(Request $request)
{
abort_if(!$request->user(), 403);
$type = $request->input('timeline');
if(is_array($type)) {
$type = $type[0];
}
if(!$type || !in_array($type, ['home', 'notifications'])) {
return $this->json([]);
}
$pid = $request->user()->profile_id;
return $this->json(MarkerService::get($pid, $type));
}
/**
* POST /api/v1/markers
*
*
* @return array
*/
public function setMarkers(Request $request)
{
abort_if(!$request->user(), 403);
$pid = $request->user()->profile_id;
$home = $request->input('home.last_read_id');
$notifications = $request->input('notifications.last_read_id');
if($home) {
return $this->json(MarkerService::set($pid, 'home', $home));
}
if($notifications) {
return $this->json(MarkerService::set($pid, 'notifications', $notifications));
}
return $this->json([]);
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace App\Providers;
use App\Auth\BearerTokenResponse;
use Laravel\Passport\Bridge;
use League\OAuth2\Server\AuthorizationServer;
class PassportServiceProvider extends \Laravel\Passport\PassportServiceProvider
{
/**
* Make the authorization service instance.
*
* @return \League\OAuth2\Server\AuthorizationServer
*/
public function makeAuthorizationServer()
{
return new AuthorizationServer(
$this->app->make(Bridge\ClientRepository::class),
$this->app->make(Bridge\AccessTokenRepository::class),
$this->app->make(Bridge\ScopeRepository::class),
$this->makeCryptKey('private'),
app('encrypter')->getKey(),
new BearerTokenResponse()
);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Services;
use Cache;
class MarkerService
{
const CACHE_KEY = 'pf:services:markers:timeline:';
public static function get($profileId, $timeline = 'home')
{
return Cache::get(self::CACHE_KEY . $timeline . ':' . $profileId);
}
public static function set($profileId, $timeline = 'home', $entityId)
{
$existing = self::get($profileId, $timeline);
$key = self::CACHE_KEY . $timeline . ':' . $profileId;
$val = [
'last_read_id' => (string) $entityId,
'version' => $existing ? ($existing['version'] + 1) : 1,
'updated_at' => now()->format('c')
];
Cache::put($key, $val, 2592000);
return $val;
}
}

View file

@ -73,6 +73,7 @@
"extra": {
"laravel": {
"dont-discover": [
"laravel/passport"
]
}
},

1146
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -165,6 +165,7 @@ return [
App\Providers\HorizonServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\PassportServiceProvider::class,
],

View file

@ -42,7 +42,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
Route::post('accounts/{id}/unmute', 'Api\ApiV1Controller@accountUnmuteById')->middleware($middleware);
Route::get('accounts/{id}/lists', 'Api\ApiV1Controller@accountListsById')->middleware($middleware);
Route::get('lists/{id}/accounts', 'Api\ApiV1Controller@accountListsById')->middleware($middleware);
Route::get('accounts/{id}', 'Api\ApiV1Controller@accountById');
Route::get('accounts/{id}', 'Api\ApiV1Controller@accountById')->middleware($middleware);
Route::post('avatar/update', 'ApiController@avatarUpdate')->middleware($middleware);
Route::get('blocks', 'Api\ApiV1Controller@accountBlocks')->middleware($middleware);
@ -83,6 +83,12 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
Route::get('timelines/public', 'Api\ApiV1Controller@timelinePublic')->middleware($middleware);
Route::get('timelines/tag/{hashtag}', 'Api\ApiV1Controller@timelineHashtag');
Route::get('discover/posts', 'Api\ApiV1Controller@discoverPosts')->middleware($middleware);
Route::get('preferences', 'Api\ApiV1Controller@getPreferences')->middleware($middleware);
Route::get('trends', 'Api\ApiV1Controller@getTrends')->middleware($middleware);
Route::get('announcements', 'Api\ApiV1Controller@getAnnouncements')->middleware($middleware);
Route::get('markers', 'Api\ApiV1Controller@getMarkers')->middleware($middleware);
Route::post('markers', 'Api\ApiV1Controller@setMarkers')->middleware($middleware);
});
Route::group(['prefix' => 'v2'], function() use($middleware) {