Browse Source

Added ability to disable catch-all for domains

Will 4 years ago
parent
commit
1917f0b3b5

+ 20 - 12
app/Http/Controllers/Api/AliasController.php

@@ -42,23 +42,31 @@ class AliasController extends Controller
             return response('', 429);
         }
 
-        if ($request->input('format', 'uuid') === 'random_words') {
-            $localPart = user()->generateRandomWordLocalPart();
-
+        if (isset($request->validated()['local_part'])) {
             $data = [
-                'email' => $localPart . '@' . $request->domain,
-                'local_part' => $localPart,
+                'email' => $request->validated()['local_part'] . '@' . $request->domain,
+                'local_part' => $request->validated()['local_part'],
             ];
         } else {
-            $uuid = Uuid::uuid4();
-
-            $data = [
-                'id' => $uuid,
-                'email' => $uuid . '@' . $request->domain,
-                'local_part' => $uuid,
-            ];
+            if ($request->input('format', 'uuid') === 'random_words') {
+                $localPart = user()->generateRandomWordLocalPart();
+
+                $data = [
+                    'email' => $localPart . '@' . $request->domain,
+                    'local_part' => $localPart,
+                ];
+            } else {
+                $uuid = Uuid::uuid4();
+
+                $data = [
+                    'id' => $uuid,
+                    'email' => $uuid . '@' . $request->domain,
+                    'local_part' => $uuid,
+                ];
+            }
         }
 
+
         // Check if domain is for additional username or custom domain
         $parentDomain = collect(config('anonaddy.all_domains'))
                     ->filter(function ($name) use ($request) {

+ 28 - 0
app/Http/Controllers/Api/CatchAllDomainController.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Controllers\Api;
+
+use App\Http\Controllers\Controller;
+use App\Http\Resources\DomainResource;
+use Illuminate\Http\Request;
+
+class CatchAllDomainController extends Controller
+{
+    public function store(Request $request)
+    {
+        $domain = user()->domains()->findOrFail($request->id);
+
+        $domain->enableCatchAll();
+
+        return new DomainResource($domain);
+    }
+
+    public function destroy($id)
+    {
+        $domain = user()->domains()->findOrFail($id);
+
+        $domain->disableCatchAll();
+
+        return response('', 204);
+    }
+}

+ 25 - 1
app/Http/Requests/StoreAliasRequest.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Requests;
 
+use App\Rules\ValidAliasLocalPart;
 use Illuminate\Foundation\Http\FormRequest;
 use Illuminate\Validation\Rule;
 
@@ -31,7 +32,30 @@ class StoreAliasRequest extends FormRequest
                 Rule::in($this->user()->domainOptions())
             ],
             'description' => 'nullable|max:100',
-            'format' => 'nullable|in:uuid,random_words'
+            'format' => 'nullable|in:uuid,random_words,custom'
+        ];
+    }
+
+    public function withValidator($validator)
+    {
+        $validator->sometimes('local_part', [
+            'required',
+            'max:50',
+            Rule::unique('aliases')->where(function ($query) {
+                return $query->where('local_part', $this->validationData()['local_part'])
+                ->where('domain', $this->validationData()['domain']);
+            }),
+            new ValidAliasLocalPart
+        ], function () {
+            $format = $this->validationData()['format'] ?? 'uuid';
+            return $format === 'custom';
+        });
+    }
+
+    public function messages()
+    {
+        return [
+            'local_part.unique' => 'That alias already exists.',
         ];
     }
 }

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

@@ -16,6 +16,7 @@ class DomainResource extends JsonResource
             'aliases' => AliasResource::collection($this->whenLoaded('aliases')),
             'default_recipient' => new RecipientResource($this->whenLoaded('defaultRecipient')),
             'active' => $this->active,
+            'catch_all' => $this->catch_all,
             'domain_verified_at' => $this->domain_verified_at ? $this->domain_verified_at->toDateTimeString() : null,
             'domain_sending_verified_at' => $this->domain_sending_verified_at ? $this->domain_sending_verified_at->toDateTimeString() : null,
             'created_at' => $this->created_at->toDateTimeString(),

+ 19 - 1
app/Models/Domain.php

@@ -24,7 +24,8 @@ class Domain extends Model
     protected $fillable = [
         'domain',
         'description',
-        'active'
+        'active',
+        'catch_all'
     ];
 
     protected $dates = [
@@ -38,6 +39,7 @@ class Domain extends Model
         'id' => 'string',
         'user_id' => 'string',
         'active' => 'boolean',
+        'catch_all' => 'boolean',
         'default_recipient_id' => 'string',
     ];
 
