Merge pull request #3329 from pixelfed/staging

Staging
This commit is contained in:
daniel 2022-03-23 06:04:16 -06:00 committed by GitHub
commit 0900045d8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 280 additions and 148 deletions

View file

@ -90,6 +90,14 @@
- Updated Inbox, fixes #3314. ([dfcd2e6d](https://github.com/pixelfed/pixelfed/commit/dfcd2e6d)) - Updated Inbox, fixes #3314. ([dfcd2e6d](https://github.com/pixelfed/pixelfed/commit/dfcd2e6d))
- Updated search service, fix banned instance edge case. ([74018e9c](https://github.com/pixelfed/pixelfed/commit/74018e9c)) - Updated search service, fix banned instance edge case. ([74018e9c](https://github.com/pixelfed/pixelfed/commit/74018e9c))
- Updated inbox, fixes #3315. ([c3c3ce18](https://github.com/pixelfed/pixelfed/commit/c3c3ce18)) - Updated inbox, fixes #3315. ([c3c3ce18](https://github.com/pixelfed/pixelfed/commit/c3c3ce18))
- Updated ApiV1Controller, fix instance endpoint. ([c383f100](https://github.com/pixelfed/pixelfed/commit/c383f100))
- Updated ApiV1Controller, marshal json without escaped slashes. ([89303fa4](https://github.com/pixelfed/pixelfed/commit/89303fa4))
- Updated ApiV1Controller, fix statusCreate validator. ([b6b15b0c](https://github.com/pixelfed/pixelfed/commit/b6b15b0c))
- Updated ApiV1Controller, fix notification entities. ([afe903c3](https://github.com/pixelfed/pixelfed/commit/afe903c3))
- Updated FederationController, fix webfinger endpoint. ([a0e15d89](https://github.com/pixelfed/pixelfed/commit/a0e15d89))
- Updated ApiV1Controller, fix context entities. ([b1ab41e0](https://github.com/pixelfed/pixelfed/commit/b1ab41e0))
- Updated ApiV1Controller, fix timeline default limit. ([a87f8301](https://github.com/pixelfed/pixelfed/commit/a87f8301))
- Updated ApiV1Controller, fix search v2 entities. ([9dac861e](https://github.com/pixelfed/pixelfed/commit/9dac861e))
- ([](https://github.com/pixelfed/pixelfed/commit/)) - ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.2 (2022-01-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.1...v0.11.2) ## [v0.11.2 (2022-01-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.1...v0.11.2)

View file

@ -91,6 +91,11 @@ class ApiV1Controller extends Controller
$this->fractal->setSerializer(new ArraySerializer()); $this->fractal->setSerializer(new ArraySerializer());
} }
public function json($res, $code = 200, $headers = [])
{
return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
}
public function apps(Request $request) public function apps(Request $request)
{ {
abort_if(!config_cache('pixelfed.oauth_enabled'), 404); abort_if(!config_cache('pixelfed.oauth_enabled'), 404);
@ -126,7 +131,7 @@ class ApiV1Controller extends Controller
'vapid_key' => null 'vapid_key' => null
]; ];
return response()->json($res, 200, [ return $this->json($res, 200, [
'Access-Control-Allow-Origin' => '*' 'Access-Control-Allow-Origin' => '*'
]); ]);
} }
@ -154,7 +159,7 @@ class ApiV1Controller extends Controller
'fields' => [] 'fields' => []
]; ];
return response()->json($res); return $this->json($res);
} }
/** /**
@ -170,7 +175,7 @@ class ApiV1Controller extends Controller
if(!$res) { if(!$res) {
return response()->json(['error' => 'Record not found'], 404); return response()->json(['error' => 'Record not found'], 404);
} }
return response()->json($res); return $this->json($res);
} }
/** /**
@ -402,7 +407,7 @@ class ApiV1Controller extends Controller
$res['bio'] = strip_tags($res['note']); $res['bio'] = strip_tags($res['note']);
$res = array_merge($res, $other); $res = array_merge($res, $other);
return response()->json($res); return $this->json($res);
} }
/** /**
@ -449,7 +454,7 @@ class ApiV1Controller extends Controller
->values() ->values()
->toArray(); ->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -496,7 +501,7 @@ class ApiV1Controller extends Controller
->values() ->values()
->toArray(); ->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -582,7 +587,7 @@ class ApiV1Controller extends Controller
}) })
->values(); ->values();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -622,7 +627,7 @@ class ApiV1Controller extends Controller
// Following already, return empty relationship // Following already, return empty relationship
if($isFollowing == true) { if($isFollowing == true) {
$res = RelationshipService::get($user->profile_id, $target->id) ?? []; $res = RelationshipService::get($user->profile_id, $target->id) ?? [];
return response()->json($res); return $this->json($res);
} }
// Rate limits, max 7500 followers per account // Rate limits, max 7500 followers per account
@ -672,7 +677,7 @@ class ApiV1Controller extends Controller
$res = RelationshipService::get($user->profile_id, $target->id); $res = RelationshipService::get($user->profile_id, $target->id);
return response()->json($res); return $this->json($res);
} }
/** /**
@ -703,7 +708,7 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Item($target, new RelationshipTransformer()); $resource = new Fractal\Resource\Item($target, new RelationshipTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
// Rate limits, follow 30 accounts per hour max // Rate limits, follow 30 accounts per hour max
@ -742,7 +747,7 @@ class ApiV1Controller extends Controller
$res = RelationshipService::get($user->profile_id, $target->id); $res = RelationshipService::get($user->profile_id, $target->id);
return response()->json($res); return $this->json($res);
} }
/** /**
@ -768,7 +773,7 @@ class ApiV1Controller extends Controller
->map(function($id) use($pid) { ->map(function($id) use($pid) {
return RelationshipService::get($pid, $id); return RelationshipService::get($pid, $id);
}); });
return response()->json($res); return $this->json($res);
} }
/** /**
@ -802,7 +807,7 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Collection($profiles, new AccountTransformer()); $resource = new Fractal\Resource\Collection($profiles, new AccountTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -834,7 +839,7 @@ class ApiV1Controller extends Controller
$profiles = Profile::findOrFail($blocked); $profiles = Profile::findOrFail($blocked);
$resource = new Fractal\Resource\Collection($profiles, new AccountTransformer()); $resource = new Fractal\Resource\Collection($profiles, new AccountTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -878,7 +883,7 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Item($profile, new RelationshipTransformer()); $resource = new Fractal\Resource\Item($profile, new RelationshipTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -913,13 +918,13 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Item($profile, new RelationshipTransformer()); $resource = new Fractal\Resource\Item($profile, new RelationshipTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
* GET /api/v1/custom_emojis * GET /api/v1/custom_emojis
* *
* Return empty array, we don't support custom emoji * Return custom emoji
* *
* @return array * @return array
*/ */
@ -1003,13 +1008,9 @@ class ApiV1Controller extends Controller
$baseUrl = config('app.url') . '/api/v1/favourites?limit=' . $limit . '&'; $baseUrl = config('app.url') . '/api/v1/favourites?limit=' . $limit . '&';
$link = '<'.$baseUrl.'max_id='.$max.'>; rel="next",<'.$baseUrl.'min_id='.$min.'>; rel="prev"'; $link = '<'.$baseUrl.'max_id='.$max.'>; rel="next",<'.$baseUrl.'min_id='.$min.'>; rel="prev"';
return response() return $this->json($res, 200, ['Link' => $link]);
->json($res)
->withHeaders([
'Link' => $link,
]);
} else { } else {
return response()->json($res); return $this->json($res);
} }
} }
@ -1064,7 +1065,7 @@ class ApiV1Controller extends Controller
$status['favourited'] = true; $status['favourited'] = true;
$status['favourites_count'] = $status['favourites_count'] + 1; $status['favourites_count'] = $status['favourites_count'] + 1;
return response()->json($status); return $this->json($status);
} }
/** /**
@ -1104,7 +1105,7 @@ class ApiV1Controller extends Controller
$res = StatusService::getMastodon($status->id, false); $res = StatusService::getMastodon($status->id, false);
$res['favourited'] = false; $res['favourited'] = false;
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1140,7 +1141,7 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Collection($profiles, new AccountTransformer()); $resource = new Fractal\Resource\Collection($profiles, new AccountTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1236,18 +1237,20 @@ class ApiV1Controller extends Controller
'description' => 'Pixelfed is an image sharing platform, an ethical alternative to centralized platforms', 'description' => 'Pixelfed is an image sharing platform, an ethical alternative to centralized platforms',
'email' => config('instance.email'), 'email' => config('instance.email'),
'version' => '2.7.2 (compatible; Pixelfed ' . config('pixelfed.version') .')', 'version' => '2.7.2 (compatible; Pixelfed ' . config('pixelfed.version') .')',
'urls' => [], 'urls' => [
'streaming_api' => 'wss://' . config('pixelfed.domain.app')
],
'stats' => $stats, 'stats' => $stats,
'thumbnail' => url('headers/default.jpg'), 'thumbnail' => url('img/pixelfed-icon-color.png'),
'languages' => ['en'], 'languages' => ['en'],
'registrations' => (bool) config('pixelfed.open_registration'), 'registrations' => (bool) config_cache('pixelfed.open_registration'),
'approval_required' => false, 'approval_required' => false,
'contact_account' => $contact, 'contact_account' => $contact,
'rules' => $rules 'rules' => $rules
]; ];
}); });
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1400,7 +1403,7 @@ class ApiV1Controller extends Controller
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
$res['preview_url'] = $media->url(). '?cb=1&_v=' . time(); $res['preview_url'] = $media->url(). '?cb=1&_v=' . time();
$res['url'] = $media->url(). '?cb=1&_v=' . time(); $res['url'] = $media->url(). '?cb=1&_v=' . time();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1431,7 +1434,31 @@ class ApiV1Controller extends Controller
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
$res['preview_url'] = url('/storage/no-preview.png'); $res['preview_url'] = url('/storage/no-preview.png');
$res['url'] = url('/storage/no-preview.png'); $res['url'] = url('/storage/no-preview.png');
return response()->json($res); return $this->json($res);
}
/**
* GET /api/v1/media/{id}
*
* @param integer $id
*
* @return MediaTransformer
*/
public function mediaGet(Request $request, $id)
{
abort_if(!$request->user(), 403);
$user = $request->user();
$media = Media::whereUserId($user->id)
->whereNull('status_id')
->findOrFail($id);
$resource = new Fractal\Resource\Item($media, new MediaTransformer());
$res = $this->fractal->createData($resource)->toArray();
$res['preview_url'] = url('/storage/no-preview.png');
$res['url'] = url('/storage/no-preview.png');
return $this->json($res);
} }
/** /**
@ -1461,7 +1488,7 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Collection($accounts, new AccountTransformer()); $resource = new Fractal\Resource\Collection($accounts, new AccountTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1493,7 +1520,7 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Item($account, new RelationshipTransformer()); $resource = new Fractal\Resource\Item($account, new RelationshipTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1527,7 +1554,7 @@ class ApiV1Controller extends Controller
$resource = new Fractal\Resource\Item($account, new RelationshipTransformer()); $resource = new Fractal\Resource\Item($account, new RelationshipTransformer());
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1562,14 +1589,14 @@ class ApiV1Controller extends Controller
$minId = null; $minId = null;
if($max) { if($max) {
$res = NotificationService::getMax($pid, $max, $limit); $res = NotificationService::getMaxMastodon($pid, $max, $limit);
$ids = NotificationService::getRankedMaxId($pid, $max, $limit); $ids = NotificationService::getRankedMaxId($pid, $max, $limit);
if(!empty($ids)) { if(!empty($ids)) {
$maxId = max($ids); $maxId = max($ids);
$minId = min($ids); $minId = min($ids);
} }
} else { } else {
$res = NotificationService::getMin($pid, $min ?? $since, $limit); $res = NotificationService::getMinMastodon($pid, $min ?? $since, $limit);
$ids = NotificationService::getRankedMinId($pid, $min ?? $since, $limit); $ids = NotificationService::getRankedMinId($pid, $min ?? $since, $limit);
if(!empty($ids)) { if(!empty($ids)) {
$maxId = max($ids); $maxId = max($ids);
@ -1600,15 +1627,8 @@ class ApiV1Controller extends Controller
$link = '<'.$baseUrl.'max_id='.$maxId.'>; rel="next",<'.$baseUrl.'min_id='.$minId.'>; rel="prev"'; $link = '<'.$baseUrl.'max_id='.$maxId.'>; rel="next",<'.$baseUrl.'min_id='.$minId.'>; rel="prev"';
} }
$res = response()->json($res); $headers = isset($link) ? ['Link' => $link] : [];
return $this->json($res, 200, $headers);
if(isset($link)) {
$res->withHeaders([
'Link' => $link,
]);
}
return $res;
} }
/** /**
@ -1629,7 +1649,7 @@ class ApiV1Controller extends Controller
$page = $request->input('page'); $page = $request->input('page');
$min = $request->input('min_id'); $min = $request->input('min_id');
$max = $request->input('max_id'); $max = $request->input('max_id');
$limit = $request->input('limit') ?? 3; $limit = $request->input('limit') ?? 20;
$pid = $request->user()->profile_id; $pid = $request->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) {
@ -1704,7 +1724,7 @@ class ApiV1Controller extends Controller
->toArray(); ->toArray();
} }
return response()->json($res); return $this->json($res);
} }
/** /**
@ -1770,7 +1790,7 @@ class ApiV1Controller extends Controller
}) })
->values(); ->values();
return response()->json($dms); return $this->json($dms);
} }
/** /**
@ -1789,7 +1809,7 @@ class ApiV1Controller extends Controller
$min = $request->input('min_id'); $min = $request->input('min_id');
$max = $request->input('max_id'); $max = $request->input('max_id');
$limit = $request->input('limit') ?? 3; $limit = $request->input('limit') ?? 20;
$user = $request->user(); $user = $request->user();
$filtered = $user ? UserFilterService::filters($user->profile_id) : []; $filtered = $user ? UserFilterService::filters($user->profile_id) : [];
@ -1825,7 +1845,8 @@ class ApiV1Controller extends Controller
}) })
->values() ->values()
->toArray(); ->toArray();
return response()->json($res);
return $this->json($res);
} }
/** /**
@ -1859,7 +1880,8 @@ class ApiV1Controller extends Controller
$res['favourited'] = LikeService::liked($user->profile_id, $res['id']); $res['favourited'] = LikeService::liked($user->profile_id, $res['id']);
$res['reblogged'] = ReblogService::get($user->profile_id, $res['id']); $res['reblogged'] = ReblogService::get($user->profile_id, $res['id']);
return response()->json($res);
return $this->json($res);
} }
/** /**
@ -1874,42 +1896,51 @@ class ApiV1Controller extends Controller
abort_if(!$request->user(), 403); abort_if(!$request->user(), 403);
$user = $request->user(); $user = $request->user();
$status = StatusService::getMastodon($id, false);
$status = Status::findOrFail($id); if(!$status || !isset($status['account'])) {
return response('', 404);
}
if($status->profile_id !== $user->profile_id) { if($status['account']['id'] != $user->profile_id) {
if($status->scope == 'private') { if($status['visibility'] == 'private') {
abort_if(!FollowerService::follows($user->profile_id, $status->profile_id), 403); if(!FollowerService::follows($user->profile_id, $status['account']['id'])) {
return response('', 404);
}
} else { } else {
abort_if(!in_array($status->scope, ['public','unlisted']), 403); if(!in_array($status['visibility'], ['public','unlisted'])) {
return response('', 404);
}
} }
} }
if($status->comments_disabled) { $ancestors = [];
$res = [ $descendants = [];
'ancestors' => [],
'descendants' => [] if($status['in_reply_to_id']) {
]; $ancestors[] = StatusService::getMastodon($status['in_reply_to_id'], false);
} else {
$ancestors = $status->parent();
if($ancestors) {
$ares = new Fractal\Resource\Item($ancestors, new StatusTransformer());
$ancestors = [
$this->fractal->createData($ares)->toArray()
];
} else {
$ancestors = [];
}
$descendants = Status::whereInReplyToId($id)->latest()->limit(20)->get();
$dres = new Fractal\Resource\Collection($descendants, new StatusTransformer());
$descendants = $this->fractal->createData($dres)->toArray();
$res = [
'ancestors' => $ancestors,
'descendants' => $descendants
];
} }
return response()->json($res); if($status['replies_count']) {
$descendants = DB::table('statuses')
->where('in_reply_to_id', $id)
->limit(20)
->pluck('id')
->map(function($sid) {
return StatusService::getMastodon($sid, false);
})
->filter(function($post) {
return $post && isset($post['account']);
})
->values();
}
$res = [
'ancestors' => $ancestors,
'descendants' => $descendants
];
return $this->json($res);
} }
/** /**
@ -1966,7 +1997,10 @@ class ApiV1Controller extends Controller
$res = collect($ids) $res = collect($ids)
->map(function($id) { ->map(function($id) {
$status = StatusService::get($id); $status = StatusService::get($id);
return AccountService::get($status['account']['id']); if($status) {
return AccountService::get($status['account']['id']);
}
return;
}) })
->filter(function($account) { ->filter(function($account) {
return $account && isset($account['id']); return $account && isset($account['id']);
@ -1979,7 +2013,7 @@ class ApiV1Controller extends Controller
$prev = $page > 1 ? $page - 1 : 1; $prev = $page > 1 ? $page - 1 : 1;
$links = '<'.$url.'?page='.$next.'&limit='.$limit.'>; rel="next", <'.$url.'?page='.$prev.'&limit='.$limit.'>; rel="prev"'; $links = '<'.$url.'?page='.$next.'&limit='.$limit.'>; rel="next", <'.$url.'?page='.$prev.'&limit='.$limit.'>; rel="prev"';
return response()->json($res, 200, ['Link' => $links]); return $this->json($res, 200, ['Link' => $links]);
} }
/** /**
@ -2045,7 +2079,7 @@ class ApiV1Controller extends Controller
$prev = $page > 1 ? $page - 1 : 1; $prev = $page > 1 ? $page - 1 : 1;
$links = '<'.$url.'?page='.$next.'&limit='.$limit.'>; rel="next", <'.$url.'?page='.$prev.'&limit='.$limit.'>; rel="prev"'; $links = '<'.$url.'?page='.$next.'&limit='.$limit.'>; rel="next", <'.$url.'?page='.$prev.'&limit='.$limit.'>; rel="prev"';
return response()->json($res, 200, ['Link' => $links]); return $this->json($res, 200, ['Link' => $links]);
} }
/** /**
@ -2060,10 +2094,9 @@ class ApiV1Controller extends Controller
$this->validate($request, [ $this->validate($request, [
'status' => 'nullable|string', 'status' => 'nullable|string',
'in_reply_to_id' => 'nullable|integer', 'in_reply_to_id' => 'nullable',
'media_ids' => 'array|max:' . config_cache('pixelfed.max_album_length'), 'media_ids' => 'sometimes|array|max:' . config_cache('pixelfed.max_album_length'),
'media_ids.*' => 'integer|min:1', 'sensitive' => 'nullable',
'sensitive' => 'nullable|boolean',
'visibility' => 'string|in:private,unlisted,public', 'visibility' => 'string|in:private,unlisted,public',
]); ]);
@ -2192,9 +2225,8 @@ class ApiV1Controller extends Controller
Cache::forget('profile:embed:' . $status->profile_id); Cache::forget('profile:embed:' . $status->profile_id);
Cache::forget($limitKey); Cache::forget($limitKey);
$resource = new Fractal\Resource\Item($status, new StatusTransformer()); $res = StatusService::getMastodon($status->id, false);
$res = $this->fractal->createData($resource)->toArray(); return $this->json($res);
return response()->json($res);
} }
/** /**
@ -2219,7 +2251,8 @@ class ApiV1Controller extends Controller
$res = $this->fractal->createData($resource)->toArray(); $res = $this->fractal->createData($resource)->toArray();
$res['text'] = $res['content']; $res['text'] = $res['content'];
unset($res['content']); unset($res['content']);
return response()->json($res);
return $this->json($res);
} }
/** /**
@ -2262,7 +2295,7 @@ class ApiV1Controller extends Controller
$res = StatusService::getMastodon($status->id); $res = StatusService::getMastodon($status->id);
$res['reblogged'] = true; $res['reblogged'] = true;
return response()->json($res); return $this->json($res);
} }
/** /**
@ -2292,17 +2325,18 @@ class ApiV1Controller extends Controller
->first(); ->first();
if(!$reblog) { if(!$reblog) {
$resource = new Fractal\Resource\Item($status, new StatusTransformer()); $res = StatusService::getMastodon($status->id);
$res = $this->fractal->createData($resource)->toArray(); $res['reblogged'] = false;
return response()->json($res); return $this->json($res);
} }
UndoSharePipeline::dispatch($reblog); UndoSharePipeline::dispatch($reblog);
ReblogService::del($user->profile_id, $status->id); ReblogService::del($user->profile_id, $status->id);
$res = StatusService::getMastodon($status->id); $res = StatusService::getMastodon($status->id);
$res['reblogged'] = true; $res['reblogged'] = false;
return response()->json($res);
return $this->json($res);
} }
/** /**
@ -2358,7 +2392,7 @@ class ApiV1Controller extends Controller
->values() ->values()
->toArray(); ->toArray();
return response()->json($res, 200, [], JSON_PRETTY_PRINT); return $this->json($res);
} }
/** /**
@ -2404,7 +2438,8 @@ class ApiV1Controller extends Controller
foreach($bookmarks as $id) { foreach($bookmarks as $id) {
$res[] = \App\Services\StatusService::getMastodon($id); $res[] = \App\Services\StatusService::getMastodon($id);
} }
return $res;
return $this->json($res);
} }
/** /**
@ -2426,9 +2461,9 @@ class ApiV1Controller extends Controller
'status_id' => $status->id, 'status_id' => $status->id,
'profile_id' => $request->user()->profile_id 'profile_id' => $request->user()->profile_id
]); ]);
$resource = new Fractal\Resource\Item($status, new StatusTransformer()); $res = StatusService::getMastodon($status->id);
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res); return $this->json($res);
} }
/** /**
@ -2446,18 +2481,13 @@ class ApiV1Controller extends Controller
->whereScope('public') ->whereScope('public')
->findOrFail($id); ->findOrFail($id);
Bookmark::firstOrCreate([
'status_id' => $status->id,
'profile_id' => $request->user()->profile_id
]);
$bookmark = Bookmark::whereStatusId($status->id) $bookmark = Bookmark::whereStatusId($status->id)
->whereProfileId($request->user()->profile_id) ->whereProfileId($request->user()->profile_id)
->firstOrFail(); ->firstOrFail();
$bookmark->delete(); $bookmark->delete();
$res = StatusService::getMastodon($status->id);
$resource = new Fractal\Resource\Item($status, new StatusTransformer()); return $this->json($res);
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res);
} }
/** /**
@ -2483,7 +2513,7 @@ class ApiV1Controller extends Controller
'following' => 'nullable' 'following' => 'nullable'
]); ]);
return SearchApiV2Service::query($request); return $this->json(SearchApiV2Service::query($request, true));
} }
/** /**
@ -2515,7 +2545,7 @@ class ApiV1Controller extends Controller
}) })
->take(12) ->take(12)
->values(); ->values();
return response()->json(compact('posts')); return $this->json(compact('posts'));
} }
/** /**
@ -2600,7 +2630,7 @@ class ApiV1Controller extends Controller
'next' => $ids->nextPageUrl() 'next' => $ids->nextPageUrl()
]; ];
return $res; return $this->json($res);
} }
/** /**
@ -2617,7 +2647,7 @@ class ApiV1Controller extends Controller
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
abort_if(!in_array($status->scope, ['public', 'unlisted', 'private']), 404); abort_if(!in_array($status->scope, ['public', 'unlisted', 'private']), 404);
return StatusService::getState($status->id, $pid); return $this->json(StatusService::getState($status->id, $pid));
} }
/** /**
@ -2650,6 +2680,6 @@ class ApiV1Controller extends Controller
->take(6) ->take(6)
->values(); ->values();
return response()->json($ids, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); return $this->json($ids);
} }
} }

View file

@ -48,9 +48,12 @@ class FederationController extends Controller
public function webfinger(Request $request) public function webfinger(Request $request)
{ {
abort_if(!config('federation.webfinger.enabled'), 400); if (!config('federation.webfinger.enabled') ||
!$request->has('resource') ||
abort_if(!$request->has('resource') || !$request->filled('resource'), 400); !$request->filled('resource')
) {
return response('', 400);
}
$resource = $request->input('resource'); $resource = $request->input('resource');
$hash = hash('sha256', $resource); $hash = hash('sha256', $resource);
@ -59,14 +62,18 @@ class FederationController extends Controller
return response()->json($cached, 200, [], JSON_UNESCAPED_SLASHES); return response()->json($cached, 200, [], JSON_UNESCAPED_SLASHES);
} }
$domain = config('pixelfed.domain.app'); $domain = config('pixelfed.domain.app');
abort_if(strpos($resource, $domain) == false, 400); if(strpos($resource, $domain) == false) {
return response('', 400);
}
$parsed = Nickname::normalizeProfileUrl($resource); $parsed = Nickname::normalizeProfileUrl($resource);
if(empty($parsed) || $parsed['domain'] !== $domain) { if(empty($parsed) || $parsed['domain'] !== $domain) {
abort(400); return response('', 400);
} }
$username = $parsed['username']; $username = $parsed['username'];
$profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail(); $profile = Profile::whereNull('domain')->whereUsername($username)->first();
abort_if($profile->status != null, 400); if(!$profile || $profile->status !== null) {
return response('', 400);
}
$webfinger = (new Webfinger($profile))->generate(); $webfinger = (new Webfinger($profile))->generate();
Cache::put($key, $webfinger, 1209600); Cache::put($key, $webfinger, 1209600);

View file

@ -16,6 +16,15 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
class NotificationService { class NotificationService {
const CACHE_KEY = 'pf:services:notifications:ids:'; const CACHE_KEY = 'pf:services:notifications:ids:';
const MASTODON_TYPES = [
'follow',
'follow_request',
'mention',
'reblog',
'favourite',
'poll',
'status'
];
public static function get($id, $start = 0, $stop = 400) public static function get($id, $start = 0, $stop = 400)
{ {
@ -85,6 +94,63 @@ class NotificationService {
return $res->toArray(); return $res->toArray();
} }
public static function getMaxMastodon($id = false, $start = 0, $limit = 10)
{
$ids = self::getRankedMaxId($id, $start, $limit);
if(empty($ids)) {
return [];
}
$res = collect([]);
foreach($ids as $id) {
$n = self::getNotification($id);
if($n != null && in_array($n['type'], self::MASTODON_TYPES)) {
$n['account'] = AccountService::getMastodon($n['account']['id']);
if(isset($n['relationship'])) {
unset($n['relationship']);
}
if(isset($n['status'])) {
$n['status'] = StatusService::getMastodon($n['status']['id'], false);
}
$res->push($n);
}
}
return $res->toArray();
}
public static function getMinMastodon($id = false, $start = 0, $limit = 10)
{
$ids = self::getRankedMinId($id, $start, $limit);
if(empty($ids)) {
return [];
}
$res = collect([]);
foreach($ids as $id) {
$n = self::getNotification($id);
if($n != null && in_array($n['type'], self::MASTODON_TYPES)) {
$n['account'] = AccountService::getMastodon($n['account']['id']);
if(isset($n['relationship'])) {
unset($n['relationship']);
}
if(isset($n['status'])) {
$n['status'] = StatusService::getMastodon($n['status']['id'], false);
}
$res->push($n);
}
}
return $res->toArray();
}
public static function getRankedMaxId($id = false, $start = null, $limit = 10) public static function getRankedMaxId($id = false, $start = null, $limit = 10)
{ {
if(!$start || !$id) { if(!$start || !$id) {

View file

@ -19,9 +19,11 @@ use App\Services\StatusService;
class SearchApiV2Service class SearchApiV2Service
{ {
private $query; private $query;
static $mastodonMode = false;
public static function query($query) public static function query($query, $mastodonMode = false)
{ {
self::$mastodonMode = $mastodonMode;
return (new self)->run($query); return (new self)->run($query);
} }
@ -81,6 +83,7 @@ class SearchApiV2Service
protected function accounts() protected function accounts()
{ {
$mastodonMode = self::$mastodonMode;
$user = request()->user(); $user = request()->user();
$limit = $this->query->input('limit') ?? 20; $limit = $this->query->input('limit') ?? 20;
$offset = $this->query->input('offset') ?? 0; $offset = $this->query->input('offset') ?? 0;
@ -93,6 +96,7 @@ class SearchApiV2Service
->where('followers.profile_id', $user->profile_id); ->where('followers.profile_id', $user->profile_id);
}) })
->where('username', 'like', $query) ->where('username', 'like', $query)
->orderBy('domain')
->orderByDesc('profiles.followers_count') ->orderByDesc('profiles.followers_count')
->orderByDesc('followers.created_at') ->orderByDesc('followers.created_at')
->offset($offset) ->offset($offset)
@ -101,8 +105,10 @@ class SearchApiV2Service
->filter(function($profile) use ($banned) { ->filter(function($profile) use ($banned) {
return in_array($profile->domain, $banned) == false; return in_array($profile->domain, $banned) == false;
}) })
->map(function($res) { ->map(function($res) use($mastodonMode) {
return AccountService::get($res['id']); return $mastodonMode ?
AccountService::getMastodon($res['id']) :
AccountService::get($res['id']);
}) })
->filter(function($account) { ->filter(function($account) {
return $account && isset($account['id']); return $account && isset($account['id']);
@ -114,6 +120,7 @@ class SearchApiV2Service
protected function hashtags() protected function hashtags()
{ {
$mastodonMode = self::$mastodonMode;
$limit = $this->query->input('limit') ?? 20; $limit = $this->query->input('limit') ?? 20;
$offset = $this->query->input('offset') ?? 0; $offset = $this->query->input('offset') ?? 0;
$query = '%' . $this->query->input('q') . '%'; $query = '%' . $this->query->input('q') . '%';
@ -122,13 +129,18 @@ class SearchApiV2Service
->offset($offset) ->offset($offset)
->limit($limit) ->limit($limit)
->get() ->get()
->map(function($tag) { ->map(function($tag) use($mastodonMode) {
return [ $res = [
'name' => $tag->name, 'name' => $tag->name,
'url' => $tag->url(), 'url' => $tag->url()
'count' => HashtagService::count($tag->id),
'history' => []
]; ];
if(!$mastodonMode) {
$res['history'] = [];
$res['count'] = HashtagService::count($tag->id);
}
return $res;
}); });
} }
@ -140,6 +152,7 @@ class SearchApiV2Service
protected function statusesById() protected function statusesById()
{ {
$mastodonMode = self::$mastodonMode;
$accountId = $this->query->input('account_id'); $accountId = $this->query->input('account_id');
$limit = $this->query->input('limit', 20); $limit = $this->query->input('limit', 20);
$query = '%' . $this->query->input('q') . '%'; $query = '%' . $this->query->input('q') . '%';
@ -147,8 +160,10 @@ class SearchApiV2Service
->whereProfileId($accountId) ->whereProfileId($accountId)
->limit($limit) ->limit($limit)
->get() ->get()
->map(function($status) { ->map(function($status) use($mastodonMode) {
return StatusService::get($status->id); return $mastodonMode ?
StatusService::getMastodon($status->id) :
StatusService::get($status->id);
}) })
->filter(function($status) { ->filter(function($status) {
return $status && isset($status['account']); return $status && isset($status['account']);
@ -158,6 +173,7 @@ class SearchApiV2Service
protected function resolveQuery() protected function resolveQuery()
{ {
$mastodonMode = self::$mastodonMode;
$query = urldecode($this->query->input('q')); $query = urldecode($this->query->input('q'));
if(Helpers::validateLocalUrl($query)) { if(Helpers::validateLocalUrl($query)) {
if(Str::contains($query, '/p/')) { if(Str::contains($query, '/p/')) {
@ -177,7 +193,7 @@ class SearchApiV2Service
if(Str::substrCount($query, '@') == 2) { if(Str::substrCount($query, '@') == 2) {
try { try {
$res = WebfingerService::lookup($query); $res = WebfingerService::lookup($query, $mastodonMode);
} catch (\Exception $e) { } catch (\Exception $e) {
return $default; return $default;
} }
@ -209,7 +225,9 @@ class SearchApiV2Service
if(!$obj || !isset($obj['id'])) { if(!$obj || !isset($obj['id'])) {
return $default; return $default;
} }
$note = StatusService::get($obj['id']); $note = $mastodonMode ?
StatusService::getMastodon($obj['id']) :
StatusService::get($obj['id']);
if(!$note) { if(!$note) {
return $default; return $default;
} }
@ -225,7 +243,9 @@ class SearchApiV2Service
if(in_array($obj['domain'], $banned)) { if(in_array($obj['domain'], $banned)) {
return $default; return $default;
} }
$default['accounts'][] = AccountService::get($obj['id']); $default['accounts'][] = $mastodonMode ?
AccountService::getMastodon($obj['id']) :
AccountService::get($obj['id']);
return $default; return $default;
break; break;
@ -254,10 +274,7 @@ class SearchApiV2Service
{ {
$query = urldecode($this->query->input('q')); $query = urldecode($this->query->input('q'));
$query = last(explode('/', $query)); $query = last(explode('/', $query));
$status = Status::whereNull('uri') $status = StatusService::getMastodon($query);
->whereScope('public')
->find($query);
if(!$status) { if(!$status) {
return [ return [
'accounts' => [], 'accounts' => [],
@ -266,14 +283,13 @@ class SearchApiV2Service
]; ];
} }
$fractal = new Fractal\Manager(); $res = [
$fractal->setSerializer(new ArraySerializer());
$resource = new Fractal\Resource\Item($status, new StatusTransformer());
return [
'accounts' => [], 'accounts' => [],
'hashtags' => [], 'hashtags' => [],
'statuses' => $fractal->createData($resource)->toArray() 'statuses' => [$status]
]; ];
return $res;
} }
protected function resolveLocalProfile() protected function resolveLocalProfile()

View file

@ -11,15 +11,17 @@ use App\Services\AccountService;
class WebfingerService class WebfingerService
{ {
public static function lookup($query) public static function lookup($query, $mastodonMode = false)
{ {
return (new self)->run($query); return (new self)->run($query, $mastodonMode);
} }
protected function run($query) protected function run($query, $mastodonMode)
{ {
if($profile = Profile::whereUsername($query)->first()) { if($profile = Profile::whereUsername($query)->first()) {
return AccountService::get($profile->id); return $mastodonMode ?
AccountService::getMastodon($profile->id, true) :
AccountService::get($profile->id);
} }
$url = WebfingerUrl::generateWebfingerUrl($query); $url = WebfingerUrl::generateWebfingerUrl($query);
if(!Helpers::validateUrl($url)) { if(!Helpers::validateUrl($url)) {
@ -54,6 +56,8 @@ class WebfingerService
->first(); ->first();
$profile = Helpers::profileFetch($link); $profile = Helpers::profileFetch($link);
return AccountService::get($profile->id); return $mastodonMode ?
AccountService::getMastodon($profile->id, true) :
AccountService::get($profile->id);
} }
} }

View file

@ -24,6 +24,7 @@ class AccountTransformer extends Fractal\TransformerAbstract
'username' => $username, 'username' => $username,
'acct' => $acct, 'acct' => $acct,
'display_name' => $profile->name, 'display_name' => $profile->name,
'discoverable' => true,
'locked' => (bool) $profile->is_private, 'locked' => (bool) $profile->is_private,
'followers_count' => (int) $profile->followerCount(), 'followers_count' => (int) $profile->followerCount(),
'following_count' => (int) $profile->followingCount(), 'following_count' => (int) $profile->followingCount(),