Jelajahi Sumber

Merge pull request #666 from pixelfed/frontend-ui-refactor

v0.7.2
daniel 6 tahun lalu
induk
melakukan
ddfc868bb7

+ 27 - 16
app/Http/Controllers/FederationController.php

@@ -48,8 +48,8 @@ class FederationController extends Controller
     {
         $this->authCheck();
         $this->validate($request, [
-        'url' => 'required|string',
-      ]);
+            'url' => 'required|string',
+        ]);
 
         if (config('pixelfed.remote_follow_enabled') !== true) {
             abort(403);
@@ -123,16 +123,15 @@ class FederationController extends Controller
     {
         $this->validate($request, ['resource'=>'required|string|min:3|max:255']);
 
-        $hash = hash('sha256', $request->input('resource'));
-
-        $webfinger = Cache::remember('api:webfinger:'.$hash, 1440, function () use ($request) {
-            $resource = $request->input('resource');
-            $parsed = Nickname::normalizeProfileUrl($resource);
-            $username = $parsed['username'];
-            $user = Profile::whereUsername($username)->firstOrFail();
-
-            return (new Webfinger($user))->generate();
-        });
+        $resource = $request->input('resource');
+        $hash = hash('sha256', $resource);
+        $parsed = Nickname::normalizeProfileUrl($resource);
+        $username = $parsed['username'];
+        $profile = Profile::whereUsername($username)->firstOrFail();
+        if($profile->status != null) {
+            return ProfileController::accountCheck($profile);
+        }
+        $webfinger = (new Webfinger($profile))->generate();
 
         return response()->json($webfinger, 200, [], JSON_PRETTY_PRINT);
     }
@@ -156,13 +155,16 @@ XML;
             abort(403);
         }
 
-        $user = Profile::whereNull('remote_url')->whereUsername($username)->firstOrFail();
-        if($user->is_private) {
+        $profile = Profile::whereNull('remote_url')->whereUsername($username)->firstOrFail();
+        if($profile->status != null) {
+            return ProfileController::accountCheck($profile);
+        }
+        if($profile->is_private) {
             return response()->json(['error'=>'403', 'msg' => 'private profile'], 403);
         }
-        $timeline = $user->statuses()->whereVisibility('public')->orderBy('created_at', 'desc')->paginate(10);
+        $timeline = $profile->statuses()->whereVisibility('public')->orderBy('created_at', 'desc')->paginate(10);
         $fractal = new Fractal\Manager();
-        $resource = new Fractal\Resource\Item($user, new ProfileOutbox());
+        $resource = new Fractal\Resource\Item($profile, new ProfileOutbox());
         $res = $fractal->createData($resource)->toArray();
 
         return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
@@ -175,6 +177,9 @@ XML;
         }
 
         $profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
+        if($profile->status != null) {
+            return ProfileController::accountCheck($profile);
+        }
         $body = $request->getContent();
         $bodyDecoded = json_decode($body, true);
         $signature = $request->header('signature');
@@ -205,6 +210,9 @@ XML;
             ->whereUsername($username)
             ->whereIsPrivate(false)
             ->firstOrFail();