@@ -107,6 +109,22 @@ class Domain extends Model
         $this->update(['active' => true]);
     }
 
+    /**
+     * Disable catch-all for the domain.
+     */
+    public function disableCatchAll()
+    {
+        $this->update(['catch_all' => false]);
+    }
+
+    /**
+     * Enable catch-all for the domain.
+     */
+    public function enableCatchAll()
+    {
+        $this->update(['catch_all' => true]);
+    }
+
     /**
      * Determine if the domain is verified.
      *

+ 40 - 0
app/Rules/ValidAliasLocalPart.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Rules;
+
+use Illuminate\Contracts\Validation\Rule;
+
+class ValidAliasLocalPart implements Rule
+{
+    /**
+     * Create a new rule instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * Determine if the validation rule passes.
+     *
+     * @param  string  $attribute
+     * @param  mixed  $value
+     * @return bool
+     */
+    public function passes($attribute, $value)
+    {
+        return preg_match('/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))$/', $value);
+    }
+
+    /**
+     * Get the validation error message.
+     *
+     * @return string
+     */
+    public function message()
+    {
+        return 'Invalid alias local part.';
+    }
+}

+ 89 - 89
composer.lock

@@ -161,16 +161,16 @@
         },
         {
             "name": "dasprid/enum",
-            "version": "1.0.2",
+            "version": "1.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/DASPRiD/Enum.git",
-                "reference": "6ccc0d7141a7f149e3c56cb0ce5f05d9152cfd07"
+                "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6ccc0d7141a7f149e3c56cb0ce5f05d9152cfd07",
-                "reference": "6ccc0d7141a7f149e3c56cb0ce5f05d9152cfd07",
+                "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
+                "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
                 "shasum": ""
             },
             "require-dev": {
@@ -200,7 +200,7 @@
                 "enum",
                 "map"
             ],
-            "time": "2020-07-30T16:37:13+00:00"
+            "time": "2020-10-02T16:03:48+00:00"
         },
         {
             "name": "defuse/php-encryption",
@@ -1402,16 +1402,16 @@
         },
         {
             "name": "laravel/framework",
-            "version": "v8.7.1",
+            "version": "v8.9.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "3fb29e904a152b3e1fe49581f66ba5e02fe991f2"
+                "reference": "8a6bf870bcfa1597e514a9c7ee6df44db98abb54"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/3fb29e904a152b3e1fe49581f66ba5e02fe991f2",
-                "reference": "3fb29e904a152b3e1fe49581f66ba5e02fe991f2",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/8a6bf870bcfa1597e514a9c7ee6df44db98abb54",
+                "reference": "8a6bf870bcfa1597e514a9c7ee6df44db98abb54",
                 "shasum": ""
             },
             "require": {
@@ -1561,7 +1561,7 @@
                 "framework",
                 "laravel"
             ],
