Will Browning преди 5 години
родител
ревизия
7c27373ef6
променени са 42 файла, в които са добавени 845 реда и са изтрити 717 реда
  1. 2 1
      app/Http/Controllers/Api/ActiveAdditionalUsernameController.php
  2. 2 1
      app/Http/Controllers/Api/ActiveAliasController.php
  3. 2 1
      app/Http/Controllers/Api/ActiveDomainController.php
  4. 12 6
      app/Http/Controllers/Api/AdditionalUsernameController.php
  5. 0 34
      app/Http/Controllers/Api/AliasApiController.php
  6. 15 15
      app/Http/Controllers/Api/AliasController.php
  7. 2 1
      app/Http/Controllers/Api/AliasRecipientController.php
  8. 12 6
      app/Http/Controllers/Api/DomainController.php
  9. 2 1
      app/Http/Controllers/Api/DomainDefaultRecipientController.php
  10. 2 1
      app/Http/Controllers/Api/EncryptedRecipientController.php
  11. 9 12
      app/Http/Controllers/Api/RecipientController.php
  12. 2 1
      app/Http/Controllers/Api/RecipientKeyController.php
  13. 3 1
      app/Http/Controllers/Auth/BackupCodeController.php
  14. 2 1
      app/Http/Controllers/Auth/TwoFactorAuthController.php
  15. 1 0
      app/Http/Controllers/DeactivateAliasController.php
  16. 13 0
      app/Http/Controllers/ShowAdditionalUsernameController.php
  17. 20 0
      app/Http/Controllers/ShowAliasController.php
  18. 13 0
      app/Http/Controllers/ShowDomainController.php
  19. 22 0
      app/Http/Controllers/ShowRecipientController.php
  20. 1 1
      app/Http/Resources/AliasResource.php
  21. 2 1
      app/Http/Resources/DomainResource.php
  22. 1 1
      app/Http/Resources/RecipientResource.php
  23. 20 20
      composer.lock
  24. 0 1
      resources/js/app.js
  25. 0 194
      resources/js/components/ApiToken.vue
  26. 5 5
      resources/js/components/passport/PersonalAccessTokens.vue
  27. 34 8
      resources/js/pages/Aliases.vue
  28. 7 7
      resources/js/pages/Domains.vue
  29. 9 9
      resources/js/pages/Recipients.vue
  30. 6 6
      resources/js/pages/Usernames.vue
  31. 40 1
      routes/api.php
  32. 9 41
      routes/web.php
  33. 33 44
      tests/Feature/Api/AdditionalUsernamesTest.php
  34. 7 12
      tests/Feature/Api/AliasRecipientsTest.php
  35. 123 0
      tests/Feature/Api/AliasesTest.php
  36. 32 63
      tests/Feature/Api/DomainsTest.php
  37. 30 152
      tests/Feature/Api/RecipientsTest.php
  38. 63 0
      tests/Feature/ShowAdditionalUsernamesTest.php
  39. 0 69
      tests/Feature/ShowAliasesTest.php
  40. 82 0
      tests/Feature/ShowDomainsTest.php
  41. 173 0
      tests/Feature/ShowRecipientsTest.php
  42. 32 0
      tests/TestCase.php

+ 2 - 1
app/Http/Controllers/ActiveAdditionalUsernameController.php → app/Http/Controllers/Api/ActiveAdditionalUsernameController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Resources\AdditionalUsernameResource;
 use App\Http\Resources\AdditionalUsernameResource;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 
 

+ 2 - 1
app/Http/Controllers/ActiveAliasController.php → app/Http/Controllers/Api/ActiveAliasController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Resources\AliasResource;
 use App\Http\Resources\AliasResource;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 
 

+ 2 - 1
app/Http/Controllers/ActiveDomainController.php → app/Http/Controllers/Api/ActiveDomainController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Resources\DomainResource;
 use App\Http\Resources\DomainResource;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 
 

+ 12 - 6
app/Http/Controllers/AdditionalUsernameController.php → app/Http/Controllers/Api/AdditionalUsernameController.php

@@ -1,8 +1,9 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
 use App\DeletedUsername;
 use App\DeletedUsername;
+use App\Http\Controllers\Controller;
 use App\Http\Requests\StoreAdditionalUsernameRequest;
 use App\Http\Requests\StoreAdditionalUsernameRequest;
 use App\Http\Requests\UpdateAdditionalUsernameRequest;
 use App\Http\Requests\UpdateAdditionalUsernameRequest;
 use App\Http\Resources\AdditionalUsernameResource;
 use App\Http\Resources\AdditionalUsernameResource;
@@ -11,9 +12,14 @@ class AdditionalUsernameController extends Controller
 {
 {
     public function index()
     public function index()
     {
     {
-        return view('usernames.index', [
-            'usernames' => user()->additionalUsernames()->latest()->get()
-        ]);
+        return AdditionalUsernameResource::collection(user()->additionalUsernames()->latest()->get());
+    }
+
+    public function show($id)
+    {
+        $username = user()->additionalUsernames()->findOrFail($id);
+
+        return new AdditionalUsernameResource($username);
     }
     }
 
 
     public function store(StoreAdditionalUsernameRequest $request)
     public function store(StoreAdditionalUsernameRequest $request)
@@ -26,7 +32,7 @@ class AdditionalUsernameController extends Controller
 
 
         user()->increment('username_count');
         user()->increment('username_count');
 
 
-        return new AdditionalUsernameResource($username->fresh());
+        return new AdditionalUsernameResource($username->refresh());
     }
     }
 
 
     public function update(UpdateAdditionalUsernameRequest $request, $id)
     public function update(UpdateAdditionalUsernameRequest $request, $id)
@@ -35,7 +41,7 @@ class AdditionalUsernameController extends Controller
 
 
         $username->update(['description' => $request->description]);
         $username->update(['description' => $request->description]);
 
 
-        return new AdditionalUsernameResource($username);
+        return new AdditionalUsernameResource($username->refresh());
     }
     }
 
 
     public function destroy($id)
     public function destroy($id)

+ 0 - 34
app/Http/Controllers/Api/AliasApiController.php

@@ -1,34 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Api;
-
-use App\Http\Controllers\Controller;
-use App\Http\Requests\StoreAliasRequest;
-use App\Http\Resources\AliasResource;
-use Ramsey\Uuid\Uuid;
-
-class AliasApiController extends Controller
-{
-    public function store(StoreAliasRequest $request)
-    {
-        if (user()->hasReachedUuidAliasLimit()) {
-            return response('', 403);
-        }
-
-        if (user()->hasExceededNewAliasLimit()) {
-            return response('', 429);
-        }
-
-        $uuid = Uuid::uuid4();
-
-        $alias = user()->aliases()->create([
-            'id' => $uuid,
-            'email' => $uuid . '@' . $request->domain,
-            'local_part' => $uuid,
-            'domain' => $request->domain,
-            'description' => $request->description
-        ]);
-
-        return new AliasResource($alias->fresh());
-    }
-}

+ 15 - 15
app/Http/Controllers/AliasController.php → app/Http/Controllers/Api/AliasController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Requests\StoreAliasRequest;
 use App\Http\Requests\StoreAliasRequest;
 use App\Http\Requests\UpdateAliasRequest;
 use App\Http\Requests\UpdateAliasRequest;
 use App\Http\Resources\AliasResource;
 use App\Http\Resources\AliasResource;
@@ -11,16 +12,14 @@ class AliasController extends Controller
 {
 {
     public function index()
     public function index()
     {
     {
-        return view('aliases.index', [
-            'defaultRecipient' => user()->defaultRecipient,
-            'aliases' => user()->aliases()->with(['recipients', 'customDomain.defaultRecipient'])->latest()->get(),
-            'recipients' => user()->verifiedRecipients,
-            'totalForwarded' => user()->totalEmailsForwarded(),
-            'totalBlocked' => user()->totalEmailsBlocked(),
-            'totalReplies' => user()->totalEmailsReplied(),
-            'domain' => user()->username.'.'.config('anonaddy.domain'),
-            'bandwidthMb' => user()->bandwidth_mb
-        ]);
+        return AliasResource::collection(user()->aliases()->with('recipients')->latest()->get());
+    }
+
+    public function show($id)
+    {
+        $alias = user()->aliases()->findOrFail($id);
+
+        return new AliasResource($alias->load('recipients'));
     }
     }
 
 
     public function store(StoreAliasRequest $request)
     public function store(StoreAliasRequest $request)
@@ -33,12 +32,13 @@ class AliasController extends Controller
 
 
         $alias = user()->aliases()->create([
         $alias = user()->aliases()->create([
             'id' => $uuid,
             'id' => $uuid,
-            'email' => $uuid.'@'.$request->domain,
+            'email' => $uuid . '@' . $request->domain,
             'local_part' => $uuid,
             'local_part' => $uuid,
-            'domain' => $request->domain
+            'domain' => $request->domain,
+            'description' => $request->description
         ]);
         ]);
 
 
-        return new AliasResource($alias->fresh());
+        return new AliasResource($alias->refresh()->load('recipients'));
     }
     }
 
 
     public function update(UpdateAliasRequest $request, $id)
     public function update(UpdateAliasRequest $request, $id)
@@ -47,7 +47,7 @@ class AliasController extends Controller
 
 
         $alias->update(['description' => $request->description]);
         $alias->update(['description' => $request->description]);
 
 
-        return new AliasResource($alias);
+        return new AliasResource($alias->refresh()->load('recipients'));
     }
     }
 
 
     public function destroy($id)
     public function destroy($id)

+ 2 - 1
app/Http/Controllers/AliasRecipientController.php → app/Http/Controllers/Api/AliasRecipientController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Requests\StoreAliasRecipientRequest;
 use App\Http\Requests\StoreAliasRecipientRequest;
 use App\Http\Resources\AliasResource;
 use App\Http\Resources\AliasResource;
 
 

+ 12 - 6
app/Http/Controllers/DomainController.php → app/Http/Controllers/Api/DomainController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Requests\StoreDomainRequest;
 use App\Http\Requests\StoreDomainRequest;
 use App\Http\Requests\UpdateDomainRequest;
 use App\Http\Requests\UpdateDomainRequest;
 use App\Http\Resources\DomainResource;
 use App\Http\Resources\DomainResource;
@@ -10,9 +11,14 @@ class DomainController extends Controller
 {
 {
     public function index()
     public function index()
     {
     {
-        return view('domains.index', [
-            'domains' => user()->domains()->with(['aliases', 'defaultRecipient'])->latest()->get()
-        ]);
+        return DomainResource::collection(user()->domains()->with(['aliases', 'defaultRecipient'])->latest()->get());
+    }
+
+    public function show($id)
+    {
+        $domain = user()->domains()->findOrFail($id);
+
+        return new DomainResource($domain->load(['aliases', 'defaultRecipient']));
     }
     }
 
 
     public function store(StoreDomainRequest $request)
     public function store(StoreDomainRequest $request)
@@ -21,7 +27,7 @@ class DomainController extends Controller
 
 
         $domain->checkVerification();
         $domain->checkVerification();
 
 
-        return new DomainResource($domain->fresh());
+        return new DomainResource($domain->refresh()->load(['aliases', 'defaultRecipient']));
     }
     }
 
 
     public function update(UpdateDomainRequest $request, $id)
     public function update(UpdateDomainRequest $request, $id)
@@ -30,7 +36,7 @@ class DomainController extends Controller
 
 
         $domain->update(['description' => $request->description]);
         $domain->update(['description' => $request->description]);
 
 
-        return new DomainResource($domain);
+        return new DomainResource($domain->refresh()->load(['aliases', 'defaultRecipient']));
     }
     }
 
 
     public function destroy($id)
     public function destroy($id)

+ 2 - 1
app/Http/Controllers/DomainDefaultRecipientController.php → app/Http/Controllers/Api/DomainDefaultRecipientController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Requests\UpdateDomainDefaultRecipientRequest;
 use App\Http\Requests\UpdateDomainDefaultRecipientRequest;
 use App\Http\Resources\DomainResource;
 use App\Http\Resources\DomainResource;
 
 

+ 2 - 1
app/Http/Controllers/EncryptedRecipientController.php → app/Http/Controllers/Api/EncryptedRecipientController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Resources\RecipientResource;
 use App\Http\Resources\RecipientResource;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 
 

+ 9 - 12
app/Http/Controllers/RecipientController.php → app/Http/Controllers/Api/RecipientController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Requests\StoreRecipientRequest;
 use App\Http\Requests\StoreRecipientRequest;
 use App\Http\Resources\RecipientResource;
 use App\Http\Resources\RecipientResource;
 
 
