瀏覽代碼

Added expiry option to API tokens

Will Browning 2 年之前
父節點
當前提交
2b11ab428a

+ 10 - 2
app/Http/Controllers/Auth/PersonalAccessTokenController.php

@@ -10,12 +10,20 @@ class PersonalAccessTokenController extends Controller
 {
 {
     public function index()
     public function index()
     {
     {
-        return PersonalAccessTokenResource::collection(user()->tokens);
+        return PersonalAccessTokenResource::collection(user()->tokens()->select(['id', 'tokenable_id', 'name', 'created_at', 'last_used_at', 'expires_at'])->get());
     }
     }
 
 
     public function store(StorePersonalAccessTokenRequest $request)
     public function store(StorePersonalAccessTokenRequest $request)
     {
     {
-        $token = user()->createToken($request->name);
+        // day, week, month, year or null
+        if ($request->expiration) {
+            $method = "add".ucfirst($request->expiration);
+            $expiration = now()->{$method}();
+        } else {
+            $expiration = null;
+        }
+
+        $token = user()->createToken($request->name, ['*'], $expiration);
 
 
         return [
         return [
             'token' => new PersonalAccessTokenResource($token->accessToken),
             'token' => new PersonalAccessTokenResource($token->accessToken),

+ 6 - 0
app/Http/Requests/StorePersonalAccessTokenRequest.php

@@ -28,6 +28,12 @@ class StorePersonalAccessTokenRequest extends FormRequest
                 'required',
                 'required',
                 'string',
                 'string',
                 'max:50'
                 'max:50'
+            ],
+            'expiration' => [
+                'nullable',
+                'string',
+                'max:5',
+                'in:day,week,month,year'
             ]
             ]
         ];
         ];
     }
     }

+ 3 - 2
app/Http/Resources/PersonalAccessTokenResource.php

@@ -14,8 +14,9 @@ class PersonalAccessTokenResource extends JsonResource
             'name' => $this->name,
             'name' => $this->name,
             'abilities' => $this->abilities,
             'abilities' => $this->abilities,
             'last_used_at' => $this->last_used_at?->toDateTimeString(),
             'last_used_at' => $this->last_used_at?->toDateTimeString(),
-            'created_at' => $this->created_at->toDateTimeString(),
-            'updated_at' => $this->updated_at->toDateTimeString(),
+            'expires_at' => $this->expires_at?->toDateTimeString(),
+            'created_at' => $this->created_at?->toDateTimeString(),
+            'updated_at' => $this->updated_at?->toDateTimeString(),
         ];
         ];
     }
     }
 }
 }

+ 29 - 15
app/Models/EmailData.php

@@ -4,9 +4,12 @@ namespace App\Models;
 
 
 use Illuminate\Support\Str;
 use Illuminate\Support\Str;
 use PhpMimeMailParser\Parser;
 use PhpMimeMailParser\Parser;
