AdminReportController.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <?php
  2. namespace App\Http\Controllers\Admin;
  3. use Cache;
  4. use Carbon\Carbon;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Support\Facades\Redis;
  7. use App\Services\AccountService;
  8. use App\Services\StatusService;
  9. use App\{
  10. AccountInterstitial,
  11. Contact,
  12. Hashtag,
  13. Newsroom,
  14. OauthClient,
  15. Profile,
  16. Report,
  17. Status,
  18. Story,
  19. User
  20. };
  21. use Illuminate\Validation\Rule;
  22. use App\Services\StoryService;
  23. trait AdminReportController
  24. {
  25. public function reports(Request $request)
  26. {
  27. $filter = $request->input('filter') == 'closed' ? 'closed' : 'open';
  28. $page = $request->input('page') ?? 1;
  29. $ai = Cache::remember('admin-dash:reports:ai-count', 3600, function() {
  30. return AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count();
  31. });
  32. $spam = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
  33. return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
  34. });
  35. $mailVerifications = Redis::scard('email:manual');
  36. if($filter == 'open' && $page == 1) {
  37. $reports = Cache::remember('admin-dash:reports:list-cache', 300, function() use($page, $filter) {
  38. return Report::whereHas('status')
  39. ->whereHas('reportedUser')
  40. ->whereHas('reporter')
  41. ->orderBy('created_at','desc')
  42. ->when($filter, function($q, $filter) {
  43. return $filter == 'open' ?
  44. $q->whereNull('admin_seen') :
  45. $q->whereNotNull('admin_seen');
  46. })
  47. ->paginate(6);
  48. });
  49. } else {
  50. $reports = Report::whereHas('status')
  51. ->whereHas('reportedUser')
  52. ->whereHas('reporter')
  53. ->orderBy('created_at','desc')
  54. ->when($filter, function($q, $filter) {
  55. return $filter == 'open' ?
  56. $q->whereNull('admin_seen') :
  57. $q->whereNotNull('admin_seen');
  58. })
  59. ->paginate(6);
  60. }
  61. return view('admin.reports.home', compact('reports', 'ai', 'spam', 'mailVerifications'));
  62. }
  63. public function showReport(Request $request, $id)
  64. {
  65. $report = Report::findOrFail($id);
  66. return view('admin.reports.show', compact('report'));
  67. }
  68. public function appeals(Request $request)
  69. {
  70. $appeals = AccountInterstitial::whereNotNull('appeal_requested_at')
  71. ->whereNull('appeal_handled_at')
  72. ->latest()
  73. ->paginate(6);
  74. return view('admin.reports.appeals', compact('appeals'));
  75. }
  76. public function showAppeal(Request $request, $id)
  77. {
  78. $appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
  79. ->whereNull('appeal_handled_at')
  80. ->findOrFail($id);
  81. $meta = json_decode($appeal->meta);
  82. return view('admin.reports.show_appeal', compact('appeal', 'meta'));
  83. }
  84. public function spam(Request $request)
  85. {
  86. $appeals = AccountInterstitial::whereType('post.autospam')
  87. ->whereNull('appeal_handled_at')
  88. ->latest()
  89. ->paginate(6);
  90. return view('admin.reports.spam', compact('appeals'));
  91. }
  92. public function showSpam(Request $request, $id)
  93. {
  94. $appeal = AccountInterstitial::whereType('post.autospam')
  95. ->whereNull('appeal_handled_at')
  96. ->findOrFail($id);
  97. $meta = json_decode($appeal->meta);
  98. return view('admin.reports.show_spam', compact('appeal', 'meta'));
  99. }
  100. public function updateSpam(Request $request, $id)
  101. {
  102. $this->validate($request, [
  103. 'action' => 'required|in:dismiss,approve'
  104. ]);
  105. $action = $request->input('action');
  106. $appeal = AccountInterstitial::whereType('post.autospam')
  107. ->whereNull('appeal_handled_at')
  108. ->findOrFail($id);
  109. $meta = json_decode($appeal->meta);
  110. if($action == 'dismiss') {
  111. $appeal->appeal_handled_at = now();
  112. $appeal->save();
  113. Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
  114. Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
  115. Cache::forget('admin-dash:reports:spam-count');
  116. return redirect('/i/admin/reports/autospam');
  117. }
  118. $status = $appeal->status;
  119. $status->is_nsfw = $meta->is_nsfw;
  120. $status->scope = 'public';
  121. $status->visibility = 'public';
  122. $status->save();
  123. $appeal->appeal_handled_at = now();
  124. $appeal->save();
  125. StatusService::del($status->id);
  126. Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
  127. Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
  128. Cache::forget('admin-dash:reports:spam-count');
  129. return redirect('/i/admin/reports/autospam');
  130. }
  131. public function updateAppeal(Request $request, $id)
  132. {
  133. $this->validate($request, [
  134. 'action' => 'required|in:dismiss,approve'
  135. ]);
  136. $action = $request->input('action');
  137. $appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
  138. ->whereNull('appeal_handled_at')
  139. ->findOrFail($id);
  140. if($action == 'dismiss') {
  141. $appeal->appeal_handled_at = now();
  142. $appeal->save();
  143. Cache::forget('admin-dash:reports:ai-count');
  144. return redirect('/i/admin/reports/appeals');
  145. }
  146. switch ($appeal->type) {
  147. case 'post.cw':
  148. $status = $appeal->status;
  149. $status->is_nsfw = false;
  150. $status->save();
  151. break;
  152. case 'post.unlist':
  153. $status = $appeal->status;
  154. $status->scope = 'public';
  155. $status->visibility = 'public';
  156. $status->save();
  157. break;
  158. default:
  159. # code...
  160. break;
  161. }
  162. $appeal->appeal_handled_at = now();
  163. $appeal->save();
  164. StatusService::del($status->id);
  165. Cache::forget('admin-dash:reports:ai-count');
  166. return redirect('/i/admin/reports/appeals');
  167. }
  168. public function updateReport(Request $request, $id)
  169. {
  170. $this->validate($request, [
  171. 'action' => 'required|string',
  172. ]);
  173. $action = $request->input('action');
  174. $actions = [
  175. 'ignore',
  176. 'cw',
  177. 'unlist',
  178. 'delete',
  179. 'shadowban',
  180. 'ban',
  181. ];
  182. if (!in_array($action, $actions)) {
  183. return abort(403);
  184. }
  185. $report = Report::findOrFail($id);
  186. $this->handleReportAction($report, $action);
  187. Cache::forget('admin-dash:reports:list-cache');
  188. return response()->json(['msg'=> 'Success']);
  189. }
  190. public function handleReportAction(Report $report, $action)
  191. {
  192. $item = $report->reported();
  193. $report->admin_seen = Carbon::now();
  194. switch ($action) {
  195. case 'ignore':
  196. $report->not_interested = true;
  197. break;
  198. case 'cw':
  199. Cache::forget('status:thumb:'.$item->id);
  200. $item->is_nsfw = true;
  201. $item->save();
  202. $report->nsfw = true;
  203. StatusService::del($item->id);
  204. break;
  205. case 'unlist':
  206. $item->visibility = 'unlisted';
  207. $item->save();
  208. Cache::forget('profiles:private');
  209. StatusService::del($item->id);
  210. break;
  211. case 'delete':
  212. // Todo: fire delete job
  213. $report->admin_seen = null;
  214. StatusService::del($item->id);
  215. break;
  216. case 'shadowban':
  217. // Todo: fire delete job
  218. $report->admin_seen = null;
  219. break;
  220. case 'ban':
  221. // Todo: fire delete job
  222. $report->admin_seen = null;
  223. break;
  224. default:
  225. $report->admin_seen = null;
  226. break;
  227. }
  228. $report->save();
  229. return $this;
  230. }
  231. protected function actionMap()
  232. {
  233. return [
  234. '1' => 'ignore',
  235. '2' => 'cw',
  236. '3' => 'unlist',
  237. '4' => 'delete',
  238. '5' => 'shadowban',
  239. '6' => 'ban'
  240. ];
  241. }
  242. public function bulkUpdateReport(Request $request)
  243. {
  244. $this->validate($request, [
  245. 'action' => 'required|integer|min:1|max:10',
  246. 'ids' => 'required|array'
  247. ]);
  248. $action = $this->actionMap()[$request->input('action')];
  249. $ids = $request->input('ids');
  250. $reports = Report::whereIn('id', $ids)->whereNull('admin_seen')->get();
  251. foreach($reports as $report) {
  252. $this->handleReportAction($report, $action);
  253. }
  254. $res = [
  255. 'message' => 'Success',
  256. 'code' => 200
  257. ];
  258. return response()->json($res);
  259. }
  260. public function reportMailVerifications(Request $request)
  261. {
  262. $ids = Redis::smembers('email:manual');
  263. $ignored = Redis::smembers('email:manual-ignored');
  264. $reports = [];
  265. if($ids) {
  266. $reports = collect($ids)
  267. ->filter(function($id) use($ignored) {
  268. return !in_array($id, $ignored);
  269. })
  270. ->map(function($id) {
  271. $account = AccountService::get($id);
  272. $user = User::whereProfileId($id)->first();
  273. if(!$user) {
  274. return [];
  275. }
  276. $account['email'] = $user->email;
  277. return $account;
  278. })
  279. ->filter(function($res) {
  280. return isset($res['id']);
  281. })
  282. ->values();
  283. }
  284. return view('admin.reports.mail_verification', compact('reports', 'ignored'));
  285. }
  286. public function reportMailVerifyIgnore(Request $request)
  287. {
  288. $id = $request->input('id');
  289. Redis::sadd('email:manual-ignored', $id);
  290. return redirect('/i/admin/reports');
  291. }
  292. public function reportMailVerifyApprove(Request $request)
  293. {
  294. $id = $request->input('id');
  295. $user = User::whereProfileId($id)->firstOrFail();
  296. Redis::srem('email:manual', $id);
  297. Redis::srem('email:manual-ignored', $id);
  298. $user->email_verified_at = now();
  299. $user->save();
  300. return redirect('/i/admin/reports');
  301. }
  302. public function reportMailVerifyClearIgnored(Request $request)
  303. {
  304. Redis::del('email:manual-ignored');
  305. return [200];
  306. }
  307. }