@@ -9,18 +10,14 @@ class RecipientController extends Controller
 {
 {
     public function index()
     public function index()
     {
     {
-        $recipients = user()->recipients()->with('aliases')->latest()->get();
-
-        $count = $recipients->count();
+        return RecipientResource::collection(user()->recipients()->with('aliases')->latest()->get());
+    }
 
 
-        $recipients->each(function ($item, $key) use ($count) {
-            $item['key'] = $count - $key;
-        });
+    public function show($id)
+    {
+        $recipient = user()->recipients()->findOrFail($id);
 
 
-        return view('recipients.index', [
-            'recipients' => $recipients,
-            'aliasesUsingDefault' => user()->aliasesUsingDefault
-        ]);
+        return new RecipientResource($recipient->load('aliases'));
     }
     }
 
 
     public function store(StoreRecipientRequest $request)
     public function store(StoreRecipientRequest $request)
@@ -29,7 +26,7 @@ class RecipientController extends Controller
 
 
         $recipient->sendEmailVerificationNotification();
         $recipient->sendEmailVerificationNotification();
 
 
-        return new RecipientResource($recipient->fresh());
+        return new RecipientResource($recipient->refresh()->load('aliases'));
     }
     }
 
 
     public function destroy($id)
     public function destroy($id)

+ 2 - 1
app/Http/Controllers/RecipientKeyController.php → app/Http/Controllers/Api/RecipientKeyController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Api;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Requests\UpdateRecipientKeyRequest;
 use App\Http\Requests\UpdateRecipientKeyRequest;
 use App\Http\Resources\RecipientResource;
 use App\Http\Resources\RecipientResource;
 
 

+ 3 - 1
app/Http/Controllers/BackupCodeController.php → app/Http/Controllers/Auth/BackupCodeController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Auth;
 
 
+use App\Http\Controllers\Controller;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Hash;
 use Illuminate\Support\Facades\Hash;
 use PragmaRX\Google2FALaravel\Support\Authenticator;
 use PragmaRX\Google2FALaravel\Support\Authenticator;
@@ -10,6 +11,7 @@ class BackupCodeController extends Controller
 {
 {
     public function __construct()
     public function __construct()
     {
     {
+        $this->middleware('auth');
         $this->middleware('throttle:3,1')->only('login');
         $this->middleware('throttle:3,1')->only('login');
     }
     }
 
 

+ 2 - 1
app/Http/Controllers/TwoFactorAuthController.php → app/Http/Controllers/Auth/TwoFactorAuthController.php

@@ -1,7 +1,8 @@
 <?php
 <?php
 
 
-namespace App\Http\Controllers;
+namespace App\Http\Controllers\Auth;
 
 
+use App\Http\Controllers\Controller;
 use App\Http\Requests\EnableTwoFactorAuthRequest;
 use App\Http\Requests\EnableTwoFactorAuthRequest;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Hash;
 use Illuminate\Support\Facades\Hash;

+ 1 - 0
app/Http/Controllers/DeactivateAliasController.php

@@ -12,6 +12,7 @@ class DeactivateAliasController extends Controller
     public function __construct()
     public function __construct()
     {
     {
         $this->middleware('signed');
         $this->middleware('signed');
+        $this->middleware('throttle:6,1');
     }
     }
 
 
     public function deactivate($id)
     public function deactivate($id)

+ 13 - 0
app/Http/Controllers/ShowAdditionalUsernameController.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class ShowAdditionalUsernameController extends Controller
+{
+    public function index()
+    {
+        return view('usernames.index', [
+            'usernames' => user()->additionalUsernames()->latest()->get()
+        ]);
+    }
+}

+ 20 - 0
app/Http/Controllers/ShowAliasController.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class ShowAliasController extends Controller
+{
+    public function index()
+    {
+        return view('aliases.index', [
+            'defaultRecipient' => user()->defaultRecipient,
+            'aliases' => user()->aliases()->with(['recipients', 'customDomain.defaultRecipient'])->latest()->get(),
+            'recipients' => user()->verifiedRecipients,
+            'totalForwarded' => user()->totalEmailsForwarded(),
+            'totalBlocked' => user()->totalEmailsBlocked(),
+            'totalReplies' => user()->totalEmailsReplied(),
+            'domain' => user()->username.'.'.config('anonaddy.domain'),
+            'bandwidthMb' => user()->bandwidth_mb
+        ]);
+    }
+}

+ 13 - 0
app/Http/Controllers/ShowDomainController.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class ShowDomainController extends Controller
+{
+    public function index()
+    {
+        return view('domains.index', [
+            'domains' => user()->domains()->with(['aliases', 'defaultRecipient'])->latest()->get()
+        ]);
+    }
+}

+ 22 - 0
app/Http/Controllers/ShowRecipientController.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class ShowRecipientController extends Controller
+{
+    public function index()
+    {
+        $recipients = user()->recipients()->with('aliases')->latest()->get();
+
+        $count = $recipients->count();
+
+        $recipients->each(function ($item, $key) use ($count) {
+            $item['key'] = $count - $key;
+        });
+
+        return view('recipients.index', [
+            'recipients' => $recipients,
+            'aliasesUsingDefault' => user()->aliasesUsingDefault
+        ]);
+    }
+}

+ 1 - 1
app/Http/Resources/AliasResource.php

@@ -21,7 +21,7 @@ class AliasResource extends JsonResource
             'emails_forwarded' => $this->emails_forwarded,
             'emails_forwarded' => $this->emails_forwarded,
             'emails_blocked' => $this->emails_blocked,
             'emails_blocked' => $this->emails_blocked,
             'emails_replied' => $this->emails_replied,
             'emails_replied' => $this->emails_replied,
-            'recipients' => $this->recipients,
+            'recipients' => RecipientResource::collection($this->whenLoaded('recipients')),
             'created_at' => $this->created_at->toDateTimeString(),
             'created_at' => $this->created_at->toDateTimeString(),
             'updated_at' => $this->updated_at->toDateTimeString(),
             'updated_at' => $this->updated_at->toDateTimeString(),
         ];
         ];

+ 2 - 1
app/Http/Resources/DomainResource.php

@@ -13,7 +13,8 @@ class DomainResource extends JsonResource
             'user_id' => $this->user_id,
             'user_id' => $this->user_id,
             'domain' => $this->domain,
             'domain' => $this->domain,
             'description' => $this->description,
             'description' => $this->description,
-            'aliases' => $this->aliases,
+            'aliases' => AliasResource::collection($this->whenLoaded('aliases')),
+            'default_recipient' => new RecipientResource($this->whenLoaded('defaultRecipient')),
             'active' => $this->active,
             'active' => $this->active,
             'domain_verified_at' => $this->domain_verified_at ? $this->domain_verified_at->toDateTimeString() : null,
             'domain_verified_at' => $this->domain_verified_at ? $this->domain_verified_at->toDateTimeString() : null,
             'created_at' => $this->created_at->toDateTimeString(),
             'created_at' => $this->created_at->toDateTimeString(),

+ 1 - 1
app/Http/Resources/RecipientResource.php

@@ -15,7 +15,7 @@ class RecipientResource extends JsonResource
             'should_encrypt' => $this->should_encrypt,
             'should_encrypt' => $this->should_encrypt,
             'fingerprint' => $this->fingerprint,
             'fingerprint' => $this->fingerprint,
             'email_verified_at' => $this->email_verified_at ? $this->email_verified_at->toDateTimeString() : null,
             'email_verified_at' => $this->email_verified_at ? $this->email_verified_at->toDateTimeString() : null,
-            'aliases' => $this->aliases,
+            'aliases' => AliasResource::collection($this->whenLoaded('aliases')),
             'created_at' => $this->created_at->toDateTimeString(),
             'created_at' => $this->created_at->toDateTimeString(),
             'updated_at' => $this->updated_at->toDateTimeString(),
             'updated_at' => $this->updated_at->toDateTimeString(),
         ];
         ];

+ 20 - 20
composer.lock

@@ -1158,16 +1158,16 @@
         },
         },
         {
         {
             "name": "laravel/framework",
             "name": "laravel/framework",
-            "version": "v6.2.0",
+            "version": "v6.3.0",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "cacce5a77fa94793cf25345d67387632134d75dd"
+                "reference": "80914c430fb5e49f492812d704ba6aeec03d80a2"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/cacce5a77fa94793cf25345d67387632134d75dd",
-                "reference": "cacce5a77fa94793cf25345d67387632134d75dd",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/80914c430fb5e49f492812d704ba6aeec03d80a2",
+                "reference": "80914c430fb5e49f492812d704ba6aeec03d80a2",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -1300,7 +1300,7 @@
                 "framework",
                 "framework",
                 "laravel"
                 "laravel"
             ],
             ],
-            "time": "2019-10-08T12:50:06+00:00"
+            "time": "2019-10-15T13:38:24+00:00"
         },
         },
         {
         {
             "name": "laravel/passport",
             "name": "laravel/passport",
@@ -1543,16 +1543,16 @@
         },
         },
         {
         {
             "name": "league/flysystem",
             "name": "league/flysystem",
-            "version": "1.0.55",
+            "version": "1.0.57",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem.git",
                 "url": "https://github.com/thephpleague/flysystem.git",
-                "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6"
+                "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6",
-                "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a",
+                "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -1623,7 +1623,7 @@
                 "sftp",
                 "sftp",
                 "storage"
                 "storage"
             ],
             ],
-            "time": "2019-08-24T11:17:19+00:00"
+            "time": "2019-10-16T21:01:05+00:00"
         },
         },
         {
         {
             "name": "league/oauth2-server",
             "name": "league/oauth2-server",
@@ -1854,16 +1854,16 @@
         },
         },
         {
         {
             "name": "nesbot/carbon",
             "name": "nesbot/carbon",
-            "version": "2.25.1",
+            "version": "2.25.2",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "d59c6cea9c4a3547bb6c0dfec451319abdaa4fb1"
+                "reference": "443fe5f1498147e0fbc792142b5dc43e2e8a533f"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/d59c6cea9c4a3547bb6c0dfec451319abdaa4fb1",
-                "reference": "d59c6cea9c4a3547bb6c0dfec451319abdaa4fb1",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/443fe5f1498147e0fbc792142b5dc43e2e8a533f",
+                "reference": "443fe5f1498147e0fbc792142b5dc43e2e8a533f",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -1917,7 +1917,7 @@
                 "datetime",
                 "datetime",
                 "time"
                 "time"
             ],
             ],
-            "time": "2019-10-05T15:52:23+00:00"
+            "time": "2019-10-14T14:18:59+00:00"
         },
         },
         {
         {
             "name": "nikic/php-parser",
             "name": "nikic/php-parser",
@@ -5157,16 +5157,16 @@
         },
         },
         {
         {
             "name": "facade/ignition",
             "name": "facade/ignition",
-            "version": "1.11.1",
+            "version": "1.11.2",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/facade/ignition.git",
                 "url": "https://github.com/facade/ignition.git",
-                "reference": "dc56111b19e160236084dc6ab460e9c08912ecac"
+                "reference": "862cbc2dfffa1fa28b47822a116e5b2e03b421db"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition/zipball/dc56111b19e160236084dc6ab460e9c08912ecac",
-                "reference": "dc56111b19e160236084dc6ab460e9c08912ecac",
+                "url": "https://api.github.com/repos/facade/ignition/zipball/862cbc2dfffa1fa28b47822a116e5b2e03b421db",
+                "reference": "862cbc2dfffa1fa28b47822a116e5b2e03b421db",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -5224,7 +5224,7 @@
                 "laravel",
                 "laravel",
                 "page"
                 "page"
             ],
             ],
