12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223 |
- <?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);
- 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();
- PublicTimelineService::del($appeal->item_id);
- }
- 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);
- StatusService::get($status->id);
- if($status->in_reply_to_id == null && $status->reblog_of_id == null) {
- PublicTimelineService::add($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);
- }
- }
|