Ver código fonte

Updated Aliases page

Will Browning 6 anos atrás
pai
commit
32e66670c5

+ 23 - 8
app/Console/Commands/ReceiveEmail.php

@@ -59,18 +59,13 @@ class ReceiveEmail extends Command
     public function handle()
     {
         try {
+            $this->exitIfFromSelf();
+
             $file = $this->argument('file');
 
             $this->parser = $this->getParser($file);
 
-            $recipients = collect($this->option('recipient'))->map(function ($item, $key) {
-                return [
-                    'email' => $item,
-                    'local_part' => $this->option('local_part')[$key],
-                    'extension' => $this->option('extension')[$key],
-                    'domain' => $this->option('domain')[$key]
-                ];
-            });
+            $recipients = $this->getRecipients();
 
             // Divide the size of the email by the number of recipients (excluding any unsubscribe recipients) to prevent it being added multiple times
             $recipientCount = $recipients->where('domain', '!=', 'unsubscribe.'.config('anonaddy.domain'))->count();
@@ -231,6 +226,18 @@ class ReceiveEmail extends Command
             );
     }
 
+    protected function getRecipients()
+    {
+        return collect($this->option('recipient'))->map(function ($item, $key) {
+            return [
+                'email' => $item,
+                'local_part' => $this->option('local_part')[$key],
+                'extension' => $this->option('extension')[$key],
+                'domain' => $this->option('domain')[$key]
+            ];
+        });
+    }
+
     protected function getParser($file)
     {
         $parser = new Parser;
@@ -248,4 +255,12 @@ class ReceiveEmail extends Command
         }
         return $parser;
     }
+
+    protected function exitIfFromSelf()
+    {
+        // To prevent recipient alias infinite nested looping
+        if (in_array($this->option('sender'), [config('mail.from.address'), config('anonaddy.return_path')])) {
+            exit(0);
+        }
+    }
 }

+ 3 - 1
app/Http/Requests/StoreDomainRequest.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Requests;
 
+use App\Rules\NotLocalDomain;
 use App\Rules\ValidDomain;
 use Illuminate\Foundation\Http\FormRequest;
 
@@ -30,7 +31,8 @@ class StoreDomainRequest extends FormRequest
                 'string',
                 'max:50',
                 'unique:domains',
-                new ValidDomain
+                new ValidDomain,
+                new NotLocalDomain
             ]
         ];
     }

+ 47 - 0
app/Rules/NotLocalDomain.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace App\Rules;
+
+use Illuminate\Contracts\Validation\Rule;
+use Illuminate\Support\Str;
+
+class NotLocalDomain 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)
+    {
+        $count = collect(config('anonaddy.all_domains'))
+        ->filter(function ($name) use ($value) {
+            return Str::endsWith(strtolower($value), $name);
+        })
+        ->count();
+
+        return $count === 0;
+    }
+
+    /**
+     * Get the validation error message.
+     *
+     * @return string
+     */
+    public function message()
+    {
+        return 'The domain cannot be a local one.';
+    }
+}

+ 25 - 23
composer.lock

@@ -430,28 +430,30 @@
         },
         {
             "name": "doctrine/lexer",
-            "version": "1.0.2",
+            "version": "1.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/lexer.git",
-                "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8"
+                "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8",
-                "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea",
+                "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.2"
+                "php": "^7.2"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.5"
+                "doctrine/coding-standard": "^6.0",
+                "phpstan/phpstan": "^0.11.8",
+                "phpunit/phpunit": "^8.2"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "1.1.x-dev"
                 }
             },
             "autoload": {
@@ -464,14 +466,14 @@
                 "MIT"
             ],
             "authors": [
-                {
-                    "name": "Roman Borschel",
-                    "email": "roman@code-factory.org"
-                },
                 {
                     "name": "Guilherme Blanco",
                     "email": "guilhermeblanco@gmail.com"
                 },
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
                 {
                     "name": "Johannes Schmitt",
                     "email": "schmittjoh@gmail.com"
@@ -486,7 +488,7 @@
                 "parser",
                 "php"
             ],
-            "time": "2019-06-08T11:03:04+00:00"
+            "time": "2019-07-30T19:33:28+00:00"
         },
         {
             "name": "dragonmantank/cron-expression",
@@ -931,16 +933,16 @@
         },
         {
             "name": "laravel/framework",
-            "version": "v5.8.29",
+            "version": "v5.8.30",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "489ae2218c7eb138caac780de584d8df9fe8160b"
+                "reference": "7ccf0cf63931a8d8391aed90e6fc011381ea6838"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/489ae2218c7eb138caac780de584d8df9fe8160b",
-                "reference": "489ae2218c7eb138caac780de584d8df9fe8160b",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/7ccf0cf63931a8d8391aed90e6fc011381ea6838",
+                "reference": "7ccf0cf63931a8d8391aed90e6fc011381ea6838",
                 "shasum": ""
             },
             "require": {
@@ -1074,7 +1076,7 @@
                 "framework",
                 "laravel"
             ],