+        if($profile->status != null) {
+            return ProfileController::accountCheck($profile);
+        }
         $obj = [
             '@context' => 'https://www.w3.org/ns/activitystreams',
             'id'       => $request->getUri(),
@@ -226,6 +234,9 @@ XML;
             ->whereUsername($username)
             ->whereIsPrivate(false)
             ->firstOrFail();
+        if($profile->status != null) {
+            return ProfileController::accountCheck($profile);
+        }
         $obj = [
             '@context' => 'https://www.w3.org/ns/activitystreams',
             'id'       => $request->getUri(),

+ 1 - 1
app/Http/Controllers/FollowerController.php

@@ -37,7 +37,7 @@ class FollowerController extends Controller
     protected function handleFollowRequest($item)
     {
         $user = Auth::user()->profile;
-        $target = Profile::where('id', '!=', $user->id)->findOrFail($item);
+        $target = Profile::where('id', '!=', $user->id)->whereNull('status')->findOrFail($item);
         $private = (bool) $target->is_private;
         $blocked = UserFilter::whereUserId($target->id)
                 ->whereFilterType('block')

+ 4 - 0
app/Http/Controllers/InternalApiController.php

@@ -112,6 +112,7 @@ class InternalApiController extends Controller
 
         $people = Profile::select('id', 'name', 'username')
             ->with('avatar')
+            ->whereNull('status')
             ->orderByRaw('rand()')
             ->whereHas('statuses')
             ->whereNull('domain')
@@ -206,6 +207,9 @@ class InternalApiController extends Controller
 
         $posts = Status::select('id', 'caption', 'profile_id')
               ->whereHas('media')
+              ->whereHas('profile', function($q) {
+                return $q->whereNull('status');
+              })
               ->whereIsNsfw(false)
               ->whereVisibility('public')
               ->whereNotIn('profile_id', $following)

+ 44 - 6
app/Http/Controllers/ProfileController.php

@@ -20,7 +20,11 @@ class ProfileController extends Controller
     public function show(Request $request, $username)
     {
         $user = Profile::whereUsername($username)->firstOrFail();
-        return $this->buildProfile($request, $user);
+        if($user->status != null) {
+            return $this->accountCheck($user);
+        } else {
+            return $this->buildProfile($request, $user);
+        }
     }
 
     // TODO: refactor this mess
@@ -30,7 +34,11 @@ class ProfileController extends Controller
         $loggedIn = Auth::check();
         $isPrivate = false;
         $isBlocked = false;
-        
+
+        if($user->status != null) {
+            return ProfileController::accountCheck($user);
+        }
+
         if ($user->remote_url) {
             $settings = new \StdClass;
             $settings->crawlable = false;
@@ -118,8 +126,27 @@ class ProfileController extends Controller
         return false;
     }
 
+    public static function accountCheck(Profile $profile)
+    {
+        switch ($profile->status) {
+            case 'disabled':
+            case 'delete':
+                return view('profile.disabled');
+                break;
+            
+            default:
+                # code...
+                break;
+        }
+
+        return abort(404);
+    }
+
     public function showActivityPub(Request $request, $user)
     {
+        if($user->status != null) {
+            return ProfileController::accountCheck($user);
+        }
         $fractal = new Fractal\Manager();
         $resource = new Fractal\Resource\Item($user, new ProfileTransformer);
         $res = $fractal->createData($resource)->toArray();
@@ -129,6 +156,9 @@ class ProfileController extends Controller
     public function showAtomFeed(Request $request, $user)
     {
         $profile = $user = Profile::whereUsername($user)->firstOrFail();
+        if($profile->status != null) {
+            return $this->accountCheck($profile);
+        }
         if($profile->is_private || Auth::check()) {
             $blocked = $this->blockedProfileCheck($profile);
             $check = $this->privateProfileCheck($profile, null);
@@ -144,7 +174,9 @@ class ProfileController extends Controller
     public function followers(Request $request, $username)
     {
         $profile = $user = Profile::whereUsername($username)->firstOrFail();
-
+        if($profile->status != null) {
+            return $this->accountCheck($profile);
+        }
         // TODO: fix $profile/$user mismatch in profile & follower templates
         $owner = Auth::check() && Auth::id() === $user->user_id;
         $is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
@@ -155,7 +187,7 @@ class ProfileController extends Controller
                 return view('profile.private', compact('user', 'is_following'));
             }
         }
-        $followers = $profile->followers()->orderBy('created_at', 'desc')->simplePaginate(12);
+        $followers = $profile->followers()->whereNull('status')->orderBy('created_at', 'desc')->simplePaginate(12);
         $is_admin = is_null($user->domain) ? $user->user->is_admin : false;
         if ($user->remote_url) {
             $settings = new \StdClass;
@@ -172,6 +204,9 @@ class ProfileController extends Controller
     public function following(Request $request, $username)
     {
         $profile = $user = Profile::whereUsername($username)->firstOrFail();
+        if($profile->status != null) {
+            return $this->accountCheck($profile);
+        }
         // TODO: fix $profile/$user mismatch in profile & follower templates
         $owner = Auth::check() && Auth::id() === $user->user_id;
         $is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
@@ -182,7 +217,7 @@ class ProfileController extends Controller
                 return view('profile.private', compact('user', 'is_following'));
             }
         }
-        $following = $profile->following()->orderBy('created_at', 'desc')->simplePaginate(12);
+        $following = $profile->following()->whereNull('status')->orderBy('created_at', 'desc')->simplePaginate(12);
         $is_admin = is_null($user->domain) ? $user->user->is_admin : false;
         if ($user->remote_url) {
             $settings = new \StdClass;
@@ -201,7 +236,10 @@ class ProfileController extends Controller
         if (Auth::check() === false || $username !== Auth::user()->username) {
             abort(403);
         }
-        $user = Auth::user()->profile;
+        $user = $profile = Auth::user()->profile;
+        if($profile->status != null) {
+            return $this->accountCheck($profile);
+        }
         $settings = User::whereUsername($username)->firstOrFail()->settings;
         $owner = true;
         $following = false;

+ 16 - 10
app/Http/Controllers/PublicApiController.php

@@ -43,6 +43,9 @@ class PublicApiController extends Controller
     		return [];
     	} else {
 	        $profile = Auth::user()->profile;
+            if($profile->status) {
+                return [];
+            }
 	        $user = new Fractal\Resource\Item($profile, new AccountTransformer());
         	return $this->fractal->createData($user)->toArray();
     	}
@@ -54,6 +57,9 @@ class PublicApiController extends Controller
             return [];
         } else {
             $profile = Auth::user()->profile;
+            if($profile->status) {
+                return [];
+            }
             $likes = $status->likedBy()->orderBy('created_at','desc')->paginate(10);
             $collection = new Fractal\Resource\Collection($likes, new AccountTransformer());
             return $this->fractal->createData($collection)->toArray();
@@ -74,8 +80,8 @@ class PublicApiController extends Controller
 
     public function status(Request $request, $username, int $postid)
     {
-        $profile = Profile::whereUsername($username)->first();
-        $status = Status::whereProfileId($profile->id)->find($postid);
+        $profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
+        $status = Status::whereProfileId($profile->id)->findOrFail($postid);
         $this->scopeCheck($profile, $status);
         $item = new Fractal\Resource\Item($status, new StatusTransformer());
         $res = [
@@ -100,8 +106,8 @@ class PublicApiController extends Controller
             'limit'     => 'nullable|integer|min:5|max:50'
         ]);
         $limit = $request->limit ?? 10;
-        $profile = Profile::whereUsername($username)->first();
-        $status = Status::whereProfileId($profile->id)->find($postId);
+        $profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
+        $status = Status::whereProfileId($profile->id)->findOrFail($postId);
         $this->scopeCheck($profile, $status);
         if($request->filled('min_id') || $request->filled('max_id')) {
             if($request->filled('min_id')) {
@@ -133,8 +139,8 @@ class PublicApiController extends Controller
 
     public function statusLikes(Request $request, $username, $id)
     {
-        $profile = Profile::whereUsername($username)->first();
-        $status = Status::whereProfileId($profile->id)->find($id);
+        $profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
+        $status = Status::whereProfileId($profile->id)->findOrFail($id);
         $this->scopeCheck($profile, $status);
         $likes = $this->getLikes($status);
         return response()->json([
@@ -144,8 +150,8 @@ class PublicApiController extends Controller
 
     public function statusShares(Request $request, $username, $id)
     {
-        $profile = Profile::whereUsername($username)->first();
-        $status = Status::whereProfileId($profile->id)->find($id);
+        $profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
+        $status = Status::whereProfileId($profile->id)->findOrFail($id);
         $this->scopeCheck($profile, $status);
         $shares = $this->getShares($status);
         return response()->json([
@@ -210,7 +216,7 @@ class PublicApiController extends Controller
         // $timeline = Timeline::build()->local();
         $pid = Auth::user()->profile->id;
 
-        $private = Profile::whereIsPrivate(true)->where('id', '!=', $pid)->pluck('id');
+        $private = Profile::whereIsPrivate(true)->orWhereNotNull('status')->where('id', '!=', $pid)->pluck('id');
         $filters = UserFilter::whereUserId($pid)
                   ->whereFilterableType('App\Profile')
                   ->whereIn('filter_type', ['mute', 'block'])
@@ -272,7 +278,7 @@ class PublicApiController extends Controller
         $following = Follower::whereProfileId($pid)->pluck('following_id');
         $following->push($pid)->toArray();
 
-        $private = Profile::whereIsPrivate(true)->where('id', '!=', $pid)->pluck('id');
+        $private = Profile::whereIsPrivate(true)->orWhereNotNull('status')->where('id', '!=', $pid)->pluck('id');
         $filters = UserFilter::whereUserId($pid)
                   ->whereFilterableType('App\Profile')
                   ->whereIn('filter_type', ['mute', 'block'])

+ 2 - 1
app/Http/Controllers/SearchController.php

@@ -22,7 +22,7 @@ class SearchController extends Controller
             return;
         }
         $hash = hash('sha256', $tag);
-        $tokens = Cache::remember('api:search:tag:'.$hash, 60, function () use ($tag) {
+        $tokens = Cache::remember('api:search:tag:'.$hash, 5, function () use ($tag) {
             $tokens = collect([]);
             $hashtags = Hashtag::select('id', 'name', 'slug')->where('slug', 'like', '%'.$tag.'%')->limit(20)->get();
             if($hashtags->count() > 0) {
@@ -39,6 +39,7 @@ class SearchController extends Controller
                 $tokens->push($tags);
             }
             $users = Profile::select('username', 'name', 'id')
+                ->whereNull('status')
                 ->where('username', 'like', '%'.$tag.'%')
                 ->limit(20)
                 ->get();

+ 22 - 5
app/Http/Controllers/SettingsController.php

@@ -5,10 +5,8 @@ namespace App\Http\Controllers;
 use App\AccountLog;
 use App\Following;
 use App\UserFilter;
-use Auth;
-use DB;
-use Cache;
-use Purify;
+use Auth, DB, Cache, Purify;
+use Carbon\Carbon;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Settings\{
     HomeSettings,
@@ -137,6 +135,18 @@ class SettingsController extends Controller
         return view('settings.remove.temporary');
     }
 
+    public function removeAccountTemporarySubmit(Request $request)
+    {
+        $user = Auth::user();
+        $profile = $user->profile;
+        $user->status = 'disabled';
+        $profile->status = 'disabled';
+        $user->save();
+        $profile->save();
+        Auth::logout();
+        return redirect('/');
+    }
+
     public function removeAccountPermanent(Request $request)
     {
         return view('settings.remove.permanent');
@@ -148,7 +158,14 @@ class SettingsController extends Controller
         if($user->is_admin == true) {
             return abort(400, 'You cannot delete an admin account.');
         }
-        DeleteAccountPipeline::dispatch($user);
+        $profile = $user->profile;
+        $ts = Carbon::now()->addMonth();
+        $user->status = 'delete';
+        $profile->status = 'delete';
+        $user->delete_after = $ts;
+        $profile->delete_after = $ts;
+        $user->save();
+        $profile->save();
         Auth::logout();
         return redirect('/');
     }

+ 12 - 0
app/Http/Controllers/StatusController.php

@@ -21,10 +21,22 @@ class StatusController extends Controller
     {
         $user = Profile::whereUsername($username)->firstOrFail();
 
+        if($user->status != null) {
+            return ProfileController::accountCheck($user);
+        }
+
         $status = Status::whereProfileId($user->id)
                 ->whereNotIn('visibility',['draft','direct'])
                 ->findOrFail($id);
 
+        if($status->uri) {
+            $url = $status->uri;
+            if(ends_with($url, '/activity')) {
+                $url = str_replace('/activity', '', $url);
+            }
+            return redirect($url);
+        }
+
         if($status->visibility == 'private' || $user->is_private) {
             if(!Auth::check()) {
                 abort(403);

+ 4 - 6
app/Jobs/StatusPipeline/NewStatusPipeline.php

@@ -37,11 +37,9 @@ class NewStatusPipeline implements ShouldQueue
         $status = $this->status;
 
         StatusEntityLexer::dispatch($status);
-        StatusActivityPubDeliver::dispatch($status);
-
-        Cache::forever('post.'.$status->id, $status);
-
-        $redis = Redis::connection();
-        $redis->lpush(config('cache.prefix').':user.'.$status->profile_id.'.posts', $status->id);
+        
+        // Cache::forever('post.'.$status->id, $status);
+        // $redis = Redis::connection();
+        // $redis->lpush(config('cache.prefix').':user.'.$status->profile_id.'.posts', $status->id);
     }
 }

+ 4 - 2
app/Jobs/StatusPipeline/StatusActivityPubDeliver.php

@@ -38,6 +38,10 @@ class StatusActivityPubDeliver implements ShouldQueue
     {
         $status = $this->status;
 
+        if($status->local == true || $status->url || $status->uri) {
+            return;
+        }
+
         $audience = $status->profile->getAudienceInbox();
         $profile = $status->profile;
 
@@ -49,7 +53,5 @@ class StatusActivityPubDeliver implements ShouldQueue
         foreach($audience as $url) {
             Helpers::sendSignedObject($profile, $url, $activity);
         }
-
-        // todo: fanout on write
     }
 }

+ 1 - 1
app/Jobs/StatusPipeline/StatusEntityLexer.php

@@ -42,7 +42,6 @@ class StatusEntityLexer implements ShouldQueue
      */
     public function handle()
     {
-        $status = $this->status;
         $this->parseEntities();
     }
 
@@ -73,6 +72,7 @@ class StatusEntityLexer implements ShouldQueue
             $status->entities = json_encode($this->entities);
             $status->save();
         });
+        StatusActivityPubDeliver::dispatch($this->status);
     }
 
     public function storeHashtags()

+ 25 - 0
app/Listeners/AuthLogin.php

@@ -61,5 +61,30 @@ class AuthLogin
                 CreateAvatar::dispatch($profile);
             });
         }
+
+        if($user->status != null) {
+            $profile = $user->profile;
+            switch ($user->status) {
+                case 'disabled':
+                    $profile->status = null;
+                    $user->status = null;
+                    $profile->save();
+                    $user->save();
+                    break;
+
+                case 'delete':
+                    $profile->status = null;
+                    $profile->delete_after = null;
+                    $user->status = null;
+                    $user->delete_after = null;
+                    $profile->save();
+                    $user->save();
+                    break;
+                
+                default:
+                    # code...
+                    break;
+            }
+        }
     }
 }

+ 4 - 3
app/Util/ActivityPub/Helpers.php

@@ -7,6 +7,7 @@ use App\{
     Activity,
     Follower,
     Like,
+    Media,
     Notification,
     Profile,
     Status
@@ -281,11 +282,11 @@ class Helpers {
 	public static function profileFirstOrNew($url, $runJobs = false)
 	{
  		$res = self::fetchProfileFromUrl($url);
- 		$domain = parse_url($res['url'], PHP_URL_HOST);
+ 		$domain = parse_url($res['id'], PHP_URL_HOST);
         $username = $res['preferredUsername'];
         $remoteUsername = "@{$username}@{$domain}";
 
-		$profile = Profile::whereRemoteUrl($res['url'])->first();
+		$profile = Profile::whereRemoteUrl($res['id'])->first();
 		if(!$profile) {
 	        $profile = new Profile;
 	        $profile->domain = $domain;
@@ -295,7 +296,7 @@ class Helpers {
 	        $profile->sharedInbox = isset($res['endpoints']) && isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : null;
 	        $profile->inbox_url = $res['inbox'];
 	        $profile->outbox_url = $res['outbox'];
-	        $profile->remote_url = $res['url'];
+	        $profile->remote_url = $res['id'];
 	        $profile->public_key = $res['publicKey']['publicKeyPem'];
 	        $profile->key_id = $res['publicKey']['id'];
 	        $profile->save();

+ 66 - 45
app/Util/ActivityPub/Inbox.php

@@ -32,7 +32,7 @@ class Inbox
 
     public function handle()
     {
-        $this->authenticatePayload();
+        $this->handleVerb();
     }
 
     public function authenticatePayload()
@@ -142,16 +142,11 @@ class Inbox
         $activity = $this->payload['object'];
         $actor = $this->actorFirstOrCreate($this->payload['actor']);
         $inReplyTo = $activity['inReplyTo'];
+        $url = $activity['id'];
         
-        if(!Helpers::statusFirstOrFetch($activity['url'], true)) {
-            $this->logger->delete();
+        if(!Helpers::statusFirstOrFetch($url, true)) {
             return;
         }
-
-        $this->logger->to_id = $this->profile->id;
-        $this->logger->from_id = $actor->id;
-        $this->logger->processed_at = Carbon::now();
-        $this->logger->save();
     }
 
     public function handleNoteCreate()
@@ -164,12 +159,11 @@ class Inbox
 
         if(Helpers::userInAudience($this->profile, $this->payload) == false) {
             //Log::error('AP:inbox:userInAudience:false - Activity#'.$this->logger->id);
-            $logger = Activity::find($this->logger->id);
-            $logger->delete();
             return;
         }
 
-        if(Status::whereUrl($activity['url'])->exists()) {
+        $url = $activity['id'];
+        if(Status::whereUrl($url)->exists()) {
             return;
         }
 
@@ -178,18 +172,12 @@ class Inbox
             $status->profile_id = $actor->id;
             $status->caption = strip_tags($activity['content']);
             $status->visibility = $status->scope = 'public';
-            $status->url = $activity['url'];
+            $status->url = $url;
             $status->save();
             return $status;
         });
 
         Helpers::importNoteAttachment($activity, $status);
-
-        $logger = Activity::find($this->logger->id);
-        $logger->to_id = $this->profile->id;
-        $logger->from_id = $actor->id;
-        $logger->processed_at = Carbon::now();
-        $logger->save();
     }
 
     public function handleFollowActivity()
@@ -214,7 +202,6 @@ class Inbox
                 'local_profile' => empty($actor->domain)
             ]);
             if($follower->wasRecentlyCreated == false) {
-                $this->logger->delete();
                 return;
             }
             // send notification
@@ -228,37 +215,53 @@ class Inbox
             $notification->item_type = "App\Profile";
             $notification->save();
 
-            \Cache::forever('notification.'.$notification->id, $notification);
-
-            $redis = Redis::connection();
-
-            $nkey = config('cache.prefix').':user.'.$target->id.'.notifications';
-            $redis->lpush($nkey, $notification->id);
-            
             // send Accept to remote profile
             $accept = [
                 '@context' => 'https://www.w3.org/ns/activitystreams',
-                'id'       => $follower->permalink('/accept'),
+                'id'       => $target->permalink().'#accepts/follows/',
                 'type'     => 'Accept',
                 'actor'    => $target->permalink(),
                 'object'   => [
-                    'id' => $this->payload['id'],
+                    'id' => $actor->permalink('#follows/'.$target->id),
                     'type'  => 'Follow',
-                    'actor' => $target->permalink(),
-                    'object' => $actor->permalink()
+                    'actor' => $actor->permalink(),
+                    'object' => $target->permalink()
                 ]
             ];
             Helpers::sendSignedObject($target, $actor->inbox_url, $accept);
         }
-        $this->logger->to_id = $target->id;
-        $this->logger->from_id = $actor->id;
-        $this->logger->processed_at = Carbon::now();
-        $this->logger->save();
     }
 
     public function handleAnnounceActivity()
     {
-
+        $actor = $this->actorFirstOrCreate($this->payload['actor']);
+        $activity = $this->payload['object'];
+        if(!$actor || $actor->domain == null) {
+            return;
+        }
+        if(Helpers::validateLocalUrl($activity) == false) {
+            return;
+        }
+        $parent = Helpers::statusFirstOrFetch($activity, true);
+        if(!$parent) {
+            return;
+        }
+        $status = Status::firstOrCreate([
+            'profile_id' => $actor->id,
+            'in_reply_to_id' => $parent->id,
+            'type' => 'reply'
+        ]);
+        if($status->wasRecentlyCreated) {
+            $notification = new Notification();
+            $notification->profile_id = $parent->profile->id;
+            $notification->actor_id = $actor->id;
+            $notification->action = 'comment';
+            $notification->message = $status->toText();
+            $notification->rendered = $status->toHtml();
+            $notification->item_id = $parent->id;
+            $notification->item_type = "App\Status";
+            $notification->save();
+        }
     }
 
     public function handleAcceptActivity()
@@ -268,7 +271,19 @@ class Inbox
 
     public function handleDeleteActivity()
     {
+        $actor = $this->payload['actor'];
+        $obj = $this->payload['object'];
+        if(is_string($obj) && Helpers::validateUrl($obj)) {
+            // actor object detected
 
+        } else if (is_array($obj) && isset($obj['type']) && $obj['type'] == 'Tombstone') {
+            // tombstone detected
+            $status = Status::whereUri($obj['id'])->first();
+            if($status == null) {
+                return;
+            }
+            $status->forceDelete();
+        }
     }
 
     public function handleLikeActivity()
@@ -289,10 +304,6 @@ class Inbox
             return;
         }
         LikePipeline::dispatch($like);
-        $this->logger->to_id = $status->profile_id;
-        $this->logger->from_id = $profile->id;
-        $this->logger->processed_at = Carbon::now();
-        $this->logger->save();
     }
 
 
@@ -306,19 +317,29 @@ class Inbox
         $actor = $this->payload['actor'];
         $profile = self::actorFirstOrCreate($actor);
         $obj = $this->payload['object'];
-        $status = Helpers::statusFirstOrFetch($obj['object']);
 
         switch ($obj['type']) {
             case 'Like':
+                $status = Helpers::statusFirstOrFetch($obj['object']);
                 Like::whereProfileId($profile->id)
                     ->whereStatusId($status->id)
-                    ->delete();
+                    ->forceDelete();
+                break;
+                
+            case 'Announce':
+                $parent = Helpers::statusFirstOrFetch($obj['object']);
+                $status = Status::whereProfileId($profile->id)
+                    ->whereReblogOfId($parent->id)
+                    ->first();
+                Notification::whereProfileId($parent->profile->id)
+                    ->whereActorId($profile->id)
+                    ->whereAction('share')
+                    ->whereItemId($status->id)
+                    ->whereItemType('App\Status')
+                    ->forceDelete();
+                $status->forceDelete();
                 break;
         }
 
-        $this->logger->to_id = $status->profile_id;
-        $this->logger->from_id = $profile->id;
-        $this->logger->processed_at = Carbon::now();
-        $this->logger->save();
     }
 }

+ 40 - 0
database/migrations/2018_12_24_032921_add_delete_after_to_user_table.php

@@ -0,0 +1,40 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class AddDeleteAfterToUserTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('profiles', function (Blueprint $table) {
+            $table->timestamp('delete_after')->nullable();
+        });
+
+        Schema::table('users', function (Blueprint $table) {
+            $table->timestamp('delete_after')->nullable();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('profiles', function (Blueprint $table) {
+            $table->dropColumn('delete_after');
+        });
+
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('delete_after');
+        });
+    }
+}

+ 147 - 98
package-lock.json

@@ -104,11 +104,15 @@
                 "json-schema-traverse": "^0.3.0"
             }
         },