+use Symfony\Component\Mime\MimeTypes;
 
 
 class EmailData
 class EmailData
 {
 {
+    private static $mimeTypes;
+
     public function __construct(Parser $parser, $size)
     public function __construct(Parser $parser, $size)
     {
     {
         $this->sender = $parser->getAddresses('from')[0]['address'];
         $this->sender = $parser->getAddresses('from')[0]['address'];
@@ -43,23 +46,34 @@ class EmailData
             $this->encryptedParts = $parser->getAttachments();
             $this->encryptedParts = $parser->getAttachments();
         } else {
         } else {
             foreach ($parser->getAttachments() as $attachment) {
             foreach ($parser->getAttachments() as $attachment) {
-                // Incorrect content type "text", set as text/plain
-                if ($attachment->getContentType() === 'text') {
+                // Fix incorrect Content Types e.g. 'png', 'pdf', '.pdf', 'text'
+                $contentType = $attachment->getContentType();
+
+                if ($contentType === 'text') {
                     $this->text = base64_encode(stream_get_contents($attachment->getStream()));
                     $this->text = base64_encode(stream_get_contents($attachment->getStream()));
-                } elseif ($attachment->getContentDisposition() === 'inline') {
-                    $this->inlineAttachments[] = [
-                        'stream' => base64_encode(stream_get_contents($attachment->getStream())),
-                        'file_name' => base64_encode($attachment->getFileName()),
-                        'mime' => base64_encode($attachment->getContentType()),
-                        'contentDisposition' => base64_encode($attachment->getContentDisposition()),
-                        'contentId' => base64_encode($attachment->getContentID()),
-                    ];
                 } else {
                 } else {
-                    $this->attachments[] = [
-                      'stream' => base64_encode(stream_get_contents($attachment->getStream())),
-                      'file_name' => base64_encode($attachment->getFileName()),
-                      'mime' => base64_encode($attachment->getContentType())
-                  ];
+                    if (! str_contains($contentType, '/')) {
+                        if (null === self::$mimeTypes) {
+                            self::$mimeTypes = new MimeTypes();
+                        }
+                        $contentType = self::$mimeTypes->getMimeTypes($contentType)[0] ?? 'application/octet-stream';
+                    }
+
+                    if ($attachment->getContentDisposition() === 'inline') {
+                        $this->inlineAttachments[] = [
+                            'stream' => base64_encode(stream_get_contents($attachment->getStream())),
+                            'file_name' => base64_encode($attachment->getFileName()),
+                            'mime' => base64_encode($contentType),
+                            'contentDisposition' => base64_encode($attachment->getContentDisposition()),
+                            'contentId' => base64_encode($attachment->getContentID()),
+                        ];
+                    } else {
+                        $this->attachments[] = [
+                          'stream' => base64_encode(stream_get_contents($attachment->getStream())),
+                          'file_name' => base64_encode($attachment->getFileName()),
+                          'mime' => base64_encode($contentType)
+                      ];
+                    }
                 }
                 }
             }
             }
         }
         }

+ 2 - 2
composer.json

@@ -13,9 +13,9 @@
         "doctrine/dbal": "^3.0",
         "doctrine/dbal": "^3.0",
         "guzzlehttp/guzzle": "^7.2",
         "guzzlehttp/guzzle": "^7.2",
         "laravel/framework": "^9.11",
         "laravel/framework": "^9.11",
-        "laravel/sanctum": "^2.15",
+        "laravel/sanctum": "^3.0",
         "laravel/tinker": "^2.7",
         "laravel/tinker": "^2.7",
-        "laravel/ui": "^3.0",
+        "laravel/ui": "^4.0",
         "maatwebsite/excel": "^3.1",
         "maatwebsite/excel": "^3.1",
         "mews/captcha": "^3.0.0",
         "mews/captcha": "^3.0.0",
         "php-mime-mail-parser/php-mime-mail-parser": "^7.0",
         "php-mime-mail-parser/php-mime-mail-parser": "^7.0",

文件差異過大導致無法顯示
+ 147 - 322
composer.lock


+ 31 - 0
database/migrations/2022_07_29_111323_add_expires_at_to_personal_access_tokens_table.php

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

+ 118 - 122
package-lock.json

@@ -211,14 +211,12 @@
             }
             }
         },
         },
         "node_modules/@babel/helper-define-polyfill-provider": {
         "node_modules/@babel/helper-define-polyfill-provider": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz",
-            "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==",
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz",
+            "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==",
             "dependencies": {
             "dependencies": {
-                "@babel/helper-compilation-targets": "^7.13.0",
-                "@babel/helper-module-imports": "^7.12.13",
-                "@babel/helper-plugin-utils": "^7.13.0",
-                "@babel/traverse": "^7.13.0",
+                "@babel/helper-compilation-targets": "^7.17.7",
+                "@babel/helper-plugin-utils": "^7.16.7",
                 "debug": "^4.1.1",
                 "debug": "^4.1.1",
                 "lodash.debounce": "^4.0.8",
                 "lodash.debounce": "^4.0.8",
                 "resolve": "^1.14.2",
                 "resolve": "^1.14.2",
@@ -1886,9 +1884,9 @@
             }
             }
         },
         },
         "node_modules/@types/express-serve-static-core": {
         "node_modules/@types/express-serve-static-core": {
-            "version": "4.17.29",
-            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz",
-            "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==",
+            "version": "4.17.30",
+            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz",
+            "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==",
             "dependencies": {
             "dependencies": {
                 "@types/node": "*",
                 "@types/node": "*",
                 "@types/qs": "*",
                 "@types/qs": "*",
@@ -1969,9 +1967,9 @@
             "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="
             "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="
         },
         },
         "node_modules/@types/node": {
         "node_modules/@types/node": {
-            "version": "18.0.6",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
-            "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw=="
+            "version": "18.6.2",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.2.tgz",
+            "integrity": "sha512-KcfkBq9H4PI6Vpu5B/KoPeuVDAbmi+2mDBqGPGUgoL7yXQtcWGu2vJWmmRkneWK3Rh0nIAX192Aa87AqKHYChQ=="
         },
         },
         "node_modules/@types/parse-json": {
         "node_modules/@types/parse-json": {
             "version": "4.0.0",
             "version": "4.0.0",
@@ -2032,9 +2030,9 @@
             }
             }
         },
         },
         "node_modules/@vue/compiler-sfc": {
         "node_modules/@vue/compiler-sfc": {
-            "version": "2.7.7",
-            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.7.tgz",
-            "integrity": "sha512-Ah8dIuo6ZVPHTq9+s4rBU/YpJu3vGSNyeOTCrPrVPQnkUfnT5Ig+IKBhePuQWFXguYb2TuEWrEfiiX9DZ3aJlA==",
+            "version": "2.7.8",
+            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.8.tgz",
+            "integrity": "sha512-2DK4YWKfgLnW9VDR9gnju1gcYRk3flKj8UNsms7fsRmFcg35slVTZEkqwBtX+wJBXaamFfn6NxSsZh3h12Ix/Q==",
             "dependencies": {
             "dependencies": {
                 "@babel/parser": "^7.18.4",
                 "@babel/parser": "^7.18.4",
                 "postcss": "^8.4.14",
                 "postcss": "^8.4.14",
@@ -2519,9 +2517,9 @@
             }
             }
         },
         },
         "node_modules/autoprefixer": {
         "node_modules/autoprefixer": {
-            "version": "10.4.7",
-            "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz",
-            "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==",
+            "version": "10.4.8",
+            "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz",
+            "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==",
             "funding": [
             "funding": [
                 {
                 {
                     "type": "opencollective",
                     "type": "opencollective",
@@ -2533,8 +2531,8 @@
                 }
                 }
             ],
             ],
             "dependencies": {
             "dependencies": {
-                "browserslist": "^4.20.3",
-                "caniuse-lite": "^1.0.30001335",
+                "browserslist": "^4.21.3",
+                "caniuse-lite": "^1.0.30001373",
                 "fraction.js": "^4.2.0",
                 "fraction.js": "^4.2.0",
                 "normalize-range": "^0.1.2",
                 "normalize-range": "^0.1.2",
                 "picocolors": "^1.0.0",
                 "picocolors": "^1.0.0",
@@ -2585,12 +2583,12 @@
             }
             }
         },
         },
         "node_modules/babel-plugin-polyfill-corejs2": {
         "node_modules/babel-plugin-polyfill-corejs2": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz",
-            "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==",
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz",
+            "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==",
             "dependencies": {
             "dependencies": {
-                "@babel/compat-data": "^7.13.11",
-                "@babel/helper-define-polyfill-provider": "^0.3.1",
+                "@babel/compat-data": "^7.17.7",
+                "@babel/helper-define-polyfill-provider": "^0.3.2",
                 "semver": "^6.1.1"
                 "semver": "^6.1.1"
             },
             },
             "peerDependencies": {
             "peerDependencies": {
@@ -2606,11 +2604,11 @@
             }
             }
         },
         },
         "node_modules/babel-plugin-polyfill-corejs3": {
         "node_modules/babel-plugin-polyfill-corejs3": {
-            "version": "0.5.2",
-            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz",
-            "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==",
+            "version": "0.5.3",
+            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz",
+            "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==",
             "dependencies": {
             "dependencies": {
-                "@babel/helper-define-polyfill-provider": "^0.3.1",
+                "@babel/helper-define-polyfill-provider": "^0.3.2",
                 "core-js-compat": "^3.21.0"
                 "core-js-compat": "^3.21.0"
             },
             },
             "peerDependencies": {
             "peerDependencies": {
@@ -2868,9 +2866,9 @@
             }
             }
         },
         },
         "node_modules/browserslist": {
         "node_modules/browserslist": {
-            "version": "4.21.2",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
-            "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
+            "version": "4.21.3",
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz",
+            "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==",
             "funding": [
             "funding": [
                 {
                 {
                     "type": "opencollective",
                     "type": "opencollective",
@@ -2882,10 +2880,10 @@
                 }
                 }
             ],
             ],
             "dependencies": {
             "dependencies": {
-                "caniuse-lite": "^1.0.30001366",
-                "electron-to-chromium": "^1.4.188",
+                "caniuse-lite": "^1.0.30001370",
+                "electron-to-chromium": "^1.4.202",
                 "node-releases": "^2.0.6",
                 "node-releases": "^2.0.6",
-                "update-browserslist-db": "^1.0.4"
+                "update-browserslist-db": "^1.0.5"
             },
             },
             "bin": {
             "bin": {
                 "browserslist": "cli.js"
                 "browserslist": "cli.js"
@@ -2976,9 +2974,9 @@
             }
             }
         },
         },
         "node_modules/caniuse-lite": {
         "node_modules/caniuse-lite": {
-            "version": "1.0.30001368",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz",
-            "integrity": "sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ==",
+            "version": "1.0.30001373",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz",
+            "integrity": "sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==",
             "funding": [
             "funding": [
                 {
                 {
                     "type": "opencollective",
                     "type": "opencollective",
@@ -3394,9 +3392,9 @@
             "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
             "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
         },
         },
         "node_modules/core-js-compat": {
         "node_modules/core-js-compat": {
-            "version": "3.23.5",
-            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.5.tgz",
-            "integrity": "sha512-fHYozIFIxd+91IIbXJgWd/igXIc8Mf9is0fusswjnGIWVG96y2cwyUdlCkGOw6rMLHKAxg7xtCIVaHsyOUnJIg==",
+            "version": "3.24.0",
+            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.0.tgz",
+            "integrity": "sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg==",
             "dependencies": {
             "dependencies": {
                 "browserslist": "^4.21.2",
                 "browserslist": "^4.21.2",
                 "semver": "7.0.0"
                 "semver": "7.0.0"
@@ -3721,9 +3719,9 @@
             "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
             "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
         },
         },
         "node_modules/date-fns": {
         "node_modules/date-fns": {
-            "version": "2.28.0",
-            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
-            "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==",
+            "version": "2.29.1",
+            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.1.tgz",
+            "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==",
             "engines": {
             "engines": {
                 "node": ">=0.11"
                 "node": ">=0.11"
             },
             },
@@ -4018,9 +4016,9 @@
             "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
             "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
         },
         },
         "node_modules/electron-to-chromium": {
         "node_modules/electron-to-chromium": {
-            "version": "1.4.196",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.196.tgz",
-            "integrity": "sha512-uxMa/Dt7PQsLBVXwH+t6JvpHJnrsYBaxWKi/J6HE+/nBtoHENhwBoNkgkm226/Kfxeg0z1eMQLBRPPKcDH8xWA=="
+            "version": "1.4.206",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz",
+            "integrity": "sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA=="
         },
         },
         "node_modules/elliptic": {
         "node_modules/elliptic": {
             "version": "6.5.4",
             "version": "6.5.4",
@@ -8649,11 +8647,11 @@
             "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
             "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
         },
         },
         "node_modules/vue": {
         "node_modules/vue": {
-            "version": "2.7.7",
-            "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.7.tgz",
-            "integrity": "sha512-osfkncsGCWqtch+YWYxbqTNQ9hl/UQ6TFRkdmK/VqAjuMpxzr5QotFsYpmJ1AB1ez2LJeIKXDmtMkXUotMOTsA==",
+            "version": "2.7.8",
+            "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.8.tgz",
+            "integrity": "sha512-ncwlZx5qOcn754bCu5/tS/IWPhXHopfit79cx+uIlLMyt3vCMGcXai5yCG5y+I6cDmEj4ukRYyZail9FTQh7lQ==",
             "dependencies": {
             "dependencies": {
-                "@vue/compiler-sfc": "2.7.7",
+                "@vue/compiler-sfc": "2.7.8",
                 "csstype": "^3.1.0"
                 "csstype": "^3.1.0"
             }
             }
         },
         },
@@ -8770,9 +8768,9 @@
             }
             }
         },
         },
         "node_modules/vue-template-compiler": {
         "node_modules/vue-template-compiler": {
-            "version": "2.7.7",
-            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.7.tgz",
-            "integrity": "sha512-vxOsjWhvDPyMW7QwXPecNmTNwKyXiF+j4KjBFjDxYPuY0xvqCT5o9WrapVItR/Nrh0XThfBaL19kXFSNYtbKmw==",
+            "version": "2.7.8",
+            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.8.tgz",
+            "integrity": "sha512-eQqdcUpJKJpBRPDdxCNsqUoT0edNvdt1jFjtVnVS/LPPmr0BU2jWzXlrf6BVMeODtdLewB3j8j3WjNiB+V+giw==",
             "dependencies": {
             "dependencies": {
                 "de-indent": "^1.0.2",
                 "de-indent": "^1.0.2",
                 "he": "^1.2.0"
                 "he": "^1.2.0"
@@ -8812,20 +8810,20 @@
             }
             }
         },
         },
         "node_modules/webpack": {
         "node_modules/webpack": {
-            "version": "5.73.0",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
-            "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
+            "version": "5.74.0",
+            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
+            "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
             "dependencies": {
             "dependencies": {
                 "@types/eslint-scope": "^3.7.3",
                 "@types/eslint-scope": "^3.7.3",
                 "@types/estree": "^0.0.51",
                 "@types/estree": "^0.0.51",
                 "@webassemblyjs/ast": "1.11.1",
                 "@webassemblyjs/ast": "1.11.1",
                 "@webassemblyjs/wasm-edit": "1.11.1",
                 "@webassemblyjs/wasm-edit": "1.11.1",
                 "@webassemblyjs/wasm-parser": "1.11.1",
                 "@webassemblyjs/wasm-parser": "1.11.1",
-                "acorn": "^8.4.1",
+                "acorn": "^8.7.1",
                 "acorn-import-assertions": "^1.7.6",
                 "acorn-import-assertions": "^1.7.6",
                 "browserslist": "^4.14.5",
                 "browserslist": "^4.14.5",
                 "chrome-trace-event": "^1.0.2",
                 "chrome-trace-event": "^1.0.2",
-                "enhanced-resolve": "^5.9.3",
+                "enhanced-resolve": "^5.10.0",
                 "es-module-lexer": "^0.9.0",
                 "es-module-lexer": "^0.9.0",
                 "eslint-scope": "5.1.1",
                 "eslint-scope": "5.1.1",
                 "events": "^3.2.0",
                 "events": "^3.2.0",
@@ -8838,7 +8836,7 @@
                 "schema-utils": "^3.1.0",
                 "schema-utils": "^3.1.0",
                 "tapable": "^2.1.1",
                 "tapable": "^2.1.1",
                 "terser-webpack-plugin": "^5.1.3",
                 "terser-webpack-plugin": "^5.1.3",
-                "watchpack": "^2.3.1",
+                "watchpack": "^2.4.0",
                 "webpack-sources": "^3.2.3"
                 "webpack-sources": "^3.2.3"
             },
             },
             "bin": {
             "bin": {
@@ -9445,14 +9443,12 @@
             }
             }
         },
         },
         "@babel/helper-define-polyfill-provider": {
         "@babel/helper-define-polyfill-provider": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz",
-            "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==",
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz",
+            "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==",
             "requires": {
             "requires": {
-                "@babel/helper-compilation-targets": "^7.13.0",
-                "@babel/helper-module-imports": "^7.12.13",
-                "@babel/helper-plugin-utils": "^7.13.0",
-                "@babel/traverse": "^7.13.0",
+                "@babel/helper-compilation-targets": "^7.17.7",
+                "@babel/helper-plugin-utils": "^7.16.7",
                 "debug": "^4.1.1",
                 "debug": "^4.1.1",
                 "lodash.debounce": "^4.0.8",
                 "lodash.debounce": "^4.0.8",
                 "resolve": "^1.14.2",
                 "resolve": "^1.14.2",
@@ -10628,9 +10624,9 @@
             }
             }
         },
         },
         "@types/express-serve-static-core": {
         "@types/express-serve-static-core": {
-            "version": "4.17.29",
-            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz",
-            "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==",
+            "version": "4.17.30",
+            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz",
+            "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==",
             "requires": {
             "requires": {
                 "@types/node": "*",
                 "@types/node": "*",
                 "@types/qs": "*",
                 "@types/qs": "*",
@@ -10711,9 +10707,9 @@
             "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="
             "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="
         },
         },
         "@types/node": {
         "@types/node": {
-            "version": "18.0.6",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
-            "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw=="
+            "version": "18.6.2",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.2.tgz",
+            "integrity": "sha512-KcfkBq9H4PI6Vpu5B/KoPeuVDAbmi+2mDBqGPGUgoL7yXQtcWGu2vJWmmRkneWK3Rh0nIAX192Aa87AqKHYChQ=="
         },
         },
         "@types/parse-json": {
         "@types/parse-json": {
             "version": "4.0.0",
             "version": "4.0.0",
@@ -10774,9 +10770,9 @@
             }
             }
         },
         },
         "@vue/compiler-sfc": {
         "@vue/compiler-sfc": {
-            "version": "2.7.7",
-            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.7.tgz",
-            "integrity": "sha512-Ah8dIuo6ZVPHTq9+s4rBU/YpJu3vGSNyeOTCrPrVPQnkUfnT5Ig+IKBhePuQWFXguYb2TuEWrEfiiX9DZ3aJlA==",
+            "version": "2.7.8",
+            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.8.tgz",
+            "integrity": "sha512-2DK4YWKfgLnW9VDR9gnju1gcYRk3flKj8UNsms7fsRmFcg35slVTZEkqwBtX+wJBXaamFfn6NxSsZh3h12Ix/Q==",
             "requires": {
             "requires": {
                 "@babel/parser": "^7.18.4",
                 "@babel/parser": "^7.18.4",
                 "postcss": "^8.4.14",
                 "postcss": "^8.4.14",
@@ -11181,12 +11177,12 @@
             "dev": true
             "dev": true
         },
         },
         "autoprefixer": {
         "autoprefixer": {
-            "version": "10.4.7",
-            "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz",
-            "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==",
+            "version": "10.4.8",
+            "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz",
+            "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==",
             "requires": {
             "requires": {
-                "browserslist": "^4.20.3",
-                "caniuse-lite": "^1.0.30001335",
+                "browserslist": "^4.21.3",
+                "caniuse-lite": "^1.0.30001373",
                 "fraction.js": "^4.2.0",
                 "fraction.js": "^4.2.0",
                 "normalize-range": "^0.1.2",
                 "normalize-range": "^0.1.2",
                 "picocolors": "^1.0.0",
                 "picocolors": "^1.0.0",
@@ -11221,12 +11217,12 @@
             }
             }
         },
         },
         "babel-plugin-polyfill-corejs2": {
         "babel-plugin-polyfill-corejs2": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz",
-            "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==",
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz",
+            "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==",
             "requires": {
             "requires": {
-                "@babel/compat-data": "^7.13.11",
-                "@babel/helper-define-polyfill-provider": "^0.3.1",
+                "@babel/compat-data": "^7.17.7",
+                "@babel/helper-define-polyfill-provider": "^0.3.2",
                 "semver": "^6.1.1"
                 "semver": "^6.1.1"
             },
             },
             "dependencies": {
             "dependencies": {
@@ -11238,11 +11234,11 @@
             }
             }
         },
         },
         "babel-plugin-polyfill-corejs3": {
         "babel-plugin-polyfill-corejs3": {
-            "version": "0.5.2",
-            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz",
-            "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==",
+            "version": "0.5.3",
+            "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz",
+            "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==",
             "requires": {
             "requires": {
-                "@babel/helper-define-polyfill-provider": "^0.3.1",
+                "@babel/helper-define-polyfill-provider": "^0.3.2",
                 "core-js-compat": "^3.21.0"
                 "core-js-compat": "^3.21.0"
             }
             }
         },
         },
@@ -11451,14 +11447,14 @@
             }
             }
         },
         },
         "browserslist": {
         "browserslist": {
-            "version": "4.21.2",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
-            "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
+            "version": "4.21.3",
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz",
+            "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==",
             "requires": {
             "requires": {
-                "caniuse-lite": "^1.0.30001366",
-                "electron-to-chromium": "^1.4.188",
+                "caniuse-lite": "^1.0.30001370",
+                "electron-to-chromium": "^1.4.202",
                 "node-releases": "^2.0.6",
                 "node-releases": "^2.0.6",
-                "update-browserslist-db": "^1.0.4"
+                "update-browserslist-db": "^1.0.5"
             }
             }
         },
         },
         "buffer": {
         "buffer": {
@@ -11531,9 +11527,9 @@
             }
             }
         },
         },
         "caniuse-lite": {
         "caniuse-lite": {
-            "version": "1.0.30001368",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz",
-            "integrity": "sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ=="
+            "version": "1.0.30001373",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz",
+            "integrity": "sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ=="
         },
         },
         "chalk": {
         "chalk": {
             "version": "4.1.2",
             "version": "4.1.2",
@@ -11836,9 +11832,9 @@
             "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
             "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
         },
         },
         "core-js-compat": {
         "core-js-compat": {
-            "version": "3.23.5",
-            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.5.tgz",
-            "integrity": "sha512-fHYozIFIxd+91IIbXJgWd/igXIc8Mf9is0fusswjnGIWVG96y2cwyUdlCkGOw6rMLHKAxg7xtCIVaHsyOUnJIg==",
+            "version": "3.24.0",
+            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.0.tgz",
+            "integrity": "sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg==",
             "requires": {
             "requires": {
                 "browserslist": "^4.21.2",
                 "browserslist": "^4.21.2",
                 "semver": "7.0.0"
                 "semver": "7.0.0"
@@ -12078,9 +12074,9 @@
             "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
             "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
         },
         },
         "date-fns": {
         "date-fns": {
-            "version": "2.28.0",
-            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
-            "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw=="
+            "version": "2.29.1",
+            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.1.tgz",
+            "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw=="
         },
         },
         "dayjs": {
         "dayjs": {
             "version": "1.11.4",
             "version": "1.11.4",
@@ -12298,9 +12294,9 @@
             "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
             "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
         },
         },
         "electron-to-chromium": {
         "electron-to-chromium": {
-            "version": "1.4.196",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.196.tgz",
-            "integrity": "sha512-uxMa/Dt7PQsLBVXwH+t6JvpHJnrsYBaxWKi/J6HE+/nBtoHENhwBoNkgkm226/Kfxeg0z1eMQLBRPPKcDH8xWA=="
+            "version": "1.4.206",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz",
+            "integrity": "sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA=="
         },
         },
         "elliptic": {
         "elliptic": {
             "version": "6.5.4",
             "version": "6.5.4",
@@ -15601,11 +15597,11 @@
             "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
             "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
         },
         },
         "vue": {
         "vue": {
-            "version": "2.7.7",
-            "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.7.tgz",
-            "integrity": "sha512-osfkncsGCWqtch+YWYxbqTNQ9hl/UQ6TFRkdmK/VqAjuMpxzr5QotFsYpmJ1AB1ez2LJeIKXDmtMkXUotMOTsA==",
+            "version": "2.7.8",
+            "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.8.tgz",
+            "integrity": "sha512-ncwlZx5qOcn754bCu5/tS/IWPhXHopfit79cx+uIlLMyt3vCMGcXai5yCG5y+I6cDmEj4ukRYyZail9FTQh7lQ==",
             "requires": {
             "requires": {
-                "@vue/compiler-sfc": "2.7.7",
+                "@vue/compiler-sfc": "2.7.8",
                 "csstype": "^3.1.0"
                 "csstype": "^3.1.0"
             }
             }
         },
         },
@@ -15696,9 +15692,9 @@
             }
             }
         },
         },
         "vue-template-compiler": {
         "vue-template-compiler": {
-            "version": "2.7.7",
-            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.7.tgz",
-            "integrity": "sha512-vxOsjWhvDPyMW7QwXPecNmTNwKyXiF+j4KjBFjDxYPuY0xvqCT5o9WrapVItR/Nrh0XThfBaL19kXFSNYtbKmw==",
+            "version": "2.7.8",
+            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.8.tgz",
+            "integrity": "sha512-eQqdcUpJKJpBRPDdxCNsqUoT0edNvdt1jFjtVnVS/LPPmr0BU2jWzXlrf6BVMeODtdLewB3j8j3WjNiB+V+giw==",
             "requires": {
             "requires": {
                 "de-indent": "^1.0.2",
                 "de-indent": "^1.0.2",
                 "he": "^1.2.0"
                 "he": "^1.2.0"
@@ -15735,20 +15731,20 @@
             }
             }
         },
         },
         "webpack": {
         "webpack": {
-            "version": "5.73.0",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
-            "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
+            "version": "5.74.0",
+            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
+            "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
             "requires": {
             "requires": {
                 "@types/eslint-scope": "^3.7.3",
                 "@types/eslint-scope": "^3.7.3",
                 "@types/estree": "^0.0.51",
                 "@types/estree": "^0.0.51",
                 "@webassemblyjs/ast": "1.11.1",
                 "@webassemblyjs/ast": "1.11.1",
                 "@webassemblyjs/wasm-edit": "1.11.1",
                 "@webassemblyjs/wasm-edit": "1.11.1",
                 "@webassemblyjs/wasm-parser": "1.11.1",
                 "@webassemblyjs/wasm-parser": "1.11.1",
-                "acorn": "^8.4.1",
+                "acorn": "^8.7.1",
                 "acorn-import-assertions": "^1.7.6",
                 "acorn-import-assertions": "^1.7.6",
                 "browserslist": "^4.14.5",
                 "browserslist": "^4.14.5",
                 "chrome-trace-event": "^1.0.2",
                 "chrome-trace-event": "^1.0.2",
-                "enhanced-resolve": "^5.9.3",
+                "enhanced-resolve": "^5.10.0",
                 "es-module-lexer": "^0.9.0",
                 "es-module-lexer": "^0.9.0",
                 "eslint-scope": "5.1.1",
                 "eslint-scope": "5.1.1",
                 "events": "^3.2.0",
                 "events": "^3.2.0",
@@ -15761,7 +15757,7 @@
                 "schema-utils": "^3.1.0",
                 "schema-utils": "^3.1.0",
                 "tapable": "^2.1.1",
                 "tapable": "^2.1.1",
                 "terser-webpack-plugin": "^5.1.3",
                 "terser-webpack-plugin": "^5.1.3",
-                "watchpack": "^2.3.1",
+                "watchpack": "^2.4.0",
                 "webpack-sources": "^3.2.3"
                 "webpack-sources": "^3.2.3"
             },
             },
             "dependencies": {
             "dependencies": {

+ 0 - 1
resources/js/components/WebauthnKeys.vue

@@ -100,7 +100,6 @@ export default {
   mounted() {
   mounted() {
     this.getWebauthnKeys()
     this.getWebauthnKeys()
   },
   },
-
   methods: {
   methods: {
     getWebauthnKeys() {
     getWebauthnKeys() {
       axios.get('/webauthn/keys').then(response => {
       axios.get('/webauthn/keys').then(response => {

+ 62 - 12
resources/js/components/sanctum/PersonalAccessTokens.vue

@@ -32,7 +32,13 @@
       to create new aliases. They can also be used with the mobile apps. Simply paste a token you've
       to create new aliases. They can also be used with the mobile apps. Simply paste a token you've
       created into the browser extension or mobile apps to get started. Your API access tokens are
       created into the browser extension or mobile apps to get started. Your API access tokens are
       secret and should be treated like your password. For more information please see the
       secret and should be treated like your password. For more information please see the
-      <a href="/docs" class="text-indigo-700">API documentation</a>.
+      <a
+        href="https://app.anonaddy.com/docs"
+        target="_blank"
+        rel="nofollow noopener noreferrer"
+        class="text-indigo-700"
+        >API documentation</a
+      >.
     </p>
     </p>
 
 
     <button
     <button
@@ -62,6 +68,7 @@
             <div class="table-cell p-1 md:p-4 font-semibold">Name</div>
             <div class="table-cell p-1 md:p-4 font-semibold">Name</div>
             <div class="table-cell p-1 md:p-4 font-semibold">Created</div>
             <div class="table-cell p-1 md:p-4 font-semibold">Created</div>
             <div class="table-cell p-1 md:p-4 font-semibold">Last Used</div>
             <div class="table-cell p-1 md:p-4 font-semibold">Last Used</div>
+            <div class="table-cell p-1 md:p-4 font-semibold">Expires At</div>
             <div class="table-cell p-1 md:p-4"></div>
             <div class="table-cell p-1 md:p-4"></div>
           </div>
           </div>
           <div
           <div
@@ -75,6 +82,10 @@
               {{ token.last_used_at | timeAgo }}
               {{ token.last_used_at | timeAgo }}
             </div>
             </div>
             <div v-else class="table-cell p-1 md:p-4">Not used yet</div>
             <div v-else class="table-cell p-1 md:p-4">Not used yet</div>
+            <div v-if="token.expires_at" class="table-cell p-1 md:p-4">
+              {{ token.expires_at | formatDate }}
+            </div>
+            <div v-else class="table-cell p-1 md:p-4">Does not expire</div>
             <div class="table-cell p-1 md:p-4 text-right">
             <div class="table-cell p-1 md:p-4 text-right">
               <a
               <a
                 class="text-red-500 font-bold cursor-pointer focus:outline-none"
                 class="text-red-500 font-bold cursor-pointer focus:outline-none"
@@ -97,12 +108,13 @@
         </h2>
         </h2>
         <p class="mt-4 text-grey-700">
         <p class="mt-4 text-grey-700">
           What's this token going to be used for? Give it a short name so that you remember later.
           What's this token going to be used for? Give it a short name so that you remember later.
+          You can also select an expiry date for the token if you wish.
         </p>
         </p>
         <div class="mt-6">
         <div class="mt-6">
-          <div v-if="form.errors.length > 0" class="mb-3 text-red-500">
+          <div v-if="isObject(form.errors)" class="mb-3 text-red-500">
             <ul>
             <ul>
-              <li v-for="error in form.errors" :key="error">
-                {{ error }}
+              <li v-for="error in form.errors" :key="error[0]">
+                {{ error[0] }}
               </li>
               </li>
             </ul>
             </ul>
           </div>
           </div>
@@ -111,11 +123,40 @@
             v-model="form.name"
             v-model="form.name"
             type="text"
             type="text"
             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="form.errors.length > 0 ? 'border-red-500' : ''"
+            class="w-full appearance-none bg-grey-100 border border-transparent text-grey-700 focus:outline-none rounded p-3 mb-4"
+            :class="form.errors.name ? 'border-red-500' : ''"
             placeholder="e.g. Firefox extension"
             placeholder="e.g. Firefox extension"
             autofocus
             autofocus
           />
           />
+          <label for="create-token-name" class="block text-grey-700 text-sm my-2">
+            Expiration:
+          </label>
+          <div class="block relative mb-6">
+            <select
+              v-model="form.expiration"
+              class="block appearance-none w-full text-grey-700 bg-grey-100 p-3 pr-8 rounded shadow focus:ring"
+              :class="form.errors.expiration ? 'border border-red-500' : ''"
+            >
+              <option value="day">1 day</option>
+              <option value="week">1 week</option>
+              <option value="month">1 month</option>
+              <option value="year">1 year</option>
+              <option :value="null">No expiration</option>
+            </select>
+            <div
+              class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
+            >
+              <svg
+                class="fill-current h-4 w-4"
+                xmlns="http://www.w3.org/2000/svg"
+                viewBox="0 0 20 20"
+              >
+                <path
+                  d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
+                />
+              </svg>
+            </div>
+          </div>
           <button
           <button
             @click="store"
             @click="store"
             class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none"
             class="bg-cyan-400 hover:bg-cyan-300 text-cyan-900 font-bold py-3 px-4 rounded focus:outline-none"
@@ -220,7 +261,8 @@ export default {
       tokenToRevoke: null,
       tokenToRevoke: null,
       form: {
       form: {
         name: '',
         name: '',
-        errors: [],
+        expiration: null,
+        errors: {},
       },
       },
       loading: false,
       loading: false,
       revokeTokenLoading: false,
       revokeTokenLoading: false,
@@ -229,7 +271,11 @@ export default {
   mounted() {
   mounted() {
     this.getTokens()
     this.getTokens()
   },
   },
-
+  watch: {
+    'form.expiration'() {
+      delete this.form.errors.expiration
+    },
+  },
   methods: {
   methods: {
     getTokens() {
     getTokens() {
       axios.get('/settings/personal-access-tokens').then(response => {
       axios.get('/settings/personal-access-tokens').then(response => {
@@ -239,22 +285,23 @@ export default {
     store() {
     store() {
       this.loading = true
       this.loading = true
       this.accessToken = null
       this.accessToken = null
-      this.form.errors = []
+      this.form.errors = {}
 
 
       axios
       axios
         .post('/settings/personal-access-tokens', this.form)
         .post('/settings/personal-access-tokens', this.form)
         .then(response => {
         .then(response => {
           this.loading = false
           this.loading = false
           this.form.name = ''
           this.form.name = ''
-          this.form.errors = []
+          this.form.expiration = null
+          this.form.errors = {}
 
 
           this.tokens.push(response.data.token)
           this.tokens.push(response.data.token)
           this.accessToken = response.data.accessToken
           this.accessToken = response.data.accessToken
         })
         })
         .catch(error => {
         .catch(error => {
           this.loading = false
           this.loading = false
-          if (typeof error.response.data === 'object') {
-            this.form.errors = _.flatten(_.toArray(error.response.data.errors))
+          if (this.isObject(error.response.data)) {
+            this.form.errors = error.response.data.errors
           } else {
           } else {
             this.error()
             this.error()
           }
           }
@@ -296,6 +343,9 @@ export default {
       textArea.focus()
       textArea.focus()
       textArea.select()
       textArea.select()
     },
     },
+    isObject(val) {
+      return _.isObject(val) && !_.isEmpty(val)
+    },
     clipboardSuccess() {
     clipboardSuccess() {
       this.success('Copied to clipboard')
       this.success('Copied to clipboard')
     },
     },

+ 54 - 0
tests/emails/email_with_invalid_content_type_attachment.eml

@@ -0,0 +1,54 @@
+Date: Wed, 20 Feb 2019 15:00:00 +0100 (CET)
+From: Will <will@anonaddy.com>
+To: attachment@johndoe.anonaddy.com
+Subject: With Attachment
+Content-Type: multipart/mixed; boundary="----=_Part_10031_1199410393.1550677940425"
+
+This is a multi-part message in MIME format.
+------=_Part_10031_1199410393.1550677940425
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 7bit
+
+This is the attachment
+
+------=_Part_10031_1199410393.1550677940425
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: 7bit
+
+<b>This is the attachment<b>
+
+------=_Part_10031_1199410393.1550677940425
+Content-Type: png; name="favicon-32x32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="favicon-32x32.png"
+
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBj
+SFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACl1BMVEUZIWwYH2sY
+HmoXH2sWHmoZIGwbKHAjPX0nSoUoTIYoTYYiO3wZImwdJW8zOnxDSodGTYk6QYEbI20ZIGsy
+aZdNtsZa3Nxd5+Ne6ORe6uVSx9AhN3kYIGscJG5WXJOrr8rY2+fm6fDp6/Lp7PPW2eZCSYcX
+H2oeMHVEnrdg7uhj9+1j+O1i9exj9uxc5OEmRYIXHmogKHGHjLP5+/z4+vz5+/35+vz2+Pvw
+8vdXXZQaJW5CmLRb4N9Rws1Qwc1UzNNh8elc4+ElRYF8gazx9Pj3+Pv3+fvl5/DIzN3BxNjA
+xNjHytzu8PXv8fYsWI1e6eRh8+pHpbwlRYIdLHMcKXEbJ3AjQ4A8QYLc3uq2utFIT4smLnQi
+KnIhKXE0O33T1uT4+fxDm7VPvsslQ4Bc4uAiQoB6fqrS1eM3Pn8VHWkYIGwqMnfQ0+IcKnFQ
+wcxi9Os3eKEYHWolQ4EmRoKkp8WSlrorMnceL3VUzdNh8OkvYZIqSYSztc54fakVHWpVztRg
+8OgvYJK0ts92fKhUzdQvX5EmR4Nd5eKzts8uNXnU1+X3+fxSxs9h8uoxaJYyaJdc4+AoR4Oq
+rcmAha5KUIvo6vEaI21Iqr4ZIm1OusiKjbS2udEfJ3CgpMP19/o0cJte6eU8iKokQoAhOXor
+VItKr8Fi8+pQVY/q7fPw8/eXm71AR4UxOXyboMD2+PofMXZQv8tZ2dslRIEfJW+mqsfj5u/Z
+2+hWXZNk+O5Hprxd5uNk+u8WHWo2PX+6vtT6+/3x8/eprcna3en7/f5SWZEjPn0/kK9Y19pW
+0tZLssMwZZUcK3I4eqJGoro5fqQtNXmAha+4vNNudKMkK3NnbJ6fo8IqMXcdLXMfM3cbJm8j
+K3MvN3r///9u4MYGAAAAAWJLR0TcB2CDtwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1F
+B+MGFBI0GyLSu+EAAAHnSURBVDjLY2AYUYCRCQwYITxmFjBghsmyMrGxc3ACARc3D5DLwsvH
+LwACgkIQeWE2EVExcQkgkJSSFpaRlZNXUFQCAmUVVTWwvLqGppaWtra2lo6unr6BoZGSsYmx
+qamZubmFJQtIgZW1jo6OjZStnb2DjqMTk7OLq5u7h6eXt4+5rxxIAZOfv05AYFBwSCinnq5j
+WHiEm3lkVHRMbFx8AkQBQ6KWVpI6EysrU3KKrmNqmrlbekamTBZzdo4JWAFrbp5WfkEhyKdF
+QAXFJcYmpSDvyZRBFQiXV2hVVsEUpFTXGLvV1qEoUK/XamhkgitoMjZvxqUgGYeCFq2GVrCC
+tnaggg6Ygs6ubogjg3u0evtAbijsr9SZMHGScfdkkAKWKVOh4TBturauxgxGxkL1mbo6jmGz
+TExmx7Aws8yZ6zYPGlDze3UWLFy0eMnSZfk6jqnLV5ivXLV6jcBai3VQBcLrN2jrLLPZ6K+l
+C1SwafMWk3Xztm5TdHObN893OzgkGTnttYCRtcN/567dKYv27N1nbmJiuv/AwUOHLY6wQBLL
+0WNSx0+cPHX6zNlzIQzM5y94qcRfvHT5ylWjazLQ5MR6XfrGTSZWRmFQipJhvnW701BNhhkI
+4SmOVVgYKf1lycgwDCkAAG51lZ/4ve9SAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTA2LTIw
+VDE4OjUyOjI3KzAyOjAwAtc5xwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wNi0yMFQxODo1
+MjoyNyswMjowMHOKgXsAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYo
+KMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcR
+dPJCNZUAAAAASUVORK5CYII=
+------=_Part_10031_1199410393.1550677940425--

部分文件因文件數量過多而無法顯示