BaseApiController.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use Illuminate\Http\Request;
  4. use App\Http\Controllers\{
  5. Controller,
  6. AvatarController
  7. };
  8. use Auth, Cache, Storage, URL;
  9. use Carbon\Carbon;
  10. use App\{
  11. Avatar,
  12. Like,
  13. Media,
  14. Notification,
  15. Profile,
  16. Status,
  17. StatusArchived
  18. };
  19. use App\Transformer\Api\{
  20. AccountTransformer,
  21. NotificationTransformer,
  22. MediaTransformer,
  23. MediaDraftTransformer,
  24. StatusTransformer,
  25. StatusStatelessTransformer
  26. };
  27. use League\Fractal;
  28. use App\Util\Media\Filter;
  29. use League\Fractal\Serializer\ArraySerializer;
  30. use League\Fractal\Pagination\IlluminatePaginatorAdapter;
  31. use App\Jobs\AvatarPipeline\AvatarOptimize;
  32. use App\Jobs\ImageOptimizePipeline\ImageOptimize;
  33. use App\Jobs\VideoPipeline\{
  34. VideoOptimize,
  35. VideoPostProcess,
  36. VideoThumbnail
  37. };
  38. use App\Services\NotificationService;
  39. use App\Services\MediaPathService;
  40. use App\Services\MediaBlocklistService;
  41. use App\Services\StatusService;
  42. class BaseApiController extends Controller
  43. {
  44. protected $fractal;
  45. public function __construct()
  46. {
  47. // $this->middleware('auth');
  48. $this->fractal = new Fractal\Manager();
  49. $this->fractal->setSerializer(new ArraySerializer());
  50. }
  51. public function notifications(Request $request)
  52. {
  53. abort_if(!$request->user(), 403);
  54. $pid = $request->user()->profile_id;
  55. $limit = $request->input('limit', 20);
  56. $since = $request->input('since_id');
  57. $min = $request->input('min_id');
  58. $max = $request->input('max_id');
  59. if(!$since && !$min && !$max) {
  60. $min = 1;
  61. }
  62. $maxId = null;
  63. $minId = null;
  64. if($max) {
  65. $res = NotificationService::getMax($pid, $max, $limit);
  66. $ids = NotificationService::getRankedMaxId($pid, $max, $limit);
  67. if(!empty($ids)) {
  68. $maxId = max($ids);
  69. $minId = min($ids);
  70. }
  71. } else {
  72. $res = NotificationService::getMin($pid, $min ?? $since, $limit);
  73. $ids = NotificationService::getRankedMinId($pid, $min ?? $since, $limit);
  74. if(!empty($ids)) {
  75. $maxId = max($ids);
  76. $minId = min($ids);
  77. }
  78. }
  79. if(empty($res) && !Cache::has('pf:services:notifications:hasSynced:'.$pid)) {
  80. Cache::put('pf:services:notifications:hasSynced:'.$pid, 1, 1209600);
  81. NotificationService::warmCache($pid, 400, true);
  82. }
  83. return response()->json($res);
  84. }
  85. public function accounts(Request $request, $id)
  86. {
  87. abort_if(!$request->user(), 403);
  88. $profile = Profile::findOrFail($id);
  89. $resource = new Fractal\Resource\Item($profile, new AccountTransformer());
  90. $res = $this->fractal->createData($resource)->toArray();
  91. return response()->json($res);
  92. }
  93. public function accountFollowers(Request $request, $id)
  94. {
  95. abort_if(!$request->user(), 403);
  96. $profile = Profile::findOrFail($id);
  97. $followers = $profile->followers;
  98. $resource = new Fractal\Resource\Collection($followers, new AccountTransformer());
  99. $res = $this->fractal->createData($resource)->toArray();
  100. return response()->json($res);
  101. }
  102. public function accountFollowing(Request $request, $id)
  103. {
  104. abort_if(!$request->user(), 403);
  105. $profile = Profile::findOrFail($id);
  106. $following = $profile->following;
  107. $resource = new Fractal\Resource\Collection($following, new AccountTransformer());
  108. $res = $this->fractal->createData($resource)->toArray();
  109. return response()->json($res);
  110. }
  111. public function accountStatuses(Request $request, $id)
  112. {
  113. abort_if(!$request->user(), 403);
  114. $this->validate($request, [
  115. 'only_media' => 'nullable',
  116. 'pinned' => 'nullable',
  117. 'exclude_replies' => 'nullable',
  118. 'max_id' => 'nullable|integer|min:1',
  119. 'since_id' => 'nullable|integer|min:1',
  120. 'min_id' => 'nullable|integer|min:1',
  121. 'limit' => 'nullable|integer|min:1|max:24'
  122. ]);
  123. $limit = $request->limit ?? 20;
  124. $max_id = $request->max_id ?? false;
  125. $min_id = $request->min_id ?? false;
  126. $since_id = $request->since_id ?? false;
  127. $only_media = $request->only_media ?? false;
  128. $user = Auth::user();
  129. $account = Profile::whereNull('status')->findOrFail($id);
  130. $statuses = $account->statuses()->getQuery();
  131. if($only_media == true) {
  132. $statuses = $statuses
  133. ->whereIn('scope', ['public','unlisted'])
  134. ->whereHas('media')
  135. ->whereNull('in_reply_to_id')
  136. ->whereNull('reblog_of_id');
  137. }
  138. if($id == $account->id && !$max_id && !$min_id && !$since_id) {
  139. $statuses = $statuses->orderBy('id', 'desc')
  140. ->paginate($limit);
  141. } else if($since_id) {
  142. $statuses = $statuses->where('id', '>', $since_id)
  143. ->orderBy('id', 'DESC')
  144. ->paginate($limit);
  145. } else if($min_id) {
  146. $statuses = $statuses->where('id', '>', $min_id)
  147. ->orderBy('id', 'ASC')
  148. ->paginate($limit);
  149. } else if($max_id) {
  150. $statuses = $statuses->where('id', '<', $max_id)
  151. ->orderBy('id', 'DESC')
  152. ->paginate($limit);
  153. } else {
  154. $statuses = $statuses->whereScope('public')->orderBy('id', 'desc')->paginate($limit);
  155. }
  156. $resource = new Fractal\Resource\Collection($statuses, new StatusTransformer());
  157. $res = $this->fractal->createData($resource)->toArray();
  158. return response()->json($res);
  159. }
  160. public function avatarUpdate(Request $request)
  161. {
  162. abort_if(!$request->user(), 403);
  163. $this->validate($request, [
  164. 'upload' => 'required|mimes:jpeg,png,gif|max:'.config('pixelfed.max_avatar_size'),
  165. ]);
  166. try {
  167. $user = Auth::user();
  168. $profile = $user->profile;
  169. $file = $request->file('upload');
  170. $path = (new AvatarController())->getPath($user, $file);
  171. $dir = $path['root'];
  172. $name = $path['name'];
  173. $public = $path['storage'];
  174. $currentAvatar = storage_path('app/'.$profile->avatar->media_path);
  175. $loc = $request->file('upload')->storeAs($public, $name);
  176. $avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
  177. $opath = $avatar->media_path;
  178. $avatar->media_path = "$public/$name";
  179. $avatar->change_count = ++$avatar->change_count;
  180. $avatar->last_processed_at = null;
  181. $avatar->save();
  182. Cache::forget("avatar:{$profile->id}");
  183. AvatarOptimize::dispatch($user->profile, $currentAvatar);
  184. } catch (Exception $e) {
  185. }
  186. return response()->json([
  187. 'code' => 200,
  188. 'msg' => 'Avatar successfully updated',
  189. ]);
  190. }
  191. public function showTempMedia(Request $request, $profileId, $mediaId, $timestamp)
  192. {
  193. abort(400, 'Endpoint deprecated');
  194. }
  195. public function uploadMedia(Request $request)
  196. {
  197. abort(400, 'Endpoint deprecated');
  198. }
  199. public function deleteMedia(Request $request)
  200. {
  201. abort(400, 'Endpoint deprecated');
  202. }
  203. public function verifyCredentials(Request $request)
  204. {
  205. $user = $request->user();
  206. abort_if(!$user, 403);
  207. if($user->status != null) {
  208. Auth::logout();
  209. return redirect('/login');
  210. }
  211. $key = 'user:last_active_at:id:'.$user->id;
  212. $ttl = now()->addMinutes(5);
  213. Cache::remember($key, $ttl, function() use($user) {
  214. $user->last_active_at = now();
  215. $user->save();
  216. return;
  217. });
  218. $resource = new Fractal\Resource\Item($user->profile, new AccountTransformer());
  219. $res = $this->fractal->createData($resource)->toArray();
  220. return response()->json($res);
  221. }
  222. public function drafts(Request $request)
  223. {
  224. $user = $request->user();
  225. abort_if(!$request->user(), 403);
  226. $medias = Media::whereUserId($user->id)
  227. ->whereNull('status_id')
  228. ->latest()
  229. ->take(13)
  230. ->get();
  231. $resource = new Fractal\Resource\Collection($medias, new MediaDraftTransformer());
  232. $res = $this->fractal->createData($resource)->toArray();
  233. return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
  234. }
  235. public function accountLikes(Request $request)
  236. {
  237. $user = $request->user();
  238. abort_if(!$request->user(), 403);
  239. $limit = 10;
  240. $page = (int) $request->input('page', 1);
  241. if($page > 20) {
  242. return [];
  243. }
  244. $favourites = $user->profile->likes()
  245. ->latest()
  246. ->simplePaginate($limit)
  247. ->pluck('status_id');
  248. $statuses = Status::find($favourites)->reverse();
  249. $resource = new Fractal\Resource\Collection($statuses, new StatusStatelessTransformer());
  250. $res = $this->fractal->createData($resource)->toArray();
  251. return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
  252. }
  253. public function archive(Request $request, $id)
  254. {
  255. abort_if(!$request->user(), 403);
  256. $status = Status::whereNull('in_reply_to_id')
  257. ->whereNull('reblog_of_id')
  258. ->whereProfileId($request->user()->profile_id)
  259. ->findOrFail($id);
  260. if($status->scope === 'archived') {
  261. return [200];
  262. }
  263. $archive = new StatusArchived;
  264. $archive->status_id = $status->id;
  265. $archive->profile_id = $status->profile_id;
  266. $archive->original_scope = $status->scope;
  267. $archive->save();
  268. $status->scope = 'archived';
  269. $status->visibility = 'draft';
  270. $status->save();
  271. StatusService::del($status->id);
  272. // invalidate caches
  273. return [200];
  274. }
  275. public function unarchive(Request $request, $id)
  276. {
  277. abort_if(!$request->user(), 403);
  278. $status = Status::whereNull('in_reply_to_id')
  279. ->whereNull('reblog_of_id')
  280. ->whereProfileId($request->user()->profile_id)
  281. ->findOrFail($id);
  282. if($status->scope !== 'archived') {
  283. return [200];
  284. }
  285. $archive = StatusArchived::whereStatusId($status->id)
  286. ->whereProfileId($status->profile_id)
  287. ->firstOrFail();
  288. $status->scope = $archive->original_scope;
  289. $status->visibility = $archive->original_scope;
  290. $status->save();
  291. $archive->delete();
  292. return [200];
  293. }
  294. public function archivedPosts(Request $request)
  295. {
  296. abort_if(!$request->user(), 403);
  297. $statuses = Status::whereProfileId($request->user()->profile_id)
  298. ->whereScope('archived')
  299. ->orderByDesc('id')
  300. ->simplePaginate(10);
  301. $fractal = new Fractal\Manager();
  302. $fractal->setSerializer(new ArraySerializer());
  303. $resource = new Fractal\Resource\Collection($statuses, new StatusStatelessTransformer());
  304. return $fractal->createData($resource)->toArray();
  305. }
  306. }