+        "ajv-errors": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+            "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ=="
+        },
         "ajv-keywords": {
             "version": "3.2.0",
             "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
-            "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
-            "dev": true
+            "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo="
         },
         "align-text": {
             "version": "0.1.4",
@@ -164,7 +168,6 @@
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
             "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-            "dev": true,
             "requires": {
                 "color-convert": "^1.9.0"
             }
@@ -199,7 +202,6 @@
             "version": "1.0.10",
             "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
             "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-            "dev": true,
             "requires": {
                 "sprintf-js": "~1.0.2"
             }
@@ -632,6 +634,11 @@
                 "babel-types": "^6.24.1"
             }
         },
+        "babel-helper-vue-jsx-merge-props": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
+            "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
+        },
         "babel-helpers": {
             "version": "6.24.1",
             "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
@@ -1243,8 +1250,7 @@
         "big.js": {
             "version": "3.2.0",
             "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
-            "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
-            "dev": true
+            "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q=="
         },
         "binary-extensions": {
             "version": "1.11.0",
@@ -1253,9 +1259,9 @@
             "dev": true
         },
         "blob": {
-            "version": "0.0.4",
-            "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
-            "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE="
+            "version": "0.0.5",
+            "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+            "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
         },
         "block-stream": {
             "version": "0.0.9",
@@ -1328,9 +1334,9 @@
             }
         },
         "bootstrap": {
-            "version": "4.1.3",
-            "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz",
-            "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w=="
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.2.1.tgz",
+            "integrity": "sha512-tt/7vIv3Gm2mnd/WeDx36nfGGHleil0Wg8IeB7eMrVkY0fZ5iTaBisSh8oNANc2IBsCc6vCgCNTIM/IEN0+50Q=="
         },
         "bootstrap-vue": {
             "version": "2.0.0-rc.11",
@@ -1651,7 +1657,6 @@
             "version": "2.4.1",
             "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
             "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-            "dev": true,
             "requires": {
                 "ansi-styles": "^3.2.1",
                 "escape-string-regexp": "^1.0.5",
@@ -1884,7 +1889,6 @@
             "version": "1.9.3",
             "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
             "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-            "dev": true,
             "requires": {
                 "color-name": "1.1.3"
             }
@@ -1892,8 +1896,7 @@
         "color-name": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-            "dev": true
+            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
         },
         "color-string": {
             "version": "0.3.0",
@@ -2123,7 +2126,6 @@
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz",
             "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==",
-            "dev": true,
             "requires": {
                 "is-directory": "^0.3.1",
                 "js-yaml": "^3.9.0",
@@ -2134,14 +2136,12 @@
                 "esprima": {
                     "version": "4.0.1",
                     "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-                    "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-                    "dev": true
+                    "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
                 },
                 "js-yaml": {
                     "version": "3.12.0",
                     "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
                     "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
-                    "dev": true,
                     "requires": {
                         "argparse": "^1.0.7",
                         "esprima": "^4.0.0"
@@ -2151,7 +2151,6 @@
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
                     "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
-                    "dev": true,
                     "requires": {
                         "error-ex": "^1.3.1",
                         "json-parse-better-errors": "^1.0.1"
@@ -2842,11 +2841,72 @@
                 "minimalistic-crypto-utils": "^1.0.0"
             }
         },
+        "emoji-mart-vue": {
+            "version": "2.6.6",
+            "resolved": "https://registry.npmjs.org/emoji-mart-vue/-/emoji-mart-vue-2.6.6.tgz",
+            "integrity": "sha512-844CI/Sa99G6goiZN6u+zT5XxB4wUhpYYTK8s3FrU2fl9y0ckbqe0uw7EuY0R53hb4bo9uJHVOPywE+C4vu4fg==",
+            "requires": {
+                "postcss-loader": "^3.0.0"
+            },
+            "dependencies": {
+                "ajv": {
+                    "version": "6.6.2",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
+                    "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
+                    "requires": {
+                        "fast-deep-equal": "^2.0.1",
+                        "fast-json-stable-stringify": "^2.0.0",
+                        "json-schema-traverse": "^0.4.1",
+                        "uri-js": "^4.2.2"
+                    }
+                },
+                "fast-deep-equal": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+                    "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+                },
+                "json-schema-traverse": {
+                    "version": "0.4.1",
+                    "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+                    "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+                },
+                "postcss": {
+                    "version": "7.0.7",
+                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz",
+                    "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==",
+                    "requires": {
+                        "chalk": "^2.4.1",
+                        "source-map": "^0.6.1",
+                        "supports-color": "^5.5.0"
+                    }
+                },
+                "postcss-loader": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz",
+                    "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==",
+                    "requires": {
+                        "loader-utils": "^1.1.0",
+                        "postcss": "^7.0.0",
+                        "postcss-load-config": "^2.0.0",
+                        "schema-utils": "^1.0.0"
+                    }
+                },
+                "schema-utils": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+                    "requires": {
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
+                    }
+                }
+            }
+        },
         "emojis-list": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
-            "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
-            "dev": true
+            "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
         },
         "encodeurl": {
             "version": "1.0.2",
@@ -2872,9 +2932,9 @@
             }
         },
         "engine.io-client": {
-            "version": "3.2.1",
-            "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
-            "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+            "version": "3.3.1",
+            "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.1.tgz",
+            "integrity": "sha512-q66JBFuQcy7CSlfAz9L3jH+v7DTT3i6ZEadYcVj2pOs8/0uJHLxKX3WBkGTvULJMdz0tUCyJag0aKT/dpXL9BQ==",
             "requires": {
                 "component-emitter": "1.2.1",
                 "component-inherit": "0.0.3",
@@ -2884,20 +2944,20 @@
                 "indexof": "0.0.1",
                 "parseqs": "0.0.5",
                 "parseuri": "0.0.5",
-                "ws": "~3.3.1",
+                "ws": "~6.1.0",
                 "xmlhttprequest-ssl": "~1.5.4",
                 "yeast": "0.1.2"
             }
         },
         "engine.io-parser": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz",