-            "time": "2020-09-29T15:39:07+00:00"
+            "time": "2020-10-06T14:22:36+00:00"
         },
         {
             "name": "laravel/passport",
@@ -2701,16 +2701,16 @@
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.40.1",
+            "version": "2.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "d9a76d8b7eb0f97cf3a82529393245212f40ba3b"
+                "reference": "8690b13ad4da6d54d692afea15aab30b36fee52e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/d9a76d8b7eb0f97cf3a82529393245212f40ba3b",
-                "reference": "d9a76d8b7eb0f97cf3a82529393245212f40ba3b",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8690b13ad4da6d54d692afea15aab30b36fee52e",
+                "reference": "8690b13ad4da6d54d692afea15aab30b36fee52e",
                 "shasum": ""
             },
             "require": {
@@ -2786,7 +2786,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-23T08:17:37+00:00"
+            "time": "2020-10-04T09:11:05+00:00"
         },
         {
             "name": "nikic/php-parser",
@@ -4404,16 +4404,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "04c3a31fe8ea94b42c9e2d1acc93d19782133b00"
+                "reference": "ae789a8a2ad189ce7e8216942cdb9b77319f5eb8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/04c3a31fe8ea94b42c9e2d1acc93d19782133b00",
-                "reference": "04c3a31fe8ea94b42c9e2d1acc93d19782133b00",
+                "url": "https://api.github.com/repos/symfony/console/zipball/ae789a8a2ad189ce7e8216942cdb9b77319f5eb8",
+                "reference": "ae789a8a2ad189ce7e8216942cdb9b77319f5eb8",
                 "shasum": ""
             },
             "require": {
@@ -4497,7 +4497,7 @@
         },
         {
             "name": "symfony/css-selector",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
@@ -4628,16 +4628,16 @@
         },
         {
             "name": "symfony/error-handler",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/error-handler.git",
-                "reference": "d2f1d4996d5499f1261164d10080e4120001f041"
+                "reference": "5e4d8ef8d71822922d1eebd130219ae3491a5ca9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/error-handler/zipball/d2f1d4996d5499f1261164d10080e4120001f041",
-                "reference": "d2f1d4996d5499f1261164d10080e4120001f041",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/5e4d8ef8d71822922d1eebd130219ae3491a5ca9",
+                "reference": "5e4d8ef8d71822922d1eebd130219ae3491a5ca9",
                 "shasum": ""
             },
             "require": {
@@ -4695,11 +4695,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-27T03:44:28+00:00"
+            "time": "2020-10-02T08:49:02+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
@@ -4862,7 +4862,7 @@
         },
         {
             "name": "symfony/finder",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
@@ -5000,16 +5000,16 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "6cca6b2e4b69fc5bace160d14cf1ee5f71483db4"
+                "reference": "353b42e7b4fd1c898aab09a059466c9cea74039b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6cca6b2e4b69fc5bace160d14cf1ee5f71483db4",
-                "reference": "6cca6b2e4b69fc5bace160d14cf1ee5f71483db4",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/353b42e7b4fd1c898aab09a059466c9cea74039b",
+                "reference": "353b42e7b4fd1c898aab09a059466c9cea74039b",
                 "shasum": ""
             },
             "require": {
@@ -5071,20 +5071,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-13T05:01:27+00:00"
+            "time": "2020-09-27T14:14:57+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "17227644c3c66dcf32bdfeceff4364d090cd6756"
+                "reference": "1764b87d2f10d5c9ce6e4850fe27934116d89708"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/17227644c3c66dcf32bdfeceff4364d090cd6756",
-                "reference": "17227644c3c66dcf32bdfeceff4364d090cd6756",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1764b87d2f10d5c9ce6e4850fe27934116d89708",
+                "reference": "1764b87d2f10d5c9ce6e4850fe27934116d89708",
                 "shasum": ""
             },
             "require": {
@@ -5185,11 +5185,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-27T04:33:19+00:00"
+            "time": "2020-10-04T07:57:28+00:00"
         },
         {
             "name": "symfony/mime",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/mime.git",
@@ -6046,7 +6046,7 @@
         },
         {
             "name": "symfony/process",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
@@ -6188,16 +6188,16 @@
         },
         {
             "name": "symfony/routing",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "d36e06eb02a55522a8eed070c1cbc3dc3c389876"
+                "reference": "720348c2ae011f8c56964c0fc3e992840cb60ccf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/d36e06eb02a55522a8eed070c1cbc3dc3c389876",
-                "reference": "d36e06eb02a55522a8eed070c1cbc3dc3c389876",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/720348c2ae011f8c56964c0fc3e992840cb60ccf",
+                "reference": "720348c2ae011f8c56964c0fc3e992840cb60ccf",
                 "shasum": ""
             },
             "require": {
@@ -6276,7 +6276,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-02T16:23:27+00:00"
+            "time": "2020-10-02T13:05:43+00:00"
         },
         {
             "name": "symfony/service-contracts",
@@ -6356,7 +6356,7 @@
         },
         {
             "name": "symfony/string",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
@@ -6441,7 +6441,7 @@
         },
         {
             "name": "symfony/translation",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
@@ -6533,16 +6533,16 @@
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v2.2.0",
+            "version": "v2.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "77ce1c3627c9f39643acd9af086631f842c50c4d"
+                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/77ce1c3627c9f39643acd9af086631f842c50c4d",
-                "reference": "77ce1c3627c9f39643acd9af086631f842c50c4d",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e2eaa60b558f26a4b0354e1bbb25636efaaad105",
+                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105",
                 "shasum": ""
             },
             "require": {
@@ -6554,7 +6554,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.2-dev"
+                    "dev-master": "2.3-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -6604,11 +6604,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-07T11:33:47+00:00"
+            "time": "2020-09-28T13:05:58+00:00"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
@@ -7290,16 +7290,16 @@
         },
         {
             "name": "facade/ignition",
-            "version": "2.3.7",
+            "version": "2.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/ignition.git",
-                "reference": "b364db8860a63c1fb58b72b9718863c21df08762"
+                "reference": "e8fed9c382cd1d02b5606688576a35619afdf82c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition/zipball/b364db8860a63c1fb58b72b9718863c21df08762",
-                "reference": "b364db8860a63c1fb58b72b9718863c21df08762",
+                "url": "https://api.github.com/repos/facade/ignition/zipball/e8fed9c382cd1d02b5606688576a35619afdf82c",
+                "reference": "e8fed9c382cd1d02b5606688576a35619afdf82c",
                 "shasum": ""
             },
             "require": {
@@ -7358,7 +7358,7 @@
                 "laravel",
                 "page"
             ],