-            "time": "2019-10-08T07:36:31+00:00"
+            "time": "2019-10-13T10:42:06+00:00"
         },
         },
         {
         {
             "name": "facade/ignition-contracts",
             "name": "facade/ignition-contracts",

+ 0 - 1
resources/js/app.js

@@ -24,7 +24,6 @@ Vue.use(VueGoodTablePlugin)
 Vue.component('loader', require('./components/Loader.vue').default)
 Vue.component('loader', require('./components/Loader.vue').default)
 Vue.component('dropdown', require('./components/DropdownNav.vue').default)
 Vue.component('dropdown', require('./components/DropdownNav.vue').default)
 Vue.component('icon', require('./components/Icon.vue').default)
 Vue.component('icon', require('./components/Icon.vue').default)
-Vue.component('api-token', require('./components/ApiToken.vue').default)
 
 
 Vue.component('aliases', require('./pages/Aliases.vue').default)
 Vue.component('aliases', require('./pages/Aliases.vue').default)
 Vue.component('recipients', require('./pages/Recipients.vue').default)
 Vue.component('recipients', require('./pages/Recipients.vue').default)

+ 0 - 194
resources/js/components/ApiToken.vue

@@ -1,194 +0,0 @@
-<template>
-  <div class="mt-6">
-    <h3 class="font-bold text-xl">{{ token ? 'Rotate' : 'Generate' }} API Token</h3>
-
-    <div class="my-4 w-24 border-b-2 border-grey-200"></div>
-
-    <p v-if="token" class="my-6">
-      To rotate your current API token simply click the button below.
-    </p>
-    <p v-else class="my-6">
-      To enable the use of the API simply click the button below to generate an API token.
-    </p>
-
-    <button
-      @click="rotate"
-      class="bg-cyan-400 w-full hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none"
-      :class="loading ? 'cursor-not-allowed' : ''"
-      :disabled="loading"
-    >
-      {{ token ? 'Rotate' : 'Generate New' }} Token
-      <loader v-if="loading" />
-    </button>
-
-    <div class="mt-6" v-if="token">
-      <h3 class="font-bold text-xl">
-        Revoke API Token
-      </h3>
-
-      <div class="my-4 w-24 border-b-2 border-grey-200"></div>
-
-      <p class="my-6">
-        To revoke the current API token simply click the button below.
-      </p>
-
-      <button @click="revokeModalOpen = true" class="text-red-500 font-bold focus:outline-none">
-        Revoke Token
-      </button>
-    </div>
-
-    <Modal :open="modalOpen" @close="closeModal">
-      <div class="max-w-lg w-full bg-white rounded-lg shadow-2xl px-6 py-6">
-        <h2
-          class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
-        >
-          API Token
-        </h2>
-        <p class="my-4 text-grey-700">
-          This is your new API token. This is the only time the token will ever be displayed, so
-          please make a note of it in a safe place (e.g. password manager)! You may revoke or rotate
-          the token at any time from your API settings.
-        </p>
-        <pre class="flex p-3 text-grey-900 bg-white border rounded">
-            <code class="break-all whitespace-normal">{{ token }}</code>
-        </pre>
-        <div class="mt-6">
-          <button
-            class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none"
-            v-clipboard="() => token"
-            v-clipboard:success="clipboardSuccess"
-            v-clipboard:error="clipboardError"
-          >
-            Copy To Clipboard
-          </button>
-          <button
-            @click="closeModal"
-            class="ml-4 px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus:outline-none"
-          >
-            Close
-          </button>
-        </div>
-      </div>
-    </Modal>
-
-    <Modal :open="revokeModalOpen" @close="closeRevokeModal">
-      <div class="max-w-lg w-full bg-white rounded-lg shadow-2xl px-6 py-6">
-        <h2
-          class="font-semibold text-grey-900 text-2xl leading-tight border-b-2 border-grey-100 pb-4"
-        >
-          Revoke API Token
-        </h2>
-        <p class="my-4 text-grey-700">
-          Any browser extension, application or script using this API token will no longer be able
-          to access the AnonAddy API. This action cannot be undone.
-        </p>
-        <div class="mt-6">
-          <button
-            @click="revoke"
-            class="bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-4 rounded focus:outline-none"
-            :class="revokeLoading ? 'cursor-not-allowed' : ''"
-            :disabled="revokeLoading"
-          >
-            Revoke Token
-            <loader v-if="revokeLoading" />
-          </button>
-          <button
-            @click="closeRevokeModal"
-            class="ml-4 px-4 py-3 text-grey-800 font-semibold bg-white hover:bg-grey-50 border border-grey-100 rounded focus:outline-none"
-          >
-            Close
-          </button>
-        </div>
-      </div>
-    </Modal>
-  </div>
-</template>
-
-<script>
-import Modal from './../components/Modal.vue'
-
-export default {
-  props: {
-    initialToken: {
-      type: String,
-      required: true,
-    },
-  },
-  components: {
-    Modal,
-  },
-  data() {
-    return {
-      loading: false,
-      revokeLoading: false,
-      modalOpen: false,
-      revokeModalOpen: false,
-      token: this.initialToken,
-    }
-  },
-  methods: {
-    rotate() {
-      this.loading = true
-
-      axios
-        .post('/settings/api-token', {
-          headers: { 'Content-Type': 'application/json' },
-        })
-        .then(response => {
-          this.modalOpen = true
-          this.loading = false
-          this.token = response.data.token
-        })
-        .catch(error => {
-          this.loading = false
-          this.error()
-        })
-    },
-    revoke() {
-      this.revokeLoading = true
-
-      axios
-        .delete('/settings/api-token', {
-          headers: { 'Content-Type': 'application/json' },
-        })
-        .then(response => {
-          this.revokeModalOpen = false
-          this.revokeLoading = false
-          this.token = ''
-          this.success('Token Revoked Successfully!')
-        })
-        .catch(error => {
-          this.revokeModalOpen = false
-          this.revokeLoading = false
-          this.error()
-        })
-    },
-    closeModal() {
-      this.modalOpen = false
-    },
-    closeRevokeModal() {
-      this.revokeModalOpen = false
-    },
-    clipboardSuccess() {
-      this.success('Copied to clipboard')
-    },
-    clipboardError() {
-      this.error('Could not copy to clipboard')
-    },
-    success(text = '') {
-      this.$notify({
-        title: 'Success',
-        text: text,
-        type: 'success',
-      })
-    },
-    error(text = 'An error has occurred, please try again later') {
-      this.$notify({
-        title: 'Error',
-        text: text,
-        type: 'error',
-      })
-    },
-  },
-}
-</script>

+ 5 - 5
resources/js/components/passport/PersonalAccessTokens.vue

@@ -15,7 +15,7 @@
         class="text-indigo-700"
         class="text-indigo-700"
         >open-source</a
         >open-source</a
       >
       >
-      AnonAddy browser extension on
+      browser extension on
       <a
       <a
         href="https://addons.mozilla.org/en-GB/firefox/addon/anonaddy/"
         href="https://addons.mozilla.org/en-GB/firefox/addon/anonaddy/"
         target="_blank"
         target="_blank"
@@ -50,8 +50,8 @@
       <div class="my-4 w-24 border-b-2 border-grey-200"></div>
       <div class="my-4 w-24 border-b-2 border-grey-200"></div>
 
 
       <p class="my-6">
       <p class="my-6">
-        Tokens you have generated that can be used to access the AnonAddy API. To revoke an access
-        token simply click the delete button next to it.
+        Tokens you have generated that can be used to access the API. To revoke an access token
+        simply click the delete button next to it.
       </p>
       </p>
 
 
       <div>
       <div>
@@ -114,7 +114,7 @@
             id="create-token-name"
             id="create-token-name"
             class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3 mb-6"
             class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3 mb-6"
             :class="form.errors.length > 0 ? 'border-red-500' : ''"
             :class="form.errors.length > 0 ? 'border-red-500' : ''"
-            placeholder="e.g. Browser extension"
+            placeholder="e.g. Firefox extension"
             autofocus
             autofocus
           />
           />
           <button
           <button
@@ -179,7 +179,7 @@
         </h2>
         </h2>
         <p class="my-4 text-grey-700">
         <p class="my-4 text-grey-700">
           Any browser extension, application or script using this API access token will no longer be
           Any browser extension, application or script using this API access token will no longer be
-          able to access the AnonAddy API. This action cannot be undone.
+          able to access the API. This action cannot be undone.
         </p>
         </p>
         <div class="mt-6">
         <div class="mt-6">
           <button
           <button

+ 34 - 8
resources/js/pages/Aliases.vue

@@ -354,7 +354,7 @@
         <label for="alias_domain" class="block text-grey-700 text-sm my-2">
         <label for="alias_domain" class="block text-grey-700 text-sm my-2">
           Alias Domain:
           Alias Domain:
         </label>
         </label>
-        <div class="block relative w-full">
+        <div class="block relative w-full mb-4">
           <select
           <select
             v-model="generateAliasDomain"
             v-model="generateAliasDomain"
             id="alias_domain"
             id="alias_domain"
@@ -379,6 +379,21 @@
             </svg>
             </svg>
           </div>
           </div>
         </div>
         </div>
+        <label for="alias_description" class="block text-grey-700 text-sm my-2">
+          Description:
+        </label>
+        <p v-show="errors.generateAliasDescription" class="mb-3 text-red-500 text-sm">
+          {{ errors.generateAliasDescription }}
+        </p>
+        <input
+          v-model="generateAliasDescription"
+          id="alias_description"
+          type="text"
+          class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3"
+          :class="errors.generateAliasDescription ? 'border-red-500' : ''"
+          placeholder="Enter description (optional)..."
+          autofocus
+        />
 
 
         <div class="mt-6">
         <div class="mt-6">
           <button
           <button
@@ -455,7 +470,8 @@
           Delete alias
           Delete alias
         </h2>
         </h2>
         <p class="mt-4 text-grey-700">
         <p class="mt-4 text-grey-700">
-          Are you sure you want to delete this alias? This action cannot be undone.
+          Are you sure you want to delete this alias? This action cannot be undone. Once deleted,
+          this alias will <b>not be able to be used again</b> and will bounce any emails sent to it.
         </p>
         </p>
         <div class="mt-6">
         <div class="mt-6">
           <button
           <button
@@ -554,6 +570,7 @@ export default {
       generateAliasModalOpen: false,
       generateAliasModalOpen: false,
       generateAliasLoading: false,
       generateAliasLoading: false,
       generateAliasDomain: this.domain,
       generateAliasDomain: this.domain,
+      generateAliasDescription: '',
       recipientsAliasToEdit: {},
       recipientsAliasToEdit: {},
       aliasRecipientsToEdit: [],
       aliasRecipientsToEdit: [],
       columns: [
       columns: [
@@ -615,6 +632,7 @@ export default {
         },
         },
       ],
       ],
       rows: this.initialAliases,
       rows: this.initialAliases,
+      errors: {},
     }
     }
   },
   },
   watch: {
   watch: {
@@ -661,7 +679,7 @@ export default {
       this.deleteAliasLoading = true
       this.deleteAliasLoading = true
 
 
       axios
       axios
-        .delete(`/aliases/${id}`)
+        .delete(`/api/v1/aliases/${id}`)
         .then(response => {
         .then(response => {
           this.rows = _.reject(this.rows, alias => alias.id === id)
           this.rows = _.reject(this.rows, alias => alias.id === id)
           this.deleteAliasModalOpen = false
           this.deleteAliasModalOpen = false
@@ -688,7 +706,7 @@ export default {
 
 
       axios
       axios
         .post(
         .post(
-          '/alias-recipients',
+          '/api/v1/alias-recipients',
           JSON.stringify({
           JSON.stringify({
             alias_id: this.recipientsAliasToEdit.id,
             alias_id: this.recipientsAliasToEdit.id,
             recipient_ids: _.map(this.aliasRecipientsToEdit, recipient => recipient.id),
             recipient_ids: _.map(this.aliasRecipientsToEdit, recipient => recipient.id),
@@ -716,13 +734,20 @@ export default {
         })
         })
     },
     },
     generateNewAlias() {
     generateNewAlias() {
+      this.errors = {}
+
+      if (this.generateAliasDescription.length > 100) {
+        return (this.errors.generateAliasDescription = 'Description cannot exceed 100 characters')
+      }
+
       this.generateAliasLoading = true
       this.generateAliasLoading = true
 
 
       axios
       axios
         .post(
         .post(
-          '/aliases',
+          '/api/v1/aliases',
           JSON.stringify({
           JSON.stringify({
             domain: this.generateAliasDomain,
             domain: this.generateAliasDomain,
+            description: this.generateAliasDescription,
           }),
           }),
           {
           {
             headers: { 'Content-Type': 'application/json' },
             headers: { 'Content-Type': 'application/json' },
@@ -730,6 +755,7 @@ export default {
         )
         )
         .then(({ data }) => {
         .then(({ data }) => {
           this.generateAliasLoading = false
           this.generateAliasLoading = false
+          this.generateAliasDescription = ''
           this.rows.push(data.data)
           this.rows.push(data.data)
           this.generateAliasModalOpen = false
           this.generateAliasModalOpen = false
           this.success('New alias generated successfully')
           this.success('New alias generated successfully')
@@ -750,7 +776,7 @@ export default {
 
 
       axios
       axios
         .patch(
         .patch(
-          `/aliases/${alias.id}`,
+          `/api/v1/aliases/${alias.id}`,
           JSON.stringify({
           JSON.stringify({
             description: this.aliasDescriptionToEdit,
             description: this.aliasDescriptionToEdit,
           }),
           }),
@@ -773,7 +799,7 @@ export default {
     activateAlias(id) {
     activateAlias(id) {
       axios
       axios
         .post(
         .post(
-          `/active-aliases`,
+          `/api/v1/active-aliases`,
           JSON.stringify({
           JSON.stringify({
             id: id,
             id: id,
           }),
           }),
@@ -790,7 +816,7 @@ export default {
     },
     },
     deactivateAlias(id) {
     deactivateAlias(id) {
       axios
       axios
-        .delete(`/active-aliases/${id}`)
+        .delete(`/api/v1/active-aliases/${id}`)
         .then(response => {
         .then(response => {
           //
           //
         })
         })

+ 7 - 7
resources/js/pages/Domains.vue

@@ -212,7 +212,7 @@
           Just include the domain/subdomain e.g. example.com without any http protocol.
           Just include the domain/subdomain e.g. example.com without any http protocol.
         </p>
         </p>
         <div class="mt-6">
         <div class="mt-6">
-          <p v-show="errors.newDomain" class="mb-3 text-red-500">
+          <p v-show="errors.newDomain" class="mb-3 text-red-500 text-sm">
             {{ errors.newDomain }}
             {{ errors.newDomain }}
           </p>
           </p>
           <input
           <input
@@ -452,7 +452,7 @@ export default {
 
 
       axios
       axios
         .post(
         .post(
-          '/domains',
+          '/api/v1/domains',
           JSON.stringify({
           JSON.stringify({
             domain: this.newDomain,
             domain: this.newDomain,
           }),
           }),
@@ -525,7 +525,7 @@ export default {
 
 
       axios
       axios
         .patch(
         .patch(
-          `/domains/${domain.id}`,
+          `/api/v1/domains/${domain.id}`,
           JSON.stringify({
           JSON.stringify({
             description: this.domainDescriptionToEdit,
             description: this.domainDescriptionToEdit,
           }),
           }),
@@ -550,7 +550,7 @@ export default {
 
 
       axios
       axios
         .patch(
         .patch(
-          `/domains/${this.defaultRecipientDomainToEdit.id}/default-recipient`,
+          `/api/v1/domains/${this.defaultRecipientDomainToEdit.id}/default-recipient`,
           JSON.stringify({
           JSON.stringify({
             default_recipient: this.defaultRecipient ? this.defaultRecipient.id : '',
             default_recipient: this.defaultRecipient ? this.defaultRecipient.id : '',
           }),
           }),
@@ -577,7 +577,7 @@ export default {
     activateDomain(id) {
     activateDomain(id) {
       axios
       axios
         .post(
         .post(
-          `/active-domains`,
+          `/api/v1/active-domains`,
           JSON.stringify({
           JSON.stringify({
             id: id,
             id: id,
           }),
           }),
@@ -594,7 +594,7 @@ export default {
     },
     },
     deactivateDomain(id) {
     deactivateDomain(id) {
       axios
       axios
-        .delete(`/active-domains/${id}`)
+        .delete(`/api/v1/active-domains/${id}`)
         .then(response => {
         .then(response => {
           //
           //
         })
         })
@@ -606,7 +606,7 @@ export default {
       this.deleteDomainLoading = true
       this.deleteDomainLoading = true
 
 
       axios
       axios
-        .delete(`/domains/${id}`)
+        .delete(`/api/v1/domains/${id}`)
         .then(response => {
         .then(response => {
           this.rows = _.reject(this.rows, domain => domain.id === id)
           this.rows = _.reject(this.rows, domain => domain.id === id)
           this.deleteDomainModalOpen = false
           this.deleteDomainModalOpen = false

+ 9 - 9
resources/js/pages/Recipients.vue

@@ -183,7 +183,7 @@
           click "Resend email" to get a new one.
           click "Resend email" to get a new one.
         </p>
         </p>
         <div class="mt-6">
         <div class="mt-6">
-          <p v-show="errors.newRecipient" class="mb-3 text-red-500">
+          <p v-show="errors.newRecipient" class="mb-3 text-red-500 text-sm">
             {{ errors.newRecipient }}
             {{ errors.newRecipient }}
           </p>
           </p>
           <input
           <input
@@ -223,7 +223,7 @@
         <p class="mt-4 text-grey-700">Enter your <b>PUBLIC</b> key data in the text area below.</p>
         <p class="mt-4 text-grey-700">Enter your <b>PUBLIC</b> key data in the text area below.</p>
         <p class="mt-4 text-grey-700">Make sure to remove <b>Comment:</b> and <b>Version:</b></p>
         <p class="mt-4 text-grey-700">Make sure to remove <b>Comment:</b> and <b>Version:</b></p>
         <div class="mt-6">
         <div class="mt-6">
-          <p v-show="errors.recipientKey" class="mb-3 text-red-500">
+          <p v-show="errors.recipientKey" class="mb-3 text-red-500 text-sm">
             {{ errors.recipientKey }}
             {{ errors.recipientKey }}
           </p>
           </p>
           <textarea
           <textarea
@@ -455,7 +455,7 @@ export default {
 
 
       axios
       axios
         .post(
         .post(
-          '/recipients',
+          '/api/v1/recipients',
           JSON.stringify({
           JSON.stringify({
             email: this.newRecipient,
             email: this.newRecipient,
           }),
           }),
@@ -518,9 +518,9 @@ export default {
       this.deleteRecipientLoading = true
       this.deleteRecipientLoading = true
 
 
       axios
       axios
-        .delete(`/recipients/${id}`)
+        .delete(`/api/v1/recipients/${id}`)
         .then(response => {
         .then(response => {
-          this.recipients = _.reject(this.rows, recipient => recipient.id === id)
+          this.rows = _.reject(this.rows, recipient => recipient.id === id)
           this.deleteRecipientModalOpen = false
           this.deleteRecipientModalOpen = false
           this.deleteRecipientLoading = false
           this.deleteRecipientLoading = false
         })
         })
@@ -542,7 +542,7 @@ export default {
       this.deleteRecipientKeyLoading = true
       this.deleteRecipientKeyLoading = true
 
 
       axios
       axios
-        .delete(`/recipient-keys/${id}`)
+        .delete(`/api/v1/recipient-keys/${id}`)
         .then(response => {
         .then(response => {
           let recipient = _.find(this.rows, ['id', this.recipientKeyIdToDelete])
           let recipient = _.find(this.rows, ['id', this.recipientKeyIdToDelete])
           recipient.should_encrypt = false
           recipient.should_encrypt = false
@@ -576,7 +576,7 @@ export default {
 
 
       axios
       axios
         .patch(
         .patch(
-          `/recipient-keys/${this.recipientToAddKey.id}`,
+          `/api/v1/recipient-keys/${this.recipientToAddKey.id}`,
           JSON.stringify({
           JSON.stringify({
             key_data: this.recipientKey,
             key_data: this.recipientKey,
           }),
           }),
@@ -611,7 +611,7 @@ export default {
     turnOnEncryption(id) {
     turnOnEncryption(id) {
       axios
       axios
         .post(
         .post(
-          `/encrypted-recipients`,
+          `/api/v1/encrypted-recipients`,
           JSON.stringify({
           JSON.stringify({
             id: id,
             id: id,
           }),
           }),
@@ -628,7 +628,7 @@ export default {
     },
     },
     turnOffEncryption(id) {
     turnOffEncryption(id) {
       axios
       axios
-        .delete(`/encrypted-recipients/${id}`)
+        .delete(`/api/v1/encrypted-recipients/${id}`)
         .then(response => {
         .then(response => {
           //
           //
         })
         })

+ 6 - 6
resources/js/pages/Usernames.vue

@@ -167,7 +167,7 @@
           signed up with.
           signed up with.
         </p>
         </p>
         <div class="mt-6">
         <div class="mt-6">
-          <p v-show="errors.newUsername" class="mb-3 text-red-500">
+          <p v-show="errors.newUsername" class="mb-3 text-red-500 text-sm">
             {{ errors.newUsername }}
             {{ errors.newUsername }}
           </p>
           </p>
           <input
           <input
@@ -335,7 +335,7 @@ export default {
 
 
       axios
       axios
         .post(
         .post(
-          '/usernames',
+          '/api/v1/usernames',
           JSON.stringify({
           JSON.stringify({
             username: this.newUsername,
             username: this.newUsername,
           }),
           }),
@@ -377,7 +377,7 @@ export default {
 
 
       axios
       axios
         .patch(
         .patch(
-          `/usernames/${username.id}`,
+          `/api/v1/usernames/${username.id}`,
           JSON.stringify({
           JSON.stringify({
             description: this.usernameDescriptionToEdit,
             description: this.usernameDescriptionToEdit,
           }),
           }),
@@ -400,7 +400,7 @@ export default {
     activateUsername(id) {
     activateUsername(id) {
       axios
       axios
         .post(
         .post(
-          `/active-usernames`,
+          `/api/v1/active-usernames`,
           JSON.stringify({
           JSON.stringify({
             id: id,
             id: id,
           }),
           }),
@@ -417,7 +417,7 @@ export default {
     },
     },
     deactivateUsername(id) {
     deactivateUsername(id) {
       axios
       axios
-        .delete(`/active-usernames/${id}`)
+        .delete(`/api/v1/active-usernames/${id}`)
         .then(response => {
         .then(response => {
           //
           //
         })
         })
@@ -429,7 +429,7 @@ export default {
       this.deleteUsernameLoading = true
       this.deleteUsernameLoading = true
 
 
       axios
       axios
-        .delete(`/usernames/${id}`)
+        .delete(`/api/v1/usernames/${id}`)
         .then(response => {
         .then(response => {
           this.rows = _.reject(this.rows, username => username.id === id)
           this.rows = _.reject(this.rows, username => username.id === id)
           this.deleteUsernameModalOpen = false
           this.deleteUsernameModalOpen = false

+ 40 - 1
routes/api.php

@@ -15,5 +15,44 @@ Route::group([
   'middleware' => ['verified'],
   'middleware' => ['verified'],
   'prefix' => 'v1'
   'prefix' => 'v1'
 ], function () {
 ], function () {
-    Route::post('/aliases', 'Api\AliasApiController@store');
+    Route::get('/aliases', 'Api\AliasController@index');
+    Route::get('/aliases/{id}', 'Api\AliasController@show');
+    Route::post('/aliases', 'Api\AliasController@store');
+    Route::patch('/aliases/{id}', 'Api\AliasController@update');
+    Route::delete('/aliases/{id}', 'Api\AliasController@destroy');
+
+    Route::post('/active-aliases', 'Api\ActiveAliasController@store');
+    Route::delete('/active-aliases/{id}', 'Api\ActiveAliasController@destroy');
+
+    Route::post('/alias-recipients', 'Api\AliasRecipientController@store');
+
+    Route::get('/recipients', 'Api\RecipientController@index');
+    Route::get('/recipients/{id}', 'Api\RecipientController@show');
+    Route::post('/recipients', 'Api\RecipientController@store');
+    Route::delete('/recipients/{id}', 'Api\RecipientController@destroy');
+
+    Route::patch('/recipient-keys/{id}', 'Api\RecipientKeyController@update');
+    Route::delete('/recipient-keys/{id}', 'Api\RecipientKeyController@destroy');
+
+    Route::post('/encrypted-recipients', 'Api\EncryptedRecipientController@store');
+    Route::delete('/encrypted-recipients/{id}', 'Api\EncryptedRecipientController@destroy');
+
+    Route::get('/domains', 'Api\DomainController@index');
+    Route::get('/domains/{id}', 'Api\DomainController@show');
+    Route::post('/domains', 'Api\DomainController@store');
+    Route::patch('/domains/{id}', 'Api\DomainController@update');
+    Route::delete('/domains/{id}', 'Api\DomainController@destroy');
+    Route::patch('/domains/{id}/default-recipient', 'Api\DomainDefaultRecipientController@update');
+
+    Route::post('/active-domains', 'Api\ActiveDomainController@store');
+    Route::delete('/active-domains/{id}', 'Api\ActiveDomainController@destroy');
+
+    Route::get('/usernames', 'Api\AdditionalUsernameController@index');
+    Route::get('/usernames/{id}', 'Api\AdditionalUsernameController@show');
+    Route::post('/usernames', 'Api\AdditionalUsernameController@store');
+    Route::patch('/usernames/{id}', 'Api\AdditionalUsernameController@update');
+    Route::delete('/usernames/{id}', 'Api\AdditionalUsernameController@destroy');
+
+    Route::post('/active-usernames', 'Api\ActiveAdditionalUsernameController@store');
+    Route::delete('/active-usernames/{id}', 'Api\ActiveAdditionalUsernameController@destroy');
 });
 });

+ 9 - 41
routes/web.php

@@ -13,53 +13,21 @@
 
 
 Auth::routes(['verify' => true, 'register' => config('anonaddy.enable_registration')]);
 Auth::routes(['verify' => true, 'register' => config('anonaddy.enable_registration')]);
 
 
-Route::post('/login/2fa', 'TwoFactorAuthController@authenticateTwoFactor')->name('login.2fa')->middleware(['2fa', 'throttle', 'auth']);
-
-Route::get('/login/backup-code', 'BackupCodeController@index')->name('login.backup_code.index')->middleware('auth');
-Route::post('/login/backup-code', 'BackupCodeController@login')->name('login.backup_code.login')->middleware('auth');
+Route::post('/login/2fa', 'Auth\TwoFactorAuthController@authenticateTwoFactor')->name('login.2fa')->middleware(['2fa', 'throttle', 'auth']);
 
 
+Route::get('/login/backup-code', 'Auth\BackupCodeController@index')->name('login.backup_code.index');
+Route::post('/login/backup-code', 'Auth\BackupCodeController@login')->name('login.backup_code.login');
 
 
 Route::middleware(['auth', 'verified', '2fa'])->group(function () {
 Route::middleware(['auth', 'verified', '2fa'])->group(function () {
-    Route::get('/', 'AliasController@index')->name('aliases.index');
-    Route::post('/aliases', 'AliasController@store')->name('aliases.store');
-    Route::patch('/aliases/{id}', 'AliasController@update')->name('aliases.update');
-    Route::delete('/aliases/{id}', 'AliasController@destroy')->name('aliases.destroy');
-
-    Route::post('/active-aliases', 'ActiveAliasController@store')->name('active_aliases.store');
-    Route::delete('/active-aliases/{id}', 'ActiveAliasController@destroy')->name('active_aliases.destroy');
-
-    Route::get('/recipients', 'RecipientController@index')->name('recipients.index');
-    Route::post('/recipients', 'RecipientController@store')->name('recipients.store');
-    Route::delete('/recipients/{id}', 'RecipientController@destroy')->name('recipients.destroy');
-
-    Route::post('/recipients/email/resend', 'RecipientVerificationController@resend')->name('recipient_verification.resend');
-
-    Route::patch('/recipient-keys/{id}', 'RecipientKeyController@update')->name('recipient_keys.update');
-    Route::delete('/recipient-keys/{id}', 'RecipientKeyController@destroy')->name('recipient_keys.destroy');
-
-    Route::post('/encrypted-recipients', 'EncryptedRecipientController@store')->name('encrypted_recipients.store');
-    Route::delete('/encrypted-recipients/{id}', 'EncryptedRecipientController@destroy')->name('encrypted_recipients.destroy');
-
-    Route::post('/alias-recipients', 'AliasRecipientController@store')->name('alias_recipients.store');
-
-    Route::get('/domains', 'DomainController@index')->name('domains.index');
-    Route::post('/domains', 'DomainController@store')->name('domains.store');
-    Route::patch('/domains/{id}', 'DomainController@update')->name('domains.update');
-    Route::delete('/domains/{id}', 'DomainController@destroy')->name('domains.destroy');
-    Route::patch('/domains/{id}/default-recipient', 'DomainDefaultRecipientController@update')->name('domains.default_recipient');
-
-    Route::get('/domains/{id}/recheck', 'DomainVerificationController@recheck')->name('domain_verification.recheck');
+    Route::get('/', 'ShowAliasController@index')->name('aliases.index');
 
 
-    Route::post('/active-domains', 'ActiveDomainController@store')->name('active_domains.store');
-    Route::delete('/active-domains/{id}', 'ActiveDomainController@destroy')->name('active_domains.destroy');
+    Route::get('/recipients', 'ShowRecipientController@index')->name('recipients.index');
+    Route::post('/recipients/email/resend', 'RecipientVerificationController@resend');
 
 
-    Route::get('/usernames', 'AdditionalUsernameController@index')->name('usernames.index');
-    Route::post('/usernames', 'AdditionalUsernameController@store')->name('usernames.store');
-    Route::patch('/usernames/{id}', 'AdditionalUsernameController@update')->name('usernames.update');
-    Route::delete('/usernames/{id}', 'AdditionalUsernameController@destroy')->name('usernames.destroy');
+    Route::get('/domains', 'ShowDomainController@index')->name('domains.index');
+    Route::get('/domains/{id}/recheck', 'DomainVerificationController@recheck');
 
 
-    Route::post('/active-usernames', 'ActiveAdditionalUsernameController@store')->name('active_usernames.store');
-    Route::delete('/active-usernames/{id}', 'ActiveAdditionalUsernameController@destroy')->name('active_usernames.destroy');
+    Route::get('/usernames', 'ShowAdditionalUsernameController@index')->name('usernames.index');
 
 
     Route::get('/deactivate/{alias}', 'DeactivateAliasController@deactivate')->name('deactivate');
     Route::get('/deactivate/{alias}', 'DeactivateAliasController@deactivate')->name('deactivate');
 });
 });

+ 33 - 44
tests/Feature/AdditionalUsernamesTest.php → tests/Feature/Api/AdditionalUsernamesTest.php

@@ -1,75 +1,64 @@
 <?php
 <?php
 
 
-namespace Tests\Feature;
+namespace Tests\Feature\Api;
 
 
 use App\AdditionalUsername;
 use App\AdditionalUsername;
 use App\DeletedUsername;
 use App\DeletedUsername;
 use App\User;
 use App\User;
 use Illuminate\Foundation\Testing\RefreshDatabase;
 use Illuminate\Foundation\Testing\RefreshDatabase;
-use Illuminate\Support\Carbon;
 use Tests\TestCase;
 use Tests\TestCase;
 
 
 class AdditionalUsernamesTest extends TestCase
 class AdditionalUsernamesTest extends TestCase
 {
 {
     use RefreshDatabase;
     use RefreshDatabase;
 
 
-    protected $user;
-
     protected function setUp(): void
     protected function setUp(): void
     {
     {
         parent::setUp();
         parent::setUp();
-
-        $this->user = factory(User::class)->create();
-        $this->actingAs($this->user);
+        parent::setUpPassport();
     }
     }
 
 
     /** @test */
     /** @test */
-    public function user_can_view_usernames_from_the_usernames_page()
+    public function user_can_get_all_additional_usernames()
     {
     {
-        $usernames = factory(AdditionalUsername::class, 3)->create([
+        // Arrange
+        factory(AdditionalUsername::class, 3)->create([
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->get('/usernames');
+        // Act
+        $response = $this->get('/api/v1/usernames');
 
 
+        // Assert
         $response->assertSuccessful();
         $response->assertSuccessful();
-        $this->assertCount(3, $response->data('usernames'));
-        $usernames->assertEquals($response->data('usernames'));
+        $this->assertCount(3, $response->json()['data']);
     }
     }
 
 
     /** @test */
     /** @test */
-    public function latest_usernames_are_listed_first()
+    public function user_can_get_individual_additional_username()
     {
     {
-        $a = factory(AdditionalUsername::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(15)
-        ]);
-        $b = factory(AdditionalUsername::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(5)
-        ]);
-        $c = factory(AdditionalUsername::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(10)
+        // Arrange
+        $username = factory(AdditionalUsername::class)->create([
+            'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->get('/usernames');
+        // Act
+        $response = $this->get('/api/v1/usernames/'.$username->id);
 
 
+        // Assert
         $response->assertSuccessful();
         $response->assertSuccessful();
-        $this->assertCount(3, $response->data('usernames'));
-        $this->assertTrue($response->data('usernames')[0]->is($b));
-        $this->assertTrue($response->data('usernames')[1]->is($c));
-        $this->assertTrue($response->data('usernames')[2]->is($a));
+        $this->assertCount(1, $response->json());
+        $this->assertEquals($username->username, $response->json()['data']['username']);
     }
     }
 
 
     /** @test */
     /** @test */
     public function user_can_create_additional_username()
     public function user_can_create_additional_username()
     {
     {
-        $response = $this->json('POST', '/usernames', [
+        $response = $this->json('POST', '/api/v1/usernames', [
             'username' => 'janedoe'
             'username' => 'janedoe'
         ]);
         ]);
 
 
-        $response->assertStatus(200);
+        $response->assertStatus(201);
         $this->assertEquals('janedoe', $response->getData()->data->username);
         $this->assertEquals('janedoe', $response->getData()->data->username);
         $this->assertEquals(1, $this->user->username_count);
         $this->assertEquals(1, $this->user->username_count);
     }
     }
@@ -77,19 +66,19 @@ class AdditionalUsernamesTest extends TestCase
     /** @test */
     /** @test */
     public function user_can_not_exceed_additional_username_limit()
     public function user_can_not_exceed_additional_username_limit()
     {
     {
-        $this->json('POST', '/usernames', [
+        $this->json('POST', '/api/v1/usernames', [
             'username' => 'username1'
             'username' => 'username1'
         ]);
         ]);
 
 
-        $this->json('POST', '/usernames', [
+        $this->json('POST', '/api/v1/usernames', [
             'username' => 'username2'
             'username' => 'username2'
         ]);
         ]);
 
 
-        $this->json('POST', '/usernames', [
+        $this->json('POST', '/api/v1/usernames', [
             'username' => 'username3'
             'username' => 'username3'
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/usernames', [
+        $response = $this->json('POST', '/api/v1/usernames', [
             'username' => 'janedoe'
             'username' => 'janedoe'
         ]);
         ]);
 
 
@@ -106,7 +95,7 @@ class AdditionalUsernamesTest extends TestCase
             'username' => 'janedoe'
             'username' => 'janedoe'
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/usernames', [
+        $response = $this->json('POST', '/api/v1/usernames', [
             'username' => 'janedoe'
             'username' => 'janedoe'
         ]);
         ]);
 
 
@@ -122,7 +111,7 @@ class AdditionalUsernamesTest extends TestCase
             'username' => 'janedoe'
             'username' => 'janedoe'
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/usernames', [
+        $response = $this->json('POST', '/api/v1/usernames', [
             'username' => 'janedoe'
             'username' => 'janedoe'
         ]);
         ]);
 
 
@@ -136,7 +125,7 @@ class AdditionalUsernamesTest extends TestCase
     {
     {
         $user = factory(User::class)->create();
         $user = factory(User::class)->create();
 
 
-        $response = $this->json('POST', '/usernames', [
+        $response = $this->json('POST', '/api/v1/usernames', [
             'username' => $user->username
             'username' => $user->username
         ]);
         ]);
 
 
@@ -148,7 +137,7 @@ class AdditionalUsernamesTest extends TestCase
     /** @test */
     /** @test */
     public function additional_username_must_be_alpha_numeric()
     public function additional_username_must_be_alpha_numeric()
     {
     {
-        $response = $this->json('POST', '/usernames', [
+        $response = $this->json('POST', '/api/v1/usernames', [
             'username' => 'username01_'
             'username' => 'username01_'
         ]);
         ]);
 
 
@@ -160,7 +149,7 @@ class AdditionalUsernamesTest extends TestCase
     /** @test */
     /** @test */
     public function additional_username_must_be_less_than_max_length()
     public function additional_username_must_be_less_than_max_length()
     {
     {
-        $response = $this->json('POST', '/usernames', [
+        $response = $this->json('POST', '/api/v1/usernames', [
             'username' => 'abcdefghijklmnopqrstu'
             'username' => 'abcdefghijklmnopqrstu'
         ]);
         ]);
 
 
@@ -177,7 +166,7 @@ class AdditionalUsernamesTest extends TestCase
             'active' => false
             'active' => false
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/active-usernames/', [
+        $response = $this->json('POST', '/api/v1/active-usernames/', [
             'id' => $username->id
             'id' => $username->id
         ]);
         ]);
 
 
@@ -193,7 +182,7 @@ class AdditionalUsernamesTest extends TestCase
             'active' => true
             'active' => true
         ]);
         ]);
 
 
-        $response = $this->json('DELETE', '/active-usernames/'.$username->id);
+        $response = $this->json('DELETE', '/api/v1/active-usernames/'.$username->id);
 
 
         $response->assertStatus(200);
         $response->assertStatus(200);
         $this->assertEquals(false, $response->getData()->data->active);
         $this->assertEquals(false, $response->getData()->data->active);
@@ -206,7 +195,7 @@ class AdditionalUsernamesTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/usernames/'.$username->id, [
+        $response = $this->json('PATCH', '/api/v1/usernames/'.$username->id, [
             'description' => 'The new description'
             'description' => 'The new description'
         ]);
         ]);
 
 
@@ -221,7 +210,7 @@ class AdditionalUsernamesTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('DELETE', '/usernames/'.$username->id);
+        $response = $this->json('DELETE', '/api/v1/usernames/'.$username->id);
 
 
         $response->assertStatus(204);
         $response->assertStatus(204);
         $this->assertEmpty($this->user->additionalUsernames);
         $this->assertEmpty($this->user->additionalUsernames);

+ 7 - 12
tests/Feature/AliasRecipientsTest.php → tests/Feature/Api/AliasRecipientsTest.php

@@ -1,11 +1,10 @@
 <?php
 <?php
 
 
-namespace Tests\Feature;
+namespace Tests\Feature\Api;
 
 
 use App\Alias;
 use App\Alias;
 use App\AliasRecipient;
 use App\AliasRecipient;
 use App\Recipient;
 use App\Recipient;
-use App\User;
 use Illuminate\Foundation\Testing\RefreshDatabase;
 use Illuminate\Foundation\Testing\RefreshDatabase;
 use Tests\TestCase;
 use Tests\TestCase;
 
 
@@ -13,14 +12,10 @@ class AliasRecipientsTest extends TestCase
 {
 {
     use RefreshDatabase;
     use RefreshDatabase;
 
 
-    protected $user;
-
     protected function setUp(): void
     protected function setUp(): void
     {
     {
         parent::setUp();
         parent::setUp();
-
-        $this->user = factory(User::class)->create();
-        $this->actingAs($this->user);
+        parent::setUpPassport();
     }
     }
 
 
     /** @test */
     /** @test */
@@ -34,7 +29,7 @@ class AliasRecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/alias-recipients', [
+        $response = $this->json('POST', '/api/v1/alias-recipients', [
             'alias_id' => $alias->id,
             'alias_id' => $alias->id,
             'recipient_ids' => [$recipient->id]
             'recipient_ids' => [$recipient->id]
         ]);
         ]);
@@ -63,7 +58,7 @@ class AliasRecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/alias-recipients', [
+        $response = $this->json('POST', '/api/v1/alias-recipients', [
             'alias_id' => $alias->id,
             'alias_id' => $alias->id,
             'recipient_ids' => [$recipient1->id, $recipient2->id, $recipient3->id]
             'recipient_ids' => [$recipient1->id, $recipient2->id, $recipient3->id]
         ]);
         ]);
@@ -96,7 +91,7 @@ class AliasRecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/alias-recipients', [
+        $response = $this->json('POST', '/api/v1/alias-recipients', [
             'alias_id' => $alias->id,
             'alias_id' => $alias->id,
             'recipient_ids' => [$recipient2->id, $recipient3->id]
             'recipient_ids' => [$recipient2->id, $recipient3->id]
         ]);
         ]);
@@ -117,7 +112,7 @@ class AliasRecipientsTest extends TestCase
             'email_verified_at' => null
             'email_verified_at' => null
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/alias-recipients', [
+        $response = $this->json('POST', '/api/v1/alias-recipients', [
             'alias_id' => $alias->id,
             'alias_id' => $alias->id,
             'recipient_ids' => [$unverifiedRecipient->id]
             'recipient_ids' => [$unverifiedRecipient->id]
         ]);
         ]);
@@ -137,7 +132,7 @@ class AliasRecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/alias-recipients', [
+        $response = $this->json('POST', '/api/v1/alias-recipients', [
             'alias_id' => $alias->id,
             'alias_id' => $alias->id,
             'recipient_ids' => $recipients->pluck('id')
             'recipient_ids' => $recipients->pluck('id')
         ]);
         ]);

+ 123 - 0
tests/Feature/Api/AliasesTest.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace Tests\Feature\Api;
+
+use App\Alias;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class AliasesTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        parent::setUpPassport();
+    }
+
+    /** @test */
+    public function user_can_get_all_aliases()
+    {
+        // Arrange
+        factory(Alias::class, 3)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        // Act
+        $response = $this->get('/api/v1/aliases');
+
+        // Assert
+        $response->assertSuccessful();
+        $this->assertCount(3, $response->json()['data']);
+    }
+
+    /** @test */
+    public function user_can_get_individual_alias()
+    {
+        // Arrange
+        $alias = factory(Alias::class)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        // Act
+        $response = $this->get('/api/v1/aliases/'.$alias->id);
+
+        // Assert
+        $response->assertSuccessful();
+        $this->assertCount(1, $response->json());
+        $this->assertEquals($alias->email, $response->json()['data']['email']);
+    }
+
+    /** @test */
+    public function user_can_generate_new_alias()
+    {
+        $response = $this->json('POST', '/api/v1/aliases', [
+            'domain' => 'anonaddy.me',
+            'description' => 'the description'
+        ]);
+
+        $response->assertStatus(201);
+        $this->assertCount(1, $this->user->aliases);
+        $this->assertEquals($this->user->aliases[0]->id, $response->getData()->data->local_part);
+        $this->assertEquals($this->user->aliases[0]->id, $this->user->aliases[0]->local_part);
+    }
+
+    /** @test */
+    public function user_can_update_alias_description()
+    {
+        $alias = factory(Alias::class)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        $response = $this->json('PATCH', '/api/v1/aliases/'.$alias->id, [
+            'description' => 'The new description'
+        ]);
+
+        $response->assertStatus(200);
+        $this->assertEquals('The new description', $response->getData()->data->description);
+    }
+
+    /** @test */
+    public function user_can_delete_alias()
+    {
+        $alias = factory(Alias::class)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        $response = $this->json('DELETE', '/api/v1/aliases/'.$alias->id);
+
+        $response->assertStatus(204);
+        $this->assertEmpty($this->user->aliases);
+    }
+
+    /** @test */
+    public function user_can_activate_alias()
+    {
+        $alias = factory(Alias::class)->create([
+            'user_id' => $this->user->id,
+            'active' => false
+        ]);
+
+        $response = $this->json('POST', '/api/v1/active-aliases/', [
+            'id' => $alias->id
+        ]);
+
+        $response->assertStatus(200);
+        $this->assertEquals(true, $response->getData()->data->active);
+    }
+
+    /** @test */
+    public function user_can_deactivate_alias()
+    {
+        $alias = factory(Alias::class)->create([
+            'user_id' => $this->user->id,
+            'active' => true
+        ]);
+
+        $response = $this->json('DELETE', '/api/v1/active-aliases/'.$alias->id);
+
+        $response->assertStatus(200);
+        $this->assertEquals(false, $response->getData()->data->active);
+    }
+}

+ 32 - 63
tests/Feature/DomainsTest.php → tests/Feature/Api/DomainsTest.php

@@ -1,75 +1,63 @@
 <?php
 <?php
 
 
-namespace Tests\Feature;
+namespace Tests\Feature\Api;
 
 
 use App\Domain;
 use App\Domain;
 use App\Recipient;
 use App\Recipient;
-use App\User;
 use Illuminate\Foundation\Testing\RefreshDatabase;
 use Illuminate\Foundation\Testing\RefreshDatabase;
-use Illuminate\Support\Carbon;
 use Tests\TestCase;
 use Tests\TestCase;
 
 
 class DomainsTest extends TestCase
 class DomainsTest extends TestCase
 {
 {
     use RefreshDatabase;
     use RefreshDatabase;
 
 
-    protected $user;
-
     protected function setUp(): void
     protected function setUp(): void
     {
     {
         parent::setUp();
         parent::setUp();
-
-        $this->user = factory(User::class)->create();
-        $this->actingAs($this->user);
+        parent::setUpPassport();
     }
     }
 
 
     /** @test */
     /** @test */
-    public function user_can_view_domains_from_the_domains_page()
+    public function user_can_get_all_domains()
     {
     {
-        $domains = factory(Domain::class, 3)->create([
+        // Arrange
+        factory(Domain::class, 3)->create([
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->get('/domains');
+        // Act
+        $response = $this->get('/api/v1/domains');
 
 
+        // Assert
         $response->assertSuccessful();
         $response->assertSuccessful();
-        $this->assertCount(3, $response->data('domains'));
-        $domains->assertEquals($response->data('domains'));
+        $this->assertCount(3, $response->json()['data']);
     }
     }
 
 
     /** @test */
     /** @test */
-    public function latest_domains_are_listed_first()
+    public function user_can_get_individual_domain()
     {
     {
-        $a = factory(Domain::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(15)
-        ]);
-        $b = factory(Domain::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(5)
-        ]);
-        $c = factory(Domain::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(10)
+        // Arrange
+        $domain = factory(Domain::class)->create([
+            'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->get('/domains');
+        // Act
+        $response = $this->get('/api/v1/domains/'.$domain->id);
 
 
+        // Assert
         $response->assertSuccessful();
         $response->assertSuccessful();
-        $this->assertCount(3, $response->data('domains'));
-        $this->assertTrue($response->data('domains')[0]->is($b));
-        $this->assertTrue($response->data('domains')[1]->is($c));
-        $this->assertTrue($response->data('domains')[2]->is($a));
+        $this->assertCount(1, $response->json());
+        $this->assertEquals($domain->domain, $response->json()['data']['domain']);
     }
     }
 
 
     /** @test */
     /** @test */
     public function user_can_create_new_domain()
     public function user_can_create_new_domain()
     {
     {
-        $response = $this->json('POST', '/domains', [
+        $response = $this->json('POST', '/api/v1/domains', [
             'domain' => 'example.com'
             'domain' => 'example.com'
         ]);
         ]);
 
 
-        $response->assertStatus(200);
+        $response->assertStatus(201);
         $this->assertEquals('example.com', $response->getData()->data->domain);
         $this->assertEquals('example.com', $response->getData()->data->domain);
     }
     }
 
 
@@ -81,7 +69,7 @@ class DomainsTest extends TestCase
             'domain' => 'example.com'
             'domain' => 'example.com'
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/domains', [
+        $response = $this->json('POST', '/api/v1/domains', [
             'domain' => 'example.com'
             'domain' => 'example.com'
         ]);
         ]);
 
 
@@ -93,7 +81,7 @@ class DomainsTest extends TestCase
     /** @test */
     /** @test */
     public function new_domain_must_be_a_valid_fqdn()
     public function new_domain_must_be_a_valid_fqdn()
     {
     {
-        $response = $this->json('POST', '/domains', [
+        $response = $this->json('POST', '/api/v1/domains', [
             'domain' => 'example.'
             'domain' => 'example.'
         ]);
         ]);
 
 
@@ -105,7 +93,7 @@ class DomainsTest extends TestCase
     /** @test */
     /** @test */
     public function new_domain_must_not_include_protocol()
     public function new_domain_must_not_include_protocol()
     {
     {
-        $response = $this->json('POST', '/domains', [
+        $response = $this->json('POST', '/api/v1/domains', [
             'domain' => 'https://example.com'
             'domain' => 'https://example.com'
         ]);
         ]);
 
 
@@ -117,7 +105,7 @@ class DomainsTest extends TestCase
     /** @test */
     /** @test */
     public function new_domain_must_not_be_local()
     public function new_domain_must_not_be_local()
     {
     {
-        $response = $this->json('POST', '/domains', [
+        $response = $this->json('POST', '/api/v1/domains', [
             'domain' => config('anonaddy.domain')
             'domain' => config('anonaddy.domain')
         ]);
         ]);
 
 
@@ -129,7 +117,7 @@ class DomainsTest extends TestCase
     /** @test */
     /** @test */
     public function new_domain_must_not_be_local_subdomain()
     public function new_domain_must_not_be_local_subdomain()
     {
     {
-        $response = $this->json('POST', '/domains', [
+        $response = $this->json('POST', '/api/v1/domains', [
             'domain' => 'subdomain'.config('anonaddy.domain')
             'domain' => 'subdomain'.config('anonaddy.domain')
         ]);
         ]);
 
 
@@ -138,25 +126,6 @@ class DomainsTest extends TestCase
             ->assertJsonValidationErrors('domain');
             ->assertJsonValidationErrors('domain');
     }
     }
 
 
-    /** @test */
-    public function user_can_verify_domain_records()
-    {
-        $domain = factory(Domain::class)->create([
-            'user_id' => $this->user->id,
-            'domain' => 'anonaddy.me'
-        ]);
-
-        $response = $this->get('/domains/'.$domain->id.'/recheck');
-
-        $response->assertStatus(200);
-
-        $this->assertDatabaseHas('domains', [
-            'user_id' => $this->user->id,
-            'domain' => 'anonaddy.me',
-            'domain_verified_at' => now()
-        ]);
-    }
-
     /** @test */
     /** @test */
     public function user_can_activate_domain()
     public function user_can_activate_domain()
     {
     {
@@ -165,7 +134,7 @@ class DomainsTest extends TestCase
             'active' => false
             'active' => false
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/active-domains/', [
+        $response = $this->json('POST', '/api/v1/active-domains/', [
             'id' => $domain->id
             'id' => $domain->id
         ]);
         ]);
 
 
@@ -181,7 +150,7 @@ class DomainsTest extends TestCase
             'active' => true
             'active' => true
         ]);
         ]);
 
 
-        $response = $this->json('DELETE', '/active-domains/'.$domain->id);
+        $response = $this->json('DELETE', '/api/v1/active-domains/'.$domain->id);
 
 
         $response->assertStatus(200);
         $response->assertStatus(200);
         $this->assertEquals(false, $response->getData()->data->active);
         $this->assertEquals(false, $response->getData()->data->active);
@@ -194,7 +163,7 @@ class DomainsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/domains/'.$domain->id, [
+        $response = $this->json('PATCH', '/api/v1/domains/'.$domain->id, [
             'description' => 'The new description'
             'description' => 'The new description'
         ]);
         ]);
 
 
@@ -209,7 +178,7 @@ class DomainsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('DELETE', '/domains/'.$domain->id);
+        $response = $this->json('DELETE', '/api/v1/domains/'.$domain->id);
 
 
         $response->assertStatus(204);
         $response->assertStatus(204);
         $this->assertEmpty($this->user->domains);
         $this->assertEmpty($this->user->domains);
@@ -227,7 +196,7 @@ class DomainsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/domains/'.$domain->id.'/default-recipient', [
+        $response = $this->json('PATCH', '/api/v1/domains/'.$domain->id.'/default-recipient', [
             'default_recipient' => $newDefaultRecipient->id
             'default_recipient' => $newDefaultRecipient->id
         ]);
         ]);
 
 
@@ -253,7 +222,7 @@ class DomainsTest extends TestCase
             'email_verified_at' => null
             'email_verified_at' => null
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/domains/'.$domain->id.'/default-recipient', [
+        $response = $this->json('PATCH', '/api/v1/domains/'.$domain->id.'/default-recipient', [
             'default_recipient' => $newDefaultRecipient->id
             'default_recipient' => $newDefaultRecipient->id
         ]);
         ]);
 
 
@@ -277,7 +246,7 @@ class DomainsTest extends TestCase
             'domain_verified_at' => now(),
             'domain_verified_at' => now(),
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/domains/'.$domain->id.'/default-recipient', [
+        $response = $this->json('PATCH', '/api/v1/domains/'.$domain->id.'/default-recipient', [
             'default_recipient' => ''
             'default_recipient' => ''
         ]);
         ]);
 
 

+ 30 - 152
tests/Feature/RecipientsTest.php → tests/Feature/Api/RecipientsTest.php

@@ -1,102 +1,62 @@
 <?php
 <?php
 
 
-namespace Tests\Feature;
+namespace Tests\Feature\Api;
 
 
-use App\Alias;
-use App\AliasRecipient;
 use App\Recipient;
 use App\Recipient;
-use App\User;
-use Illuminate\Auth\Notifications\VerifyEmail;
 use Illuminate\Foundation\Testing\RefreshDatabase;
 use Illuminate\Foundation\Testing\RefreshDatabase;
-use Illuminate\Support\Carbon;
-use Illuminate\Support\Facades\Config;
-use Illuminate\Support\Facades\Notification;
-use Illuminate\Support\Facades\URL;
 use Tests\TestCase;
 use Tests\TestCase;
 
 
 class RecipientsTest extends TestCase
 class RecipientsTest extends TestCase
 {
 {
     use RefreshDatabase;
     use RefreshDatabase;
 
 
-    protected $user;
-
     protected function setUp(): void
     protected function setUp(): void
     {
     {
         parent::setUp();
         parent::setUp();
-
-        $this->user = factory(User::class)->create();
-        $this->actingAs($this->user);
+        parent::setUpPassport();
     }
     }
 
 
     /** @test */
     /** @test */
-    public function user_can_view_recipients_from_the_recipients_page()
+    public function user_can_get_all_recipients()
     {
     {
-        $recipients = factory(Recipient::class, 5)->create([
+        // Arrange
+        factory(Recipient::class, 3)->create([
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->get('/recipients');
-
-        $response->assertSuccessful();
-        $this->assertCount(5, $response->data('recipients'));
-        $recipients->assertEquals($response->data('recipients'));
-    }
-
-    /** @test */
-    public function latest_recipients_are_listed_first()
-    {
-        $a = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(15)
-        ]);
-        $b = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(5)
-        ]);
-        $c = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'created_at' => Carbon::now()->subDays(10)
-        ]);
-
-        $response = $this->get('/recipients');
+        // Act
+        $response = $this->get('/api/v1/recipients');
 
 
+        // Assert
         $response->assertSuccessful();
         $response->assertSuccessful();
-        $this->assertCount(3, $response->data('recipients'));
-        $this->assertTrue($response->data('recipients')[0]->is($b));
-        $this->assertTrue($response->data('recipients')[1]->is($c));
-        $this->assertTrue($response->data('recipients')[2]->is($a));
+        $this->assertCount(3, $response->json()['data']);
     }
     }
 
 
     /** @test */
     /** @test */
-    public function recipients_are_listed_with_aliases_count()
+    public function user_can_get_individual_recipient()
     {
     {
+        // Arrange
         $recipient = factory(Recipient::class)->create([
         $recipient = factory(Recipient::class)->create([
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        factory(Alias::class, 3)->create(['user_id' => $this->user->id])
-        ->each(function ($alias) use ($recipient) {
-            AliasRecipient::create([
-                'alias' => $alias,
-                'recipient' => $recipient
-            ]);
-        });
-
-        $response = $this->get('/recipients');
+        // Act
+        $response = $this->get('/api/v1/recipients/'.$recipient->id);
 
 
+        // Assert
         $response->assertSuccessful();
         $response->assertSuccessful();
-        $this->assertCount(1, $response->data('recipients'));
-        $this->assertCount(3, $response->data('recipients')[0]['aliases']);
+        $this->assertCount(1, $response->json());
+        $this->assertEquals($recipient->email, $response->json()['data']['email']);
     }
     }
 
 
     /** @test */
     /** @test */
     public function user_can_create_new_recipient()
     public function user_can_create_new_recipient()
     {
     {
-        $response = $this->json('POST', '/recipients', [
+        $response = $this->json('POST', '/api/v1/recipients', [
             'email' => 'johndoe@example.com'
             'email' => 'johndoe@example.com'
         ]);
         ]);
 
 
-        $response->assertStatus(200);
+        $response->assertStatus(201);
         $this->assertEquals('johndoe@example.com', $response->getData()->data->email);
         $this->assertEquals('johndoe@example.com', $response->getData()->data->email);
     }
     }
 
 
@@ -108,7 +68,7 @@ class RecipientsTest extends TestCase
             'email' => 'johndoe@example.com'
             'email' => 'johndoe@example.com'
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/recipients', [
+        $response = $this->json('POST', '/api/v1/recipients', [
             'email' => 'johndoe@example.com'
             'email' => 'johndoe@example.com'
         ]);
         ]);
 
 
@@ -125,7 +85,7 @@ class RecipientsTest extends TestCase
             'email' => 'johndoe@example.com'
             'email' => 'johndoe@example.com'
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/recipients', [
+        $response = $this->json('POST', '/api/v1/recipients', [
             'email' => 'JOHNdoe@example.com'
             'email' => 'JOHNdoe@example.com'
         ]);
         ]);
 
 
@@ -139,7 +99,7 @@ class RecipientsTest extends TestCase
     {
     {
         $this->user->recipients()->save($this->user->defaultRecipient);
         $this->user->recipients()->save($this->user->defaultRecipient);
 
 
-        $response = $this->json('POST', '/recipients', [
+        $response = $this->json('POST', '/api/v1/recipients', [
             'email' => $this->user->email
             'email' => $this->user->email
         ]);
         ]);
 
 
@@ -151,7 +111,7 @@ class RecipientsTest extends TestCase
     /** @test */
     /** @test */
     public function new_recipient_must_have_valid_email()
     public function new_recipient_must_have_valid_email()
     {
     {
-        $response = $this->json('POST', '/recipients', [
+        $response = $this->json('POST', '/api/v1/recipients', [
             'email' => 'johndoe@example.'
             'email' => 'johndoe@example.'
         ]);
         ]);
 
 
@@ -167,7 +127,7 @@ class RecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('DELETE', '/recipients/'.$recipient->id);
+        $response = $this->json('DELETE', '/api/v1/recipients/'.$recipient->id);
 
 
         $response->assertStatus(204);
         $response->assertStatus(204);
         $this->assertEmpty($this->user->recipients);
         $this->assertEmpty($this->user->recipients);
@@ -180,95 +140,13 @@ class RecipientsTest extends TestCase
 
 
         $defaultRecipient = $this->user->defaultRecipient;
         $defaultRecipient = $this->user->defaultRecipient;
 
 
-        $response = $this->json('DELETE', '/recipients/'.$defaultRecipient->id);
+        $response = $this->json('DELETE', '/api/v1/recipients/'.$defaultRecipient->id);
 
 
         $response->assertStatus(403);
         $response->assertStatus(403);
         $this->assertCount(1, $this->user->recipients);
         $this->assertCount(1, $this->user->recipients);
         $this->assertEquals($defaultRecipient->id, $this->user->defaultRecipient->id);
         $this->assertEquals($defaultRecipient->id, $this->user->defaultRecipient->id);
     }
     }
 
 
-    /** @test */
-    public function user_can_resend_recipient_verification_email()
-    {
-        Notification::fake();
-
-        Notification::assertNothingSent();
-
-        $recipient = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'email_verified_at' => null
-        ]);
-
-        $response = $this->json('POST', '/recipients/email/resend', [
-            'recipient_id' => $recipient->id
-        ]);
-
-        $response->assertStatus(200);
-
-        Notification::assertSentTo(
-            $recipient,
-            VerifyEmail::class
-        );
-    }
-
-    /** @test */
-    public function user_can_verify_recipient_email_successfully()
-    {
-        $recipient = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'email_verified_at' => null
-        ]);
-
-        $this->assertNull($recipient->refresh()->email_verified_at);
-
-        $verificationUrl = URL::temporarySignedRoute(
-            'verification.verify',
-            Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
-            [
-                'id' => $recipient->getKey(),
-                'hash' => sha1($recipient->getEmailForVerification()),
-            ]
-        );
-
-        $response = $this->get($verificationUrl);
-
-        $response
-            ->assertRedirect('/recipients')
-            ->assertSessionHas('verified');
-
-        $this->assertNotNull($recipient->refresh()->email_verified_at);
-    }
-
-    /** @test */
-    public function user_must_wait_before_resending_recipient_verification_email()
-    {
-        Notification::fake();
-
-        Notification::assertNothingSent();
-
-        $recipient = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'email_verified_at' => null
-        ]);
-
-        $response = $this->json('POST', '/recipients/email/resend', [
-            'recipient_id' => $recipient->id
-        ]);
-
-        $response->assertStatus(200);
-
-        Notification::assertSentTo(
-            $recipient,
-            VerifyEmail::class
-        );
-
-        $response2 = $this->json('POST', '/recipients/email/resend', [
-            'recipient_id' => $recipient->id
-        ]);
-
-        $response2->assertStatus(429);
-    }
-
     /** @test */
     /** @test */
     public function user_can_add_gpg_key_to_recipient()
     public function user_can_add_gpg_key_to_recipient()
     {
     {
@@ -279,7 +157,7 @@ class RecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/recipient-keys/'.$recipient->id, [
+        $response = $this->json('PATCH', '/api/v1/recipient-keys/'.$recipient->id, [
             'key_data' => file_get_contents(base_path('tests/keys/AnonAddyPublicKey.asc'))
             'key_data' => file_get_contents(base_path('tests/keys/AnonAddyPublicKey.asc'))
         ]);
         ]);
 
 
@@ -294,7 +172,7 @@ class RecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/recipient-keys/'.$recipient->id, [
+        $response = $this->json('PATCH', '/api/v1/recipient-keys/'.$recipient->id, [
             'key_data' => 'Invalid Key Data'
             'key_data' => 'Invalid Key Data'
         ]);
         ]);
 
 
@@ -310,7 +188,7 @@ class RecipientsTest extends TestCase
             'user_id' => $this->user->id
             'user_id' => $this->user->id
         ]);
         ]);
 
 
-        $response = $this->json('PATCH', '/recipient-keys/'.$recipient->id, [
+        $response = $this->json('PATCH', '/api/v1/recipient-keys/'.$recipient->id, [
             'key_data' => file_get_contents(base_path('tests/keys/InvalidAnonAddyPublicKey.asc'))
             'key_data' => file_get_contents(base_path('tests/keys/InvalidAnonAddyPublicKey.asc'))
         ]);
         ]);
 
 
@@ -330,7 +208,7 @@ class RecipientsTest extends TestCase
             'fingerprint' => '26A987650243B28802524E2F809FD0D502E2F695'
             'fingerprint' => '26A987650243B28802524E2F809FD0D502E2F695'
         ]);
         ]);
 
 
-        $response = $this->json('DELETE', '/recipient-keys/'.$recipient->id);
+        $response = $this->json('DELETE', '/api/v1/recipient-keys/'.$recipient->id);
 
 
         $response->assertStatus(204);
         $response->assertStatus(204);
         $this->assertNull($this->user->recipients[0]->fingerprint);
         $this->assertNull($this->user->recipients[0]->fingerprint);
@@ -346,7 +224,7 @@ class RecipientsTest extends TestCase
             'fingerprint' => '26A987650243B28802524E2F809FD0D502E2F695'
             'fingerprint' => '26A987650243B28802524E2F809FD0D502E2F695'
         ]);
         ]);
 
 
-        $response = $this->json('POST', '/encrypted-recipients/', [
+        $response = $this->json('POST', '/api/v1/encrypted-recipients/', [
             'id' => $recipient->id
             'id' => $recipient->id
         ]);
         ]);
 
 
@@ -363,7 +241,7 @@ class RecipientsTest extends TestCase
             'fingerprint' => '26A987650243B28802524E2F809FD0D502E2F695'
             'fingerprint' => '26A987650243B28802524E2F809FD0D502E2F695'
         ]);
         ]);
 
 
-        $response = $this->json('DELETE', '/encrypted-recipients/'.$recipient->id);
+        $response = $this->json('DELETE', '/api/v1/encrypted-recipients/'.$recipient->id);
 
 
         $response->assertStatus(200);
         $response->assertStatus(200);
         $this->assertEquals(false, $response->getData()->data->should_encrypt);
         $this->assertEquals(false, $response->getData()->data->should_encrypt);

+ 63 - 0
tests/Feature/ShowAdditionalUsernamesTest.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\AdditionalUsername;
+use App\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Support\Carbon;
+use Tests\TestCase;
+
+class AdditionalUsernamesTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $user;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->user = factory(User::class)->create();
+        $this->actingAs($this->user);
+    }
+
+    /** @test */
+    public function user_can_view_usernames_from_the_usernames_page()
+    {
+        $usernames = factory(AdditionalUsername::class, 3)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        $response = $this->get('/usernames');
+
+        $response->assertSuccessful();
+        $this->assertCount(3, $response->data('usernames'));
+        $usernames->assertEquals($response->data('usernames'));
+    }
+
+    /** @test */
+    public function latest_usernames_are_listed_first()
+    {
+        $a = factory(AdditionalUsername::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(15)
+        ]);
+        $b = factory(AdditionalUsername::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(5)
+        ]);
+        $c = factory(AdditionalUsername::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(10)
+        ]);
+
+        $response = $this->get('/usernames');
+
+        $response->assertSuccessful();
+        $this->assertCount(3, $response->data('usernames'));
+        $this->assertTrue($response->data('usernames')[0]->is($b));
+        $this->assertTrue($response->data('usernames')[1]->is($c));
+        $this->assertTrue($response->data('usernames')[2]->is($a));
+    }
+}

+ 0 - 69
tests/Feature/AliasesTest.php → tests/Feature/ShowAliasesTest.php

@@ -142,73 +142,4 @@ class AliasesTest extends TestCase
         $this->assertCount(1, $response->data('recipients'));
         $this->assertCount(1, $response->data('recipients'));
         $this->assertEquals($aliasRecipient->recipient->email, $response->data('recipients')[0]['email']);
         $this->assertEquals($aliasRecipient->recipient->email, $response->data('recipients')[0]['email']);
     }
     }
-
-    /** @test */
-    public function user_can_generate_new_alias()
-    {
-        $response = $this->json('POST', '/aliases', ['domain' => 'anonaddy.com']);
-
-        $response->assertStatus(200);
-        $this->assertCount(1, $this->user->aliases);
-        $this->assertEquals($this->user->aliases[0]->id, $response->getData()->data->local_part);
-        $this->assertEquals($this->user->aliases[0]->id, $this->user->aliases[0]->local_part);
-    }
-
-    /** @test */
-    public function user_can_activate_alias()
-    {
-        $alias = factory(Alias::class)->create([
-            'user_id' => $this->user->id,
-            'active' => false
-        ]);
-
-        $response = $this->json('POST', '/active-aliases/', [
-            'id' => $alias->id
-        ]);
-
-        $response->assertStatus(200);
-        $this->assertEquals(true, $response->getData()->data->active);
-    }
-
-    /** @test */
-    public function user_can_deactivate_alias()
-    {
-        $alias = factory(Alias::class)->create([
-            'user_id' => $this->user->id,
-            'active' => true
-        ]);
-
-        $response = $this->json('DELETE', '/active-aliases/'.$alias->id);
-
-        $response->assertStatus(200);
-        $this->assertEquals(false, $response->getData()->data->active);
-    }
-
-    /** @test */
-    public function user_can_update_alias_description()
-    {
-        $alias = factory(Alias::class)->create([
-            'user_id' => $this->user->id
-        ]);
-
-        $response = $this->json('PATCH', '/aliases/'.$alias->id, [
-            'description' => 'The new description'
-        ]);
-
-        $response->assertStatus(200);
-        $this->assertEquals('The new description', $response->getData()->data->description);
-    }
-
-    /** @test */
-    public function user_can_delete_alias()
-    {
-        $alias = factory(Alias::class)->create([
-            'user_id' => $this->user->id
-        ]);
-
-        $response = $this->json('DELETE', '/aliases/'.$alias->id);
-
-        $response->assertStatus(204);
-        $this->assertEmpty($this->user->aliases);
-    }
 }
 }

+ 82 - 0
tests/Feature/ShowDomainsTest.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Domain;
+use App\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Support\Carbon;
+use Tests\TestCase;
+
+class DomainsTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $user;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->user = factory(User::class)->create();
+        $this->actingAs($this->user);
+    }
+
+    /** @test */
+    public function user_can_view_domains_from_the_domains_page()
+    {
+        $domains = factory(Domain::class, 3)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        $response = $this->get('/domains');
+
+        $response->assertSuccessful();
+        $this->assertCount(3, $response->data('domains'));
+        $domains->assertEquals($response->data('domains'));
+    }
+
+    /** @test */
+    public function latest_domains_are_listed_first()
+    {
+        $a = factory(Domain::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(15)
+        ]);
+        $b = factory(Domain::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(5)
+        ]);
+        $c = factory(Domain::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(10)
+        ]);
+
+        $response = $this->get('/domains');
+
+        $response->assertSuccessful();
+        $this->assertCount(3, $response->data('domains'));
+        $this->assertTrue($response->data('domains')[0]->is($b));
+        $this->assertTrue($response->data('domains')[1]->is($c));
+        $this->assertTrue($response->data('domains')[2]->is($a));
+    }
+
+    /** @test */
+    public function user_can_verify_domain_records()
+    {
+        $domain = factory(Domain::class)->create([
+            'user_id' => $this->user->id,
+            'domain' => 'anonaddy.me'
+        ]);
+
+        $response = $this->get('/domains/'.$domain->id.'/recheck');
+
+        $response->assertStatus(200);
+
+        $this->assertDatabaseHas('domains', [
+            'user_id' => $this->user->id,
+            'domain' => 'anonaddy.me',
+            'domain_verified_at' => now()
+        ]);
+    }
+}

+ 173 - 0
tests/Feature/ShowRecipientsTest.php

@@ -0,0 +1,173 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Alias;
+use App\AliasRecipient;
+use App\Recipient;
+use App\User;
+use Illuminate\Auth\Notifications\VerifyEmail;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\Notification;
+use Illuminate\Support\Facades\URL;
+use Tests\TestCase;
+
+class RecipientsTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $user;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->user = factory(User::class)->create();
+        $this->actingAs($this->user);
+    }
+
+    /** @test */
+    public function user_can_view_recipients_from_the_recipients_page()
+    {
+        $recipients = factory(Recipient::class, 5)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        $response = $this->get('/recipients');
+
+        $response->assertSuccessful();
+        $this->assertCount(5, $response->data('recipients'));
+        $recipients->assertEquals($response->data('recipients'));
+    }
+
+    /** @test */
+    public function latest_recipients_are_listed_first()
+    {
+        $a = factory(Recipient::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(15)
+        ]);
+        $b = factory(Recipient::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(5)
+        ]);
+        $c = factory(Recipient::class)->create([
+            'user_id' => $this->user->id,
+            'created_at' => Carbon::now()->subDays(10)
+        ]);
+
+        $response = $this->get('/recipients');
+
+        $response->assertSuccessful();
+        $this->assertCount(3, $response->data('recipients'));
+        $this->assertTrue($response->data('recipients')[0]->is($b));
+        $this->assertTrue($response->data('recipients')[1]->is($c));
+        $this->assertTrue($response->data('recipients')[2]->is($a));
+    }
+
+    /** @test */
+    public function recipients_are_listed_with_aliases_count()
+    {
+        $recipient = factory(Recipient::class)->create([
+            'user_id' => $this->user->id
+        ]);
+
+        factory(Alias::class, 3)->create(['user_id' => $this->user->id])
+        ->each(function ($alias) use ($recipient) {
+            AliasRecipient::create([
+                'alias' => $alias,
+                'recipient' => $recipient
+            ]);
+        });
+
+        $response = $this->get('/recipients');
+
+        $response->assertSuccessful();
+        $this->assertCount(1, $response->data('recipients'));
+        $this->assertCount(3, $response->data('recipients')[0]['aliases']);
+    }
+
+    /** @test */
+    public function user_can_resend_recipient_verification_email()
+    {
+        Notification::fake();
+
+        Notification::assertNothingSent();
+
+        $recipient = factory(Recipient::class)->create([
+            'user_id' => $this->user->id,
+            'email_verified_at' => null
+        ]);
+
+        $response = $this->json('POST', '/recipients/email/resend', [
+            'recipient_id' => $recipient->id
+        ]);
+
+        $response->assertStatus(200);
+
+        Notification::assertSentTo(
+            $recipient,
+            VerifyEmail::class
+        );
+    }
+
+    /** @test */
+    public function user_can_verify_recipient_email_successfully()
+    {
+        $recipient = factory(Recipient::class)->create([
+            'user_id' => $this->user->id,
+            'email_verified_at' => null
+        ]);
+
+        $this->assertNull($recipient->refresh()->email_verified_at);
+
+        $verificationUrl = URL::temporarySignedRoute(
+            'verification.verify',
+            Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
+            [
+                'id' => $recipient->getKey(),
+                'hash' => sha1($recipient->getEmailForVerification()),
+            ]
+        );
+
+        $response = $this->get($verificationUrl);
+
+        $response
+            ->assertRedirect('/recipients')
+            ->assertSessionHas('verified');
+
+        $this->assertNotNull($recipient->refresh()->email_verified_at);
+    }
+
+    /** @test */
+    public function user_must_wait_before_resending_recipient_verification_email()
+    {
+        Notification::fake();
+
+        Notification::assertNothingSent();
+
+        $recipient = factory(Recipient::class)->create([
+            'user_id' => $this->user->id,
+            'email_verified_at' => null
+        ]);
+
+        $response = $this->json('POST', '/recipients/email/resend', [
+            'recipient_id' => $recipient->id
+        ]);
+
+        $response->assertStatus(200);
+
+        Notification::assertSentTo(
+            $recipient,
+            VerifyEmail::class
+        );
+
+        $response2 = $this->json('POST', '/recipients/email/resend', [
+            'recipient_id' => $recipient->id
+        ]);
+
+        $response2->assertStatus(429);
+    }
+}

+ 32 - 0
tests/TestCase.php

@@ -2,9 +2,13 @@
 
 
 namespace Tests;
 namespace Tests;
 
 
+use App\User;
 use Illuminate\Database\Eloquent\Collection as EloquentCollection;
 use Illuminate\Database\Eloquent\Collection as EloquentCollection;
 use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 use Illuminate\Foundation\Testing\TestResponse;
 use Illuminate\Foundation\Testing\TestResponse;
+use Illuminate\Support\Facades\DB;
+use Laravel\Passport\ClientRepository;
+use Laravel\Passport\Passport;
 use PHPUnit\Framework\Assert;
 use PHPUnit\Framework\Assert;
 
 
 abstract class TestCase extends BaseTestCase
 abstract class TestCase extends BaseTestCase
@@ -35,4 +39,32 @@ abstract class TestCase extends BaseTestCase
             });
             });
         });
         });
     }
     }
+
+    protected function setUpPassport(): void
+    {
+        $this->user = factory(User::class)->create();
+        Passport::actingAs($this->user, []);
+
+        $clientRepository = new ClientRepository();
+        $client = $clientRepository->createPersonalAccessClient(
+            null,
+            'Test Personal Access Client',
+            config('app.url')
+        );
+        DB::table('oauth_personal_access_clients')->insert([
+            'client_id' => $client->id,
+            'created_at' => now(),
+            'updated_at' => now(),
+        ]);
+
+        DB::table('oauth_access_tokens')->insert([
+            'id' => '1830c31e8e17dc4e871aa21ebe82e6cbfdd0d5781bec42631dd381119f355a911075f7e1a3dc2240',
+            'name' => 'New',
+            'user_id' => $this->user->id,
+            'revoked' => false,
+            'client_id' => 1,
+            'created_at' => now(),
+            'updated_at' => now(),
+        ]);
+    }
 }
 }