-            "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==",
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
+            "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
             "requires": {
                 "after": "0.8.2",
                 "arraybuffer.slice": "~0.0.7",
                 "base64-arraybuffer": "0.1.5",
-                "blob": "0.0.4",
+                "blob": "0.0.5",
                 "has-binary2": "~1.0.2"
             }
         },
@@ -2926,7 +2986,6 @@
             "version": "1.3.2",
             "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
             "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-            "dev": true,
             "requires": {
                 "is-arrayish": "^0.2.1"
             }
@@ -3458,8 +3517,7 @@
         "fast-json-stable-stringify": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-            "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
-            "dev": true
+            "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
         },
         "fastparse": {
             "version": "1.1.1",
@@ -4563,8 +4621,7 @@
         "has-flag": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-            "dev": true
+            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
         },
         "has-unicode": {
             "version": "2.0.1",
@@ -4945,7 +5002,6 @@
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
             "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
-            "dev": true,
             "requires": {
                 "import-from": "^2.1.0"
             }
@@ -4954,7 +5010,6 @@
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
             "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
-            "dev": true,
             "requires": {
                 "resolve-from": "^3.0.0"
             }
@@ -5141,8 +5196,7 @@
         "is-arrayish": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-            "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-            "dev": true
+            "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
         },
         "is-binary-path": {
             "version": "1.0.1",
@@ -5222,8 +5276,7 @@
         "is-directory": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
-            "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
-            "dev": true
+            "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
         },
         "is-dotfile": {
             "version": "1.0.3",
@@ -5477,8 +5530,7 @@
         "json-parse-better-errors": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-            "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-            "dev": true
+            "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
         },
         "json-schema": {
             "version": "0.2.3",
@@ -5507,8 +5559,7 @@
         "json5": {
             "version": "0.5.1",
             "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
-            "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
-            "dev": true
+            "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
         },
         "jsonfile": {
             "version": "3.0.1",
@@ -5544,9 +5595,9 @@
             "dev": true
         },
         "laravel-echo": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-1.4.0.tgz",
-            "integrity": "sha512-O0vkToCCpxuH9dYAlugTLQzG0BmxvGrjXim0LHZ0VPsFu/Y+sXnV9GvYbmcBq1rcJymQc/6GHMUCDY01lN26lQ=="
+            "version": "1.5.2",
+            "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-1.5.2.tgz",
+            "integrity": "sha512-Xw9QsxJKapv0C2UTnXRRIM1+epL3+qaSRGd7V8pXEuIHnxjcdpk2I7YLauDzYhBNMKEKWtlE/sv9Wgs+hYKnbg=="
         },
         "laravel-mix": {
             "version": "2.1.14",
@@ -5638,7 +5689,6 @@
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
             "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
-            "dev": true,
             "requires": {
                 "big.js": "^3.1.3",
                 "emojis-list": "^2.0.0",
@@ -6642,6 +6692,11 @@
                 }
             }
         },