-            "time": "2020-09-06T19:26:27+00:00"
+            "time": "2020-10-01T23:01:14+00:00"
         },
         {
             "name": "facade/ignition-contracts",
@@ -8235,16 +8235,16 @@
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "9.1.11",
+            "version": "9.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "c9394cb9d07ecfa9351b96f2e296bad473195f4d"
+                "reference": "53a4b737e83be724efd2bc4e7b929b9a30c48972"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c9394cb9d07ecfa9351b96f2e296bad473195f4d",
-                "reference": "c9394cb9d07ecfa9351b96f2e296bad473195f4d",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/53a4b737e83be724efd2bc4e7b929b9a30c48972",
+                "reference": "53a4b737e83be724efd2bc4e7b929b9a30c48972",
                 "shasum": ""
             },
             "require": {
@@ -8272,7 +8272,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "9.1-dev"
+                    "dev-master": "9.2-dev"
                 }
             },
             "autoload": {
@@ -8304,7 +8304,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-19T05:29:17+00:00"
+            "time": "2020-10-02T03:37:32+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
@@ -8533,16 +8533,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.3.11",
+            "version": "9.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "f7316ea106df7c9507f4fdaa88c47bc10a3b27a1"
+                "reference": "ef533467a7974c4b6c354f3eff42a115910bd4e5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f7316ea106df7c9507f4fdaa88c47bc10a3b27a1",
-                "reference": "f7316ea106df7c9507f4fdaa88c47bc10a3b27a1",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ef533467a7974c4b6c354f3eff42a115910bd4e5",
+                "reference": "ef533467a7974c4b6c354f3eff42a115910bd4e5",
                 "shasum": ""
             },
             "require": {
@@ -8558,7 +8558,7 @@
                 "phar-io/version": "^3.0.2",
                 "php": ">=7.3",
                 "phpspec/prophecy": "^1.11.1",
-                "phpunit/php-code-coverage": "^9.1.11",
+                "phpunit/php-code-coverage": "^9.2",
                 "phpunit/php-file-iterator": "^3.0.4",
                 "phpunit/php-invoker": "^3.1",
                 "phpunit/php-text-template": "^2.0.2",
@@ -8589,7 +8589,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "9.3-dev"
+                    "dev-master": "9.4-dev"
                 }
             },
             "autoload": {
@@ -8628,7 +8628,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-24T08:08:49+00:00"
+            "time": "2020-10-02T03:54:37+00:00"
         },
         {
             "name": "scrivo/highlight.php",
@@ -8759,16 +8759,16 @@
         },
         {
             "name": "sebastian/code-unit",
-            "version": "1.0.6",
+            "version": "1.0.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/code-unit.git",
-                "reference": "d3a241b6028ff9d8e97d2b6ebd4090d01f92fad8"
+                "reference": "59236be62b1bb9919e6d7f60b0b832dc05cef9ab"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/d3a241b6028ff9d8e97d2b6ebd4090d01f92fad8",
-                "reference": "d3a241b6028ff9d8e97d2b6ebd4090d01f92fad8",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/59236be62b1bb9919e6d7f60b0b832dc05cef9ab",
+                "reference": "59236be62b1bb9919e6d7f60b0b832dc05cef9ab",
                 "shasum": ""
             },
             "require": {
@@ -8807,7 +8807,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T05:28:46+00:00"
+            "time": "2020-10-02T14:47:54+00:00"
         },
         {
             "name": "sebastian/code-unit-reverse-lookup",
@@ -9506,16 +9506,16 @@
         },
         {
             "name": "sebastian/type",
-            "version": "2.2.2",
+            "version": "2.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/type.git",
-                "reference": "e494dcaeb89d1458c9ccd8c819745245a1669aea"
+                "reference": "fa592377f3923946cb90bf1f6a71ba2e5f229909"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e494dcaeb89d1458c9ccd8c819745245a1669aea",
-                "reference": "e494dcaeb89d1458c9ccd8c819745245a1669aea",
+                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fa592377f3923946cb90bf1f6a71ba2e5f229909",
+                "reference": "fa592377f3923946cb90bf1f6a71ba2e5f229909",
                 "shasum": ""
             },
             "require": {
@@ -9527,7 +9527,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.2-dev"
+                    "dev-master": "2.3-dev"
                 }
             },
             "autoload": {
@@ -9554,7 +9554,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-28T06:01:38+00:00"
+            "time": "2020-10-06T08:41:03+00:00"
         },
         {
             "name": "sebastian/version",
@@ -9607,16 +9607,16 @@
         },
         {
             "name": "symfony/filesystem",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "f3194303d3077829dbbc1d18f50288b2a01146f2"
+                "reference": "1a8697545a8d87b9f2f6b1d32414199cc5e20aae"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/f3194303d3077829dbbc1d18f50288b2a01146f2",
-                "reference": "f3194303d3077829dbbc1d18f50288b2a01146f2",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/1a8697545a8d87b9f2f6b1d32414199cc5e20aae",
+                "reference": "1a8697545a8d87b9f2f6b1d32414199cc5e20aae",
                 "shasum": ""
             },
             "require": {
@@ -9667,11 +9667,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-02T16:23:27+00:00"
+            "time": "2020-09-27T14:02:37+00:00"
         },
         {
             "name": "symfony/options-resolver",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/options-resolver.git",
@@ -9741,7 +9741,7 @@
         },
         {
             "name": "symfony/stopwatch",
-            "version": "v5.1.6",
+            "version": "v5.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/stopwatch.git",

+ 32 - 0
database/migrations/2020_10_07_141852_add_catch_all_to_domains_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddCatchAllToDomainsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('domains', function (Blueprint $table) {
+            $table->boolean('catch_all')->after('active')->default(true);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('domains', function (Blueprint $table) {
+            $table->dropColumn('catch_all');
+        });
+    }
+}

+ 42 - 40
package-lock.json

@@ -786,13 +786,14 @@
                     }
                 },
                 "postcss-selector-parser": {
-                    "version": "6.0.2",
-                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
-                    "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+                    "version": "6.0.4",
+                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
+                    "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==",
                     "requires": {
                         "cssesc": "^3.0.0",
                         "indexes-of": "^1.0.1",
-                        "uniq": "^1.0.1"
+                        "uniq": "^1.0.1",
+                        "util-deprecate": "^1.0.2"
                     }
                 },
                 "purgecss": {
@@ -1113,9 +1114,9 @@
             },
             "dependencies": {
                 "acorn": {
-                    "version": "7.4.0",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
-                    "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
+                    "version": "7.4.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
                 }
             }
         },
