mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-08-04 03:05:13 -04:00
code formatting and remove regex
This commit is contained in:
260
app/frontend/package-lock.json
generated
260
app/frontend/package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"bootstrap": "^5.1.3",
|
||||
"dotenv": "^16.0.0",
|
||||
@@ -42,9 +43,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
|
||||
"integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
|
||||
"version": "7.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
||||
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -65,9 +66,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-free": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz",
|
||||
"integrity": "sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.1.tgz",
|
||||
"integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -122,13 +123,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"version": "0.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
|
||||
"integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
@@ -172,10 +173,9 @@
|
||||
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g=="
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
|
||||
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
|
||||
"peer": true,
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
@@ -280,15 +280,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||
"integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true
|
||||
},
|
||||
"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.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz",
|
||||
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ=="
|
||||
},
|
||||
"node_modules/@types/pug": {
|
||||
"version": "2.0.6",
|
||||
@@ -313,9 +313,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
||||
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@@ -337,9 +337,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
@@ -371,9 +371,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.0.tgz",
|
||||
"integrity": "sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz",
|
||||
"integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -385,7 +385,7 @@
|
||||
}
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@popperjs/core": "^2.11.5"
|
||||
"@popperjs/core": "^2.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
@@ -550,9 +550,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
|
||||
"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==",
|
||||
"version": "16.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
||||
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -578,9 +578,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
@@ -594,9 +594,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
|
||||
"integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -735,9 +735,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
"integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
@@ -774,9 +774,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
|
||||
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has": "^1.0.3"
|
||||
@@ -982,9 +982,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.5.6",
|
||||
@@ -1086,9 +1089,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
||||
"version": "8.4.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -1193,9 +1196,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.77.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz",
|
||||
"integrity": "sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==",
|
||||
"version": "2.79.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
@@ -1283,6 +1286,7 @@
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
|
||||
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
|
||||
"deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
@@ -1375,9 +1379,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz",
|
||||
"integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==",
|
||||
"version": "1.56.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz",
|
||||
"integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==",
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
@@ -1494,7 +1498,8 @@
|
||||
"node_modules/sourcemap-codec": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"deprecated": "Please use @jridgewell/sourcemap-codec instead"
|
||||
},
|
||||
"node_modules/strip-indent": {
|
||||
"version": "3.0.0",
|
||||
@@ -1532,9 +1537,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "3.49.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.49.0.tgz",
|
||||
"integrity": "sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==",
|
||||
"version": "3.53.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.53.1.tgz",
|
||||
"integrity": "sha512-Q4/hHkktZogGhN5iqxqSi9sjEVoe/NbIxX4hXEHoasTxj+TxEQVAq66LnDMdAZxjmsodkoI5F3slqsS68U7FNw==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
@@ -1605,9 +1610,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"version": "5.16.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz",
|
||||
"integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
@@ -1696,9 +1701,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
|
||||
"integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
|
||||
"version": "7.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
||||
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/highlight": {
|
||||
@@ -1713,9 +1718,9 @@
|
||||
}
|
||||
},
|
||||
"@fortawesome/fontawesome-free": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz",
|
||||
"integrity": "sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg=="
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.1.tgz",
|
||||
"integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A=="
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
@@ -1757,13 +1762,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"version": "0.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
|
||||
"integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
@@ -1798,10 +1803,9 @@
|
||||
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g=="
|
||||
},
|
||||
"@popperjs/core": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
|
||||
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
|
||||
"peer": true
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
|
||||
},
|
||||
"@rollup/plugin-commonjs": {
|
||||
"version": "17.1.0",
|
||||
@@ -1883,15 +1887,15 @@
|
||||
}
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
|
||||
"integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true
|
||||
},
|
||||
"@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.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz",
|
||||
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ=="
|
||||
},
|
||||
"@types/pug": {
|
||||
"version": "2.0.6",
|
||||
@@ -1916,9 +1920,9 @@
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
||||
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
@@ -1931,9 +1935,9 @@
|
||||
}
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
@@ -1956,9 +1960,9 @@
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
|
||||
},
|
||||
"bootstrap": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.0.tgz",
|
||||
"integrity": "sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz",
|
||||
"integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"brace-expansion": {
|
||||
@@ -2085,9 +2089,9 @@
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
|
||||
"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ=="
|
||||
"version": "16.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
||||
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "3.3.1",
|
||||
@@ -2107,9 +2111,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
@@ -2120,9 +2124,9 @@
|
||||
}
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
|
||||
"integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -2227,9 +2231,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
"integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
|
||||
"dev": true
|
||||
},
|
||||
"immutable": {
|
||||
@@ -2260,9 +2264,9 @@
|
||||
}
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
|
||||
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
@@ -2422,9 +2426,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.6",
|
||||
@@ -2496,9 +2500,9 @@
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
||||
"version": "8.4.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"picocolors": "^1.0.0",
|
||||
@@ -2560,9 +2564,9 @@
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.77.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz",
|
||||
"integrity": "sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==",
|
||||
"version": "2.79.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
@@ -2684,9 +2688,9 @@
|
||||
}
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz",
|
||||
"integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==",
|
||||
"version": "1.56.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz",
|
||||
"integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==",
|
||||
"requires": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
@@ -2799,9 +2803,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"svelte": {
|
||||
"version": "3.49.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.49.0.tgz",
|
||||
"integrity": "sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA=="
|
||||
"version": "3.53.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.53.1.tgz",
|
||||
"integrity": "sha512-Q4/hHkktZogGhN5iqxqSi9sjEVoe/NbIxX4hXEHoasTxj+TxEQVAq66LnDMdAZxjmsodkoI5F3slqsS68U7FNw=="
|
||||
},
|
||||
"svelte-preprocess": {
|
||||
"version": "4.10.7",
|
||||
@@ -2817,9 +2821,9 @@
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"version": "5.16.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz",
|
||||
"integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/** This script modifies the project to support TS code in .svelte files like:
|
||||
|
||||
<script lang="ts">
|
||||
export let name: string;
|
||||
export let name: string;
|
||||
</script>
|
||||
|
||||
As well as validating the code for CI.
|
||||
@@ -82,11 +82,11 @@ const tsconfig = `{
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
|
||||
}`
|
||||
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
|
||||
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
|
||||
fs.writeFileSync(tsconfigPath, tsconfig)
|
||||
|
||||
// Add global.d.ts
|
||||
const dtsPath = path.join(projectRoot, "src", "global.d.ts")
|
||||
const dtsPath = path.join(projectRoot, "src", "global.d.ts")
|
||||
fs.writeFileSync(dtsPath, `/// <reference types="svelte" />`)
|
||||
|
||||
// Delete this script, but not during testing
|
||||
|
||||
@@ -1,48 +1,58 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import socketStore from './socketStore.js';
|
||||
import { onMount } from "svelte";
|
||||
import socketStore from "./socketStore.js";
|
||||
import Navbar from "./components/Navbar.svelte";
|
||||
import DeviceCard from "./components/DeviceCard.svelte";
|
||||
import Toast from "./components/Toast.svelte";
|
||||
|
||||
let visitors = 0;
|
||||
let devices = [];
|
||||
let settings = {}
|
||||
let settings = {};
|
||||
let toast = {
|
||||
title: "",
|
||||
message: "",
|
||||
color: "",
|
||||
show: false
|
||||
}
|
||||
show: false,
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
socketStore.subscribeStatus(status => {
|
||||
socketStore.subscribeStatus((status) => {
|
||||
if (status == "open") {
|
||||
showToast("Websocket", "Connected", "success")
|
||||
showToast("Websocket", "Connected", "success");
|
||||
} else if (status == "close") {
|
||||
showToast("Websocket", "Connection closed. Trying to reconnect ...", "danger")
|
||||
showToast(
|
||||
"Websocket",
|
||||
"Connection closed. Trying to reconnect ...",
|
||||
"danger"
|
||||
);
|
||||
} else if (status == "error") {
|
||||
showToast("Websocket", "Error when connecting to websocket", "danger")
|
||||
showToast(
|
||||
"Websocket",
|
||||
"Error when connecting to websocket",
|
||||
"danger"
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
socketStore.subscribeMsg(currentMessage => {
|
||||
socketStore.subscribeMsg((currentMessage) => {
|
||||
if (currentMessage.type == "init") {
|
||||
// create devices
|
||||
devices = [...currentMessage.message.devices]
|
||||
devices = [...currentMessage.message.devices];
|
||||
devices = devices;
|
||||
devices.sort(compare);
|
||||
settings = currentMessage.message.settings;
|
||||
} else if (currentMessage.type == "status") {
|
||||
// set device array and sort
|
||||
const index = devices.findIndex(x => x.id == currentMessage.message.id);
|
||||
const index = devices.findIndex(
|
||||
(x) => x.id == currentMessage.message.id
|
||||
);
|
||||
if (devices.length === 0 || index === -1) {
|
||||
devices.push(currentMessage.message);
|
||||
devices = devices;
|
||||
} else {
|
||||
devices[index] = currentMessage.message;
|
||||
}
|
||||
devices.sort(compare)
|
||||
devices.sort(compare);
|
||||
// set device status
|
||||
if (currentMessage.message.up == true) {
|
||||
setUp(currentMessage.message);
|
||||
@@ -51,22 +61,25 @@
|
||||
}
|
||||
} else if (currentMessage.type == "pending") {
|
||||
// set device pending
|
||||
setPending(currentMessage.message)
|
||||
setPending(currentMessage.message);
|
||||
} else if (currentMessage.type == "visitor") {
|
||||
// update visitor count
|
||||
visitors = currentMessage.message;
|
||||
} else if (currentMessage.type == "delete") {
|
||||
// delete device
|
||||
const devCol = document.querySelector(`#device-col-${currentMessage.message}`);
|
||||
const devCol = document.querySelector(
|
||||
`#device-col-${currentMessage.message}`
|
||||
);
|
||||
devCol.remove();
|
||||
} else if (currentMessage.type == "scan_network") {
|
||||
// set scanned network devices
|
||||
if (!currentMessage.message) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
settings["scan_network"] = currentMessage.message;
|
||||
const btnScan = document.querySelector("#btnScan");
|
||||
const btnScanSpinner = document.querySelector("#btnScanSpinner");
|
||||
const btnScanSpinner =
|
||||
document.querySelector("#btnScanSpinner");
|
||||
const btnScanText = document.querySelector("#btnScanText");
|
||||
btnScan.disabled = false;
|
||||
btnScanSpinner.classList.add("d-none");
|
||||
@@ -74,28 +87,39 @@
|
||||
} else if (currentMessage.type == "backup") {
|
||||
// download backup file
|
||||
const now = new Date();
|
||||
const fileName = `upsnap_backup_${now.toISOString()}.json`
|
||||
const fileName = `upsnap_backup_${now.toISOString()}.json`;
|
||||
const a = document.createElement("a");
|
||||
const file = new Blob([JSON.stringify(currentMessage.message)], { type: "text/plain" });
|
||||
const file = new Blob(
|
||||
[JSON.stringify(currentMessage.message)],
|
||||
{ type: "text/plain" }
|
||||
);
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = fileName;
|
||||
a.click();
|
||||
} else if (currentMessage.type == "operationStatus") {
|
||||
if (currentMessage.message == "Success") {
|
||||
showToast(currentMessage.message, "Changes were saved", "success")
|
||||
showToast(
|
||||
currentMessage.message,
|
||||
"Changes were saved",
|
||||
"success"
|
||||
);
|
||||
} else if (currentMessage.message == "Error") {
|
||||
showToast(currentMessage.message, "Error while saving the device. Please check the logs.", "danger")
|
||||
showToast(
|
||||
currentMessage.message,
|
||||
"Error while saving the device. Please check the logs.",
|
||||
"danger"
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function setUp(device) {
|
||||
const dot = document.querySelector(`#dot-${device.id}`)
|
||||
const dot = document.querySelector(`#dot-${device.id}`);
|
||||
const spinner = document.querySelector(`#spinner-${device.id}`);
|
||||
if (dot) {
|
||||
if (dot.classList.contains("danger")) {
|
||||
showToast(device.name, "Device is up!", "success")
|
||||
showToast(device.name, "Device is up!", "success");
|
||||
}
|
||||
dot.style.animation = "none";
|
||||
dot.offsetWidth;
|
||||
@@ -110,11 +134,11 @@
|
||||
}
|
||||
|
||||
function setDown(device) {
|
||||
const dot = document.querySelector(`#dot-${device.id}`)
|
||||
const dot = document.querySelector(`#dot-${device.id}`);
|
||||
const spinner = document.querySelector(`#spinner-${device.id}`);
|
||||
if (dot) {
|
||||
if (dot.classList.contains("success")) {
|
||||
showToast(device.name, "Device is down!", "danger")
|
||||
showToast(device.name, "Device is down!", "danger");
|
||||
}
|
||||
dot.style.animation = "none";
|
||||
dot.offsetWidth;
|
||||
@@ -137,7 +161,7 @@
|
||||
|
||||
function showToast(title, message, color) {
|
||||
if (settings.notifications === false) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
toast.title = title;
|
||||
toast.message = message;
|
||||
@@ -145,31 +169,30 @@
|
||||
toast.show = true;
|
||||
setTimeout(() => {
|
||||
toast.show = false;
|
||||
}, 4000)
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
function compare(a, b) {
|
||||
if (a.name < b.name){
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.name > b.name){
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<Navbar settings={settings} visitors={visitors} />
|
||||
<Navbar {settings} {visitors} />
|
||||
<div class="container mb-3">
|
||||
<div class="row">
|
||||
{#each devices as device}
|
||||
<DeviceCard device={device}/>
|
||||
<DeviceCard {device} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<Toast toast={toast} />
|
||||
<Toast {toast} />
|
||||
</main>
|
||||
|
||||
<style lang="scss" global>
|
||||
@@ -201,29 +224,30 @@
|
||||
border-top: 1px solid var(--color-text);
|
||||
}
|
||||
|
||||
.btn, button {
|
||||
.btn,
|
||||
button {
|
||||
&.btn-light {
|
||||
color: var(--color-text);
|
||||
background-color: var(--bg-lighter);
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
&.btn-outline-success {
|
||||
border-color: $success;
|
||||
&:hover {
|
||||
background-color: $success;
|
||||
}
|
||||
}
|
||||
&.btn-outline-success {
|
||||
border-color: $success;
|
||||
&:hover {
|
||||
background-color: $success;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-outline-danger {
|
||||
border-color: $danger;
|
||||
&:hover {
|
||||
background-color: $danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.btn-outline-danger {
|
||||
border-color: $danger;
|
||||
&:hover {
|
||||
background-color: $danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-control{
|
||||
.form-control {
|
||||
&:focus {
|
||||
border-color: inherit;
|
||||
box-shadow: none;
|
||||
@@ -231,48 +255,48 @@
|
||||
}
|
||||
|
||||
.callout {
|
||||
padding: 1rem;
|
||||
border-left-width: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
padding: 1rem;
|
||||
border-left-width: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
|
||||
&.callout-info {
|
||||
&.callout-info {
|
||||
background-color: var(--info-dark-transparent);
|
||||
border: 1px solid var(--info-dark-transparent);
|
||||
border-left: 5px solid var(--info);
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-danger {
|
||||
&.callout-danger {
|
||||
background-color: var(--danger-dark-transparent);
|
||||
border: 1px solid var(--danger-dark-transparent);
|
||||
border-left: 5px solid var(--danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes on-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 $success;
|
||||
}
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 $success;
|
||||
}
|
||||
|
||||
70% {
|
||||
box-shadow: 0 0 0 15px rgba(0, 0, 0, 0);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 15px rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes off-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 $danger;
|
||||
}
|
||||
@keyframes off-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 $danger;
|
||||
}
|
||||
|
||||
70% {
|
||||
box-shadow: 0 0 0 15px rgba(0, 0, 0, 0);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 15px rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script>
|
||||
|
||||
const preferesDark = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
preferesDark.addEventListener("change", e => {
|
||||
preferesDark.addEventListener("change", (e) => {
|
||||
setTheme(e.matches ? "dark" : "light");
|
||||
})
|
||||
});
|
||||
|
||||
if (localStorage.getItem("data-theme") === null) {
|
||||
setTheme(preferesDark.matches ? "dark" : "light");
|
||||
@@ -12,36 +11,44 @@
|
||||
setTheme(localStorage.getItem("data-theme"));
|
||||
}
|
||||
|
||||
function setTheme(color) {
|
||||
function setTheme(color) {
|
||||
if (color == "system") {
|
||||
document.documentElement.setAttribute("data-theme", preferesDark ? "dark" : "light");
|
||||
document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
preferesDark ? "dark" : "light"
|
||||
);
|
||||
localStorage.setItem("data-theme", preferesDark ? "dark" : "light");
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-theme", color);
|
||||
localStorage.setItem("data-theme", color);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-light dropdown-toggle px-3 me-2 py-2" type="button" id="darkModeButton" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa-solid fa-palette me-2"></i>Theme
|
||||
<button
|
||||
class="btn btn-light dropdown-toggle px-3 me-2 py-2"
|
||||
type="button"
|
||||
id="darkModeButton"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fa-solid fa-palette me-2" />Theme
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="darkModeButton">
|
||||
<li>
|
||||
<button class="dropdown-item" on:click={() => setTheme("dark")}>
|
||||
<i class="fa-solid fa-moon me-2"></i>Dark
|
||||
<i class="fa-solid fa-moon me-2" />Dark
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="dropdown-item" on:click={() => setTheme("light")}>
|
||||
<i class="fa-solid fa-sun me-2"></i>Light
|
||||
<i class="fa-solid fa-sun me-2" />Light
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="dropdown-item" on:click={() => setTheme("system")}>
|
||||
<i class="fa-solid fa-desktop me-2"></i>System
|
||||
<i class="fa-solid fa-desktop me-2" />System
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,44 +1,46 @@
|
||||
<script>
|
||||
import socketStore from '../socketStore.js';
|
||||
import socketStore from "../socketStore.js";
|
||||
export let device;
|
||||
|
||||
let modalDevice = JSON.parse(JSON.stringify(device));
|
||||
let customPort = {}
|
||||
let customPort = {};
|
||||
|
||||
function wake(id) {
|
||||
function wake(id) {
|
||||
socketStore.sendMessage({
|
||||
type: "wake",
|
||||
id: id
|
||||
})
|
||||
}
|
||||
id: id,
|
||||
});
|
||||
}
|
||||
|
||||
function shutdown(id) {
|
||||
socketStore.sendMessage({
|
||||
type: "shutdown",
|
||||
id: id
|
||||
})
|
||||
}
|
||||
id: id,
|
||||
});
|
||||
}
|
||||
|
||||
function deleteDevice() {
|
||||
function deleteDevice() {
|
||||
socketStore.sendMessage({
|
||||
type: "delete_device",
|
||||
id: modalDevice.id
|
||||
})
|
||||
}
|
||||
id: modalDevice.id,
|
||||
});
|
||||
}
|
||||
|
||||
function updateDevice() {
|
||||
device = modalDevice;
|
||||
socketStore.sendMessage({
|
||||
type: "update_device",
|
||||
data: modalDevice
|
||||
})
|
||||
data: modalDevice,
|
||||
});
|
||||
}
|
||||
|
||||
function updatePort() {
|
||||
if (!customPort.number) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const index = modalDevice.ports.findIndex(x => x.number == customPort.number);
|
||||
const index = modalDevice.ports.findIndex(
|
||||
(x) => x.number == customPort.number
|
||||
);
|
||||
if (customPort.name) {
|
||||
// add port
|
||||
if (index === -1) {
|
||||
@@ -46,75 +48,127 @@
|
||||
modalDevice.ports.push(JSON.parse(JSON.stringify(customPort)));
|
||||
} else {
|
||||
customPort.checked = modalDevice.ports[index].checked;
|
||||
modalDevice.ports[index] = JSON.parse(JSON.stringify(customPort));
|
||||
modalDevice.ports[index] = JSON.parse(
|
||||
JSON.stringify(customPort)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// delete port
|
||||
if (index >= 0) {
|
||||
modalDevice.ports.splice(index, 1)
|
||||
modalDevice.ports.splice(index, 1);
|
||||
}
|
||||
}
|
||||
modalDevice = modalDevice;
|
||||
// send to backend
|
||||
socketStore.sendMessage({
|
||||
type: "update_port",
|
||||
data: customPort
|
||||
})
|
||||
data: customPort,
|
||||
});
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
modalDevice = Object.assign({}, device)
|
||||
modalDevice = Object.assign({}, device);
|
||||
}
|
||||
|
||||
function validatePort() {
|
||||
if (typeof customPort.number != "number") {
|
||||
customPort.number = 1
|
||||
customPort.number = 1;
|
||||
} else if (customPort.number > 65535) {
|
||||
customPort.number = 65535
|
||||
customPort.number = 65535;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="device-col-{device.id}" class="col-xs-12 col-sm-6 col-md-4 col-lg-3 g-4">
|
||||
<div
|
||||
id="device-col-{device.id}"
|
||||
class="col-xs-12 col-sm-6 col-md-4 col-lg-3 g-4"
|
||||
>
|
||||
<div class="card border-0 p-3 pt-2">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-auto me-auto">
|
||||
<div id="spinner-{device.id}" class="spinner-border warning d-none" role="status"></div>
|
||||
<div
|
||||
id="spinner-{device.id}"
|
||||
class="spinner-border warning d-none"
|
||||
role="status"
|
||||
/>
|
||||
{#if device.up === true}
|
||||
{#if device.shutdown.command}
|
||||
<div class="hover" on:click="{() => shutdown(device.id)}" data-bs-toggle="tooltip" title="Shutdown command: {device.shutdown.command}" role="button">
|
||||
<i id="dot-{device.id}" class="fa-solid fa-power-off fa-2x success"></i>
|
||||
</div>
|
||||
<div
|
||||
class="hover"
|
||||
on:click={() => shutdown(device.id)}
|
||||
on:keydown={() => shutdown(device.id)}
|
||||
data-bs-toggle="tooltip"
|
||||
title="Shutdown command: {device.shutdown
|
||||
.command}"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
id="dot-{device.id}"
|
||||
class="fa-solid fa-power-off fa-2x success"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<i id="dot-{device.id}" class="fa-solid fa-power-off fa-2x success"></i>
|
||||
<i
|
||||
id="dot-{device.id}"
|
||||
class="fa-solid fa-power-off fa-2x success"
|
||||
/>
|
||||
{/if}
|
||||
{:else if device.up === false}
|
||||
<div class="hover" on:click="{() => wake(device.id)}" role="button">
|
||||
<i id="dot-{device.id}" class="fa-solid fa-power-off fa-2x danger"></i>
|
||||
<div
|
||||
class="hover"
|
||||
on:click={() => wake(device.id)}
|
||||
on:keydown={() => wake(device.id)}
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
id="dot-{device.id}"
|
||||
class="fa-solid fa-power-off fa-2x danger"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="hover" role="button">
|
||||
<i id="dot-{device.id}" class="fa-solid fa-power-off fa-2x text-muted"></i>
|
||||
<i
|
||||
id="dot-{device.id}"
|
||||
class="fa-solid fa-power-off fa-2x text-muted"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if device.wake.enabled}
|
||||
<div class="col-auto px-2" data-bs-toggle="tooltip" title="Wake cron: {device.wake.cron}">
|
||||
<i class="fa-solid fa-circle-play fa-2x text-muted"></i>
|
||||
<div
|
||||
class="col-auto px-2"
|
||||
data-bs-toggle="tooltip"
|
||||
title="Wake cron: {device.wake.cron}"
|
||||
>
|
||||
<i class="fa-solid fa-circle-play fa-2x text-muted" />
|
||||
</div>
|
||||
{/if}
|
||||
{#if device.shutdown.enabled}
|
||||
<div class="col-auto px-2" data-bs-toggle="tooltip" title="Shutdown cron: {device.wake.cron}">
|
||||
<i class="fa-solid fa-circle-stop fa-2x text-muted"></i>
|
||||
<div
|
||||
class="col-auto px-2"
|
||||
data-bs-toggle="tooltip"
|
||||
title="Shutdown cron: {device.wake.cron}"
|
||||
>
|
||||
<i class="fa-solid fa-circle-stop fa-2x text-muted" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="col-auto hover" data-bs-toggle="modal" data-bs-target="#device-modal-{device.id}" role="button" on:click="{() => openModal()}">
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-2x"></i>
|
||||
<div
|
||||
class="col-auto hover"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#device-modal-{device.id}"
|
||||
role="button"
|
||||
on:click={() => openModal()}
|
||||
on:keydown={() => openModal()}
|
||||
>
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-2x" />
|
||||
</div>
|
||||
</div>
|
||||
{#if device.link}
|
||||
<h5 class="card-title fw-bold my-2"><a class="inherit-color" href={device.link}>{device.name}</a></h5>
|
||||
<h5 class="card-title fw-bold my-2">
|
||||
<a class="inherit-color" href={device.link}>{device.name}</a
|
||||
>
|
||||
</h5>
|
||||
{:else}
|
||||
<h5 class="card-title fw-bold my-2">{device.name}</h5>
|
||||
{/if}
|
||||
@@ -123,8 +177,13 @@
|
||||
{#each device.ports as port}
|
||||
{#if port.checked === true}
|
||||
<li class="list-group-item">
|
||||
<i class="fa-solid fa-circle align-middle {port.open ? 'success' : 'danger'}"></i>
|
||||
{port.name} <span class="text-muted">({port.number})</span>
|
||||
<i
|
||||
class="fa-solid fa-circle align-middle {port.open
|
||||
? 'success'
|
||||
: 'danger'}"
|
||||
/>
|
||||
{port.name}
|
||||
<span class="text-muted">({port.number})</span>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -132,119 +191,328 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="device-modal-{device.id}" tabindex="-1" aria-labelledby="device-modal-{device.id}-label" aria-hidden="true">
|
||||
<div
|
||||
class="modal fade"
|
||||
id="device-modal-{device.id}"
|
||||
tabindex="-1"
|
||||
aria-labelledby="device-modal-{device.id}-label"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fw-bold" id="device-modal-{modalDevice.id}-label">{modalDevice.name}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<h5
|
||||
class="modal-title fw-bold"
|
||||
id="device-modal-{modalDevice.id}-label"
|
||||
>
|
||||
{modalDevice.name}
|
||||
</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="form-{modalDevice.id}" on:submit|preventDefault={updateDevice}>
|
||||
<form
|
||||
id="form-{modalDevice.id}"
|
||||
on:submit|preventDefault={updateDevice}
|
||||
>
|
||||
<!-- general -->
|
||||
<h5 class="fw-bold">General</h5>
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm">
|
||||
<label for="inputName{modalDevice.id}" class="form-label">Device name</label>
|
||||
<input type="text" class="form-control" id="inputName{modalDevice.id}" bind:value="{modalDevice.name}" required>
|
||||
<label
|
||||
for="inputName{modalDevice.id}"
|
||||
class="form-label">Device name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputName{modalDevice.id}"
|
||||
bind:value={modalDevice.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<label for="inputMac{modalDevice.id}" class="form-label">Mac address</label>
|
||||
<input type="text" class="form-control" id="inputMac{modalDevice.mac}" bind:value="{modalDevice.mac}" required>
|
||||
<label
|
||||
for="inputMac{modalDevice.id}"
|
||||
class="form-label">Mac address</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputMac{modalDevice.mac}"
|
||||
bind:value={modalDevice.mac}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm">
|
||||
<label for="inputIp{modalDevice.id}" class="form-label">IP address</label>
|
||||
<input type="text" class="form-control" id="inputIp{modalDevice.id}" bind:value="{modalDevice.ip}" pattern="^([01]?\d\d?|2[0-4]\d|25[0-5])(?:\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])){'{'}3{'}'}$" required>
|
||||
<label
|
||||
for="inputIp{modalDevice.id}"
|
||||
class="form-label">IP address</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputIp{modalDevice.id}"
|
||||
bind:value={modalDevice.ip}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<label for="inputNetmask{modalDevice.id}" class="form-label">Netmask</label>
|
||||
<input type="text" class="form-control" id="inputNetmask{modalDevice.id}" bind:value="{modalDevice.netmask}" pattern="^(((255\.){'{'}3{'}'}(255|254|252|248|240|224|192|128|0+))|((255\.){'{'}2{'}'}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){'{'}2{'}'})|((255|254|252|248|240|224|192|128|0+)(\.0+){'{'}3{'}'}))$" required>
|
||||
<label
|
||||
for="inputNetmask{modalDevice.id}"
|
||||
class="form-label">Netmask</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputNetmask{modalDevice.id}"
|
||||
bind:value={modalDevice.netmask}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-3">
|
||||
<label for="inputLinkAddDevice" class="form-label">Web link</label>
|
||||
<input type="text" class="form-control" id="inputILinkAddDevice" placeholder="http://...." bind:value="{modalDevice.link}">
|
||||
<label
|
||||
for="inputLinkAddDevice"
|
||||
class="form-label">Web link</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputILinkAddDevice"
|
||||
placeholder="http://...."
|
||||
bind:value={modalDevice.link}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ports -->
|
||||
<h5 class="fw-bold mt-4">Ports</h5>
|
||||
<p class="mb-2">Select ports to check if they are open.</p>
|
||||
<p class="mb-2">
|
||||
Select ports to check if they are open.
|
||||
</p>
|
||||
{#if modalDevice.ports.length === 0}
|
||||
<p class="mb-0">No ports available. Add ports below.</p>
|
||||
<p class="mb-0">
|
||||
No ports available. Add ports below.
|
||||
</p>
|
||||
{/if}
|
||||
{#each modalDevice.ports as port}
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="{device.id}-port-{port.number}" bind:checked="{port['checked']}">
|
||||
<label class="form-check-label" for="{device.id}-port-{port.number}">{port.name} <span class="text-muted">({port.number})</span></label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
id="{device.id}-port-{port.number}"
|
||||
bind:checked={port["checked"]}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="{device.id}-port-{port.number}"
|
||||
>{port.name}
|
||||
<span class="text-muted"
|
||||
>({port.number})</span
|
||||
></label
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
<label class="form-label mt-2" for="{device.id}-custom-port">Custom port</label>
|
||||
<label
|
||||
class="form-label mt-2"
|
||||
for="{device.id}-custom-port">Custom port</label
|
||||
>
|
||||
<div class="input-group mb-2">
|
||||
<input type="text" id="{device.id}-custom-port" class="form-control rounded-0 rounded-start" placeholder="Name" aria-label="Name" aria-describedby="button-addon2" bind:value={customPort.name}>
|
||||
<input type="number" min="1" max="65535" class="form-control rounded-0" placeholder="Port" aria-label="Port" aria-describedby="button-addon2" bind:value={customPort.number} on:input={validatePort}>
|
||||
<button class="btn btn-secondary" type="button" id="button-addon2" on:click="{updatePort}">Update Port</button>
|
||||
<input
|
||||
type="text"
|
||||
id="{device.id}-custom-port"
|
||||
class="form-control rounded-0 rounded-start"
|
||||
placeholder="Name"
|
||||
aria-label="Name"
|
||||
aria-describedby="button-addon2"
|
||||
bind:value={customPort.name}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
class="form-control rounded-0"
|
||||
placeholder="Port"
|
||||
aria-label="Port"
|
||||
aria-describedby="button-addon2"
|
||||
bind:value={customPort.number}
|
||||
on:input={validatePort}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
type="button"
|
||||
id="button-addon2"
|
||||
on:click={updatePort}>Update Port</button
|
||||
>
|
||||
</div>
|
||||
<button class="btn btn-secondary mt-2" type="button" data-bs-toggle="collapse" data-bs-target="#info-ports" aria-expanded="false" aria-controls="info-ports">
|
||||
<i class="fa-solid fa-angle-down me-2"></i>How to use
|
||||
<button
|
||||
class="btn btn-secondary mt-2"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#info-ports"
|
||||
aria-expanded="false"
|
||||
aria-controls="info-ports"
|
||||
>
|
||||
<i class="fa-solid fa-angle-down me-2" />How to use
|
||||
</button>
|
||||
<div class="collapse mt-3" id="info-ports">
|
||||
<div class="callout callout-info">
|
||||
<p class="mb-0">Ports must be between 1 and 65535. Enter the same port with a differen name to change it. Leave name empty to delete port.</p>
|
||||
<p class="mb-0">
|
||||
Ports must be between 1 and 65535. Enter the
|
||||
same port with a differen name to change it.
|
||||
Leave name empty to delete port.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- scheduled wake -->
|
||||
<h5 class="fw-bold mt-4">Scheduled wake</h5>
|
||||
<p class="mb-2">Wake your device at a given time.</p>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="wake-disable" id="wake-radio-disabled-{modalDevice.id}"
|
||||
bind:group={modalDevice["wake"]["enabled"]} value={false} checked={!modalDevice["wake"]["enabled"]}>
|
||||
<label class="form-check-label" for="wake-radio-disabled-{modalDevice.id}">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="wake-disable"
|
||||
id="wake-radio-disabled-{modalDevice.id}"
|
||||
bind:group={modalDevice["wake"]["enabled"]}
|
||||
value={false}
|
||||
checked={!modalDevice["wake"]["enabled"]}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="wake-radio-disabled-{modalDevice.id}"
|
||||
>
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="wake-cron" id="wake-radio-cron-{modalDevice.id}"
|
||||
bind:group={modalDevice["wake"]["enabled"]} value={true} checked={modalDevice["wake"]["enabled"]}>
|
||||
<label class="form-check-label" for="wake-radio-cron-{modalDevice.id}">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="wake-cron"
|
||||
id="wake-radio-cron-{modalDevice.id}"
|
||||
bind:group={modalDevice["wake"]["enabled"]}
|
||||
value={true}
|
||||
checked={modalDevice["wake"]["enabled"]}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="wake-radio-cron-{modalDevice.id}"
|
||||
>
|
||||
Enabled
|
||||
</label>
|
||||
</div>
|
||||
<div class="input-group my-1" hidden={!modalDevice["wake"]["enabled"]}>
|
||||
<span class="input-group-text rounded-0 rounded-start" id="wake-cron-{modalDevice.id}">Cron</span>
|
||||
<input type="text" class="form-control rounded-0 rounded-end" placeholder="* /4 * * *" aria-label="Crontab" aria-describedby="wake-cron-{modalDevice.id}" bind:value={modalDevice["wake"]["cron"]}>
|
||||
<div
|
||||
class="input-group my-1"
|
||||
hidden={!modalDevice["wake"]["enabled"]}
|
||||
>
|
||||
<span
|
||||
class="input-group-text rounded-0 rounded-start"
|
||||
id="wake-cron-{modalDevice.id}">Cron</span
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control rounded-0 rounded-end"
|
||||
placeholder="* /4 * * *"
|
||||
aria-label="Crontab"
|
||||
aria-describedby="wake-cron-{modalDevice.id}"
|
||||
bind:value={modalDevice["wake"]["cron"]}
|
||||
/>
|
||||
</div>
|
||||
<button class="btn btn-secondary mt-2" type="button" data-bs-toggle="collapse" data-bs-target="#info-wake" aria-expanded="false" aria-controls="info-wake">
|
||||
<i class="fa-solid fa-angle-down me-2"></i>How to use
|
||||
<button
|
||||
class="btn btn-secondary mt-2"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#info-wake"
|
||||
aria-expanded="false"
|
||||
aria-controls="info-wake"
|
||||
>
|
||||
<i class="fa-solid fa-angle-down me-2" />How to use
|
||||
</button>
|
||||
<div class="collapse mt-3" id="info-wake">
|
||||
<div class="callout callout-info">
|
||||
<p class="mb-2">Cron is a syntax describing a time pattern when to execute jobs. The above field uses common cron syntax. Examples:</p>
|
||||
<pre class="mb-2">Minute Hour DayOfMonth Month DayOfWeek
|
||||
<p class="mb-2">
|
||||
Cron is a syntax describing a time pattern
|
||||
when to execute jobs. The above field uses
|
||||
common cron syntax. Examples:
|
||||
</p>
|
||||
<pre
|
||||
class="mb-2">Minute Hour DayOfMonth Month DayOfWeek
|
||||
* /4 * * * (Wake every 4 hours)
|
||||
0 9 * * 1-5 (Wake from Mo-Fr at 9 a.m.)
|
||||
</pre>
|
||||
<p class="mb-0">Read more about <a href="https://linux.die.net/man/5/crontab" target="_blank">valid syntax here</a> or <a href="https://crontab.guru/" target="_blank">use a generator</a>. Expressions starting with "@..." are not supported.</p>
|
||||
<p class="mb-0">
|
||||
Read more about <a
|
||||
href="https://linux.die.net/man/5/crontab"
|
||||
target="_blank"
|
||||
rel="noreferrer">valid syntax here</a
|
||||
>
|
||||
or
|
||||
<a
|
||||
href="https://crontab.guru/"
|
||||
target="_blank"
|
||||
rel="noreferrer">use a generator</a
|
||||
>. Expressions starting with "@..." are not
|
||||
supported.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- scheduled shutdown -->
|
||||
<h5 class="fw-bold mt-4">Shutdown</h5>
|
||||
<p class="mb-2">Set the shutdown command here. This shell command will be executed when clicking the power button on the device card. You can use cron below, which will then execute the command at the given time.</p>
|
||||
<p class="mb-2">
|
||||
Set the shutdown command here. This shell command
|
||||
will be executed when clicking the power button on
|
||||
the device card. You can use cron below, which will
|
||||
then execute the command at the given time.
|
||||
</p>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text rounded-0 rounded-start" id="shutdown-command-{modalDevice.id}">Command</span>
|
||||
<input type="text" class="form-control rounded-0 rounded-end" placeholder="sshpass -p your_password ssh -o 'StrictHostKeyChecking=no' user@hostname 'sudo shutdown'" aria-label="Ccommand" aria-describedby="shutdown-command-{modalDevice.id}" bind:value={modalDevice["shutdown"]["command"]}>
|
||||
<span
|
||||
class="input-group-text rounded-0 rounded-start"
|
||||
id="shutdown-command-{modalDevice.id}"
|
||||
>Command</span
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control rounded-0 rounded-end"
|
||||
placeholder="sshpass -p your_password ssh -o 'StrictHostKeyChecking=no' user@hostname 'sudo shutdown'"
|
||||
aria-label="Ccommand"
|
||||
aria-describedby="shutdown-command-{modalDevice.id}"
|
||||
bind:value={modalDevice["shutdown"]["command"]}
|
||||
/>
|
||||
</div>
|
||||
<button class="btn btn-secondary mt-2" type="button" data-bs-toggle="collapse" data-bs-target="#info-shutdown" aria-expanded="false" aria-controls="info-shutdown">
|
||||
<i class="fa-solid fa-angle-down me-2"></i>How to use
|
||||
<button
|
||||
class="btn btn-secondary mt-2"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#info-shutdown"
|
||||
aria-expanded="false"
|
||||
aria-controls="info-shutdown"
|
||||
>
|
||||
<i class="fa-solid fa-angle-down me-2" />How to use
|
||||
</button>
|
||||
<div class="collapse mt-3" id="info-shutdown">
|
||||
<div class="callout callout-info">
|
||||
<p class="mb-2">This field takes a shell command to trigger the shutdown. You can use <code>sshpass</code> for Linux or <code>net rpc</code> for Windows hosts.</p>
|
||||
<div class="callout callout-danger mb-2">Note: This command is safed as cleartext. Meaning, passwords are clearly visible in the database.</div>
|
||||
<p class="mb-2">
|
||||
This field takes a shell command to trigger
|
||||
the shutdown. You can use <code
|
||||
>sshpass</code
|
||||
>
|
||||
for Linux or <code>net rpc</code> for Windows
|
||||
hosts.
|
||||
</p>
|
||||
<div class="callout callout-danger mb-2">
|
||||
Note: This command is safed as cleartext.
|
||||
Meaning, passwords are clearly visible in
|
||||
the database.
|
||||
</div>
|
||||
<p class="mb-2">Examples:</p>
|
||||
<pre class="mb-2"># wake linux hosts
|
||||
sshpass -p your_password ssh -o 'StrictHostKeyChecking=no' user@hostname 'sudo shutdown'
|
||||
@@ -252,35 +520,92 @@
|
||||
# wake windows hosts
|
||||
net rpc shutdown --ipaddress 192.168.1.1 --user user%password
|
||||
</pre>
|
||||
<p class="mb-0">Read more about <a href="https://linux.die.net/man/1/sshpass" target="_blank">sshpass</a> or <a href="https://linux.die.net/man/8/net" target="_blank">net rpc</a>.</p>
|
||||
<p class="mb-0">
|
||||
Read more about <a
|
||||
href="https://linux.die.net/man/1/sshpass"
|
||||
target="_blank"
|
||||
rel="noreferrer">sshpass</a
|
||||
>
|
||||
or
|
||||
<a
|
||||
href="https://linux.die.net/man/8/net"
|
||||
target="_blank"
|
||||
rel="noreferrer">net rpc</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label mt-2" for="{device.id}-shutdown-cron">Use cron</label>
|
||||
<label
|
||||
class="form-label mt-2"
|
||||
for="{device.id}-shutdown-cron">Use cron</label
|
||||
>
|
||||
</div>
|
||||
<div class="form-check" id="{device.id}-shutdown-cron">
|
||||
<input class="form-check-input" type="radio" name="shutdown-disable" id="shutdown-radio-disabled-{modalDevice.id}"
|
||||
bind:group={modalDevice["shutdown"]["enabled"]} value={false} checked={!modalDevice["shutdown"]["enabled"]}>
|
||||
<label class="form-check-label" for="shutdown-radio-disabled-{modalDevice.id}">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="shutdown-disable"
|
||||
id="shutdown-radio-disabled-{modalDevice.id}"
|
||||
bind:group={modalDevice["shutdown"]["enabled"]}
|
||||
value={false}
|
||||
checked={!modalDevice["shutdown"]["enabled"]}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="shutdown-radio-disabled-{modalDevice.id}"
|
||||
>
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="shutdown-enable" id="shutdown-radio-enabled-{modalDevice.id}"
|
||||
bind:group={modalDevice["shutdown"]["enabled"]} value={true} checked={modalDevice["shutdown"]["enabled"]}>
|
||||
<label class="form-check-label" for="shutdown-radio-enabled-{modalDevice.id}">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="shutdown-enable"
|
||||
id="shutdown-radio-enabled-{modalDevice.id}"
|
||||
bind:group={modalDevice["shutdown"]["enabled"]}
|
||||
value={true}
|
||||
checked={modalDevice["shutdown"]["enabled"]}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="shutdown-radio-enabled-{modalDevice.id}"
|
||||
>
|
||||
Enabled
|
||||
</label>
|
||||
</div>
|
||||
<div class="input-group my-1" hidden={!modalDevice["shutdown"]["enabled"]}>
|
||||
<span class="input-group-text rounded-0 rounded-start" id="shutdown-cron-{modalDevice.id}">Cron</span>
|
||||
<input type="text" class="form-control rounded-0 rounded-end" placeholder="* /4 * * *" aria-label="Crontab" aria-describedby="shutdown-cron-{modalDevice.id}" bind:value={modalDevice["shutdown"]["cron"]}>
|
||||
<div
|
||||
class="input-group my-1"
|
||||
hidden={!modalDevice["shutdown"]["enabled"]}
|
||||
>
|
||||
<span
|
||||
class="input-group-text rounded-0 rounded-start"
|
||||
id="shutdown-cron-{modalDevice.id}">Cron</span
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control rounded-0 rounded-end"
|
||||
placeholder="* /4 * * *"
|
||||
aria-label="Crontab"
|
||||
aria-describedby="shutdown-cron-{modalDevice.id}"
|
||||
bind:value={modalDevice["shutdown"]["cron"]}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-between">
|
||||
<button type="button" class="btn btn-outline-danger" data-bs-dismiss="modal" on:click={deleteDevice}>Delete</button>
|
||||
<button type="submit" form="form-{modalDevice.id}" class="btn btn-outline-success">Save changes</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-danger"
|
||||
data-bs-dismiss="modal"
|
||||
on:click={deleteDevice}>Delete</button
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
form="form-{modalDevice.id}"
|
||||
class="btn btn-outline-success">Save changes</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -309,7 +634,7 @@
|
||||
|
||||
.card {
|
||||
border-radius: 2em;
|
||||
background-color: var(--bg-lighter);
|
||||
background-color: var(--bg-lighter);
|
||||
}
|
||||
|
||||
.spinner-border {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import socketStore from '../socketStore.js';
|
||||
import socketStore from "../socketStore.js";
|
||||
import DarkToggle from "./DarkToggle.svelte";
|
||||
export let visitors;
|
||||
export let settings;
|
||||
@@ -7,37 +7,37 @@
|
||||
let addDevice = {
|
||||
wake: {
|
||||
enabled: false,
|
||||
cron: ""
|
||||
cron: "",
|
||||
},
|
||||
shutdown: {
|
||||
enabled: false,
|
||||
cron: "",
|
||||
command: ""
|
||||
}
|
||||
}
|
||||
command: "",
|
||||
},
|
||||
};
|
||||
|
||||
function updateDevice(data) {
|
||||
if (Object.keys(data).length < 4) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
socketStore.sendMessage({
|
||||
type: "update_device",
|
||||
data: data
|
||||
})
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
function updateSettings() {
|
||||
socketStore.sendMessage({
|
||||
type: "update_settings",
|
||||
data: settings
|
||||
})
|
||||
data: settings,
|
||||
});
|
||||
hideModal("settings");
|
||||
}
|
||||
|
||||
function scanNetwork() {
|
||||
socketStore.sendMessage({
|
||||
type: "scan_network"
|
||||
})
|
||||
type: "scan_network",
|
||||
});
|
||||
const btnScan = document.querySelector("#btnScan");
|
||||
const btnScanSpinner = document.querySelector("#btnScanSpinner");
|
||||
const btnScanText = document.querySelector("#btnScanText");
|
||||
@@ -52,33 +52,33 @@
|
||||
updateDevice(dev);
|
||||
}
|
||||
|
||||
const restoreFromFile = (e) => {
|
||||
const restoreFromFile = (e) => {
|
||||
let file = e.target.files[0];
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(file);
|
||||
reader.onload = e => {
|
||||
reader.onload = (e) => {
|
||||
let data = JSON.parse(e.target.result);
|
||||
if (Array.isArray(data)) {
|
||||
// v2 file restore
|
||||
data.forEach((device) => {
|
||||
updateDevice(device)
|
||||
})
|
||||
updateDevice(device);
|
||||
});
|
||||
} else {
|
||||
// v1 file restore
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
value["mac"] = key
|
||||
value["link"] = ""
|
||||
updateDevice(value)
|
||||
value["mac"] = key;
|
||||
value["link"] = "";
|
||||
updateDevice(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
hideModal("settings");
|
||||
}
|
||||
};
|
||||
|
||||
function backupToFile() {
|
||||
socketStore.sendMessage({
|
||||
type: "backup"
|
||||
})
|
||||
type: "backup",
|
||||
});
|
||||
}
|
||||
|
||||
function hideModal(id) {
|
||||
@@ -86,102 +86,219 @@
|
||||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||
modal.hide();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<nav class="navbar navbar-expand-sm">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-brand" href="/">
|
||||
<img src="favicon.png" alt="Logo" width="24" height="24" class="me-2">
|
||||
<img
|
||||
src="favicon.png"
|
||||
alt="Logo"
|
||||
width="24"
|
||||
height="24"
|
||||
class="me-2"
|
||||
/>
|
||||
UpSnap
|
||||
</div>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNavAltMarkup"
|
||||
aria-controls="navbarNavAltMarkup"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-icon" />
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<span class="ms-auto d-flex">
|
||||
<DarkToggle />
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-light dropdown-toggle px-3 me-2 py-2" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa-solid fa-wrench me-2"></i>More
|
||||
<button
|
||||
class="btn btn-light dropdown-toggle px-3 me-2 py-2"
|
||||
type="button"
|
||||
id="dropdownMenuButton1"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fa-solid fa-wrench me-2" />More
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<ul
|
||||
class="dropdown-menu"
|
||||
aria-labelledby="dropdownMenuButton1"
|
||||
>
|
||||
<li>
|
||||
<button class="dropdown-item" data-bs-toggle="modal" data-bs-target="#addDevice">
|
||||
<i class="fa-solid fa-plus me-2"></i>Add device
|
||||
<button
|
||||
class="dropdown-item"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#addDevice"
|
||||
>
|
||||
<i class="fa-solid fa-plus me-2" />Add device
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="dropdown-item" data-bs-toggle="modal" data-bs-target="#settings">
|
||||
<i class="fa-solid fa-sliders me-2"></i>Settings
|
||||
<button
|
||||
class="dropdown-item"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#settings"
|
||||
>
|
||||
<i class="fa-solid fa-sliders me-2" />Settings
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn btn-light px-3 me-2 py-2 pe-none">
|
||||
<i class="me-2 fa-solid {visitors === 1 ? "fa-user" : "fa-user-group"}"></i>{visitors} {visitors === 1 ? "Visitor" : "Visitors"}
|
||||
<i
|
||||
class="me-2 fa-solid {visitors === 1
|
||||
? 'fa-user'
|
||||
: 'fa-user-group'}"
|
||||
/>{visitors}
|
||||
{visitors === 1 ? "Visitor" : "Visitors"}
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="modal fade" id="addDevice" tabindex="-1" aria-labelledby="addDeviceLabel" aria-hidden="true">
|
||||
<div
|
||||
class="modal fade"
|
||||
id="addDevice"
|
||||
tabindex="-1"
|
||||
aria-labelledby="addDeviceLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fw-bold" id="addDeviceLabel">Add device</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<h5 class="modal-title fw-bold" id="addDeviceLabel">
|
||||
Add device
|
||||
</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addForm" on:submit|preventDefault={() => updateDevice(addDevice)}>
|
||||
<form
|
||||
id="addForm"
|
||||
on:submit|preventDefault={() => updateDevice(addDevice)}
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="mb-3">
|
||||
<label for="inputNameAddDevice" class="form-label">Device name</label>
|
||||
<input type="text" class="form-control" id="inputNameAddDevice" placeholder="Max PC" bind:value="{addDevice.name}" required>
|
||||
<label
|
||||
for="inputNameAddDevice"
|
||||
class="form-label">Device name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputNameAddDevice"
|
||||
placeholder="Max PC"
|
||||
bind:value={addDevice.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="mb-3">
|
||||
<label for="inputMacAddDevice" class="form-label">Mac address</label>
|
||||
<input type="text" class="form-control" id="inputMacAddDevice" placeholder="aa:aa:aa:aa:aa:aa" bind:value="{addDevice.mac}" pattern="^([0-9A-Fa-f]{'{'}2{'}'}[:-]){'{'}5{'}'}([0-9A-Fa-f]{'{'}2{'}'})|([0-9a-fA-F]{'{'}4{'}'}\\.[0-9a-fA-F]{'{'}4{'}'}\\.[0-9a-fA-F]{'{'}4{'}'})$" required>
|
||||
<label
|
||||
for="inputMacAddDevice"
|
||||
class="form-label">Mac address</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputMacAddDevice"
|
||||
placeholder="aa:aa:aa:aa:aa:aa"
|
||||
bind:value={addDevice.mac}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="mb-3">
|
||||
<label for="inputIpAddDevice" class="form-label">IP address</label>
|
||||
<input type="text" class="form-control" id="inputIpAddDevice" placeholder="192.168.1.1" bind:value="{addDevice.ip}" pattern="^([01]?\d\d?|2[0-4]\d|25[0-5])(?:\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])){'{'}3{'}'}$" required>
|
||||
<label for="inputIpAddDevice" class="form-label"
|
||||
>IP address</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputIpAddDevice"
|
||||
placeholder="192.168.1.1"
|
||||
bind:value={addDevice.ip}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="mb-3">
|
||||
<label for="inputNetmaskAddDevice" class="form-label">Netmask</label>
|
||||
<input type="text" class="form-control" id="inputNetmaskAddDevice" placeholder="255.255.255.0" bind:value="{addDevice.netmask}" pattern="^(((255\.){'{'}3{'}'}(255|254|252|248|240|224|192|128|0+))|((255\.){'{'}2{'}'}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){'{'}2{'}'})|((255|254|252|248|240|224|192|128|0+)(\.0+){'{'}3{'}'}))$" required>
|
||||
<label
|
||||
for="inputNetmaskAddDevice"
|
||||
class="form-label">Netmask</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputNetmaskAddDevice"
|
||||
placeholder="255.255.255.0"
|
||||
bind:value={addDevice.netmask}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-3">
|
||||
<label for="inputLinkAddDevice" class="form-label">Web link</label>
|
||||
<input type="text" class="form-control" id="inputILinkAddDevice" placeholder="http://...." bind:value="{addDevice.link}">
|
||||
<label for="inputLinkAddDevice" class="form-label"
|
||||
>Web link</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputILinkAddDevice"
|
||||
placeholder="http://...."
|
||||
bind:value={addDevice.link}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-auto ms-auto">
|
||||
<button type="submit" form="addForm" class="btn btn-outline-success">Add device</button>
|
||||
<button
|
||||
type="submit"
|
||||
form="addForm"
|
||||
class="btn btn-outline-success"
|
||||
>Add device</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="fw-bold">Network discovery</h5>
|
||||
{#if !settings.discovery}
|
||||
<div class="callout callout-danger mb-2">
|
||||
<p class="m-0">To enable this option, please enter your network address in the settings.</p>
|
||||
<p class="m-0">
|
||||
To enable this option, please enter your network
|
||||
address in the settings.
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
<button id="btnScan" class="btn btn-secondary" type="button" on:click={scanNetwork} disabled={!settings.discovery}>
|
||||
<span id="btnScanSpinner" class="spinner-grow spinner-grow-sm d-none" role="status" aria-hidden="true"></span>
|
||||
<button
|
||||
id="btnScan"
|
||||
class="btn btn-secondary"
|
||||
type="button"
|
||||
on:click={scanNetwork}
|
||||
disabled={!settings.discovery}
|
||||
>
|
||||
<span
|
||||
id="btnScanSpinner"
|
||||
class="spinner-grow spinner-grow-sm d-none"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span id="btnScanText">Scan</span>
|
||||
</button>
|
||||
{#if settings.scan_network?.length}
|
||||
@@ -203,8 +320,15 @@
|
||||
<td>{device.netmask}</td>
|
||||
<td>{device.mac}</td>
|
||||
<td>
|
||||
<button type="button" id="btnAdd{i}" class="btn btn-outline-secondary py-0" on:click={() => addScan(i)}>
|
||||
<i class="fa-solid fa-plus fa-sm"></i>
|
||||
<button
|
||||
type="button"
|
||||
id="btnAdd{i}"
|
||||
class="btn btn-outline-secondary py-0"
|
||||
on:click={() => addScan(i)}
|
||||
>
|
||||
<i
|
||||
class="fa-solid fa-plus fa-sm"
|
||||
/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -218,57 +342,119 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="settings" tabindex="-1" aria-labelledby="settingsLabel" aria-hidden="true">
|
||||
<div
|
||||
class="modal fade"
|
||||
id="settings"
|
||||
tabindex="-1"
|
||||
aria-labelledby="settingsLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fw-bold" id="settingsLabel">Settings</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="settingsForm" on:submit|preventDefault={updateSettings}>
|
||||
<form
|
||||
id="settingsForm"
|
||||
on:submit|preventDefault={updateSettings}
|
||||
>
|
||||
<h5 class="fw-bold">General</h5>
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<div class="mb-3">
|
||||
<label for="inputNetworkDiscovery" class="form-label">Network discovery address</label>
|
||||
<input type="text" class="form-control" id="inputNetworkDiscovery" placeholder="192.168.1.0/24" bind:value="{settings.discovery}" pattern="^([01]?\d\d?|2[0-4]\d|25[0-5])(?:\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])){'{'}2{'}'}(?:\.(?:0))(?:/[0-2]\d|/3[0-2])$">
|
||||
<label
|
||||
for="inputNetworkDiscovery"
|
||||
class="form-label"
|
||||
>Network discovery address</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputNetworkDiscovery"
|
||||
placeholder="192.168.1.0/24"
|
||||
bind:value={settings.discovery}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="mb-3">
|
||||
<label for="inputIntervalSettings" class="form-label">Interval (seconds)</label>
|
||||
<input type="number" class="form-control" id="inputIntervalSettings" min="5" bind:value="{settings.interval}" required>
|
||||
<label
|
||||
for="inputIntervalSettings"
|
||||
class="form-label">Interval (seconds)</label
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
id="inputIntervalSettings"
|
||||
min="5"
|
||||
bind:value={settings.interval}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label" for="flexCheckDefault">
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="flexCheckDefault"
|
||||
>
|
||||
Enable notifications
|
||||
</label>
|
||||
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" bind:checked={settings.notifications}>
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
id="flexCheckDefault"
|
||||
bind:checked={settings.notifications}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row mb-3">
|
||||
<div class="col-auto ms-auto">
|
||||
<button type="submit" form="settingsForm" class="btn btn-outline-success">Save</button>
|
||||
<button
|
||||
type="submit"
|
||||
form="settingsForm"
|
||||
class="btn btn-outline-success">Save</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="fw-bold">Backup/Restore</h5>
|
||||
<div class="callout callout-info mb-2">
|
||||
<p class="m-0">Backup file structure has changed in v2. You can still restore both versions with this file upload.</p>
|
||||
<p class="m-0">
|
||||
Backup file structure has changed in v2. You can still
|
||||
restore both versions with this file upload.
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="inputRestore" class="form-label">Restore from .json</label>
|
||||
<input id="inputRestore" type="file" class="form-control" accept=".json" on:change={(e) => restoreFromFile(e)}>
|
||||
<label for="inputRestore" class="form-label"
|
||||
>Restore from .json</label
|
||||
>
|
||||
<input
|
||||
id="inputRestore"
|
||||
type="file"
|
||||
class="form-control"
|
||||
accept=".json"
|
||||
on:change={(e) => restoreFromFile(e)}
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button type="button" class="btn btn-secondary" on:click={backupToFile}>
|
||||
<i class="fa-solid fa-download me-2"></i>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
on:click={backupToFile}
|
||||
>
|
||||
<i class="fa-solid fa-download me-2" />
|
||||
Export .json
|
||||
</button>
|
||||
</div>
|
||||
@@ -289,15 +475,15 @@
|
||||
.dropdown-menu {
|
||||
background-color: var(--bg-lighter);
|
||||
|
||||
li:first-child>button {
|
||||
li:first-child > button {
|
||||
border-radius: 0.5rem 0.5rem 0rem 0rem;
|
||||
}
|
||||
|
||||
li:last-child>button {
|
||||
li:last-child > button {
|
||||
border-radius: 0rem 0rem 0.5rem 0.5rem;
|
||||
}
|
||||
|
||||
li:only-child>button {
|
||||
li:only-child > button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,20 @@
|
||||
</script>
|
||||
|
||||
<div class="position-fixed bottom-0 end-0 p-3 toast-container">
|
||||
<div class="toast fade {toast.show ? 'show' : 'hide'} {toast.color}-bg" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div
|
||||
class="toast fade {toast.show ? 'show' : 'hide'} {toast.color}-bg"
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<div class="toast-header">
|
||||
<strong id="toast-title" class="me-auto">{toast.title}</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="toast"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
<div class="toast-body fw-bold">
|
||||
{toast.message}
|
||||
@@ -18,19 +28,19 @@
|
||||
@import "../variables.scss";
|
||||
|
||||
.toast-container {
|
||||
z-index: 11;
|
||||
border-radius: $toast-border-radius;
|
||||
z-index: 11;
|
||||
border-radius: $toast-border-radius;
|
||||
|
||||
div>.toast-body {
|
||||
div > .toast-body {
|
||||
color: $dark-lighter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.success-bg {
|
||||
.success-bg {
|
||||
background-color: var(--success);
|
||||
}
|
||||
|
||||
.danger-bg {
|
||||
.danger-bg {
|
||||
background-color: var(--danger);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user