+        "opencollective-postinstall": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.1.tgz",
+            "integrity": "sha512-saQQ9hjLwu/oS0492eyYotoh+bra1819cfAT5rjY/e4REWwuc8IgZ844Oo44SiftWcJuBiqp0SA0BFVbmLX0IQ=="
+        },
         "opn": {
             "version": "5.3.0",
             "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
@@ -6964,9 +7019,9 @@
             }
         },
         "popper.js": {
-            "version": "1.14.4",
-            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.4.tgz",
-            "integrity": "sha1-juwdj/AqWjoVLdQ0FKFce3n9abY="
+            "version": "1.14.6",
+            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz",
+            "integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA=="
         },
         "portfinder": {
             "version": "1.0.17",
@@ -7662,7 +7717,6 @@
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz",
             "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==",
-            "dev": true,
             "requires": {
                 "cosmiconfig": "^4.0.0",
                 "import-cwd": "^2.0.0"
@@ -9591,8 +9645,7 @@
         "require-from-string": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
-            "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
-            "dev": true
+            "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
         },
         "require-main-filename": {
             "version": "1.0.1",
@@ -9627,8 +9680,7 @@
         "resolve-from": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-            "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
-            "dev": true
+            "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
         },
         "resolve-url": {
             "version": "0.2.1",
@@ -9759,7 +9811,8 @@
         "safe-buffer": {
             "version": "5.1.2",
             "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+            "dev": true
         },
         "safe-regex": {
             "version": "1.1.0",
@@ -10208,30 +10261,30 @@
             }
         },
         "socket.io-client": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
-            "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz",
+            "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==",
             "requires": {
                 "backo2": "1.0.2",
                 "base64-arraybuffer": "0.1.5",
                 "component-bind": "1.0.0",
                 "component-emitter": "1.2.1",
                 "debug": "~3.1.0",
-                "engine.io-client": "~3.2.0",
+                "engine.io-client": "~3.3.1",
                 "has-binary2": "~1.0.2",
                 "has-cors": "1.1.0",
                 "indexof": "0.0.1",
                 "object-component": "0.0.3",
                 "parseqs": "0.0.5",
                 "parseuri": "0.0.5",
-                "socket.io-parser": "~3.2.0",
+                "socket.io-parser": "~3.3.0",
                 "to-array": "0.1.4"
             }
         },
         "socket.io-parser": {
-            "version": "3.2.0",
-            "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
-            "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
+            "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
             "requires": {
                 "component-emitter": "1.2.1",
                 "debug": "~3.1.0",
@@ -10307,8 +10360,7 @@
         "source-map": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-            "dev": true
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
         },
         "source-map-resolve": {
             "version": "0.5.2",
@@ -10441,8 +10493,7 @@
         "sprintf-js": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-            "dev": true
+            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
         },
         "sshpk": {
             "version": "1.14.2",
@@ -10636,7 +10687,6 @@
             "version": "5.5.0",
             "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
             "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-            "dev": true,
             "requires": {
                 "has-flag": "^3.0.0"
             }
@@ -10657,9 +10707,9 @@
             }
         },
         "sweetalert": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.0.tgz",