-            "time": "2019-07-16T14:05:28+00:00"
+            "time": "2019-07-30T14:08:47+00:00"
         },
         {
             "name": "laravel/tinker",
@@ -1370,16 +1372,16 @@
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.21.3",
+            "version": "2.22.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "58bdbbfab17ccd2ec7347b99e997f18232def4dc"
+                "reference": "1a0e48b5f656065ba3c265b058b25d36c2162a5e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/58bdbbfab17ccd2ec7347b99e997f18232def4dc",
-                "reference": "58bdbbfab17ccd2ec7347b99e997f18232def4dc",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/1a0e48b5f656065ba3c265b058b25d36c2162a5e",
+                "reference": "1a0e48b5f656065ba3c265b058b25d36c2162a5e",
                 "shasum": ""
             },
             "require": {
@@ -1390,7 +1392,7 @@
             "require-dev": {
                 "friendsofphp/php-cs-fixer": "^2.14 || ^3.0",
                 "kylekatarnls/multi-tester": "^1.1",
-                "phpmd/phpmd": "^2.6",
+                "phpmd/phpmd": "dev-php-7.1-compatibility",
                 "phpstan/phpstan": "^0.11",
                 "phpunit/phpunit": "^7.5 || ^8.0",
                 "squizlabs/php_codesniffer": "^3.4"
@@ -1433,7 +1435,7 @@
                 "datetime",
                 "time"
             ],
-            "time": "2019-07-18T18:47:28+00:00"
+            "time": "2019-07-28T09:02:12+00:00"
         },
         {
             "name": "nikic/php-parser",

+ 6 - 0
resources/js/components/Icon.vue

@@ -50,6 +50,12 @@
     ></path>
   </svg>
 
+  <svg v-else-if="name === 'desc'" xmlns="http://www.w3.org/2000/svg" viewBox="-4 -2 24 24">
+    <path
+      d="M3 0h10a3 3 0 0 1 3 3v14a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm2 1h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 12h2a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zm0-4h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zm0-4h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"
+    ></path>
+  </svg>
+
   <svg v-else-if="name === 'check'" xmlns="http://www.w3.org/2000/svg" viewBox="-5 -7 24 24">
     <path
       d="M5.486 9.73a.997.997 0 0 1-.707-.292L.537 5.195A1 1 0 1 1 1.95 3.78l3.535 3.535L11.85.952a1 1 0 0 1 1.415 1.414L6.193 9.438a.997.997 0 0 1-.707.292z"

+ 59 - 22
resources/js/pages/Aliases.vue

@@ -122,7 +122,7 @@
     <div class="bg-white rounded shadow overflow-x-auto">
       <table v-if="initialAliases.length" class="w-full whitespace-no-wrap">
         <tr class="text-left font-semibold text-grey-500 text-sm tracking-wider">
-          <th class="p-4">
+          <th class="pl-4 pr-2 py-4">
             <div class="flex items-center">
               Created
               <div class="inline-flex flex-col">
@@ -143,7 +143,7 @@
               </div>
             </div>
           </th>
-          <th class="p-4">
+          <th class="px-2 py-4">
             <div class="flex items-center">
               Alias
               <div class="inline-flex flex-col">
@@ -162,7 +162,7 @@
               </div>
             </div>
           </th>
-          <th class="p-4">
+          <th class="px-2 py-4">
             <div class="flex items-center">
               Recipients
               <div class="inline-flex flex-col">
@@ -181,12 +181,12 @@
               </div>
             </div>
           </th>
-          <th class="p-4">
+          <th class="px-2 py-4">
             <div class="flex items-center">
               Description
             </div>
           </th>
-          <th class="p-4">
+          <th class="px-2 py-4">
             <div class="flex items-center">
               Forwarded
               <div class="inline-flex flex-col">
@@ -209,7 +209,7 @@
               </div>
             </div>
           </th>
-          <th class="p-4 items-center">
+          <th class="px-2 py-4 items-center">
             <div class="flex items-center">
               Blocked
               <div class="inline-flex flex-col">
@@ -232,7 +232,30 @@
               </div>
             </div>
           </th>
-          <th class="p-4 items-center" colspan="2">
+          <th class="px-2 py-4 items-center">
+            <div class="flex items-center">
+              Replies
+              <div class="inline-flex flex-col">
+                <icon
+                  name="chevron-up"
+                  @click.native="sort('emails_replied', 'asc')"
+                  class="w-4 h-4 text-grey-300 fill-current cursor-pointer"
+                  :class="{
+                    'text-grey-800': isCurrentSort('emails_replied', 'asc'),
+                  }"
+                />
+                <icon
+                  name="chevron-down"
+                  @click.native="sort('emails_replied', 'desc')"
+                  class="w-4 h-4 text-grey-300 fill-current cursor-pointer"
+                  :class="{
+                    'text-grey-800': isCurrentSort('emails_replied', 'desc'),
+                  }"
+                />
+              </div>
+            </div>
+          </th>
+          <th class="px-2 py-4 items-center" colspan="2">
             <div class="flex items-center">
               Active
               <div class="inline-flex flex-col">
