1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219 |
- <?php
- namespace App\Http\Controllers\Admin;
- use Cache;
- use Carbon\Carbon;
- use Illuminate\Http\Request;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Redis;
- use App\Services\AccountService;
- use App\Services\StatusService;
- use App\{
- AccountInterstitial,
- Contact,
- Hashtag,
- Newsroom,
- Notification,
- OauthClient,
- Profile,
- Report,
- Status,
- Story,
- User
- };
- use Illuminate\Validation\Rule;
- use App\Services\StoryService;
- use App\Services\ModLogService;
- use App\Jobs\DeletePipeline\DeleteAccountPipeline;
- use App\Jobs\DeletePipeline\DeleteRemoteProfilePipeline;
- use App\Jobs\DeletePipeline\DeleteRemoteStatusPipeline;
- use App\Jobs\StatusPipeline\StatusDelete;
- use App\Http\Resources\AdminReport;
- use App\Http\Resources\AdminSpamReport;
- use App\Services\NotificationService;
- use App\Services\PublicTimelineService;
- use App\Services\NetworkTimelineService;
- trait AdminReportController
- {
- public function reports(Request $request)
- {
- $filter = $request->input('filter') == 'closed' ? 'closed' : 'open';
- $page = $request->input('page') ?? 1;
- $ai = Cache::remember('admin-dash:reports:ai-count', 3600, function() {
- return AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count();
- });
- $spam = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
- return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
- });
- $mailVerifications = Redis::scard('email:manual');
- if($filter == 'open' && $page == 1) {
- $reports = Cache::remember('admin-dash:reports:list-cache', 300, function() use($page, $filter) {
- return Report::whereHas('status')
- ->whereHas('reportedUser')
- ->whereHas('reporter')
- ->orderBy('created_at','desc')
- ->when($filter, function($q, $filter) {
- return $filter == 'open' ?
- $q->whereNull('admin_seen') :
- $q->whereNotNull('admin_seen');
- })
- ->paginate(6);
- });
- } else {
- $reports = Report::whereHas('status')
- ->whereHas('reportedUser')
- ->whereHas('reporter')
- ->orderBy('created_at','desc')
- ->when($filter, function($q, $filter) {
- return $filter == 'open' ?
- $q->whereNull('admin_seen') :
- $q->whereNotNull('admin_seen');
- })
- ->paginate(6);
- }
- return view('admin.reports.home', compact('reports', 'ai', 'spam', 'mailVerifications'));
- }
- public function showReport(Request $request, $id)
- {
- $report = Report::with('status')->findOrFail($id);
- if($request->has('ref') && $request->input('ref') == 'email') {
- return redirect('/i/admin/reports?tab=report&id=' . $report->id);
- }
- return view('admin.reports.show', compact('report'));
- }
- public function appeals(Request $request)
- {
- $appeals = AccountInterstitial::whereNotNull('appeal_requested_at')
- ->whereNull('appeal_handled_at')
- ->latest()
- ->paginate(6);
- return view('admin.reports.appeals', compact('appeals'));
- }
- public function showAppeal(Request $request, $id)
- {
- $appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
- ->whereNull('appeal_handled_at')
- ->findOrFail($id);
- $meta = json_decode($appeal->meta);
- return view('admin.reports.show_appeal', compact('appeal', 'meta'));
- }
- public function spam(Request $request)
- {
- $this->validate($request, [
- 'tab' => 'sometimes|in:home,not-spam,spam,settings,custom,exemptions'
- ]);
- $tab = $request->input('tab', 'home');
- $openCount = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
- return AccountInterstitial::whereType('post.autospam')
- ->whereNull('appeal_handled_at')
- ->count();
- });
- $monthlyCount = Cache::remember('admin-dash:reports:spam-count:30d', 43200, function() {
- return AccountInterstitial::whereType('post.autospam')
- ->where('created_at', '>', now()->subMonth())
- ->count();
- });
- $totalCount = Cache::remember('admin-dash:reports:spam-count:total', 43200, function() {
- return AccountInterstitial::whereType('post.autospam')->count();
- });
- $uncategorized = Cache::remember('admin-dash:reports:spam-sync', 3600, function() {
- return AccountInterstitial::whereType('post.autospam')
- ->whereIsSpam(null)
- ->whereNotNull('appeal_handled_at')
- ->exists();
- });
- $avg = Cache::remember('admin-dash:reports:spam-count:avg', 43200, function() {
- if(config('database.default') != 'mysql') {
- return 0;
- }
- return AccountInterstitial::selectRaw('*, count(id) as counter')
- ->whereType('post.autospam')
- ->groupBy('user_id')
- ->get()
- ->avg('counter');
- });
- $avgOpen = Cache::remember('admin-dash:reports:spam-count:avgopen', 43200, function() {
- if(config('database.default') != 'mysql') {
- return "0";
- }
- $seconds = AccountInterstitial::selectRaw('DATE(created_at) AS start_date, AVG(TIME_TO_SEC(TIMEDIFF(appeal_handled_at, created_at))) AS timediff')->whereType('post.autospam')->whereNotNull('appeal_handled_at')->where('created_at', '>', now()->subMonth())->get();
- if(!$seconds) {
- return "0";
- }
- $mins = floor($seconds->avg('timediff') / 60);
- if($mins < 60) {
- return $mins . ' min(s)';
- }
- if($mins < 2880) {
- return floor($mins / 60) . ' hour(s)';
- }
- return floor($mins / 60 / 24) . ' day(s)';
- });
- $avgCount = $totalCount && $avg ? floor($totalCount / $avg) : "0";
- if(in_array($tab, ['home', 'spam', 'not-spam'])) {
- $appeals = AccountInterstitial::whereType('post.autospam')
- ->when($tab, function($q, $tab) {
- switch($tab) {
- case 'home':
- return $q->whereNull('appeal_handled_at');
- break;
- case 'spam':
- return $q->whereIsSpam(true);
- break;
- case 'not-spam':
- return $q->whereIsSpam(false);
- break;
- }
- })
- ->latest()
- ->paginate(6);
- if($tab !== 'home') {
- $appeals = $appeals->appends(['tab' => $tab]);
- }
- } else {
- $appeals = new class {
- public function count() {
- return 0;
- }
- public function render() {
- return;
- }
- };
- }
- return view('admin.reports.spam', compact('tab', 'appeals', 'openCount', 'monthlyCount', 'totalCount', 'avgCount', 'avgOpen', 'uncategorized'));
- }
- public function showSpam(Request $request, $id)
- {
- $appeal = AccountInterstitial::whereType('post.autospam')
- ->findOrFail($id);
- if($request->has('ref') && $request->input('ref') == 'email') {
- return redirect('/i/admin/reports?tab=autospam&id=' . $appeal->id);
- }
- $meta = json_decode($appeal->meta);
- return view('admin.reports.show_spam', compact('appeal', 'meta'));
- }
- public function fixUncategorizedSpam(Request $request)
- {
- if(Cache::get('admin-dash:reports:spam-sync-active')) {
- return redirect('/i/admin/reports/autospam');
- }
- Cache::put('admin-dash:reports:spam-sync-active', 1, 900);
- AccountInterstitial::chunk(500, function($reports) {
- foreach($reports as $report) {
- if($report->item_type != 'App\Status') {
- continue;
- }
- if($report->type != 'post.autospam') {
- continue;
- }
- if($report->is_spam != null) {
- continue;
- }
- $status = StatusService::get($report->item_id, false);
- if(!$status) {
- return;
- }
- $scope = $status['visibility'];
- $report->is_spam = $scope == 'unlisted';
- $report->in_violation = $report->is_spam;
- $report->severity_index = 1;
- $report->save();
- }
- });
- Cache::forget('admin-dash:reports:spam-sync');
- return redirect('/i/admin/reports/autospam');
- }
- public function updateSpam(Request $request, $id)
- {
- $this->validate($request, [
- 'action' => 'required|in:dismiss,approve,dismiss-all,approve-all,delete-account,mark-spammer'
- ]);
- $action = $request->input('action');
- $appeal = AccountInterstitial::whereType('post.autospam')
- ->whereNull('appeal_handled_at')
- ->findOrFail($id);
- $meta = json_decode($appeal->meta);
- $res = ['status' => 'success'];
- $now = now();
- Cache::forget('admin-dash:reports:spam-count:total');
- Cache::forget('admin-dash:reports:spam-count:30d');
- if($action == 'delete-account') {
- if(config('pixelfed.account_deletion') == false) {
- abort(404);
- }
- $user = User::findOrFail($appeal->user_id);
- $profile = $user->profile;
- if($user->is_admin == true) {
- $mid = $request->user()->id;
- abort_if($user->id < $mid, 403);
- }
- $ts = now()->addMonth();
- $user->status = 'delete';
- $profile->status = 'delete';
- $user->delete_after = $ts;
- $profile->delete_after = $ts;
- $user->save();
- $profile->save();
- ModLogService::boot()
- ->objectUid($user->id)
- ->objectId($user->id)
- ->objectType('App\User::class')
- ->user($request->user())
- ->action('admin.user.delete')
- ->accessLevel('admin')
- ->save();
- Cache::forget('profiles:private');
- DeleteAccountPipeline::dispatch($user);
- return;
- }
- if($action == 'dismiss') {
- $appeal->is_spam = true;
- $appeal->appeal_handled_at = $now;
- $appeal->save();
- Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
- Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
- Cache::forget('admin-dash:reports:spam-count');
- return $res;
- }
- if($action == 'dismiss-all') {
- AccountInterstitial::whereType('post.autospam')
- ->whereItemType('App\Status')
- ->whereNull('appeal_handled_at')
- ->whereUserId($appeal->user_id)
- ->update(['appeal_handled_at' => $now, 'is_spam' => true]);
- Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
- Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
- Cache::forget('admin-dash:reports:spam-count');
- return $res;
- }
- if($action == 'approve-all') {
- AccountInterstitial::whereType('post.autospam')
- ->whereItemType('App\Status')
- ->whereNull('appeal_handled_at')
- ->whereUserId($appeal->user_id)
- ->get()
- ->each(function($report) use($meta) {
- $report->is_spam = false;
- $report->appeal_handled_at = now();
- $report->save();
- $status = Status::find($report->item_id);
- if($status) {
- $status->is_nsfw = $meta->is_nsfw;
- $status->scope = 'public';
- $status->visibility = 'public';
- $status->save();
- StatusService::del($status->id, true);
- }
- });
- Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
- Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
- Cache::forget('admin-dash:reports:spam-count');
- return $res;
- }
- if($action == 'mark-spammer') {
- AccountInterstitial::whereType('post.autospam')
- ->whereItemType('App\Status')
- ->whereNull('appeal_handled_at')
- ->whereUserId($appeal->user_id)
- ->update(['appeal_handled_at' => $now, 'is_spam' => true]);
- $pro = Profile::whereUserId($appeal->user_id)->firstOrFail();
- $pro->update([
- 'unlisted' => true,
- 'cw' => true,
- 'no_autolink' => true
- ]);
- Status::whereProfileId($pro->id)
- ->get()
- ->each(function($report) {
- $status->is_nsfw = $meta->is_nsfw;
- $status->scope = 'public';
- $status->visibility = 'public';
- $status->save();
- StatusService::del($status->id, true);
- });
- Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
- Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
- Cache::forget('admin-dash:reports:spam-count');
- return $res;
- }
- $status = $appeal->status;
- $status->is_nsfw = $meta->is_nsfw;
- $status->scope = 'public';
- $status->visibility = 'public';
- $status->save();
- $appeal->is_spam = false;
- $appeal->appeal_handled_at = now();
- $appeal->save();
- StatusService::del($status->id);
- Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
- Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
- Cache::forget('admin-dash:reports:spam-count');
- return $res;
- }
- public function updateAppeal(Request $request, $id)
- {
- $this->validate($request, [
- 'action' => 'required|in:dismiss,approve'
- ]);
- $action = $request->input('action');
- $appeal = AccountInterstitial::whereNotNull('appeal_requested_at')
- ->whereNull('appeal_handled_at')
- ->findOrFail($id);
- if($action == 'dismiss') {
- $appeal->appeal_handled_at = now();
- $appeal->save();
- Cache::forget('admin-dash:reports:ai-count');
- return redirect('/i/admin/reports/appeals');
- }
- switch ($appeal->type) {
- case 'post.cw':
- $status = $appeal->status;
- $status->is_nsfw = false;
- $status->save();
- break;
- case 'post.unlist':
- $status = $appeal->status;
- $status->scope = 'public';
- $status->visibility = 'public';
- $status->save();
- break;
- default:
- # code...
- break;
- }
- $appeal->appeal_handled_at = now();
- $appeal->save();
- StatusService::del($status->id, true);
- Cache::forget('admin-dash:reports:ai-count');
- return redirect('/i/admin/reports/appeals');
- }
- public function updateReport(Request $request, $id)
- {
- $this->validate($request, [
- 'action' => 'required|string',
- ]);
- $action = $request->input('action');
- $actions = [
- 'ignore',
- 'cw',
- 'unlist',
- 'delete',
- 'shadowban',
- 'ban',
- ];
- if (!in_array($action, $actions)) {
- return abort(403);
- }
- $report = Report::findOrFail($id);
- $this->handleReportAction($report, $action);
- Cache::forget('admin-dash:reports:list-cache');
- return response()->json(['msg'=> 'Success']);
- }
- public function handleReportAction(Report $report, $action)
- {
- $item = $report->reported();
- $report->admin_seen = Carbon::now();
- switch ($action) {
- case 'ignore':
- $report->not_interested = true;
- break;
- case 'cw':
- Cache::forget('status:thumb:'.$item->id);
- $item->is_nsfw = true;
- $item->save();
- $report->nsfw = true;
- StatusService::del($item->id, true);
- break;
- case 'unlist':
- $item->visibility = 'unlisted';
- $item->save();
- Cache::forget('profiles:private');
- StatusService::del($item->id, true);
- break;
- case 'delete':
- // Todo: fire delete job
- $report->admin_seen = null;
- StatusService::del($item->id, true);
- break;
- case 'shadowban':
- // Todo: fire delete job
- $report->admin_seen = null;
- break;
- case 'ban':
- // Todo: fire delete job
- $report->admin_seen = null;
- break;
- default:
- $report->admin_seen = null;
- break;
- }
- $report->save();
- return $this;
- }
- protected function actionMap()
- {
- return [
- '1' => 'ignore',
- '2' => 'cw',
- '3' => 'unlist',
- '4' => 'delete',
- '5' => 'shadowban',
- '6' => 'ban'
- ];
- }
- public function bulkUpdateReport(Request $request)
- {
- $this->validate($request, [
- 'action' => 'required|integer|min:1|max:10',
- 'ids' => 'required|array'
- ]);
- $action = $this->actionMap()[$request->input('action')];
- $ids = $request->input('ids');
- $reports = Report::whereIn('id', $ids)->whereNull('admin_seen')->get();
- foreach($reports as $report) {
- $this->handleReportAction($report, $action);
- }
- $res = [
- 'message' => 'Success',
- 'code' => 200
- ];
- return response()->json($res);
- }
- public function reportMailVerifications(Request $request)
- {
- $ids = Redis::smembers('email:manual');
- $ignored = Redis::smembers('email:manual-ignored');
- $reports = [];
- if($ids) {
- $reports = collect($ids)
- ->filter(function($id) use($ignored) {
- return !in_array($id, $ignored);
- })
- ->map(function($id) {
- $user = User::whereProfileId($id)->first();
- if(!$user || $user->email_verified_at) {
- return [];
- }
- $account = AccountService::get($id, true);
- if(!$account) {
- return [];
- }
- $account['email'] = $user->email;
- return $account;
- })
- ->filter(function($res) {
- return $res && isset($res['id']);
- })
- ->values();
- }
- return view('admin.reports.mail_verification', compact('reports', 'ignored'));
- }
- public function reportMailVerifyIgnore(Request $request)
- {
- $id = $request->input('id');
- Redis::sadd('email:manual-ignored', $id);
- return redirect('/i/admin/reports');
- }
- public function reportMailVerifyApprove(Request $request)
- {
- $id = $request->input('id');
- $user = User::whereProfileId($id)->firstOrFail();
- Redis::srem('email:manual', $id);
- Redis::srem('email:manual-ignored', $id);
- $user->email_verified_at = now();
- $user->save();
- return redirect('/i/admin/reports');
- }
- public function reportMailVerifyClearIgnored(Request $request)
- {
- Redis::del('email:manual-ignored');
- return [200];
- }
- public function reportsStats(Request $request)
- {
- $stats = [
- 'total' => Report::count(),
- 'open' => Report::whereNull('admin_seen')->count(),
- 'closed' => Report::whereNotNull('admin_seen')->count(),
- 'autospam' => AccountInterstitial::whereType('post.autospam')->count(),
- 'autospam_open' => AccountInterstitial::whereType('post.autospam')->whereNull(['appeal_handled_at'])->count(),
- 'appeals' => AccountInterstitial::whereNotNull('appeal_requested_at')->whereNull('appeal_handled_at')->count(),
- 'email_verification_requests' => Redis::scard('email:manual')
- ];
- return $stats;
- }
- public function reportsApiAll(Request $request)
- {
- $filter = $request->input('filter') == 'closed' ? 'closed' : 'open';
- $reports = AdminReport::collection(
- Report::orderBy('id','desc')
- ->when($filter, function($q, $filter) {
- return $filter == 'open' ?
- $q->whereNull('admin_seen') :
- $q->whereNotNull('admin_seen');
- })
- ->groupBy(['object_id', 'object_type'])
- ->cursorPaginate(6)
- ->withQueryString()
- );
- return $reports;
- }
- public function reportsApiGet(Request $request, $id)
- {
- $report = Report::findOrFail($id);
- return new AdminReport($report);
- }
- public function reportsApiHandle(Request $request)
- {
- $this->validate($request, [
- 'object_id' => 'required',
- 'object_type' => 'required',
- 'id' => 'required',
- 'action' => 'required|in:ignore,nsfw,unlist,private,delete',
- 'action_type' => 'required|in:post,profile'
- ]);
- $report = Report::whereObjectId($request->input('object_id'))->findOrFail($request->input('id'));
- if($request->input('action_type') === 'profile') {
- return $this->reportsHandleProfileAction($report, $request->input('action'));
- } else if($request->input('action_type') === 'post') {
- return $this->reportsHandleStatusAction($report, $request->input('action'));
- }
- return $report;
- }
- protected function reportsHandleProfileAction($report, $action)
- {
- switch($action) {
- case 'ignore':
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'nsfw':
- if($report->object_type === 'App\Profile') {
- $profile = Profile::find($report->object_id);
- } else if($report->object_type === 'App\Status') {
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- $profile = Profile::find($status->profile_id);
- }
- if(!$profile) {
- return;
- }
- abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.');
- $profile->cw = true;
- $profile->save();
- foreach(Status::whereProfileId($profile->id)->cursor() as $status) {
- $status->is_nsfw = true;
- $status->save();
- StatusService::del($status->id);
- PublicTimelineService::rem($status->id);
- }
- ModLogService::boot()
- ->objectUid($profile->id)
- ->objectId($profile->id)
- ->objectType('App\Profile::class')
- ->user(request()->user())
- ->action('admin.user.moderate')
- ->metadata([
- 'action' => 'cw',
- 'message' => 'Success!'
- ])
- ->accessLevel('admin')
- ->save();
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'nsfw' => true,
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'unlist':
- if($report->object_type === 'App\Profile') {
- $profile = Profile::find($report->object_id);
- } else if($report->object_type === 'App\Status') {
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- $profile = Profile::find($status->profile_id);
- }
- if(!$profile) {
- return;
- }
- abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.');
- $profile->unlisted = true;
- $profile->save();
- foreach(Status::whereProfileId($profile->id)->whereScope('public')->cursor() as $status) {
- $status->scope = 'unlisted';
- $status->visibility = 'unlisted';
- $status->save();
- StatusService::del($status->id);
- PublicTimelineService::rem($status->id);
- }
- ModLogService::boot()
- ->objectUid($profile->id)
- ->objectId($profile->id)
- ->objectType('App\Profile::class')
- ->user(request()->user())
- ->action('admin.user.moderate')
- ->metadata([
- 'action' => 'unlisted',
- 'message' => 'Success!'
- ])
- ->accessLevel('admin')
- ->save();
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'private':
- if($report->object_type === 'App\Profile') {
- $profile = Profile::find($report->object_id);
- } else if($report->object_type === 'App\Status') {
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- $profile = Profile::find($status->profile_id);
- }
- if(!$profile) {
- return;
- }
- abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot moderate an admin account.');
- $profile->unlisted = true;
- $profile->save();
- foreach(Status::whereProfileId($profile->id)->cursor() as $status) {
- $status->scope = 'private';
- $status->visibility = 'private';
- $status->save();
- StatusService::del($status->id);
- PublicTimelineService::rem($status->id);
- }
- ModLogService::boot()
- ->objectUid($profile->id)
- ->objectId($profile->id)
- ->objectType('App\Profile::class')
- ->user(request()->user())
- ->action('admin.user.moderate')
- ->metadata([
- 'action' => 'private',
- 'message' => 'Success!'
- ])
- ->accessLevel('admin')
- ->save();
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'delete':
- if(config('pixelfed.account_deletion') == false) {
- abort(404);
- }
- if($report->object_type === 'App\Profile') {
- $profile = Profile::find($report->object_id);
- } else if($report->object_type === 'App\Status') {
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- $profile = Profile::find($status->profile_id);
- }
- if(!$profile) {
- return;
- }
- abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot delete an admin account.');
- $ts = now()->addMonth();
- if($profile->user_id) {
- $user = $profile->user;
- abort_if($user->is_admin, 403, 'You cannot delete admin accounts.');
- $user->status = 'delete';
- $user->delete_after = $ts;
- $user->save();
- }
- $profile->status = 'delete';
- $profile->delete_after = $ts;
- $profile->save();
- ModLogService::boot()
- ->objectUid($profile->id)
- ->objectId($profile->id)
- ->objectType('App\Profile::class')
- ->user(request()->user())
- ->action('admin.user.delete')
- ->accessLevel('admin')
- ->save();
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- if($profile->user_id) {
- DB::table('oauth_access_tokens')->whereUserId($user->id)->delete();
- DB::table('oauth_auth_codes')->whereUserId($user->id)->delete();
- $user->email = $user->id;
- $user->password = '';
- $user->status = 'delete';
- $user->save();
- $profile->status = 'delete';
- $profile->delete_after = now()->addMonth();
- $profile->save();
- AccountService::del($profile->id);
- DeleteAccountPipeline::dispatch($user)->onQueue('high');
- } else {
- $profile->status = 'delete';
- $profile->delete_after = now()->addMonth();
- $profile->save();
- AccountService::del($profile->id);
- DeleteRemoteProfilePipeline::dispatch($profile)->onQueue('high');
- }
- return [200];
- break;
- }
- }
- protected function reportsHandleStatusAction($report, $action)
- {
- switch($action) {
- case 'ignore':
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'nsfw':
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.');
- $status->is_nsfw = true;
- $status->save();
- StatusService::del($status->id);
- ModLogService::boot()
- ->objectUid($status->profile_id)
- ->objectId($status->profile_id)
- ->objectType('App\Status::class')
- ->user(request()->user())
- ->action('admin.status.moderate')
- ->metadata([
- 'action' => 'cw',
- 'message' => 'Success!'
- ])
- ->accessLevel('admin')
- ->save();
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'nsfw' => true,
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'private':
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.');
- $status->scope = 'private';
- $status->visibility = 'private';
- $status->save();
- StatusService::del($status->id);
- PublicTimelineService::rem($status->id);
- ModLogService::boot()
- ->objectUid($status->profile_id)
- ->objectId($status->profile_id)
- ->objectType('App\Status::class')
- ->user(request()->user())
- ->action('admin.status.moderate')
- ->metadata([
- 'action' => 'private',
- 'message' => 'Success!'
- ])
- ->accessLevel('admin')
- ->save();
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'unlist':
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- abort_if($status->profile->user && $status->profile->user->is_admin, 400, 'Cannot moderate an admin account post.');
- if($status->scope === 'public') {
- $status->scope = 'unlisted';
- $status->visibility = 'unlisted';
- $status->save();
- StatusService::del($status->id);
- PublicTimelineService::rem($status->id);
- }
- ModLogService::boot()
- ->objectUid($status->profile_id)
- ->objectId($status->profile_id)
- ->objectType('App\Status::class')
- ->user(request()->user())
- ->action('admin.status.moderate')
- ->metadata([
- 'action' => 'unlist',
- 'message' => 'Success!'
- ])
- ->accessLevel('admin')
- ->save();
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- case 'delete':
- $status = Status::find($report->object_id);
- if(!$status) {
- return [200];
- }
- $profile = $status->profile;
- abort_if($profile->user && $profile->user->is_admin, 400, 'Cannot delete an admin account post.');
- StatusService::del($status->id);
- if($profile->user_id != null && $profile->domain == null) {
- PublicTimelineService::del($status->id);
- StatusDelete::dispatch($status)->onQueue('high');
- } else {
- NetworkTimelineService::del($status->id);
- DeleteRemoteStatusPipeline::dispatch($status)->onQueue('high');
- }
- Report::whereObjectId($report->object_id)
- ->whereObjectType($report->object_type)
- ->update([
- 'admin_seen' => now()
- ]);
- return [200];
- break;
- }
- }
- public function reportsApiSpamAll(Request $request)
- {
- $tab = $request->input('tab', 'home');
- $appeals = AdminSpamReport::collection(
- AccountInterstitial::orderBy('id', 'desc')
- ->whereType('post.autospam')
- ->whereNull('appeal_handled_at')
- ->cursorPaginate(6)
- ->withQueryString()
- );
- return $appeals;
- }
- public function reportsApiSpamHandle(Request $request)
- {
- $this->validate($request, [
- 'id' => 'required',
- 'action' => 'required|in:mark-read,mark-not-spam,mark-all-read,mark-all-not-spam,delete-profile',
- ]);
- $action = $request->input('action');
- abort_if(
- $action === 'delete-profile' &&
- !config('pixelfed.account_deletion'),
- 404,
- "Cannot delete profile, account_deletion is disabled.\n\n Set `ACCOUNT_DELETION=true` in .env and re-cache config."
- );
- $report = AccountInterstitial::with('user')
- ->whereType('post.autospam')
- ->whereNull('appeal_handled_at')
- ->findOrFail($request->input('id'));
- $this->reportsHandleSpamAction($report, $action);
- Cache::forget('admin-dash:reports:spam-count');
- Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $report->user->profile_id);
- Cache::forget('pf:bouncer_v0:recent_by_pid:' . $report->user->profile_id);
- PublicTimelineService::warmCache(true, 400);
- return [$action, $report];
- }
- public function reportsHandleSpamAction($appeal, $action)
- {
- $meta = json_decode($appeal->meta);
- if($action == 'mark-read') {
- $appeal->is_spam = true;
- $appeal->appeal_handled_at = now();
- $appeal->save();
- }
- if($action == 'mark-not-spam') {
- $status = $appeal->status;
- $status->is_nsfw = $meta->is_nsfw;
- $status->scope = 'public';
- $status->visibility = 'public';
- $status->save();
- $appeal->is_spam = false;
- $appeal->appeal_handled_at = now();
- $appeal->save();
- Notification::whereAction('autospam.warning')
- ->whereProfileId($appeal->user->profile_id)
- ->get()
- ->each(function($n) use($appeal) {
- NotificationService::del($appeal->user->profile_id, $n->id);
- $n->forceDelete();
- });
- StatusService::del($status->id);
- }
- if($action == 'mark-all-read') {
- AccountInterstitial::whereType('post.autospam')
- ->whereItemType('App\Status')
- ->whereNull('appeal_handled_at')
- ->whereUserId($appeal->user_id)
- ->update([
- 'appeal_handled_at' => now(),
- 'is_spam' => true
- ]);
- }
- if($action == 'mark-all-not-spam') {
- AccountInterstitial::whereType('post.autospam')
- ->whereItemType('App\Status')
- ->whereUserId($appeal->user_id)
- ->get()
- ->each(function($report) use($meta) {
- $report->is_spam = false;
- $report->appeal_handled_at = now();
- $report->save();
- $status = Status::find($report->item_id);
- if($status) {
- $status->is_nsfw = $meta->is_nsfw;
- $status->scope = 'public';
- $status->visibility = 'public';
- $status->save();
- StatusService::del($status->id);
- }
- Notification::whereAction('autospam.warning')
- ->whereProfileId($report->user->profile_id)
- ->get()
- ->each(function($n) use($report) {
- NotificationService::del($report->user->profile_id, $n->id);
- $n->forceDelete();
- });
- });
- }
- if($action == 'delete-profile') {
- $user = User::findOrFail($appeal->user_id);
- $profile = $user->profile;
- if($user->is_admin == true) {
- $mid = request()->user()->id;
- abort_if($user->id < $mid, 403, 'You cannot delete an admin account.');
- }
- $ts = now()->addMonth();
- $user->status = 'delete';
- $profile->status = 'delete';
- $user->delete_after = $ts;
- $profile->delete_after = $ts;
- $user->save();
- $profile->save();
- $appeal->appeal_handled_at = now();
- $appeal->save();
- ModLogService::boot()
- ->objectUid($user->id)
- ->objectId($user->id)
- ->objectType('App\User::class')
- ->user(request()->user())
- ->action('admin.user.delete')
- ->accessLevel('admin')
- ->save();
- Cache::forget('profiles:private');
- DeleteAccountPipeline::dispatch($user);
- }
- }
- public function reportsApiSpamGet(Request $request, $id)
- {
- $report = AccountInterstitial::findOrFail($id);
- return new AdminSpamReport($report);
- }
- }
|