-            "integrity": "sha512-9YKj0SvjKyBfRWco50UOsIbXVeifYbxzT9Qda7EsqC01eafHGCSG0IR7g942ufjzt7lnwO8ZZBwr6emXv2fQrg==",
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.2.tgz",
+            "integrity": "sha512-iWx7X4anRBNDa/a+AdTmvAzQtkN1+s4j/JJRWlHpYE8Qimkohs8/XnFcWeYHH2lMA8LRCa5tj2d244If3S/hzA==",
             "requires": {
                 "es6-object-assign": "^1.1.0",
                 "promise-polyfill": "^6.0.2"
@@ -10989,11 +11039,6 @@
                 }
             }
         },
-        "ultron": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
-            "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
-        },
         "union-value": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
@@ -11127,7 +11172,6 @@
             "version": "4.2.2",
             "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
             "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
-            "dev": true,
             "requires": {
                 "punycode": "^2.1.0"
             },
@@ -11135,8 +11179,7 @@
                 "punycode": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-                    "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-                    "dev": true
+                    "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
                 }
             }
         },
@@ -11263,11 +11306,19 @@
             }
         },
         "vue": {
-            "version": "2.5.17",
-            "resolved": "https://registry.npmjs.org/vue/-/vue-2.5.17.tgz",
-            "integrity": "sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ==",
+            "version": "2.5.21",
+            "resolved": "https://registry.npmjs.org/vue/-/vue-2.5.21.tgz",
+            "integrity": "sha512-Aejvyyfhn0zjVeLvXd70h4hrE4zZDx1wfZqia6ekkobLmUZ+vNFQer53B4fu0EjWBSiqApxPejzkO1Znt3joxQ==",
             "dev": true
         },