@@ -7130,9 +7131,9 @@
             },
             "dependencies": {
                 "postcss": {
-                    "version": "7.0.32",
-                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
-                    "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
+                    "version": "7.0.35",
+                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
+                    "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
                     "requires": {
                         "chalk": "^2.4.2",
                         "source-map": "^0.6.1",
@@ -7454,9 +7455,9 @@
                     "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
                 },
                 "postcss": {
-                    "version": "7.0.32",
-                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
-                    "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
+                    "version": "7.0.35",
+                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
+                    "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
                     "requires": {
                         "chalk": "^2.4.2",
                         "source-map": "^0.6.1",
@@ -7464,13 +7465,14 @@
                     }
                 },
                 "postcss-selector-parser": {
-                    "version": "6.0.2",
-                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
-                    "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+                    "version": "6.0.4",
+                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
+                    "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==",
                     "requires": {
                         "cssesc": "^3.0.0",
                         "indexes-of": "^1.0.1",
-                        "uniq": "^1.0.1"
+                        "uniq": "^1.0.1",
+                        "util-deprecate": "^1.0.2"
                     }
                 },
                 "source-map": {
@@ -9423,9 +9425,9 @@
             "dev": true
         },
         "tailwindcss": {
-            "version": "1.8.10",
-            "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.8.10.tgz",
-            "integrity": "sha512-7QkERG/cWCzsuMqHMwjOaLMVixOGLNBiXsrkssxlE1aWfkxVbGqiuMokR2162xRyaH2mBIHKxmlf1qb3DvIPqw==",
+            "version": "1.8.11",
+            "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.8.11.tgz",
+            "integrity": "sha512-k7Mam6s+rmr79z0eil56tdGH7cvTlzWfijlO5qDLlpybDqPwzYRGQ/1CksE/H192oeAem1bFy9IDJL97q7lSag==",
             "requires": {
                 "@fullhuman/postcss-purgecss": "^2.1.2",
                 "autoprefixer": "^9.4.5",
@@ -9452,29 +9454,28 @@
             },
             "dependencies": {
                 "ansi-styles": {
-                    "version": "4.2.1",
-                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
-                    "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
                     "requires": {
-                        "@types/color-name": "^1.1.1",
                         "color-convert": "^2.0.1"
                     }
                 },
                 "browserslist": {
-                    "version": "4.14.2",
-                    "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz",
-                    "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==",
+                    "version": "4.14.5",
+                    "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz",
+                    "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==",
                     "requires": {
-                        "caniuse-lite": "^1.0.30001125",
-                        "electron-to-chromium": "^1.3.564",
-                        "escalade": "^3.0.2",
+                        "caniuse-lite": "^1.0.30001135",
+                        "electron-to-chromium": "^1.3.571",
+                        "escalade": "^3.1.0",
                         "node-releases": "^1.1.61"
                     }
                 },
                 "caniuse-lite": {
-                    "version": "1.0.30001131",
-                    "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001131.tgz",
-                    "integrity": "sha512-4QYi6Mal4MMfQMSqGIRPGbKIbZygeN83QsWq1ixpUwvtfgAZot5BrCKzGygvZaV+CnELdTwD0S4cqUNozq7/Cw=="
+                    "version": "1.0.30001144",
+                    "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001144.tgz",
+                    "integrity": "sha512-4GQTEWNMnVZVOFG3BK0xvGeaDAtiPAbG2N8yuMXuXzx/c2Vd4XoMPO8+E918zeXn5IF0FRVtGShBfkfQea2wHQ=="
                 },
                 "chalk": {
                     "version": "4.1.0",
@@ -9504,9 +9505,9 @@
                     "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
                 },
                 "electron-to-chromium": {
-                    "version": "1.3.570",
-                    "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.570.tgz",
-                    "integrity": "sha512-Y6OCoVQgFQBP5py6A/06+yWxUZHDlNr/gNDGatjH8AZqXl8X0tE4LfjLJsXGz/JmWJz8a6K7bR1k+QzZ+k//fg=="
+                    "version": "1.3.578",
+                    "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz",
+                    "integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q=="
                 },
                 "fs-extra": {
                     "version": "8.1.0",
@@ -9529,13 +9530,14 @@
                     "integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g=="
                 },
                 "postcss-selector-parser": {
-                    "version": "6.0.2",
-                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
-                    "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+                    "version": "6.0.4",
+                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
+                    "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==",
                     "requires": {
                         "cssesc": "^3.0.0",
                         "indexes-of": "^1.0.1",
-                        "uniq": "^1.0.1"
+                        "uniq": "^1.0.1",
+                        "util-deprecate": "^1.0.2"
                     }
                 },
                 "postcss-value-parser": {

+ 1 - 1
package.json

@@ -21,7 +21,7 @@
         "postcss-import": "^11.1.0",
         "postcss-nesting": "^5.0.0",
         "resolve-url-loader": "^2.3.2",
-        "tailwindcss": "^1.8.10",
+        "tailwindcss": "^1.8.11",
         "tippy.js": "^4.3.5",
         "v-clipboard": "^2.2.3",
         "vue": "^2.6.12",

+ 37 - 0
resources/js/pages/Aliases.vue

@@ -445,6 +445,24 @@
           </div>
         </div>
 
+        <div v-if="generateAliasFormat === 'custom'">
+          <label for="alias_local_part" class="block text-grey-700 text-sm my-2">
+            Alias Local Part:
+          </label>
+          <p v-show="errors.generateAliasLocalPart" class="mb-3 text-red-500 text-sm">
+            {{ errors.generateAliasLocalPart }}
+          </p>
+          <input
+            v-model="generateAliasLocalPart"
+            id="alias_local_part"
+            type="text"
+            class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3"
+            :class="errors.generateAliasLocalPart ? 'border-red-500' : ''"
+            placeholder="Enter local part..."
+            autofocus
+          />
+        </div>
+
         <label for="alias_description" class="block text-grey-700 text-sm my-2">
           Description:
         </label>
@@ -680,6 +698,7 @@ export default {
       generateAliasModalOpen: false,
       generateAliasLoading: false,
       generateAliasDomain: this.defaultAliasDomain ? this.defaultAliasDomain : this.domain,
+      generateAliasLocalPart: '',
       generateAliasDescription: '',
       generateAliasFormat: this.defaultAliasFormat ? this.defaultAliasFormat : 'uuid',
       aliasFormatOptions: [
@@ -691,6 +710,10 @@ export default {
           value: 'random_words',
           label: 'Random Words',
         },
+        {
+          value: 'custom',
+          label: 'Custom',
+        },
       ],
       recipientsAliasToEdit: {},
       aliasRecipientsToEdit: [],
@@ -899,6 +922,14 @@ export default {
     generateNewAlias() {
       this.errors = {}
 
+      // Validate alias local part
+      if (
+        this.generateAliasFormat === 'custom' &&
+        !this.validLocalPart(this.generateAliasLocalPart)
+      ) {
+        return (this.errors.generateAliasLocalPart = 'Valid local part required')
+      }
+
       if (this.generateAliasDescription.length > 100) {
         return (this.errors.generateAliasDescription = 'Description cannot exceed 100 characters')
       }
@@ -910,6 +941,7 @@ export default {
           '/api/v1/aliases',
           JSON.stringify({
             domain: this.generateAliasDomain,
+            local_part: this.generateAliasLocalPart,
             description: this.generateAliasDescription,
             format: this.generateAliasFormat,
           }),
@@ -919,6 +951,7 @@ export default {
         )
         .then(({ data }) => {
           this.generateAliasLoading = false
+          this.generateAliasLocalPart = ''
           this.generateAliasDescription = ''
           this.rows.push(data.data)
           this.generateAliasModalOpen = false
@@ -1002,6 +1035,10 @@ export default {
     has(object, path) {
       return _.has(object, path)
     },
+    validLocalPart(part) {
+      let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))$/
+      return re.test(part)
+    },
     clipboardSuccess() {
       this.success('Copied to clipboard')
     },

+ 49 - 0
resources/js/pages/Domains.vue

@@ -143,6 +143,13 @@
             @off="deactivateDomain(props.row.id)"
           />
         </span>
+        <span v-else-if="props.column.field === 'catch_all'" class="flex items-center">
+          <Toggle
+            v-model="rows[props.row.originalIndex].catch_all"
+            @on="enableCatchAll(props.row.id)"
+            @off="disableCatchAll(props.row.id)"
+          />
+        </span>
         <span v-else-if="props.column.field === 'domain_sending_verified_at'">
           <span
             name="check"
@@ -473,6 +480,12 @@ export default {
           type: 'boolean',
           globalSearchDisabled: true,
         },
+        {
+          label: 'Catch-All',
+          field: 'catch_all',
+          type: 'boolean',
+          globalSearchDisabled: true,
+        },
         {
           label: 'Verified for Sending',
           field: 'domain_sending_verified_at',
@@ -690,6 +703,42 @@ export default {
           this.error()
         })
     },
+    enableCatchAll(id) {
+      axios
+        .post(
+          `/api/v1/catch-all-domains`,
+          JSON.stringify({
+            id: id,
+          }),
+          {
+            headers: { 'Content-Type': 'application/json' },
+          }
+        )
+        .then(response => {
+          //
+        })
+        .catch(error => {
+          if (error.response !== undefined) {
+            this.error(error.response.data)
+          } else {
+            this.error()
+          }
+        })
+    },
+    disableCatchAll(id) {
+      axios
+        .delete(`/api/v1/catch-all-domains/${id}`)
+        .then(response => {
+          //
+        })
+        .catch(error => {
+          if (error.response !== undefined) {
+            this.error(error.response.data)
+          } else {
+            this.error()
+          }
+        })
+    },
     deleteDomain(id) {
       this.deleteDomainLoading = true
 

+ 3 - 0
routes/api.php

@@ -50,6 +50,9 @@ Route::group([
     Route::post('/active-domains', 'Api\ActiveDomainController@store');
     Route::delete('/active-domains/{id}', 'Api\ActiveDomainController@destroy');
 
+    Route::post('/catch-all-domains', 'Api\CatchAllDomainController@store');
+    Route::delete('/catch-all-domains/{id}', 'Api\CatchAllDomainController@destroy');
+
     Route::get('/usernames', 'Api\AdditionalUsernameController@index');
     Route::get('/usernames/{id}', 'Api\AdditionalUsernameController@show');
     Route::post('/usernames', 'Api\AdditionalUsernameController@store');

+ 26 - 0
tests/Feature/Api/AliasRecipientsTest.php

@@ -140,4 +140,30 @@ class AliasRecipientsTest extends TestCase
         $response->assertStatus(422);
         $this->assertCount(0, $alias->recipients);
     }
+
+    /** @test */
+    public function alias_recipient_record_is_deleted_if_recipient_is_deleted()
+    {
+        $alias = Alias::factory()->create([
+            'user_id' => $this->user->id
+        ]);
+
+        $recipient = Recipient::factory()->create([
+            'user_id' => $this->user->id
+        ]);
+
+        AliasRecipient::create([
+            'alias' => $alias,
+            'recipient' => $recipient
+        ]);
+
+        $this->assertEquals($alias->recipients[0]->email, $recipient->email);
+
+        $recipient->delete();
+        $this->assertCount(0, AliasRecipient::all());
+        $this->assertDatabaseMissing('alias_recipients', [
+            'alias_id' => $alias->id,
+            'recipient_id' => $recipient->id
+        ]);
+    }
 }

+ 33 - 1
tests/Feature/Api/AliasesTest.php

@@ -98,7 +98,8 @@ class AliasesTest extends TestCase
     {
         $response = $this->json('POST', '/api/v1/aliases', [
             'domain' => 'anonaddy.me',
-            'description' => 'the description'
+            'description' => 'the description',
+            'local_part' => 'not-required-for-shared-alias'
         ]);
 
         $response->assertStatus(201);
@@ -107,6 +108,37 @@ class AliasesTest extends TestCase
         $this->assertEquals($this->user->aliases[0]->id, $this->user->aliases[0]->local_part);
     }
 
+    /** @test */
+    public function user_can_generate_new_alias_with_local_part()
+    {
+        $response = $this->json('POST', '/api/v1/aliases', [
+            'domain' => $this->user->username . '.anonaddy.com',
+            'format' => 'custom',
+            'description' => 'the description',
+            'local_part' => 'valid-local-part'
+        ]);
+
+        $response->assertStatus(201);
+        $this->assertCount(1, $this->user->aliases);
+        $this->assertEquals('valid-local-part', $response->getData()->data->local_part);
+        $this->assertEquals('valid-local-part@'.$this->user->username . '.anonaddy.com', $this->user->aliases[0]->email);
+    }
+
+    /** @test */
+    public function user_cannot_generate_new_alias_with_invalid_local_part()
+    {
+        $response = $this->json('POST', '/api/v1/aliases', [
+            'domain' => $this->user->username . '.anonaddy.com',
+            'format' => 'custom',
+            'description' => 'the description',
+            'local_part' => 'invalid-local-part.'
+        ]);
+
+        $response->assertStatus(422);
+        $this->assertCount(0, $this->user->aliases);
+        $response->assertJsonValidationErrors('local_part');
+    }
+
     /** @test */
     public function user_can_generate_new_random_word_alias()
     {

+ 30 - 0
tests/Feature/Api/DomainsTest.php

@@ -156,6 +156,36 @@ class DomainsTest extends TestCase
         $this->assertFalse($this->user->domains[0]->active);
     }
 
+    /** @test */
+    public function user_can_enable_catch_all_for_domain()
+    {
+        $domain = Domain::factory()->create([
+            'user_id' => $this->user->id,
+            'catch_all' => false
+        ]);
+
+        $response = $this->json('POST', '/api/v1/catch-all-domains/', [
+            'id' => $domain->id
+        ]);
+
+        $response->assertStatus(200);
+        $this->assertTrue($response->getData()->data->catch_all);
+    }
+
+    /** @test */
+    public function user_can_disable_catch_all_for_domain()
+    {
+        $domain = Domain::factory()->create([
+            'user_id' => $this->user->id,
+            'catch_all' => true
+        ]);
+
+        $response = $this->json('DELETE', '/api/v1/catch-all-domains/'.$domain->id);
+
+        $response->assertStatus(204);
+        $this->assertFalse($this->user->domains[0]->catch_all);
+    }
+
     /** @test */
     public function user_can_update_domain_description()
     {