diff --git a/package-lock.json b/package-lock.json index d5247f84..0a837605 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@fortawesome/vue-fontawesome": "^3.0.3", "@kyvg/vue3-notification": "^3.0.2", "@vitejs/plugin-vue": "^4.3.4", + "@vueuse/core": "^10.4.1", "axios": "^1.1.2", "bulma": "^0.9.4", "bulma-checkradio": "^2.1.3", @@ -19,11 +20,21 @@ "laravel-vue-i18n": "^2.7.1", "pinia": "^2.1.6", "sass": "^1.67.0", + "unplugin-auto-import": "^0.16.6", "vite": "^4.4.9", "vue": "^3.3.4", "vue-router": "^4.2.4" } }, + "node_modules/@antfu/utils": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.6.tgz", + "integrity": "sha512-pvFiLP2BeOKA/ZOS6jxx4XhKzdVLHDhGlFEaZ2flWWYf2xOqVniqpk38I04DFRyz+L0ASggl7SkItTc+ZLju4w==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@babel/parser": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz", @@ -532,6 +543,69 @@ "vue": "^3.0.0" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz", + "integrity": "sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@types/estree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", + "dev": true + }, "node_modules/@types/node": { "version": "20.4.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", @@ -540,6 +614,12 @@ "optional": true, "peer": true }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz", + "integrity": "sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==", + "dev": true + }, "node_modules/@vitejs/plugin-vue": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz", @@ -671,13 +751,99 @@ "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==", "dev": true }, + "node_modules/@vueuse/core": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.4.1.tgz", + "integrity": "sha512-DkHIfMIoSIBjMgRRvdIvxsyboRZQmImofLyOHADqiVbQVilP8VVHDhBX2ZqoItOgu7dWa8oXiNnScOdPLhdEXg==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.17", + "@vueuse/metadata": "10.4.1", + "@vueuse/shared": "10.4.1", + "vue-demi": ">=0.14.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.4.1.tgz", + "integrity": "sha512-2Sc8X+iVzeuMGHr6O2j4gv/zxvQGGOYETYXEc41h0iZXIRnRbJZGmY/QP8dvzqUelf8vg0p/yEA5VpCEu+WpZg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.4.1.tgz", + "integrity": "sha512-vz5hbAM4qA0lDKmcr2y3pPdU+2EVw/yzfRsBdu+6+USGa4PxqSQRYIUC9/NcT06y+ZgaTsyURw2I9qOFaaXHAg==", + "dev": true, + "dependencies": { + "vue-demi": ">=0.14.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, - "optional": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -715,6 +881,12 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -724,6 +896,15 @@ "node": ">=8" } }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -856,12 +1037,49 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -982,6 +1200,12 @@ "node": ">=0.12.0" } }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "node_modules/laravel-vite-plugin": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.0.tgz", @@ -1008,6 +1232,18 @@ "vue": "^3.2.45" } }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/magic-string": { "version": "0.30.3", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", @@ -1020,6 +1256,28 @@ "node": ">=12" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1041,6 +1299,33 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mlly": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", + "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "ufo": "^1.3.0" + } + }, "node_modules/nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -1068,6 +1353,12 @@ "node": ">=0.10.0" } }, + "node_modules/pathe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", + "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", + "dev": true + }, "node_modules/php-parser": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/php-parser/-/php-parser-3.1.3.tgz", @@ -1144,6 +1435,17 @@ } } }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "node_modules/postcss": { "version": "8.4.30", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", @@ -1178,6 +1480,26 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1190,6 +1512,16 @@ "node": ">=8.10.0" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "3.29.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.2.tgz", @@ -1206,6 +1538,29 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/sass": { "version": "1.67.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.67.0.tgz", @@ -1223,6 +1578,12 @@ "node": ">=14.0.0" } }, + "node_modules/scule": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.0.0.tgz", + "integrity": "sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==", + "dev": true + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1255,6 +1616,18 @@ "source-map": "^0.6.0" } }, + "node_modules/strip-literal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", + "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/terser": { "version": "5.18.2", "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.2.tgz", @@ -1295,6 +1668,77 @@ "node": ">=8.0" } }, + "node_modules/ufo": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.0.tgz", + "integrity": "sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==", + "dev": true + }, + "node_modules/unimport": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-3.3.0.tgz", + "integrity": "sha512-3jhq3ZG5hFZzrWGDCpx83kjPzefP/EeuKkIO1T0MA4Zwj+dO/Og1mFvZ4aZ5WSDm0FVbbdVIRH1zKBG7c4wOpg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.4", + "escape-string-regexp": "^5.0.0", + "fast-glob": "^3.3.1", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.3", + "mlly": "^1.4.1", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "scule": "^1.0.0", + "strip-literal": "^1.3.0", + "unplugin": "^1.4.0" + } + }, + "node_modules/unplugin": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.5.0.tgz", + "integrity": "sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0", + "chokidar": "^3.5.3", + "webpack-sources": "^3.2.3", + "webpack-virtual-modules": "^0.5.0" + } + }, + "node_modules/unplugin-auto-import": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-0.16.6.tgz", + "integrity": "sha512-M+YIITkx3C/Hg38hp8HmswP5mShUUyJOzpifv7RTlAbeFlO2Tyw0pwrogSSxnipHDPTtI8VHFBpkYkNKzYSuyA==", + "dev": true, + "dependencies": { + "@antfu/utils": "^0.7.5", + "@rollup/pluginutils": "^5.0.2", + "fast-glob": "^3.3.0", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.1", + "minimatch": "^9.0.2", + "unimport": "^3.0.14", + "unplugin": "^1.3.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@nuxt/kit": "^3.2.2", + "@vueuse/core": "*" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@vueuse/core": { + "optional": true + } + } + }, "node_modules/vite": { "version": "4.4.9", "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", @@ -1390,6 +1834,21 @@ "peerDependencies": { "vue": "^3.2.0" } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz", + "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==", + "dev": true } } } diff --git a/package.json b/package.json index 9dc3b963..614a646a 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@fortawesome/vue-fontawesome": "^3.0.3", "@kyvg/vue3-notification": "^3.0.2", "@vitejs/plugin-vue": "^4.3.4", + "@vueuse/core": "^10.4.1", "axios": "^1.1.2", "bulma": "^0.9.4", "bulma-checkradio": "^2.1.3", @@ -20,6 +21,7 @@ "laravel-vue-i18n": "^2.7.1", "pinia": "^2.1.6", "sass": "^1.67.0", + "unplugin-auto-import": "^0.16.6", "vite": "^4.4.9", "vue": "^3.3.4", "vue-router": "^4.2.4" diff --git a/resources/js_vue3/app.js b/resources/js_vue3/app.js index bea7cd0e..e90d4528 100644 --- a/resources/js_vue3/app.js +++ b/resources/js_vue3/app.js @@ -1,11 +1,11 @@ import '/resources/js_vue3/assets/app.scss'; -import { createApp } from 'vue' -import { i18nVue } from 'laravel-vue-i18n' -import { createPinia } from 'pinia' +// import { createApp } from 'vue' +// import { i18nVue } from 'laravel-vue-i18n' +// import { createPinia } from 'pinia' +import Notifications from '@kyvg/vue3-notification' import App from './App.vue' import router from './router' -import Notifications from '@kyvg/vue3-notification' import FontAwesomeIcon from './icons' const app = createApp(App) diff --git a/resources/js_vue3/router.js b/resources/js_vue3/router.js deleted file mode 100644 index a30924a5..00000000 --- a/resources/js_vue3/router.js +++ /dev/null @@ -1,28 +0,0 @@ -import { createRouter, createWebHistory } from 'vue-router' - -import Accounts from './views/Accounts.vue' -import SettingsOptions from './views/settings/Options.vue' -// import SettingsAccount from './views/settings/Account' -// import SettingsOAuth from './views/settings/OAuth' -// import SettingsWebAuthn from './views/settings/WebAuthn' -// import EditCredential from './views/settings/Credentials/Edit' -// import GeneratePAT from './views/settings/PATokens/Create' - -const router = createRouter({ - history: createWebHistory('/'), - routes: [ - { path: '/accounts', name: 'accounts', component: Accounts, meta: { requiresAuth: true }, alias: '/', props: true }, - - { path: '/settings/options', name: 'settings.options', component: SettingsOptions, meta: { requiresAuth: true, showAbout: true } }, - // { path: '/settings/account', name: 'settings.account', component: SettingsAccount, meta: { requiresAuth: true, showAbout: true } }, - // { path: '/settings/oauth', name: 'settings.oauth.tokens', component: SettingsOAuth, meta: { requiresAuth: true, showAbout: true } }, - // { path: '/settings/oauth/pat/create', name: 'settings.oauth.generatePAT', component: GeneratePAT, meta: { requiresAuth: true, showAbout: true } }, - // { path: '/settings/webauthn/:credentialId/edit', name: 'settings.webauthn.editCredential', component: EditCredential, meta: { requiresAuth: true, showAbout: true }, props: true }, - // { path: '/settings/webauthn', name: 'settings.webauthn.devices', component: SettingsWebAuthn, meta: { requiresAuth: true, showAbout: true } }, - - // Lazy loaded view - { path: '/about', name: 'about', component: () => import('./views/About.vue') } - ] -}) - -export default router diff --git a/resources/js_vue3/router/index.js b/resources/js_vue3/router/index.js new file mode 100644 index 00000000..9267cbd4 --- /dev/null +++ b/resources/js_vue3/router/index.js @@ -0,0 +1,90 @@ +import { createRouter, createWebHistory } from 'vue-router' +import middlewarePipeline from "@/router/middlewarePipeline"; +import { useUserStore } from '@/stores/user' + +// import Start from './views/Start.vue' +// import Capture from './views/Capture.vue' +import Accounts from '../views/Accounts.vue' +// import CreateAccount from './views/twofaccounts/Create.vue' +// import EditAccount from './views/twofaccounts/Edit.vue' +// import ImportAccount from './views/twofaccounts/Import.vue' +// import QRcodeAccount from './views/twofaccounts/QRcode.vue' +// import Groups from './views/Groups.vue' +// import CreateGroup from './views/groups/Create.vue' +// import EditGroup from './views/groups/Edit.vue' +import Login from '../views/auth/Login.vue' +import Register from '../views/auth/Register.vue' +// import Autolock from './views/auth/Autolock.vue' +import PasswordRequest from '../views/auth/password/Request.vue' +// import PasswordReset from './views/auth/password/Reset.vue' +import WebauthnLost from '../views/auth/webauthn/Lost.vue' +// import WebauthnRecover from './views/auth/webauthn/Recover.vue' +import SettingsOptions from '../views/settings/Options.vue' +// import SettingsAccount from './views/settings/Account.vue' +// import SettingsOAuth from './views/settings/OAuth.vue' +// import SettingsWebAuthn from './views/settings/WebAuthn.vue' +// import EditCredential from './views/settings/Credentials/Edit.vue' +// import GeneratePAT from './views/settings/PATokens/Create.vue' +// import Errors from './views/Error.vue' +import About from '../views/About.vue' + +import authGuard from './middlewares/authGuard' + +const router = createRouter({ + history: createWebHistory('/'), + routes: [ + // { path: '/start', name: 'start', component: Start, meta: { requiresAuth: true }, props: true }, + // { path: '/capture', name: 'capture', component: Capture, meta: { requiresAuth: true }, props: true }, + + { path: '/accounts', name: 'accounts', component: Accounts, meta: { middlewares: [authGuard], requiresAuth: true }, alias: '/', props: true }, + // { path: '/account/create', name: 'createAccount', component: CreateAccount, meta: { requiresAuth: true } }, + // { path: '/account/import', name: 'importAccounts', component: ImportAccount, meta: { requiresAuth: true } }, + // { path: '/account/:twofaccountId/edit', name: 'editAccount', component: EditAccount, meta: { requiresAuth: true } }, + // { path: '/account/:twofaccountId/qrcode', name: 'showQRcode', component: QRcodeAccount, meta: { requiresAuth: true } }, + + // { path: '/groups', name: 'groups', component: Groups, meta: { requiresAuth: true }, props: true }, + // { path: '/group/create', name: 'createGroup', component: CreateGroup, meta: { requiresAuth: true } }, + // { path: '/group/:groupId/edit', name: 'editGroup', component: EditGroup, meta: { requiresAuth: true }, props: true }, + + { path: '/settings/options', name: 'settings.options', component: SettingsOptions, meta: { requiresAuth: true, showAbout: true } }, + // { path: '/settings/account', name: 'settings.account', component: SettingsAccount, meta: { requiresAuth: true, showAbout: true } }, + // { path: '/settings/oauth', name: 'settings.oauth.tokens', component: SettingsOAuth, meta: { requiresAuth: true, showAbout: true } }, + // { path: '/settings/oauth/pat/create', name: 'settings.oauth.generatePAT', component: GeneratePAT, meta: { requiresAuth: true, showAbout: true } }, + // { path: '/settings/webauthn/:credentialId/edit', name: 'settings.webauthn.editCredential', component: EditCredential, meta: { requiresAuth: true, showAbout: true }, props: true }, + // { path: '/settings/webauthn', name: 'settings.webauthn.devices', component: SettingsWebAuthn, meta: { requiresAuth: true, showAbout: true } }, + + { path: '/login', name: 'login', component: Login, meta: { disabledWithAuthProxy: true, showAbout: true } }, + { path: '/register', name: 'register', component: Register, meta: { disabledWithAuthProxy: true, showAbout: true } }, + // { path: '/autolock', name: 'autolock',component: Autolock, meta: { disabledWithAuthProxy: true, showAbout: true } }, + { path: '/password/request', name: 'password.request', component: PasswordRequest, meta: { disabledWithAuthProxy: true, showAbout: true } }, + // { path: '/user/password/reset', name: 'password.reset', component: PasswordReset, meta: { disabledWithAuthProxy: true, showAbout: true } }, + { path: '/webauthn/lost', name: 'webauthn.lost', component: WebauthnLost, meta: { disabledWithAuthProxy: true, showAbout: true } }, + // { path: '/webauthn/recover', name: 'webauthn.recover', component: WebauthnRecover, meta: { disabledWithAuthProxy: true, showAbout: true } }, + + { path: '/about', name: 'about',component: About, meta: { showAbout: true } }, + // { path: '/error', name: 'genericError',component: Errors, props: true }, + // { path: '/404', name: '404',component: Errors, props: true }, + // { path: '*', redirect: { name: '404' } }, + + // Lazy loaded view + { path: '/about', name: 'about', component: () => import('../views/About.vue') } + ] +}) + +router.beforeEach((to, from, next) => { + const middlewares = to.meta.middlewares + const user = useUserStore() + const stores = { user: user } + const context = { to, from, next, stores } + + if (!middlewares) { + return next(); + } + + middlewares[0]({ + ...context, + next: middlewarePipeline(context, middlewares, 1), + }); +}); + +export default router diff --git a/resources/js_vue3/router/middlewarePipeline.js b/resources/js_vue3/router/middlewarePipeline.js new file mode 100644 index 00000000..57bb4a3b --- /dev/null +++ b/resources/js_vue3/router/middlewarePipeline.js @@ -0,0 +1,12 @@ +export default function middlewarePipeline(context, middleware, index) { + const nextMiddleware = middleware[index]; + if (!nextMiddleware) { + return context.next; + } + return () => { + nextMiddleware({ + ...context, + next: middlewarePipeline(context, middleware, index + 1), + }); + }; +} \ No newline at end of file diff --git a/resources/js_vue3/router/middlewares/authGuard.js b/resources/js_vue3/router/middlewares/authGuard.js new file mode 100644 index 00000000..c484f6a1 --- /dev/null +++ b/resources/js_vue3/router/middlewares/authGuard.js @@ -0,0 +1,24 @@ +import authService from '@/services/authService' + +export default async function auth({ to, next, stores }) { + const { user } = stores + + // No authenticated user on the front-end side, we try to + // get an active user from the back-end side + if (! user.isAuthenticated) { + const currentUser = await authService.getCurrentUser() + if (currentUser) { + user.$patch({ + name: currentUser.name, + preferences: currentUser.preferences, + isAdmin: currentUser.is_admin, + }) + } + } + + if (! user.isAuthenticated) { + next({ name: 'login' }) + } else { + next() + } +} \ No newline at end of file diff --git a/resources/js_vue3/services/authService.js b/resources/js_vue3/services/authService.js new file mode 100644 index 00000000..5f2f5995 --- /dev/null +++ b/resources/js_vue3/services/authService.js @@ -0,0 +1,26 @@ +import { httpClientFactory } from '@/services/httpClientFactory' + +const webClient = httpClientFactory('web') +const apiClient = httpClientFactory('api') + +export default { + /** + * + */ + logout() { + return webClient.get('/user/logout') + }, + + /** + * + */ + async getCurrentUser() { + try { + const { data } = await apiClient.get('/user') + return data + } catch (error) { + return null + } + }, + +} \ No newline at end of file diff --git a/resources/js_vue3/stores/user.js b/resources/js_vue3/stores/user.js index fc81ee7f..10da6093 100644 --- a/resources/js_vue3/stores/user.js +++ b/resources/js_vue3/stores/user.js @@ -1,22 +1,48 @@ import { defineStore } from 'pinia' -// import { useApi } from '@/api/useAPI.js' - -// const api = useApi() +import authService from '@/services/authService' +import router from '@/router' export const useUserStore = defineStore({ - id: 'user', + id: 'user', - state: () => { + state: () => { return { - name: 'guest', - preferences: window.userPreferences, + name: undefined, + preferences: window.defaultPreferences, isAdmin: false, } }, - actions: { - updatePreference(preference) { - this.preferences = { ...this.state.preferences, ...preference } - }, - }, + getters: { + isAuthenticated() { + return this.name != undefined + } + }, + + actions: { + updatePreference(preference) { + this.preferences = { ...this.state.preferences, ...preference } + }, + + logout() { + // async appLogout(evt) { + if (this.$2fauth.config.proxyAuth) { + if (this.$2fauth.config.proxyLogoutUrl) { + location.assign(this.$2fauth.config.proxyLogoutUrl) + } + else return false + } + else { + return authService.logout().then(() => { + localStorage.clear() + this.$reset() + router.push({ name: 'login' }) + }) + + // this.$router.push({ name: 'login', params: { forceRefresh: true } }) + } + // }, + } + + }, }) diff --git a/vite.config.js b/vite.config.js index e26bb851..47876beb 100644 --- a/vite.config.js +++ b/vite.config.js @@ -2,6 +2,7 @@ import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; import vue from '@vitejs/plugin-vue'; import i18n from 'laravel-vue-i18n/vite'; +import AutoImport from 'unplugin-auto-import/vite'; export default defineConfig({ plugins: [ @@ -27,6 +28,33 @@ export default defineConfig({ }, }), i18n('resources/lang'), + AutoImport({ + // https://github.com/unplugin/unplugin-auto-import?tab=readme-ov-file#configuration + imports: [ + 'vue', + 'vue-router', + 'pinia', + { + '@vueuse/core': [ + 'useStorage', + ], + 'laravel-vue-i18n': [ + 'i18nVue', + 'trans' + ], + '@kyvg/vue3-notification': [ + 'useNotification' + ], + }, + ], + // resolvers: [ + // ElementPlusResolver(), + // ], + dirs: [ + '@/composables/**', + ], + vueTemplate: true, + }), ], resolve: { alias: {