+        "vue-content-loader": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/vue-content-loader/-/vue-content-loader-0.2.1.tgz",
+            "integrity": "sha1-DrMy4qcmQ9V/sgnXLWUmVzsZH1o=",
+            "requires": {
+                "babel-helper-vue-jsx-merge-props": "^2.0.3"
+            }
+        },
         "vue-functional-data-merge": {
             "version": "2.0.7",
             "resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-2.0.7.tgz",
@@ -11347,9 +11398,9 @@
             }
         },
         "vue-loading-overlay": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/vue-loading-overlay/-/vue-loading-overlay-3.1.0.tgz",
-            "integrity": "sha512-EJOaqxfkSwt6LRoKYnWWPch6fLRRzHWFxLBnRHjXHIK/fP0MSmbBLh9ZRpxarXJeDBiyykQevDXa7h7809JaAA=="
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/vue-loading-overlay/-/vue-loading-overlay-3.1.1.tgz",
+            "integrity": "sha512-6Iv0V/S++/LDRR3bgIZDJwBTgMVupuj+hjDb2YzTrEXbSEygtD10eJwZdMnEenLcD3ZAFz5D30qkNUYow9W2kw=="
         },
         "vue-style-loader": {
             "version": "3.1.2",
@@ -11362,9 +11413,9 @@
             }
         },
         "vue-template-compiler": {
-            "version": "2.5.17",
-            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.5.17.tgz",
-            "integrity": "sha512-63uI4syCwtGR5IJvZM0LN5tVsahrelomHtCxvRkZPJ/Tf3ADm1U1wG6KWycK3qCfqR+ygM5vewUvmJ0REAYksg==",
+            "version": "2.5.21",
+            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.5.21.tgz",
+            "integrity": "sha512-Vmk5Cv7UcmI99B9nXJEkaK262IQNnHp5rJYo+EwYpe2epTAXqcVyExhV6pk8jTkxQK2vRc8v8KmZBAwdmUZvvw==",
             "dev": true,
             "requires": {
                 "de-indent": "^1.0.2",
@@ -11747,13 +11798,11 @@
             "dev": true
         },
         "ws": {
-            "version": "3.3.3",
-            "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
-            "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+            "version": "6.1.2",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz",
+            "integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==",
             "requires": {
-                "async-limiter": "~1.0.0",
-                "safe-buffer": "~5.1.0",
-                "ultron": "~1.1.0"
+                "async-limiter": "~1.0.0"
             }
         },
         "xmlhttprequest": {

+ 10 - 7
package.json

@@ -12,29 +12,32 @@
     },
     "devDependencies": {
         "axios": "^0.18",
-        "bootstrap": "^4.1.3",
+        "bootstrap": "^4.2.1",
         "cross-env": "^5.2.0",
         "jquery": "^3.2",
         "laravel-mix": "^2.1.14",
         "lodash": "^4.17.11",
-        "popper.js": "^1.14.4",
-        "vue": "^2.5.17"
+        "popper.js": "^1.14.6",
+        "vue": "^2.5.21",
+        "vue-template-compiler": "^2.5.21"
     },
     "dependencies": {
         "bootstrap-vue": "^2.0.0-rc.11",
+        "emoji-mart-vue": "^2.6.6",
         "filesize": "^3.6.1",
         "infinite-scroll": "^3.0.4",
-        "laravel-echo": "^1.4.0",
+        "laravel-echo": "^1.5.2",
         "opencollective": "^1.0.3",
         "opencollective-postinstall": "^2.0.1",
         "plyr": "^3.4.7",
         "pusher-js": "^4.2.2",
         "readmore-js": "^2.2.1",
-        "socket.io-client": "^2.1.1",
-        "sweetalert": "^2.1.0",
+        "socket.io-client": "^2.2.0",
+        "sweetalert": "^2.1.2",
         "twitter-text": "^2.0.5",
+        "vue-content-loader": "^0.2.1",
         "vue-infinite-loading": "^2.4.3",
-        "vue-loading-overlay": "^3.1.0",
+        "vue-loading-overlay": "^3.1.1",
         "vue-timeago": "^5.0.0"
     },
     "collective": {

TEMPAT SAMPAH
public/css/app.css


TEMPAT SAMPAH
public/js/app.js


TEMPAT SAMPAH
public/js/components.js


TEMPAT SAMPAH
public/mix-manifest.json


+ 8 - 9
resources/assets/js/components.js

@@ -2,7 +2,7 @@ window.Vue = require('vue');
 import BootstrapVue from 'bootstrap-vue'
 import InfiniteLoading from 'vue-infinite-loading';
 import Loading from 'vue-loading-overlay';
-import VueTimeago from 'vue-timeago'
+import VueTimeago from 'vue-timeago';
 
 Vue.use(BootstrapVue);
 Vue.use(InfiniteLoading);
@@ -36,8 +36,8 @@ require('./components/commentform');
 require('./components/searchform');
 require('./components/bookmarkform');
 require('./components/statusform');
-require('./components/embed');
-require('./components/notifications');
+//require('./components/embed');
+//require('./components/notifications');
 
 // import Echo from "laravel-echo"
 
@@ -78,12 +78,16 @@ Vue.component(
     require('./components/presenter/VideoAlbumPresenter.vue')
 );
 
-
 Vue.component(
     'mixed-album-presenter',
     require('./components/presenter/MixedAlbumPresenter.vue')
 );
 
+// Vue.component(
+//     'micro',
+//     require('./components/Micro.vue')
+// );
+
 Vue.component(
     'follow-suggestions',
     require('./components/FollowSuggestions.vue')
@@ -114,11 +118,6 @@ Vue.component(
     require('./components/Timeline.vue')
 );
 
-// Vue.component(
-//     'micro',
-//     require('./components/Micro.vue')
-// );
-
 Vue.component(
     'passport-clients',
     require('./components/passport/Clients.vue')

+ 9 - 3
resources/assets/js/components/Timeline.vue

@@ -3,7 +3,9 @@
 	<div class="row">
 		<div class="col-md-8 col-lg-8 pt-2 px-0 my-3 timeline order-2 order-md-1">
 			<div class="loader text-center">
-				<div class="lds-ring"><div></div><div></div><div></div><div></div></div> 
+				<div class="spinner-border" role="status">
+				  <span class="sr-only">Loading...</span>
+				</div>
 			</div>
 			<div class="card mb-4 status-card card-md-rounded-0" :data-status-id="status.id" v-for="(status, index) in feed" :key="status.id">
 
@@ -101,7 +103,9 @@
 			<div class="mb-4">
 				<div class="card profile-card">
 					<div class="card-body loader text-center">
-						<div class="lds-ring"><div></div><div></div><div></div><div></div></div> 
+						<div class="spinner-border" role="status">
+							<span class="sr-only">Loading...</span>
+						</div>
 					</div>
 					<div class="card-body contents d-none">
 						<div class="media d-flex align-items-center">
@@ -142,7 +146,9 @@
 						</p>
 					</div>
 					<div class="card-body loader text-center" style="height: 300px;">
-						<div class="lds-ring"><div></div><div></div><div></div><div></div></div> 
+						<div class="spinner-border" role="status">
+							<span class="sr-only">Loading...</span>
+						</div>
 					</div>
 					<div class="card-body pt-2 contents" style="max-height: 300px; overflow-y: scroll;">
 						<div class="media mb-3 align-items-center" v-for="(n, index) in notifications">

+ 3 - 0
resources/lang/en/profile.php

@@ -9,4 +9,7 @@ return [
   'privateProfileWarning' => 'This Account is Private',
   'alreadyFollow'         => 'Already follow :username?',
   'loginToSeeProfile'     => 'to see their photos and videos.',
+
+  'status.disabled.header' 	  => 'Profile Unavailable',
+  'status.disabled.body'	  => 'Sorry, this profile is not available at the moment. Please try again shortly.',
 ];

+ 18 - 0
resources/views/profile/disabled.blade.php

@@ -0,0 +1,18 @@
+@extends('layouts.app',['title' => "Account Temporarily Unavailable"])
+
+@section('content')
+<div class="container">
+	<div class="profile-timeline mt-2 mt-md-4">
+		<div class="alert alert-danger">
+			<div class="py-2">
+				<p class="lead font-weight-bold mb-0">
+					{{__('profile.status.disabled.header')}}
+				</p>
+				<p class="mb-0">
+					{{__('profile.status.disabled.body')}}
+				</p>
+			</div>
+		</div>
+	</div>
+</div>
+@endsection

+ 3 - 2
resources/views/settings/home.blade.php

@@ -82,8 +82,9 @@
     </div>
     <hr>
     <div class="form-group row">
-      <div class="col-12 text-right">
-        <button type="submit" class="btn btn-primary font-weight-bold">Submit</button>
+      <div class="col-12 d-flex align-items-center justify-content-between">
+        <a class="font-weight-bold" href="{{route('settings.remove.temporary')}}">Temporarily Disable Account</a>
+        <button type="submit" class="btn btn-primary font-weight-bold float-right">Submit</button>
       </div>
     </div>
   </form>

+ 1 - 1
resources/views/settings/remove/permanent.blade.php

@@ -22,7 +22,7 @@
   	<p>
       <form method="post">
         @csrf
-        <div class="custom-control custom-checkbox mb-3">
+        <div class="custom-control custom-switch mb-3">
           <input type="checkbox" class="custom-control-input" id="confirm-check">
           <label class="custom-control-label font-weight-bold" for="confirm-check">I confirm that this action is not reversible, and will result in the permanent deletion of my account.</label>
         </div>

+ 30 - 0
resources/views/settings/remove/temporary.blade.php

@@ -0,0 +1,30 @@
+@extends('settings.template')
+
+@section('section')
+
+  <div class="title">
+    <h3 class="font-weight-bold">Temporarily Disable Your Account</h3>
+  </div>
+  <hr>
+  <div class="mt-3">
+  	<p>Hi <span class="font-weight-bold">{{Auth::user()->username}}</span>,</p>
+
+  	<p>You can disable your account instead of deleting it. This means your account will be hidden until you reactivate it by logging back in.</p>
+
+  	<p class="pb-1">You can only disable your account once a week.</p>
+
+  	<p class="font-weight-bold">Keeping Your Data Safe</p>
+  	<p class="pb-3">Nothing is more important to us than the safety and security of this community. People put their trust in us by sharing moments of their lives on Pixelfed. So we will never make any compromises when it comes to safeguarding your data.</p>
+
+  	<p class="pb-2">When you press the button below, your photos, comments and likes will be hidden until you reactivate your account by logging back in.</p>
+
+  	<p>
+  		<form method="post">
+        @csrf
+  		  <button type="submit" class="btn btn-primary font-weight-bold py-0">Temporarily Disable Account</button>
+  		</form>
+  	</p>
+  </div>
+
+
+@endsection

+ 2 - 0
routes/api.php

@@ -12,3 +12,5 @@ use Illuminate\Http\Request;
 | is assigned the "api" middleware group. Enjoy building your API!
 |
 */
+
+Route::post('/users/{username}/inbox', 'FederationController@userInbox');

+ 7 - 6
routes/web.php

@@ -76,6 +76,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
         Route::post('follow', 'FollowerController@store')->middleware('throttle:250,1440');
         Route::post('bookmark', 'BookmarkController@store')->middleware('throttle:250,1440');
         Route::get('lang/{locale}', 'SiteController@changeLocale');
+        Route::get('restored', 'AccountController@accountRestored');
 
         Route::get('verify-email', 'AccountController@verifyEmail');
         Route::post('verify-email', 'AccountController@sendVerifyEmail')->middleware('throttle:10,1440');
@@ -133,12 +134,12 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
         Route::get('privacy/blocked-instances', 'SettingsController@blockedInstances')->name('settings.privacy.blocked-instances');
 
         // Todo: Release in 0.7.2
-        // Route::group(['prefix' => 'remove', 'middleware' => 'dangerzone'], function() {
-        //     Route::get('request/temporary', 'SettingsController@removeAccountTemporary')->name('settings.remove.temporary');
-        //     Route::post('request/temporary', 'SettingsController@removeAccountTemporarySubmit');
-        //     Route::get('request/permanent', 'SettingsController@removeAccountPermanent')->name('settings.remove.permanent');
-        //     Route::post('request/permanent', 'SettingsController@removeAccountPermanentSubmit');
-        // });
+        Route::group(['prefix' => 'remove', 'middleware' => 'dangerzone'], function() {
+            Route::get('request/temporary', 'SettingsController@removeAccountTemporary')->name('settings.remove.temporary');
+            Route::post('request/temporary', 'SettingsController@removeAccountTemporarySubmit');
+            Route::get('request/permanent', 'SettingsController@removeAccountPermanent')->name('settings.remove.permanent');
+            Route::post('request/permanent', 'SettingsController@removeAccountPermanentSubmit');
+        });
 
         Route::group(['prefix' => 'security', 'middleware' => 'dangerzone'], function() {
             Route::get(