@@ -258,7 +281,7 @@
           class="hover:bg-grey-50 focus-within:bg-grey-50 h-20"
         >
           <td class="border-grey-200 border-t">
-            <div class="p-4 flex items-center">
+            <div class="pl-4 pr-2 py-4 flex items-center">
               <span
                 class="tooltip outline-none text-sm"
                 :data-tippy-content="alias.created_at | formatDate"
@@ -267,7 +290,7 @@
             </div>
           </td>
           <td class="border-grey-200 border-t">
-            <div class="p-4 flex items-center">
+            <div class="px-2 py-4 flex items-center">
               <span
                 class="tooltip cursor-pointer outline-none"
                 data-tippy-content="Click to copy"
@@ -278,17 +301,17 @@
                 <span class="font-semibold text-indigo-800">{{
                   alias.local_part | truncate(20)
                 }}</span>
-                <span class="block text-grey-400 text-sm">{{ alias.email | truncate(28) }}</span>
+                <span class="block text-grey-400 text-sm">{{ alias.email | truncate(35) }}</span>
               </span>
             </div>
           </td>
           <td class="border-grey-200 border-t">
-            <div class="px-4 flex items-center">
+            <div class="px-2 flex items-center">
               <span
                 v-if="alias.recipients.length && alias.id !== recipientsAliasToEdit.id"
                 class="tooltip outline-none"
                 :data-tippy-content="recipientsTooltip(alias.recipients)"
-                >{{ alias.recipients[0].email | truncate(20) }}
+                >{{ alias.recipients[0].email | truncate(25) }}
                 <span
                   v-if="alias.recipients.length > 1"
                   class="block text-center text-grey-500 text-sm"
@@ -313,8 +336,8 @@
               />
             </div>
           </td>
-          <td class="border-grey-200 border-t w-64">
-            <div class="p-4 text-sm">
+          <td class="border-grey-200 border-t">
+            <div class="px-2 py-4 text-sm">
               <div
                 v-if="aliasIdToEdit === alias.id"
                 class="w-full flex items-center justify-between"
@@ -343,10 +366,19 @@
                   @click.native="editAlias(alias)"
                 />
               </div>
-              <div v-else-if="alias.description" class="flex items-center justify-between w-full">
-                <span class="tooltip outline-none" :data-tippy-content="alias.description">{{
-                  alias.description | truncate(25)
-                }}</span>
+              <div v-else-if="alias.description" class="flex items-center justify-around">
+                <span
+                  class="tooltip outline-none"
+                  :data-tippy-content="alias.description"
+                  v-clipboard="() => alias.description"
+                  v-clipboard:success="clipboardSuccess"
+                  v-clipboard:error="clipboardError"
+                >
+                  <icon
+                    name="desc"
+                    class="inline-block w-6 h-6 text-grey-200 fill-current cursor-pointer"
+                  />
+                </span>
                 <icon
                   name="edit"
                   class="inline-block w-6 h-6 text-grey-200 fill-current cursor-pointer"
@@ -365,17 +397,22 @@
             </div>
           </td>
           <td class="border-grey-200 border-t">
-            <div class="p-4 flex items-center justify-center font-semibold text-indigo-800">
+            <div class="px-2 py-4 flex items-center justify-center font-semibold text-indigo-800">
               {{ alias.emails_forwarded }}
             </div>
           </td>
           <td class="border-grey-200 border-t">
-            <div class="p-4 flex items-center justify-center font-semibold text-indigo-800">
+            <div class="px-2 py-4 flex items-center justify-center font-semibold text-indigo-800">
               {{ alias.emails_blocked }}
             </div>
           </td>
           <td class="border-grey-200 border-t">
-            <div class="p-4 flex items-center justify-center">
+            <div class="px-2 py-4 flex items-center justify-center font-semibold text-indigo-800">
+              {{ alias.emails_replied }}
+            </div>
+          </td>
+          <td class="border-grey-200 border-t">
+            <div class="px-2 py-4 flex items-center justify-center">
               <Toggle
                 v-model="alias.active"
                 @on="activateAlias(alias)"
@@ -396,7 +433,7 @@
         <tr v-if="queriedAliases.length === 0">
           <td
             class="border-grey-200 border-t px-6 py-4 text-center h-24 text-lg text-grey-700"
-            colspan="8"
+            colspan="9"
           >
             No aliases found for that search!
           </td>

+ 5 - 1
resources/js/pages/Domains.vue

@@ -428,7 +428,11 @@ export default {
         })
         .catch(error => {
           this.addDomainLoading = false
-          this.error()
+          if (error.response.status == 422) {
+            this.error(error.response.data.errors.domain[0])
+          } else {
+            this.error()
+          }
         })
     },
     openDeleteModal(id) {

+ 5 - 2
resources/js/pages/Recipients.vue

@@ -473,7 +473,11 @@ export default {
         })
         .catch(error => {
           this.addRecipientLoading = false
-          this.error()
+          if (error.response.status == 422) {
+            this.error(error.response.data.errors.email[0])
+          } else {
+            this.error()
+          }
         })
     },
     resendVerification(id) {
@@ -487,7 +491,6 @@ export default {
         })
         .catch(error => {
           this.resendVerificationLoading = false
-          console.log(error.response)
           if (error.response.status === 429) {
             this.error('You can only resend the email once every 5 minutes')
           } else {

+ 24 - 0
tests/Feature/DomainsTest.php

@@ -114,6 +114,30 @@ class DomainsTest extends TestCase
             ->assertJsonValidationErrors('domain');
     }
 
+    /** @test */
+    public function new_domain_must_not_be_local()
+    {
+        $response = $this->json('POST', '/domains', [
+            'domain' => config('anonaddy.domain')
+        ]);
+
+        $response
+            ->assertStatus(422)
+            ->assertJsonValidationErrors('domain');
+    }
+
+    /** @test */
+    public function new_domain_must_not_be_local_subdomain()
+    {
+        $response = $this->json('POST', '/domains', [
+            'domain' => 'subdomain'.config('anonaddy.domain')
+        ]);
+
+        $response
+            ->assertStatus(422)
+            ->assertJsonValidationErrors('domain');
+    }
+
     /** @test */
     public function user_can_activate_domain()
     {