Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c2dc0f183 | ||
|
|
8d08392cad | ||
|
|
ba43ad2bbc | ||
|
|
84025a83ab | ||
|
|
17caf491f1 | ||
|
|
133d0b7844 | ||
|
|
5ca7125328 | ||
|
|
0389c8b7fe | ||
|
|
dd56403d93 | ||
|
|
709cac04f6 | ||
|
|
530e54a596 | ||
|
|
20e9f4940e | ||
|
|
ec5fcf1ee7 | ||
|
|
127f58bd36 | ||
|
|
08e7820f37 |
No files matched your search
@@ -32,3 +32,8 @@ coverage
|
||||
# files created with miaoShare service
|
||||
temp
|
||||
miaoShare.db
|
||||
addUser.js
|
||||
removeUser.js
|
||||
|
||||
# JWT SK
|
||||
*.key
|
||||
@@ -14,6 +14,8 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width:100%;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"PORT":17057,
|
||||
"userGroupSetting":{
|
||||
"admin":{
|
||||
"cloudCapacity":2147483648
|
||||
},
|
||||
"guest":{
|
||||
"cloudCapacity":2147483648
|
||||
}
|
||||
},
|
||||
"enableGuest":false,
|
||||
"tokenExpiresIn":168
|
||||
}
|
||||
Generated
+231
-24
@@ -9,22 +9,30 @@
|
||||
"version": "1.2.0",
|
||||
"dependencies": {
|
||||
"aes-js": "^3.1.2",
|
||||
"better-sqlite3": "^8.7.0",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.2.0",
|
||||
"express": "^4.18.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"js-base64": "^3.7.6",
|
||||
"js-base64": "^3.7.7",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"node-cron": "^3.0.3",
|
||||
"pinia": "^2.1.7",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"uuid": "^9.0.1",
|
||||
"vicons": "^0.0.1",
|
||||
"vue": "^3.4.15",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-router": "^4.2.5"
|
||||
"vue-router": "^4.2.5",
|
||||
"websocket-heartbeat-js": "^1.1.3",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vicons/fluent": "^0.12.0",
|
||||
"@vicons/ionicons5": "^0.12.0",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"better-sqlite3": "^8.7.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"naive-ui": "^2.37.3",
|
||||
"cors": "^2.8.5",
|
||||
"naive-ui": "^2.38.1",
|
||||
"vfonts": "^0.0.3",
|
||||
"vite": "^5.0.11"
|
||||
}
|
||||
@@ -651,6 +659,18 @@
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@vicons/fluent": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vicons/fluent/-/fluent-0.12.0.tgz",
|
||||
"integrity": "sha512-ATCiqPuiJ6RI5GBlD3BIpZ9Xw4MsCA4RpI5oR6MCti4quS4mX1Gp6N74FCzw7lgOj+80rV4HMKhZTVInwimpVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vicons/ionicons5": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz",
|
||||
"integrity": "sha512-Iy1EUVRpX0WWxeu1VIReR1zsZLMc4fqpt223czR+Rpnrwu7pt46nbnC2ycO7ItI/uqDLJxnbcMC7FujKs9IfFA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz",
|
||||
@@ -811,12 +831,14 @@
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/better-sqlite3": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-8.7.0.tgz",
|
||||
"integrity": "sha512-99jZU4le+f3G6aIl6PmmV0cxUIWqKieHxsiF7G34CVFiE+/UabpYqkU0NJIkY/96mQKikHeBjtR27vFfs5JpEw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"bindings": "^1.5.0",
|
||||
@@ -827,6 +849,7 @@
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
@@ -835,6 +858,7 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
@@ -868,11 +892,17 @@
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -923,7 +953,8 @@
|
||||
"node_modules/chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/clipboard": {
|
||||
"version": "2.0.11",
|
||||
@@ -1027,6 +1058,7 @@
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
@@ -1035,6 +1067,11 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||
},
|
||||
"node_modules/css-render": {
|
||||
"version": "0.15.12",
|
||||
"resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz",
|
||||
@@ -1089,6 +1126,7 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-response": "^3.1.0"
|
||||
},
|
||||
@@ -1100,6 +1138,7 @@
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
@@ -1144,10 +1183,19 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -1171,6 +1219,7 @@
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
@@ -1266,6 +1315,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz",
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -1319,7 +1369,8 @@
|
||||
"node_modules/file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
@@ -1357,7 +1408,8 @@
|
||||
"node_modules/fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
@@ -1405,7 +1457,8 @@
|
||||
"node_modules/github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
|
||||
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/good-listener": {
|
||||
"version": "1.2.2",
|
||||
@@ -1505,7 +1558,8 @@
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
@@ -1515,7 +1569,8 @@
|
||||
"node_modules/ini": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
@@ -1535,15 +1590,60 @@
|
||||
}
|
||||
},
|
||||
"node_modules/js-base64": {
|
||||
"version": "3.7.6",
|
||||
"resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.6.tgz",
|
||||
"integrity": "sha512-NPrWuHFxFUknr1KqJRDgUQPexQF0uIJWjeT+2KjEePhitQxQEx5EJBG1lVn5/hc8aLycTpXrDOgPQ6Zq+EDiTA=="
|
||||
"version": "3.7.7",
|
||||
"resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.7.tgz",
|
||||
"integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="
|
||||
},
|
||||
"node_modules/jsbn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/jwa/-/jwa-1.4.1.tgz",
|
||||
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||
@@ -1556,6 +1656,41 @@
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
@@ -1633,6 +1768,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@@ -1640,12 +1776,14 @@
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
@@ -1653,9 +1791,9 @@
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/naive-ui": {
|
||||
"version": "2.37.3",
|
||||
"resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.37.3.tgz",
|
||||
"integrity": "sha512-aUkHFXVIluSi8Me+npbcsdv1NYhVMj5t9YaruoCESlqmfqspj+R2QHEVXkTtUI1kQwVrABMCtAGq/wountqjZA==",
|
||||
"version": "2.38.1",
|
||||
"resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.38.1.tgz",
|
||||
"integrity": "sha512-AnU1FQ7K/CbhguAX++V4kCFjk7h7RvWt4nvZPRjORMpq+fUIlzD+EcQ5Cv1VqDloNF8+eMv4Akc2Ogacc9S+5A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@css-render/plugin-bem": "^0.15.12",
|
||||
@@ -1696,7 +1834,8 @@
|
||||
"node_modules/napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
@@ -1710,6 +1849,7 @@
|
||||
"version": "3.54.0",
|
||||
"resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.54.0.tgz",
|
||||
"integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
@@ -1717,10 +1857,30 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/node-cron": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/node-cron/-/node-cron-3.0.3.tgz",
|
||||
"integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==",
|
||||
"dependencies": {
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-cron/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -1745,6 +1905,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -1828,6 +1989,7 @@
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
||||
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.0",
|
||||
"expand-template": "^2.0.3",
|
||||
@@ -1865,6 +2027,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
@@ -1907,6 +2070,7 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
@@ -1921,6 +2085,7 @@
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@@ -2107,12 +2272,14 @@
|
||||
"node_modules/simple-concat": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/simple-get": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz",
|
||||
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"decompress-response": "^6.0.0",
|
||||
"once": "^1.3.1",
|
||||
@@ -2153,6 +2320,7 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
@@ -2187,6 +2355,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -2207,6 +2376,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.1.tgz",
|
||||
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
@@ -2218,6 +2388,7 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
@@ -2267,6 +2438,7 @@
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
},
|
||||
@@ -2297,7 +2469,8 @@
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
@@ -2307,6 +2480,14 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
|
||||
@@ -2462,6 +2643,11 @@
|
||||
"vue": "^3.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/websocket-heartbeat-js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/websocket-heartbeat-js/-/websocket-heartbeat-js-1.1.3.tgz",
|
||||
"integrity": "sha512-wiCMVsWK7KflVCC5f+rSTyzILDZX88A7BJfdJXt5THNDut7uUNP83r5AvdZ1El/8yOwGujC7K3iuxj96CT4gbQ=="
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
@@ -2479,7 +2665,28 @@
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.16.0.tgz",
|
||||
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
|
||||
+17
-7
@@ -4,30 +4,40 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"server": "concurrently \"node server.js\" \"node server_page.js\"",
|
||||
"vite": "vite",
|
||||
"dev": "concurrently \"node server.js\" \"vite\"",
|
||||
"server": "concurrently \"node server.js\"",
|
||||
"miaoShare": "concurrently \"node server.js\" \"vite\"",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"aes-js": "^3.1.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"express": "^4.18.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"js-base64": "^3.7.6",
|
||||
"js-base64": "^3.7.7",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"node-cron": "^3.0.3",
|
||||
"pinia": "^2.1.7",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"uuid": "^9.0.1",
|
||||
"vicons": "^0.0.1",
|
||||
"vue": "^3.4.15",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-router": "^4.2.5"
|
||||
"vue-router": "^4.2.5",
|
||||
"websocket-heartbeat-js": "^1.1.3",
|
||||
"workerpool": "^9.1.3",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"better-sqlite3": "^8.7.0",
|
||||
"cors": "^2.8.5",
|
||||
"@vicons/fluent": "^0.12.0",
|
||||
"@vicons/ionicons5": "^0.12.0",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"better-sqlite3": "^8.7.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"naive-ui": "^2.37.3",
|
||||
"cors": "^2.8.5",
|
||||
"naive-ui": "^2.38.1",
|
||||
"vfonts": "^0.0.3",
|
||||
"vite": "^5.0.11"
|
||||
}
|
||||
|
||||
Generated
+2196
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,131 +1,234 @@
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import Database from 'better-sqlite3';
|
||||
import fs from "fs"
|
||||
/*
|
||||
仅作为临时测试使用
|
||||
后续对这个进行拆分
|
||||
*/
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import Database from "better-sqlite3";
|
||||
import fs from "fs";
|
||||
import { WebSocketServer } from "ws";
|
||||
import http from "http";
|
||||
import { v4 as uuid4 } from "uuid";
|
||||
import jwt from "jsonwebtoken";
|
||||
import sha256 from "crypto-js/sha256.js";
|
||||
import Config from "./miaoShareConfig.json" assert { type: "json" };
|
||||
import { verify } from "crypto";
|
||||
// function loadJSON(path){
|
||||
// return JSON.parse(fs.readFileSync(path).toString());
|
||||
// }
|
||||
// const Config = loadJSON("./miaoShareConfig.json");
|
||||
|
||||
|
||||
|
||||
const HOST = '0.0.0.0'
|
||||
const PORT = '17058'
|
||||
const HOST = "0.0.0.0";
|
||||
const PORT = Config.PORT;
|
||||
// const PORT = 17057;
|
||||
const fileDir = "./temp";
|
||||
const dbPath = './miaoShare.db'
|
||||
const dbPath = "./miaoShare.db";
|
||||
const PATH = "./dist";
|
||||
|
||||
const JWT_SK = fs.readFileSync("JWT_SK.key").toString();
|
||||
|
||||
// 上传文件最大大小:200MB
|
||||
const uploadMaxSize=200*1024*1024
|
||||
const uploadMaxSize = 200 * 1024 * 1024;
|
||||
// 一天的毫秒数
|
||||
const millisecondsOfTheDay = parseInt(24*3600*1000)
|
||||
const millisecondsOfOneDay = parseInt(24 * 3600 * 1000);
|
||||
// 文件3天过期
|
||||
const LifeOfFile=millisecondsOfTheDay*3
|
||||
const LifeOfFile = millisecondsOfOneDay * 3;
|
||||
// const LifeOfFile=0
|
||||
|
||||
// 数据库相关
|
||||
const db = new Database(dbPath, { verbose: null });
|
||||
db.prepare(`CREATE TABLE IF NOT EXISTS id2fileName (
|
||||
db.exec(
|
||||
`CREATE TABLE IF NOT EXISTS id2fileName (
|
||||
id CHAR(8) PRIMARY KEY NOT NULL UNIQUE,
|
||||
fileName TEXT NOT NULL,
|
||||
timestamp INT NOT NULL,
|
||||
freeze tinyint(1) NOT NULL,
|
||||
downloadCredit INT NOT NULL
|
||||
);`).run()
|
||||
const stmt_getFileName = db.prepare(`SELECT fileName FROM id2fileName WHERE id = ?`)
|
||||
const stmt_getTimestamp = db.prepare(`SELECT timestamp FROM id2fileName WHERE id = ?`)
|
||||
const stmt_getFreeze = db.prepare(`SELECT freeze FROM id2fileName WHERE id = ?`)
|
||||
const stmt_getDownloadCredit = db.prepare(`SELECT downloadCredit FROM id2fileName WHERE id = ?`)
|
||||
const stmt_getFreezenIDs = db.prepare(`SELECT id FROM id2fileName WHERE freeze = 1`)
|
||||
const stmt_insertId2FileName = db.prepare(`INSERT INTO id2fileName (id, fileName, timestamp, freeze, downloadCredit) VALUES (?, ?, ?, ?, ?)`)
|
||||
const stmt_deleteExpireData = db.prepare(`DELETE FROM id2fileName WHERE freeze=1`)
|
||||
const stmt_freezeExpireData = db.prepare(`UPDATE id2fileName SET freeze=1 WHERE timestamp < ? OR downloadCredit < 1`)
|
||||
const stmt_updateDownloadCredit = db.prepare(`UPDATE id2fileName SET downloadCredit=downloadCredit-1 WHERE id = ?`)
|
||||
downloadCredit INT NOT NULL,
|
||||
fileSize INT NOT NULL,
|
||||
sender_id CHAR(32) NOT NULL
|
||||
);`
|
||||
);
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS USERS(
|
||||
user_id CHAR(32) PRIMARY KEY UNIQUE,
|
||||
password_hash CHAR(32) NOT NULL,
|
||||
user_group VARCHAR(32) NOT NULL,
|
||||
uploadFilesSizeCount BIGINT NOT NULL,
|
||||
email CHAR(64)
|
||||
);`);
|
||||
const stmt_getPermissionGroup = db.prepare("SELECT user_group FROM USERS WHERE user_id = ? AND password_hash = ?");
|
||||
const stmt_getFileName = db.prepare(`SELECT fileName FROM id2fileName WHERE id = ?`);
|
||||
const stmt_getFileInfo = db.prepare(`SELECT fileName,fileSize FROM id2fileName WHERE id = ?`);
|
||||
const stmt_getUserUploadFilesSizeCount = db.prepare("SELECT uploadFilesSizeCount FROM USERS WHERE user_id = ?");
|
||||
const stmt_getUserGroup = db.prepare("SELECT user_group FROM USERS WHERE user_id = ?");
|
||||
const stmt_getTimestamp = db.prepare(`SELECT timestamp FROM id2fileName WHERE id = ?`);
|
||||
const stmt_getFileSize = db.prepare(`SELECT fileSize FROM id2fileName WHERE id = ?`);
|
||||
const stmt_getFreeze = db.prepare(`SELECT freeze FROM id2fileName WHERE id = ?`);
|
||||
const stmt_getDownloadCredit = db.prepare(`SELECT downloadCredit FROM id2fileName WHERE id = ?`);
|
||||
const stmt_getFreezenIDs = db.prepare(`SELECT id,fileSize,sender_id FROM id2fileName WHERE freeze = 1`);
|
||||
const stmt_insertId2FileName = db.prepare(
|
||||
`INSERT INTO id2fileName (id, fileName, timestamp, freeze, downloadCredit, fileSize, sender_id) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
||||
);
|
||||
const stmt_deleteExpireData = db.prepare(`DELETE FROM id2fileName WHERE freeze=1`);
|
||||
const stmt_freezeExpireData = db.prepare(`UPDATE id2fileName SET freeze=1 WHERE timestamp < ? OR downloadCredit < 1`);
|
||||
const stmt_updateDownloadCredit = db.prepare(`UPDATE id2fileName SET downloadCredit=downloadCredit-1 WHERE id = ?`);
|
||||
const stmt_updateUserUploadFilesSizeCount = db.prepare(`UPDATE USERS SET uploadFilesSizeCount=uploadFilesSizeCount+? WHERE user_id = ?`);
|
||||
const stmt_updateUserUploadFilesSizeCount_d = db.prepare(`UPDATE USERS SET uploadFilesSizeCount=uploadFilesSizeCount-? WHERE user_id = ?`);
|
||||
|
||||
const db_insert = (id,fileName,downloadCredit)=>{
|
||||
stmt_insertId2FileName.run(id,fileName,parseInt(Date.now()),0,downloadCredit)
|
||||
}
|
||||
const db_getFileName = (id)=>{
|
||||
try{
|
||||
if(stmt_getFreeze.get(id)['freeze'] == 1)return null
|
||||
return stmt_getFileName.get(id)['fileName']
|
||||
}catch(e){
|
||||
return null
|
||||
const db_insert = (id, fileName, downloadCredit, fileSize, senderId) => {
|
||||
stmt_insertId2FileName.run(id, fileName, parseInt(Date.now()), 0, downloadCredit, fileSize, senderId);
|
||||
};
|
||||
const db_getFileName = id => {
|
||||
try {
|
||||
if (stmt_getFreeze.get(id)["freeze"] == 1) return null;
|
||||
return stmt_getFileName.get(id)["fileName"];
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
// const db_getFileInfo = id => {
|
||||
// try {
|
||||
// if (stmt_getFreeze.get(id)["freeze"] == 1) return null;
|
||||
// return stmt_getFileSize.get(id);
|
||||
// } catch (e) {
|
||||
// return null;
|
||||
// }
|
||||
// };
|
||||
const db_getFileInfo = (function () {
|
||||
const infoCache = {};
|
||||
return function (id) {
|
||||
if (infoCache[id]) {
|
||||
return infoCache[id];
|
||||
}
|
||||
try {
|
||||
if (stmt_getFreeze.get(id)["freeze"] == 1) return null;
|
||||
const info = stmt_getFileInfo.get(id);
|
||||
infoCache[id] = info;
|
||||
return info;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
})();
|
||||
// 检查是否可下载
|
||||
const db_downloadable = (id)=>{
|
||||
try{
|
||||
const db_downloadable = id => {
|
||||
try {
|
||||
// 懒
|
||||
if(stmt_getDownloadCredit.get(id)['downloadCredit'] < 1)return false
|
||||
if(stmt_getFreeze.get(id)['freeze'] == 1)return false
|
||||
return true
|
||||
}catch(e){
|
||||
return false
|
||||
if (stmt_getDownloadCredit.get(id)["downloadCredit"] < 1) return false;
|
||||
if (stmt_getFreeze.get(id)["freeze"] == 1) return false;
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const db_updateDownloadCredit = (id)=>{
|
||||
try{
|
||||
stmt_updateDownloadCredit.run(id)
|
||||
return true
|
||||
}catch(e){
|
||||
return false
|
||||
};
|
||||
const db_updateDownloadCredit = id => {
|
||||
try {
|
||||
stmt_updateDownloadCredit.run(id);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const db_getTimestamp = id => {
|
||||
return stmt_getTimestamp.get(id)["timestamp"];
|
||||
};
|
||||
|
||||
const db_getFileSize = id => {
|
||||
return stmt_getFileSize.get(id)["fileSize"];
|
||||
};
|
||||
const db_getUserUploadFilesSizeCount = id => {
|
||||
return stmt_getUserUploadFilesSizeCount.get(id)["uploadFilesSizeCount"];
|
||||
};
|
||||
|
||||
function db_updateUserUploadFilesSizeCount(id, size) {
|
||||
return stmt_updateUserUploadFilesSizeCount.run(size, id);
|
||||
}
|
||||
|
||||
const db_getTimestamp = (id)=>{
|
||||
return stmt_getTimestamp.get(id)['timestamp']
|
||||
// 减
|
||||
function db_updateUserUploadFilesSizeCount_d(id, size) {
|
||||
return stmt_updateUserUploadFilesSizeCount_d.run(size, id);
|
||||
}
|
||||
|
||||
// 更新存储的文件,删除已经被冻结的文件以及相关记录,然后冻结过期文件
|
||||
const updateFiles = ()=>{
|
||||
try{
|
||||
let rows = stmt_getFreezenIDs.all()
|
||||
stmt_deleteExpireData.run()
|
||||
for(let row of rows){
|
||||
let fileName = row['id']
|
||||
let path = fileDir + '/' + fileName
|
||||
try{
|
||||
fs.unlink(path,(e)=>{if(e)throw e})
|
||||
}catch(e){
|
||||
console.log(e)
|
||||
continue
|
||||
const updateDBAndFiles = () => {
|
||||
try {
|
||||
let rows = stmt_getFreezenIDs.all();
|
||||
stmt_deleteExpireData.run();
|
||||
for (let row of rows) {
|
||||
let fileName = row["id"];
|
||||
let sid = row["sender_id"];
|
||||
let fsize = row["fileSize"];
|
||||
db_updateUserUploadFilesSizeCount_d(sid, fsize);
|
||||
let path = fileDir + "/" + fileName;
|
||||
try {
|
||||
fs.unlink(path, e => {
|
||||
if (e) throw e;
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
stmt_freezeExpireData.run(parseInt(Date.now()) - LifeOfFile)
|
||||
}catch(e){
|
||||
console.log(e)
|
||||
return
|
||||
stmt_freezeExpireData.run(parseInt(Date.now()) - LifeOfFile);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateFiles()
|
||||
};
|
||||
updateDBAndFiles();
|
||||
// 3小时更新一次
|
||||
setInterval(updateFiles,1000*3600*3)
|
||||
setInterval(updateDBAndFiles, 1000 * 3600 * 3);
|
||||
// // 测试环境一分钟更新一次,记得注释掉
|
||||
// setInterval(updateDBAndFiles, 1000 * 60);
|
||||
|
||||
const app = express();
|
||||
|
||||
var app = express();
|
||||
app.use(cors());
|
||||
var tempMap = {}
|
||||
app.options("*", cors());
|
||||
// var tempMap = {}
|
||||
|
||||
if (!fs.existsSync(fileDir)) {
|
||||
fs.mkdirSync(fileDir);
|
||||
}
|
||||
|
||||
function generateId(){
|
||||
function db_getPermissionGroup(id, pwdH) {
|
||||
let res = stmt_getPermissionGroup.get(id, pwdH);
|
||||
// console.log(id,pwdH,res)
|
||||
if (res && res.user_group) {
|
||||
return res.user_group;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function generateId(isGID) {
|
||||
var returnStr = "",
|
||||
range = 8,
|
||||
charStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
charStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
for(var i=0; i<range; i++){
|
||||
var index = Math.round(Math.random() * 35);
|
||||
returnStr += charStr.substring(index,index+1);
|
||||
for (var i = 0; i < range; i++) {
|
||||
if (isGID === true && i == 0) {
|
||||
var index = parseInt(Math.random() * 26);
|
||||
} else {
|
||||
var index = parseInt(Math.random() * 36);
|
||||
}
|
||||
returnStr += charStr.substring(index, index + 1);
|
||||
}
|
||||
return returnStr;
|
||||
}
|
||||
|
||||
// 上传文件接口
|
||||
function uploadFile(req, res, fileName, downloadCredit){
|
||||
function uploadFile(req, res, fileName, downloadCredit, fileSize) {
|
||||
let id = generateId();
|
||||
const fileStream = fs.createWriteStream(fileDir + '/' + id);
|
||||
db_insert(id,fileName,downloadCredit)
|
||||
const fileStream = fs.createWriteStream(fileDir + "/" + id);
|
||||
db_insert(id, fileName, downloadCredit, fileSize, req.sender_id);
|
||||
db_updateUserUploadFilesSizeCount(req.sender_id, fileSize);
|
||||
// tempMap[id] = fileName;
|
||||
// console.log(tempMap);
|
||||
req.pipe(fileStream);
|
||||
res.on('data',(c)=>{console.log(c)})
|
||||
res.on("data", c => {
|
||||
console.log(c);
|
||||
});
|
||||
req.on("end", function () {
|
||||
fileStream.end();
|
||||
res.statusCode = 200;
|
||||
@@ -138,25 +241,25 @@ function uploadFile(req, res, fileName, downloadCredit){
|
||||
});
|
||||
}
|
||||
|
||||
function downloadFile(req, res, fileId){
|
||||
function downloadFile(req, res, fileId) {
|
||||
try {
|
||||
if(db_downloadable(fileId) == false){
|
||||
if (db_downloadable(fileId) == false) {
|
||||
res.writeHead(404);
|
||||
res.end(null);
|
||||
return
|
||||
return;
|
||||
}
|
||||
db_updateDownloadCredit(fileId)
|
||||
var fileName = db_getFileName(fileId)
|
||||
if(fileName == null){
|
||||
db_updateDownloadCredit(fileId);
|
||||
var fileName = db_getFileName(fileId);
|
||||
if (fileName == null) {
|
||||
res.writeHead(404);
|
||||
res.end(null);
|
||||
return
|
||||
return;
|
||||
}
|
||||
let data = fs.readFileSync(fileDir + '/' + fileId);
|
||||
let data = fs.readFileSync(fileDir + "/" + fileId);
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "application/octet-stream ",
|
||||
"File-Name": fileName,
|
||||
'Content-Length':data.length,
|
||||
"Content-Length": data.length
|
||||
});
|
||||
res.end(data);
|
||||
} catch (err) {
|
||||
@@ -166,48 +269,345 @@ function downloadFile(req, res, fileId){
|
||||
}
|
||||
}
|
||||
|
||||
// 设置一些通用的中间件
|
||||
// app.use(bodyParser.json());
|
||||
// app.use(bodyParser.urlencoded({extended: true}));
|
||||
function getFileInfo(req, res, fileId) {
|
||||
try {
|
||||
if (db_downloadable(fileId) == false) {
|
||||
res.writeHead(404);
|
||||
res.end(null);
|
||||
return;
|
||||
}
|
||||
// db_updateDownloadCredit(fileId);
|
||||
let info = db_getFileInfo(fileId);
|
||||
if (info == null) {
|
||||
res.writeHead(404);
|
||||
res.end(null);
|
||||
return;
|
||||
}
|
||||
res.end(JSON.stringify(info));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.writeHead(404);
|
||||
res.end(null);
|
||||
}
|
||||
}
|
||||
|
||||
const getUserGroup = (function () {
|
||||
const cache = {};
|
||||
return function (userId) {
|
||||
if (!cache[userId]) {
|
||||
cache[userId] = stmt_getUserGroup.get(userId)["user_group"];
|
||||
}
|
||||
return cache[userId];
|
||||
};
|
||||
})();
|
||||
|
||||
// 验证token
|
||||
const verifyToken = (req, res, next) => {
|
||||
try {
|
||||
const token = req.headers.authorization.split(" ")[1]; // 从Authorization头中提取JWT
|
||||
console.log(req.headers.authorization, token);
|
||||
let verifyData = jwt.verify(token, JWT_SK);
|
||||
if (verifyData.exp > parseInt(Date.now())) {
|
||||
throw "token has expired";
|
||||
}
|
||||
req.sender_id = verifyData.id;
|
||||
next();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return res.status(401).json({ message: "Invalid token", err });
|
||||
}
|
||||
};
|
||||
|
||||
app.use(express.static(PATH));
|
||||
app.post("/user/login", express.json(), function (req, res) {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "*");
|
||||
res.header("Access-Control-Allow-Methods", "POST");
|
||||
// console.log(req.body)
|
||||
let { id, pwd } = req.body;
|
||||
console.log(req.body);
|
||||
let pwdH = sha256(id + pwd).toString();
|
||||
let userGroup = db_getPermissionGroup(id, pwdH);
|
||||
if (userGroup !== null) {
|
||||
res.writeHead(200);
|
||||
let token = jwt.sign({ id, pwd }, JWT_SK, { expiresIn: `${Config.tokenExpiresIn}h` });
|
||||
res.end(JSON.stringify({ status: "Success", userGroup, token }));
|
||||
} else {
|
||||
res.end(JSON.stringify({ status: "Error" }));
|
||||
}
|
||||
// console.log(req)
|
||||
});
|
||||
|
||||
app.post("/user/refrashToken", express.json(), function (req, res) {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "*");
|
||||
res.header("Access-Control-Allow-Methods", "POST");
|
||||
// console.log(req.body)
|
||||
try {
|
||||
const token = req.headers.authorization.split(" ")[1]; // 从Authorization头中提取JWT
|
||||
// const token = req.body.token
|
||||
console.log(token)
|
||||
let verifyData = jwt.verify(token, JWT_SK);
|
||||
if (verifyData.exp > parseInt(Date.now())) {
|
||||
return res.status(401).json({ status: "Error", message: "Invalid token" });
|
||||
}
|
||||
let id = verifyData.id;
|
||||
let pwd = verifyData.pwd;
|
||||
let new_token = jwt.sign({ id, pwd }, JWT_SK, { expiresIn: `${Config.tokenExpiresIn}h` });
|
||||
console.log({ token, new_token });
|
||||
res.end(JSON.stringify({ status: "Success", new_token }));
|
||||
} catch (e) {
|
||||
return res.status(401).json({ status: "Error", message: "Invalid token" });
|
||||
}
|
||||
});
|
||||
// app.all()
|
||||
app.post('/file/upload', function(req, res){
|
||||
app.post("/file/upload", verifyToken, function (req, res) {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "*");
|
||||
res.header("Access-Control-Allow-Methods", "POST");
|
||||
res.header("Access-Control-Expose-Headers", "File-Name");
|
||||
var fileSize = parseInt(req.get('Content-Length'))
|
||||
var fileSize = parseInt(req.get("Content-Length"));
|
||||
var fileName = req.query.fileName;
|
||||
var downloadCredit = parseInt(req.query.downloadCredit);
|
||||
if(downloadCredit == -1)downloadCredit = 2147483647
|
||||
if(!fileName){
|
||||
if (downloadCredit == -1) downloadCredit = 2147483647;
|
||||
if (!fileName) {
|
||||
res.statusCode = 500;
|
||||
res.end("FileName Not Found");
|
||||
}else if(fileSize > uploadMaxSize){
|
||||
} else if (
|
||||
fileSize > uploadMaxSize ||
|
||||
db_getUserUploadFilesSizeCount(req.sender_id) + fileSize > Config.userGroupSetting[getUserGroup(req.sender_id)].cloudCapacity
|
||||
) {
|
||||
res.statusCode = 500;
|
||||
res.end("Maximum size limit exceeded");
|
||||
}
|
||||
else{
|
||||
uploadFile(req, res, fileName, downloadCredit);
|
||||
} else {
|
||||
uploadFile(req, res, fileName, downloadCredit, fileSize);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/file/download', function(req, res){
|
||||
app.get("/file/download", verifyToken, function (req, res) {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "*");
|
||||
res.header("Access-Control-Allow-Methods", "GET");
|
||||
res.header("Access-Control-Expose-Headers", "File-Name");
|
||||
var fileId = req.query.fileName;
|
||||
if(!fileId){
|
||||
if (!fileId) {
|
||||
res.statusCode = 500;
|
||||
res.end("FileName Not Found");
|
||||
}else{
|
||||
} else {
|
||||
downloadFile(req, res, fileId);
|
||||
}
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
app.listen(PORT, HOST, function(){
|
||||
// console.log('Example app listening on port ' + PORT + '.');
|
||||
app.get("/file/info", verifyToken, function (req, res) {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "*");
|
||||
res.header("Access-Control-Allow-Methods", "GET");
|
||||
res.header("Access-Control-Expose-Headers", "File-Name");
|
||||
var fileId = req.query.fileName;
|
||||
if (!fileId) {
|
||||
res.statusCode = 500;
|
||||
res.end("FileName Not Found");
|
||||
} else {
|
||||
getFileInfo(req, res, fileId);
|
||||
}
|
||||
});
|
||||
|
||||
// // 启动服务器
|
||||
// app.listen(PORT, HOST, function(){
|
||||
// console.log(app)
|
||||
// // console.log(`miaoShare server listening on ${}`)
|
||||
// });
|
||||
|
||||
const server = http.createServer(app);
|
||||
const wss = new WebSocketServer({ server });
|
||||
// 先实现最低限度功能,暂时不考虑身份认证
|
||||
const groupsMap = {};
|
||||
// 一天不活跃的群组将被清除
|
||||
const LifeOfGroup = millisecondsOfOneDay * 1;
|
||||
// const LifeOfGroup = 0;
|
||||
// 根据uuid查询加入的群组
|
||||
const uuid2Groups = {};
|
||||
|
||||
db.exec(
|
||||
`CREATE TABLE IF NOT EXISTS CHATGROUPS (
|
||||
gID CHAR(8) PRIMARY KEY UNIQUE NOT NULL,
|
||||
verify TEXT NOT NULL,
|
||||
activeTimestamp INT NOT NULL
|
||||
);`
|
||||
);
|
||||
|
||||
const stmt_getExpiredGroups = db.prepare(`SELECT gID FROM CHATGROUPS WHERE activeTimestamp < ? `);
|
||||
|
||||
const stmt_getGroupVerify = db.prepare(`SELECT verify FROM CHATGROUPS WHERE gID = ? `);
|
||||
|
||||
const stmt_insertCHATGROUPS = db.prepare(`INSERT INTO CHATGROUPS (gID,verify, activeTimestamp) VALUES (?, ?, ?)`);
|
||||
|
||||
const stmt_updateCHATGROUPS_ActiveTimestamp = db.prepare(`UPDATE CHATGROUPS SET activeTimestamp=? WHERE gID=?`);
|
||||
|
||||
const stmt_deleteGroup = db.prepare(`DELETE FROM CHATGROUPS WHERE gID=?`);
|
||||
|
||||
const generateStmt_insertMessage_map = {};
|
||||
|
||||
const updateCHATGROUPS = () => {
|
||||
// console.log(1)
|
||||
try {
|
||||
let gIDs = stmt_getExpiredGroups.all(parseInt(Date.now()) - LifeOfGroup);
|
||||
for (let row of gIDs) {
|
||||
let gID = row.gID;
|
||||
db.exec(`DROP TABLE IF EXISTS ${gID}`);
|
||||
stmt_deleteGroup.run(gID);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
updateCHATGROUPS();
|
||||
setInterval(updateCHATGROUPS, 1000 * 3600 * 3);
|
||||
|
||||
// 创建群组的数据库
|
||||
function createGroupTableDB(gID, verify) {
|
||||
db.exec(
|
||||
`CREATE TABLE IF NOT EXISTS ${gID} (
|
||||
message TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
type1 TEXT NOT NULL,
|
||||
sender TEXT NOT NULL,
|
||||
timestamp INT NOT NULL
|
||||
);`
|
||||
);
|
||||
stmt_insertCHATGROUPS.run(gID, verify, parseInt(Date.now()));
|
||||
}
|
||||
|
||||
// 插入信息
|
||||
function insertMessageByGID(gID, msg, type1, from, timestamp) {
|
||||
if (!generateStmt_insertMessage_map[gID]) {
|
||||
generateStmt_insertMessage_map[gID] = db.prepare(`INSERT INTO ${gID} (message,type,type1,sender, timestamp) VALUES (?, ?, ?, ?, ?)`);
|
||||
}
|
||||
generateStmt_insertMessage_map[gID].run(msg, "gMsg", type1, from, timestamp);
|
||||
}
|
||||
|
||||
function updateActiveTimestamp(gID) {
|
||||
stmt_updateCHATGROUPS_ActiveTimestamp.run(parseInt(Date.now()), gID);
|
||||
}
|
||||
|
||||
function createGroup(verify) {
|
||||
// 暂时不考虑id随机生成时重复的问题
|
||||
let gID = generateId(true);
|
||||
groupsMap[gID] = { verify, member: [], msgHistory: [] };
|
||||
createGroupTableDB(gID, verify);
|
||||
return gID;
|
||||
}
|
||||
|
||||
function getGroupVerify(gID) {
|
||||
initMsgHistory(gID);
|
||||
return groupsMap[gID].verify;
|
||||
}
|
||||
|
||||
// 正式加入群组
|
||||
function joinGroup(socket, gID, callback) {
|
||||
initMsgHistory(gID);
|
||||
groupsMap[gID].member.push(socket);
|
||||
updateActiveTimestamp(gID);
|
||||
callback();
|
||||
}
|
||||
|
||||
// 暂时这么写
|
||||
function getHistory(gID) {
|
||||
initMsgHistory(gID);
|
||||
return groupsMap[gID].msgHistory;
|
||||
}
|
||||
|
||||
function broadcastMsg(gID, msg, type1, from) {
|
||||
const timestamp = parseInt(Date.now());
|
||||
initMsgHistory(gID);
|
||||
updateActiveTimestamp(gID);
|
||||
insertMessageByGID(gID, msg, type1, from, timestamp);
|
||||
|
||||
let bmsg = JSON.stringify({ type: `gMsg`, type1, gID, msg, from, timestamp });
|
||||
groupsMap[gID].msgHistory.push({ type: `gMsg`, type1, msg, from, timestamp });
|
||||
for (let socket of groupsMap[gID].member) {
|
||||
socket.send(bmsg);
|
||||
}
|
||||
}
|
||||
|
||||
function initMsgHistory(gID) {
|
||||
if (!groupsMap[gID]) {
|
||||
let verify = stmt_getGroupVerify.get(gID).verify;
|
||||
// 暂时这么写
|
||||
let msgHistory = db
|
||||
.prepare(`SELECT message,type,type1,sender,timestamp FROM ${gID}`)
|
||||
.all()
|
||||
.map(v => {
|
||||
return { type: v.type, type1: v.type1, msg: v.message, from: v.sender, timestamp: v.timestamp };
|
||||
});
|
||||
// console.log(msgHistory)
|
||||
groupsMap[gID] = { verify, member: [], msgHistory };
|
||||
}
|
||||
}
|
||||
|
||||
// 灵车
|
||||
wss.on("connection", socket => {
|
||||
console.log("新的连接已建立");
|
||||
const no = wss.clients.size;
|
||||
const joinedGroup = [];
|
||||
var uuid = uuid4();
|
||||
function setUuid(new_uuid) {
|
||||
uuid = new_uuid;
|
||||
}
|
||||
socket.on("message", message => {
|
||||
try {
|
||||
console.log(message.toString());
|
||||
let msg = JSON.parse(message.toString());
|
||||
if (msg.type) {
|
||||
let type = msg.type;
|
||||
// 创建群组
|
||||
if (type === "create") {
|
||||
socket.send(JSON.stringify({ type: "sMsg", type1: "create", gID: createGroup(msg.verify) }));
|
||||
} else if (type === "join") {
|
||||
// 加入群组——客端验证key
|
||||
if (msg.type1 === "verify") {
|
||||
// 已经加入则return
|
||||
if (joinedGroup.includes(msg.gID)) {
|
||||
return;
|
||||
}
|
||||
socket.send(JSON.stringify({ type: "sMsg", type1: "joinVerify", verify: getGroupVerify(msg.gID), gID: msg.gID }));
|
||||
} else if (msg.type1 === "success") {
|
||||
if (joinedGroup.includes(msg.gID)) {
|
||||
return;
|
||||
}
|
||||
// 成功加入
|
||||
joinGroup(socket, msg.gID, () => {
|
||||
joinedGroup.push(msg.gID);
|
||||
// 不考虑失败属于是
|
||||
socket.send(JSON.stringify({ type: "sMsg", gID: msg.gID, type1: "history", history: JSON.stringify(getHistory(msg.gID)) }));
|
||||
});
|
||||
}
|
||||
} else if (type === "message") {
|
||||
let gID = msg.gID;
|
||||
broadcastMsg(gID, msg.msg, msg.type1, uuid);
|
||||
} else if (type === "heartbeat") {
|
||||
socket.send(JSON.stringify({ type: "sMsg", type1: "heartbeat" }));
|
||||
} else if (type === "uuid") {
|
||||
if (msg.type1 === "get") {
|
||||
socket.send(JSON.stringify({ type: "sMsg", type1: "uuid", uuid }));
|
||||
} else if (msg.type1 === "set") {
|
||||
uuid = msg.uuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("close", () => {
|
||||
for (let gID of joinedGroup) {
|
||||
groupsMap[gID].member = groupsMap[gID].member.filter(v => v !== socket);
|
||||
}
|
||||
console.log(`No.${no} exit.`);
|
||||
console.log(groupsMap);
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(PORT, HOST, () => {});
|
||||
@@ -1,3 +1,5 @@
|
||||
// 已废弃
|
||||
|
||||
import express from 'express'
|
||||
|
||||
const HOST = '0.0.0.0'
|
||||
|
||||
+161
-18
@@ -1,29 +1,161 @@
|
||||
<script setup>
|
||||
import uploadFileMiao from "./components/uploadFileMiao.vue";
|
||||
import downloadFileMiao from "./components/downloadFileMiao.vue";
|
||||
import settingMiao from "./components/setting.vue";
|
||||
import {NNotificationProvider,NMessageProvider,NCard,NTabs,NTabPane} from 'naive-ui'
|
||||
import upload from "./views/uploadFileMiao.vue";
|
||||
import download from "./views/downloadFileMiao.vue";
|
||||
import setting from "./views/setting.vue";
|
||||
import chat from "./views/chat.vue";
|
||||
import login from "./views/login.vue";
|
||||
import account from "./views/accountInfo.vue";
|
||||
import getQueryString from '@/func/getQuery'
|
||||
import { NNotificationProvider, NMessageProvider, NCard, NMenu, NIcon, NModalProvider } from 'naive-ui'
|
||||
import { ref, watch, h, shallowRef, onMounted, computed } from "vue";
|
||||
// import { useRoute } from "vue-router";
|
||||
import {
|
||||
CloudUploadOutline as uploadIcon,
|
||||
CloudDownloadOutline as downloadIcon,
|
||||
// ChatboxEllipsesOutline as chatIcon,
|
||||
ChatbubblesOutline as chatIcon,
|
||||
SettingsOutline as settingIcon,
|
||||
FootstepsOutline as AccountIcon
|
||||
|
||||
} from '@vicons/ionicons5'
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import config from "../miaoShareConfig.json"
|
||||
|
||||
// console.log(process.env.NODE_ENV)
|
||||
|
||||
|
||||
const settingData = useSettingStore();
|
||||
const serverPort = settingData.serverPort;
|
||||
const baseUrl =
|
||||
location.protocol + "//" + location.hostname + ":" + serverPort + "/";
|
||||
const tokenExpiresIn = config.tokenExpiresIn
|
||||
|
||||
|
||||
const activePage = ref('upload')
|
||||
const isComponent = shallowRef(upload)
|
||||
watch(activePage, () => {
|
||||
isComponent.value = { upload, download, setting, chat, login, account }[activePage.value]
|
||||
})
|
||||
function renderIcon(icon) {
|
||||
return () => h(NIcon, null, { default: () => h(icon) })
|
||||
}
|
||||
|
||||
const menuOptions = ref([
|
||||
{
|
||||
label: '上传',
|
||||
key: 'upload',
|
||||
icon: renderIcon(uploadIcon),
|
||||
disabled: computed(() => {
|
||||
// console.log(settingData)
|
||||
return settingData.token === null
|
||||
})
|
||||
},
|
||||
{
|
||||
label: '下载',
|
||||
key: 'download',
|
||||
icon: renderIcon(downloadIcon),
|
||||
disabled: computed(() => {
|
||||
return settingData.token === null
|
||||
})
|
||||
},
|
||||
{
|
||||
label: 'Chat',
|
||||
key: 'chat',
|
||||
icon: renderIcon(chatIcon),
|
||||
disabled: computed(() => {
|
||||
return settingData.token === null
|
||||
})
|
||||
},
|
||||
{
|
||||
label: '设置',
|
||||
key: 'setting',
|
||||
icon: renderIcon(settingIcon),
|
||||
disabled: computed(() => {
|
||||
return settingData.token === null
|
||||
})
|
||||
},
|
||||
{
|
||||
label: '账户',
|
||||
key: 'account',
|
||||
icon: renderIcon(AccountIcon),
|
||||
disabled: computed(() => {
|
||||
return settingData.token === null
|
||||
})
|
||||
}
|
||||
])
|
||||
// function getQueryString(key) {
|
||||
// var reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i');
|
||||
// var r = window.location.search.substr(1).match(reg);
|
||||
// if (r != null) {
|
||||
// return unescape(r[2]);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
let aP = getQueryString('aP')
|
||||
// console.log(aP)
|
||||
if (aP) {
|
||||
activePage.value = aP
|
||||
}
|
||||
if (settingData.getToken() === null) {
|
||||
aP = activePage.value
|
||||
activePage.value = 'login'
|
||||
// 登录后跳原来的activePage
|
||||
const w = watch(() => settingData.token, () => {
|
||||
activePage.value = aP
|
||||
w()
|
||||
})
|
||||
} else {
|
||||
// 刷新token
|
||||
(async function refreshToken() {
|
||||
const old_token = settingData.getToken()
|
||||
console.log(old_token)
|
||||
const response = await fetch(`${baseUrl}user/refrashToken`, {
|
||||
// credentials: 'include',
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': `Bearer ${settingData.token}`,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
// body: JSON.stringify({
|
||||
// token:settingData.token
|
||||
// })
|
||||
});
|
||||
console.log(response)
|
||||
let data = await response.json()
|
||||
if (data.status === 'Error') {
|
||||
console.log('falied')
|
||||
} else {
|
||||
localStorage.setItem('token', data.new_token)
|
||||
localStorage.setItem('tokenExpiredTime', parseInt(Date.now()) + parseInt(tokenExpiresIn) * 3600000)
|
||||
}
|
||||
})()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="windows">
|
||||
<n-notification-provider placement="bottom-right" :max="3">
|
||||
<n-message-provider>
|
||||
<n-card content-style="padding: 0; margin:0;" id="theCard">
|
||||
<n-tabs type="line" size="large" :tabs-padding="20" pane-style="padding: 20px;">
|
||||
<n-tab-pane name="上传文件">
|
||||
<uploadFileMiao style />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="下载文件">
|
||||
<downloadFileMiao style/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="设置">
|
||||
<settingMiao style="max-width:100%" />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<n-modal-provider>
|
||||
<n-card content-style="padding: 0; margin:0" id="theCard">
|
||||
<div id="menu">
|
||||
<n-menu mode="horizontal" :responsive="true" v-model:value="activePage"
|
||||
:options="menuOptions" :icon-size="26"></n-menu>
|
||||
</div>
|
||||
|
||||
<div style="padding: 20px;">
|
||||
<KeepAlive>
|
||||
<component :is="isComponent"></component>
|
||||
</KeepAlive>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal-provider>
|
||||
</n-message-provider>
|
||||
</n-notification-provider>
|
||||
</div>
|
||||
@@ -39,10 +171,21 @@ import {NNotificationProvider,NMessageProvider,NCard,NTabs,NTabPane} from 'naive
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#theCard {
|
||||
width: 50%;
|
||||
height: 80%;
|
||||
width: 40vw;
|
||||
height: 90vh;
|
||||
}
|
||||
|
||||
#menu {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
/* background-color: blue; */
|
||||
padding-top: 5px;
|
||||
border-bottom: 1px solid rgb(60, 60, 60, 0.1);
|
||||
user-select: none
|
||||
}
|
||||
|
||||
@media screen and (max-width:768px) {
|
||||
#theCard {
|
||||
width: 100vw;
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<!-- <div style="border: 1px solid rgb(239, 239, 245);border-radius: 5px;position: relative;"> -->
|
||||
<n-scrollbar
|
||||
style="padding: 10px;padding-right: 15px; border: 1px solid rgb(239, 239, 245);border-radius: 5px;position: relative;">
|
||||
<n-image-group>
|
||||
|
||||
<div v-for="msg of props.messages">
|
||||
<div style="color:slategrey;">{{ `${msg.from.split('-')[0]} ${msg.timestampString}` }}</div>
|
||||
<div style="background-color: rgb(239, 239, 245); padding: 2px 7px; width: fit-content; border-radius: 5px; white-space: pre-wrap;"
|
||||
v-if="msg.type1 === 'text'">
|
||||
<span>{{ msg.msg }}</span>
|
||||
<!-- <span v-if="msg.type1 === 'img'"> -->
|
||||
<!-- <n-image :src="imgUrlCache[msg.msg]" width="100" style="margin-top: 5px;border-radius: 5px;" /> -->
|
||||
<!-- img->>{{ msg.msg }} -->
|
||||
<!-- </span> -->
|
||||
</div>
|
||||
<div v-if="msg.type1 === 'img'">
|
||||
<span>
|
||||
<n-image :src="imgUrlCache[msg.msg]" width="100" style="margin-top: 5px;border-radius: 5px;" />
|
||||
<n-skeleton v-show="!imgUrlCache[msg.msg]" height="80px" width="100px" :sharp="false"
|
||||
style="border-radius: 5px;" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="msg.type1 === 'file'"
|
||||
style="background-color: rgb(239, 239, 245); padding: 2px 7px; width: fit-content; border-radius: 5px;">
|
||||
<n-flex style="justify-content: center;align-items: center;user-select: none;cursor: pointer;"
|
||||
@click="downloadFile(msg.msg)">
|
||||
<n-icon :component="DownloadIcon" size="50" style="margin-top: 0px;"></n-icon>
|
||||
<n-flex vertical v-if="fileInfoCache[msg.msg]">
|
||||
<div style="font-weight: 500;margin-top: 8px;font-size: large;">
|
||||
{{ fileInfoCache[msg.msg].fileName ? fileInfoCache[msg.msg].fileName : 'Unknow' }}
|
||||
</div>
|
||||
<n-flex style="margin-bottom: 8px;" justify="space-between">
|
||||
<div>
|
||||
{{ fileInfoCache[msg.msg].fileSize ? fileInfoCache[msg.msg].fileSize : 'Unknow'
|
||||
}}
|
||||
</div>
|
||||
<!-- lets show u a bullshit -->
|
||||
<div>
|
||||
{{ fileStatus2String[fileInfoCache[msg.msg].status ? fileInfoCache[msg.msg].status :
|
||||
'Unknow'] ? fileStatus2String[fileInfoCache[msg.msg].status ?
|
||||
fileInfoCache[msg.msg].status : 'Unknow'] : fileInfoCache[msg.msg].status ?
|
||||
fileInfoCache[msg.msg].status : 'Unknow' }}
|
||||
</div>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<!-- {{ fileInfoCache[msg.msg] }} -->
|
||||
</div>
|
||||
</div>
|
||||
<n-empty description="无可显示消息" v-show="props.messages.length === 0" style="margin-top: 25vh;">
|
||||
<template #extra>
|
||||
</template>
|
||||
</n-empty>
|
||||
<div style="height: 30px;" v-show="props.messages.length !== 0"></div>
|
||||
</n-image-group>
|
||||
</n-scrollbar>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NScrollbar, NEmpty, NImage, NImageGroup, NSkeleton, NIcon, NFlex } from 'naive-ui'
|
||||
import { ref, watch } from 'vue';
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import { decrypt, decryptString,downloadFromUint8Array } from '../func/download'
|
||||
import { CubeOutline as DownloadIcon } from '@vicons/ionicons5'
|
||||
import fileSize2String from '@/func/fileSize2String'
|
||||
|
||||
const setting = useSettingStore();
|
||||
const serverPort = setting.serverPort;
|
||||
const baseUrl = location.protocol + "//" + location.hostname + ":" + serverPort + "/";
|
||||
const props = defineProps({
|
||||
messages: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
})
|
||||
async function getFileInfo(code) {
|
||||
let id = code.substring(0, 8);
|
||||
const params = {
|
||||
fileName: id
|
||||
};
|
||||
const options = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Authorization': `Bearer ${setting.token}`
|
||||
// 'Content-Type':'application/json'
|
||||
},
|
||||
};
|
||||
let response = await fetch(baseUrl + "file/info?" + new URLSearchParams(params), options)
|
||||
let info = await response.json()
|
||||
return info
|
||||
}
|
||||
|
||||
async function downloadFile(code) {
|
||||
if (fileInfoCache.value[code].status === 'waittingDownload') {
|
||||
fileInfoCache.value[code].status = `downloading`;
|
||||
return new Promise((resolve, reject) => {
|
||||
let id = code.substring(0, 8);
|
||||
let key = code.substring(8);
|
||||
const params = {
|
||||
fileName: id
|
||||
};
|
||||
const options = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Authorization': `Bearer ${setting.token}`
|
||||
},
|
||||
};
|
||||
fetch(baseUrl + "file/download?" + new URLSearchParams(params), options)
|
||||
.then(res => {
|
||||
// fileName = res.headers.get("File-Name");
|
||||
// fileName = decryptString(key, fileName);
|
||||
// updateDownloadQuene(code, "fileName", fileName);
|
||||
const total = +res.headers.get("Content-Length");
|
||||
let loaded = 0;
|
||||
|
||||
const clone = res.clone();
|
||||
const reader = res.body.getReader();
|
||||
// 感觉怪怪的
|
||||
const read = async () => {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
// console.log(clone)
|
||||
return clone;
|
||||
}
|
||||
loaded += value.length;
|
||||
const percent = (loaded / total) * 100;
|
||||
fileInfoCache.value[code].status = `下载进度:${percent.toFixed(2)}%`;
|
||||
return read();
|
||||
};
|
||||
return read();
|
||||
})
|
||||
.then(res => {
|
||||
var data = res.blob();
|
||||
return data;
|
||||
})
|
||||
.then(data => {
|
||||
decrypt(key, data).then(data => {
|
||||
fileInfoCache.value[code].status = `finish`;
|
||||
fileInfoCache.value[code].data = data
|
||||
downloadFromUint8Array(data,fileInfoCache.value[code].fileName)
|
||||
resolve(data)
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
fileInfoCache.value[code].status = `error`;
|
||||
console.error(err);
|
||||
});
|
||||
})
|
||||
}else if(fileInfoCache.value[code].status === 'finish'){
|
||||
downloadFromUint8Array(fileInfoCache.value[code].data,fileInfoCache.value[code].fileName)
|
||||
}
|
||||
|
||||
};
|
||||
async function downloadImage(code) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var id = code.substring(0, 8);
|
||||
var key = code.substring(8);
|
||||
var fileName;
|
||||
const params = {
|
||||
fileName: id
|
||||
};
|
||||
const options = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Authorization': `Bearer ${setting.token}`
|
||||
// 'Content-Type':'application/json'
|
||||
},
|
||||
};
|
||||
fetch(baseUrl + "file/download?" + new URLSearchParams(params), options)
|
||||
.then(res => {
|
||||
// fileName = res.headers.get("File-Name");
|
||||
// fileName = decryptString(key, fileName);
|
||||
// updateDownloadQuene(code, "fileName", fileName);
|
||||
const total = +res.headers.get("Content-Length");
|
||||
let loaded = 0;
|
||||
|
||||
const clone = res.clone();
|
||||
const reader = res.body.getReader();
|
||||
// 感觉怪怪的
|
||||
const read = async () => {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
// console.log(clone)
|
||||
return clone;
|
||||
}
|
||||
loaded += value.length;
|
||||
// const percent = (loaded / total) * 100;
|
||||
// downloadProcess['downloadProcess'] = `下载进度:${percent.toFixed(2)}%`;
|
||||
// updateDownloadQuene(
|
||||
// code,
|
||||
// "downloadProcess",
|
||||
// `下载进度:${percent.toFixed(2)}%`
|
||||
// );
|
||||
// downloadProcess.value = percent.toFixed(2);
|
||||
return read();
|
||||
};
|
||||
return read();
|
||||
})
|
||||
.then(res => {
|
||||
var data = res.blob();
|
||||
return data;
|
||||
})
|
||||
.then(data => {
|
||||
// downloadProcess['downloadProcess'] = '解密中'
|
||||
// updateDownloadQuene(code, "downloadProcess", `解密中`);
|
||||
decrypt(key, data).then(data => {
|
||||
// updateDownloadQuene(code, "downloadProcess", `已完成`);
|
||||
// const url = URL.createObjectURL(new Blob(data))
|
||||
resolve(data)
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
// updateDownloadQuene(
|
||||
// code,
|
||||
// "fileName",
|
||||
// `出错,可能是文件过期或下载次数用完`
|
||||
// );
|
||||
// updateDownloadQuene(code, "downloadProcess", `出错`);
|
||||
console.error(err);
|
||||
});
|
||||
})
|
||||
};
|
||||
const imgUrlCache = ref({})
|
||||
const fileInfoCache = ref({})
|
||||
const fileStatus2String = ref({
|
||||
waittingDownload: '点击下载',
|
||||
downloading: '下载中',
|
||||
finish: '下载完成',
|
||||
Unknow: '获取文件信息中',
|
||||
error: '出错'
|
||||
})
|
||||
const imgDownloadingQueue = []
|
||||
const getFileInfoQueue = []
|
||||
watch(() => props.messages, async () => {
|
||||
for (let msg of props.messages) {
|
||||
if (msg.type1 === 'img') {
|
||||
if (!imgDownloadingQueue.includes(msg.msg)) {
|
||||
imgDownloadingQueue.push(msg.msg)
|
||||
const imgData = await downloadImage(msg.msg)
|
||||
const url = URL.createObjectURL(new Blob([imgData], { type: "image/png" }))
|
||||
imgUrlCache.value[msg.msg] = url
|
||||
}
|
||||
} else if (msg.type1 === 'file') {
|
||||
if (!getFileInfoQueue.includes(msg.msg)) {
|
||||
getFileInfoQueue.push(msg.msg)
|
||||
let key = msg.msg.substring(8);
|
||||
const info = await getFileInfo(msg.msg)
|
||||
info.fileName = decryptString(key, info.fileName)
|
||||
info.fileSize = fileSize2String(info.fileSize)
|
||||
info.status = 'waittingDownload'
|
||||
fileInfoCache.value[msg.msg] = info
|
||||
console.log(fileInfoCache.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,13 @@
|
||||
import aesjs from 'aes-js'
|
||||
import aes_worker from '@/worker/aes_ctr?worker'
|
||||
|
||||
export default (key, data) => {
|
||||
key = aesjs.utils.utf8.toBytes(key)
|
||||
return new Promise((resolve) => {
|
||||
const aesWorker = new aes_worker()
|
||||
aesWorker.onmessage = (event) => {
|
||||
resolve(event.data)
|
||||
}
|
||||
aesWorker.postMessage({ data, key })
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import workerpool from 'workerpool'
|
||||
|
||||
|
||||
function randName(){
|
||||
return `randName${Math.ceil(Math.random()*100000)}`
|
||||
}
|
||||
|
||||
|
||||
class MiaoWorkerPool{
|
||||
constructor(){
|
||||
this.workers = {}
|
||||
this.pool = workerpool.pool()
|
||||
}
|
||||
|
||||
/**
|
||||
* Description 添加function
|
||||
* @param {function} func 待添加function
|
||||
* @returns {String} 返回添加的function名
|
||||
*/
|
||||
init(func){
|
||||
let funcName = func.name.length > 0 ? func.name : randName();
|
||||
this[funcName] = async (...args)=>{
|
||||
try{
|
||||
let result = await this.pool.exec(func,args)
|
||||
return {
|
||||
isSuccess:true,
|
||||
result
|
||||
}
|
||||
}catch(e){
|
||||
throw {
|
||||
isSuccess:false,
|
||||
result:e
|
||||
}
|
||||
}
|
||||
}
|
||||
return funcName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default MiaoWorkerPool
|
||||
@@ -0,0 +1,19 @@
|
||||
import sm4_worker from '@/worker/sm4?worker'
|
||||
|
||||
export default async (key, data) => {
|
||||
// console.log(key)
|
||||
// key = aesjs.utils.utf8.toBytes(key)
|
||||
const encryptedBytes = await new Promise((resolve) => {
|
||||
const aesWorker = new sm4_worker()
|
||||
aesWorker.onmessage = (event) => {
|
||||
resolve(event.data)
|
||||
}
|
||||
aesWorker.postMessage({ data, key })
|
||||
})
|
||||
// var encryptedBytes = sm4.encrypt(data, key, {
|
||||
// padding: 'pkcs#7',
|
||||
// output: 'array'
|
||||
// })
|
||||
// console.log(encryptedBytes, sm4.decrypt(encryptedBytes, key,{output:'array'}))
|
||||
return new Uint8Array(encryptedBytes)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import aesjs from "aes-js";
|
||||
import { Base64 } from "js-base64";
|
||||
import { saveAs } from "file-saver";
|
||||
import { sm4 } from "sm-crypto";
|
||||
|
||||
const blob2Uint8Array = function (blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob);
|
||||
reader.onload = function () {
|
||||
let buffer = reader.result;
|
||||
let uint8array = new Uint8Array(buffer);
|
||||
resolve(uint8array);
|
||||
};
|
||||
reader.onerror = function () {
|
||||
reject(reader.error);
|
||||
};
|
||||
});
|
||||
};
|
||||
const decrypt = (key, data) => {
|
||||
key = key.trim()
|
||||
return blob2Uint8Array(data).then(data => {
|
||||
// 无加密
|
||||
if (key.length === 0) {
|
||||
return data;
|
||||
}
|
||||
// SM4
|
||||
if (key.length === 19) {
|
||||
key = [...aesjs.utils.utf8.toBytes(key.substring(3))];
|
||||
// console.log([...data])
|
||||
var decryptedBytes = new Uint8Array(sm4.decrypt([...data], key, { output: "array" }));
|
||||
return decryptedBytes;
|
||||
} else {
|
||||
// AES
|
||||
key = aesjs.utils.utf8.toBytes(key);
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
var decryptedBytes = aesCtr.decrypt(data);
|
||||
return decryptedBytes;
|
||||
}
|
||||
});
|
||||
};
|
||||
const decryptString = (key, str) => {
|
||||
key = key.trim()
|
||||
// console.log(key.length)
|
||||
// 无加密
|
||||
if (key.length === 0) {
|
||||
// return sm4.decrypt(str, "miaospringmiaospringmiaospringao");
|
||||
return Base64.decode(str)
|
||||
}
|
||||
// SM4
|
||||
// console.log(key,key.length)
|
||||
if (key.length === 19) {
|
||||
key = [...aesjs.utils.utf8.toBytes(key.substring(3))];
|
||||
return sm4.decrypt(str, key);
|
||||
}
|
||||
// AES
|
||||
// var data = new Uint8Array(
|
||||
// Base64.decode(str)
|
||||
// .split(",")
|
||||
// .map(n => parseInt(n))
|
||||
// );
|
||||
var data = Base64.toUint8Array(str)
|
||||
// console.log(data)
|
||||
// data = new TextDecoder().decode(data)
|
||||
key = aesjs.utils.utf8.toBytes(key);
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
var decryptedBytes = aesCtr.decrypt(data);
|
||||
// var decryptedStr = aesjs.utils.utf8.fromBytes(decryptedBytes);
|
||||
var decryptedStr = new TextDecoder().decode(decryptedBytes)
|
||||
return decryptedStr;
|
||||
};
|
||||
|
||||
const _saveAs = (blob,fileName)=>{
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.download = fileName;
|
||||
a.href = blobUrl;
|
||||
a.click();
|
||||
}
|
||||
|
||||
const downloadFromUint8Array = (data, fileName) => {
|
||||
let uint8Array = data;
|
||||
let blob = new Blob([uint8Array], { type: "application/octet-stream" });
|
||||
// const blobUrl = window.URL.createObjectURL(blob);
|
||||
// 使用file-saver.js
|
||||
saveAs(blob, fileName);
|
||||
// _saveAs(blob, fileName);
|
||||
};
|
||||
|
||||
export { blob2Uint8Array, decrypt, decryptString, downloadFromUint8Array };
|
||||
@@ -0,0 +1,14 @@
|
||||
export default function filePicker({ fn, accept, multiple } = {}) {
|
||||
const inpEle = document.createElement("input");
|
||||
inpEle.id = `__file_${parseInt(Math.random() * 100000000)}`;
|
||||
inpEle.type = "file";
|
||||
inpEle.style.display = "none";
|
||||
// 文件类型限制
|
||||
// inpEle.accept="image/*"
|
||||
accept && (inpEle.accept = accept);
|
||||
// 多选限制
|
||||
multiple && (inpEle.multiple = multiple);
|
||||
// inpEle.addEventListener("change", event => fn.call(inpEle, event, inpEle.files), { once: true });
|
||||
inpEle.addEventListener("change", e => fn(inpEle.files), { once: true });
|
||||
inpEle.click();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
function pow1024(num) {
|
||||
return Math.pow(1024, num)
|
||||
}
|
||||
|
||||
export default function(size){
|
||||
if (!size) return '';
|
||||
return size < 1024 ? size + ' B' :
|
||||
size < pow1024(2) ? (size / 1024).toFixed(2) + ' KB' :
|
||||
size < pow1024(3) ? (size / pow1024(2)).toFixed(2) + ' MB' :
|
||||
size < pow1024(4) ? (size / pow1024(3)).toFixed(2) + ' GB' :
|
||||
(size / pow1024(4)).toFixed(2) + ' TB'
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function getQueryString(key) {
|
||||
var reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i');
|
||||
var r = window.location.search.substring(1).match(reg);
|
||||
if (r != null) {
|
||||
return decodeURI(r[2]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import aesjs from 'aes-js'
|
||||
import { Base64 } from 'js-base64'
|
||||
import { sm4 } from 'sm-crypto'
|
||||
import AES from './AES'
|
||||
import SM4 from './SM4'
|
||||
|
||||
// const setting = useSettingStore();
|
||||
// const message = useMessage();
|
||||
|
||||
// 生成密钥
|
||||
const generateKey = function (setting) {
|
||||
if (setting.activeFuction === '不加密') {
|
||||
return ''
|
||||
}
|
||||
var charMap = ''
|
||||
for (let i of setting.activeKeyMap) {
|
||||
if (i === 'A-Z') {
|
||||
charMap = charMap + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
} else if (i === 'a-z') {
|
||||
charMap = charMap + 'abcdefghijklmnopqrstuvwxyz'
|
||||
} else if (i === '0-9') {
|
||||
charMap = charMap + '0123456789'
|
||||
}
|
||||
}
|
||||
var returnStr = '',
|
||||
length = setting.activeFuction !== 'AES256' ? 16 : 32
|
||||
// charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var index = parseInt(Math.random() * charMap.length)
|
||||
returnStr += charMap.substring(index, index + 1)
|
||||
}
|
||||
if (setting.activeFuction === 'SM4') {
|
||||
returnStr = 'SM4' + returnStr
|
||||
}
|
||||
// console.log(returnStr,returnStr.length)
|
||||
return returnStr
|
||||
}
|
||||
const blob2Uint8Array = function (blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader()
|
||||
reader.readAsArrayBuffer(blob)
|
||||
reader.onload = function () {
|
||||
let buffer = reader.result
|
||||
let uint8array = new Uint8Array(buffer)
|
||||
resolve(uint8array)
|
||||
}
|
||||
reader.onerror = function () {
|
||||
reject(reader.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// const AES = (key, data) => {
|
||||
// key = aesjs.utils.utf8.toBytes(key)
|
||||
// // var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5))
|
||||
// // var encryptedBytes = aesCtr.encrypt(data)
|
||||
// // return encryptedBytes
|
||||
// return new Promise((resolve) => {
|
||||
// const aesWorker = new aes_worker()
|
||||
// aesWorker.onmessage = (event) => {
|
||||
// resolve(event.data)
|
||||
// }
|
||||
// aesWorker.postMessage({ data, key })
|
||||
// })
|
||||
// }
|
||||
// const SM4 = (key, data) => {
|
||||
// // console.log(key)
|
||||
// var encryptedBytes = sm4.encrypt(data, key, {
|
||||
// padding: 'pkcs#7',
|
||||
// output: 'array'
|
||||
// })
|
||||
// // console.log(encryptedBytes, sm4.decrypt(encryptedBytes, key,{output:'array'}))
|
||||
// return new Uint8Array(encryptedBytes)
|
||||
// }
|
||||
const encrypt = (key, data, fileName, setting, message) => {
|
||||
// if(!encryptFunction){
|
||||
// encryptFunction = setting.activeFuction
|
||||
// }
|
||||
let encryptFunction = {
|
||||
0: '不加密',
|
||||
16: 'AES128',
|
||||
19: 'SM4',
|
||||
32: 'AES256'
|
||||
}[key.length]
|
||||
return blob2Uint8Array(data).then(async (data) => {
|
||||
let encrypted_data = null
|
||||
if (encryptFunction === '不加密' || key === '') {
|
||||
encrypted_data = data
|
||||
// AES
|
||||
} else if (encryptFunction != 'SM4') {
|
||||
if (message) message.info(`开始加密${fileName}`)
|
||||
encrypted_data = await AES(key, data)
|
||||
if (message) message.info(`${fileName}加密完成`)
|
||||
// SM4
|
||||
} else {
|
||||
if (message) message.info(`开始加密${fileName}`)
|
||||
key = [...aesjs.utils.utf8.toBytes(key.substring(3))]
|
||||
encrypted_data = await SM4(key, data)
|
||||
if (message) message.info(`${fileName}加密完成`)
|
||||
}
|
||||
return encrypted_data
|
||||
})
|
||||
}
|
||||
|
||||
// setting为pinia存储的setting。。。
|
||||
const encryptString = (key, str, setting) => {
|
||||
// if(!encryptFunction){
|
||||
// encryptFunction = setting.activeFuction
|
||||
// }
|
||||
// console.log(key,key.length)
|
||||
let encryptFunction = {
|
||||
0: '不加密',
|
||||
16: 'AES128',
|
||||
19: 'SM4',
|
||||
32: 'AES256'
|
||||
}[key.length]
|
||||
if (encryptFunction === '不加密' || key === '') {
|
||||
// return sm4.encrypt(str, 'miaospringmiaospringmiaospringao')
|
||||
return Base64.encode(str)
|
||||
} else if (encryptFunction !== 'SM4') {
|
||||
key = aesjs.utils.utf8.toBytes(key)
|
||||
// var data = aesjs.utils.utf8.toBytes(str);
|
||||
var data = new TextEncoder().encode(str)
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5))
|
||||
var encryptedBytes = aesCtr.encrypt(data)
|
||||
var res = Base64.fromUint8Array(encryptedBytes, true)
|
||||
// console.log(encryptedBytes,res)
|
||||
return res
|
||||
} else {
|
||||
key = [...aesjs.utils.utf8.toBytes(key.substring(3))]
|
||||
// console.log(key)
|
||||
return sm4.encrypt(str, key)
|
||||
}
|
||||
}
|
||||
|
||||
export { encrypt, encryptString, generateKey }
|
||||
+2
-2
@@ -3,7 +3,7 @@ import 'vfonts/Lato.css'
|
||||
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
// import router from "./router";
|
||||
// import naive from "naive-ui";
|
||||
import { createPinia } from "pinia";
|
||||
|
||||
@@ -14,7 +14,7 @@ import { createPinia } from "pinia";
|
||||
const pinia = createPinia()
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(router);
|
||||
// app.use(router);
|
||||
app.use(pinia);
|
||||
// app.use(naive);
|
||||
|
||||
|
||||
+43
-1
@@ -1,5 +1,18 @@
|
||||
import { defineStore } from "pinia";
|
||||
import config from "../../miaoShareConfig.json"
|
||||
|
||||
const userID = localStorage.getItem('userID');
|
||||
const userGroup = localStorage.getItem('userGroup');
|
||||
|
||||
const tokenExpiresIn = config.tokenExpiresIn
|
||||
if(localStorage.getItem('tokenExpiredTime') == null ||
|
||||
parseInt(localStorage.getItem('tokenExpiredTime')) < parseInt(Date.now())){
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
// console.log(token)
|
||||
|
||||
var activeFuctionLocal = localStorage.getItem('activeFuction')
|
||||
if (activeFuctionLocal == null){
|
||||
@@ -13,16 +26,45 @@ if (activeKeyMap == null ){
|
||||
activeKeyMap = JSON.stringify(['A-Z','0-9'])
|
||||
localStorage.setItem('activeKeyMap',activeKeyMap)
|
||||
}
|
||||
|
||||
// 开发环境下服务器端口使用设置的端口,否则使用与页面相同的端口
|
||||
const serverPort = process.env.NODE_ENV === 'development' ? config.PORT : location.port
|
||||
|
||||
// console.log(process.env.NODE_ENV,config.DEVPORT,location.port,serverPort)
|
||||
export const useSettingStore = defineStore("setting", {
|
||||
state: () => ({
|
||||
activeFuction: activeFuctionLocal,
|
||||
activeKeyMap:JSON.parse(activeKeyMap),
|
||||
serverPort:"17058"
|
||||
enableGuest:config.enableGuest,
|
||||
serverPort,
|
||||
token,
|
||||
userID,
|
||||
userGroup
|
||||
}),
|
||||
getters: {
|
||||
// double: state => state.count * 2
|
||||
},
|
||||
actions: {
|
||||
setJWT( token,keepTokenLocal ){
|
||||
this.token = token
|
||||
if(keepTokenLocal){
|
||||
localStorage.setItem('token',token)
|
||||
localStorage.setItem('tokenExpiredTime',parseInt(Date.now())+parseInt(tokenExpiresIn)*3600000)
|
||||
}
|
||||
},
|
||||
setUserID( id,keepTokenLocal ){
|
||||
this.userID = id
|
||||
if(keepTokenLocal)
|
||||
localStorage.setItem('userID',id)
|
||||
},
|
||||
setUserGroup( g,keepTokenLocal ){
|
||||
this.userGroup = g
|
||||
if(keepTokenLocal)
|
||||
localStorage.setItem('userGroup',g)
|
||||
},
|
||||
getToken(){
|
||||
return localStorage.getItem('token')
|
||||
}
|
||||
// increment() {
|
||||
// this.count++;
|
||||
// }
|
||||
|
||||
+42
-14
@@ -1,22 +1,50 @@
|
||||
// 未使用
|
||||
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
// import HomeView from '../views/HomeView.vue'
|
||||
import uploadFileMiao from "../views/uploadFileMiao.vue";
|
||||
import downloadFileMiao from "../views/downloadFileMiao.vue";
|
||||
import settingMiao from "../views/setting.vue";
|
||||
import chat from "../views/chat.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
// {
|
||||
// path: '/',
|
||||
// name: 'home',
|
||||
// component: HomeView
|
||||
// },
|
||||
// {
|
||||
// path: '/about',
|
||||
// name: 'about',
|
||||
// // route level code-splitting
|
||||
// // this generates a separate chunk (About.[hash].js) for this route
|
||||
// // which is lazy-loaded when the route is visited.
|
||||
// component: () => import('../views/AboutView.vue')
|
||||
// }
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/upload',
|
||||
},
|
||||
{
|
||||
path: '/upload',
|
||||
name: 'upload',
|
||||
component: uploadFileMiao,
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/download',
|
||||
name: 'download',
|
||||
component: downloadFileMiao,
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/setting',
|
||||
name: 'setting',
|
||||
component: settingMiao,
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/chat',
|
||||
name: 'chat',
|
||||
component: chat,
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
}
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<n-button @click="signOut" type="error">
|
||||
退出登录
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import { NCheckbox, NInputGroupLabel, NInputGroup, NP, NFlex, NDataTable, NButton, NInput, NCard } from 'naive-ui'
|
||||
|
||||
function signOut(){
|
||||
const items = ['token']
|
||||
items.forEach(v=>{
|
||||
localStorage.removeItem(v)
|
||||
})
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<n-flex vertical>
|
||||
<n-flex style="padding-top: 10px;">
|
||||
<n-input v-model:value="groupCode" type="text" placeholder="输入邀请码" style="flex:1">
|
||||
<template #suffix>
|
||||
<n-icon :component="CopyOutline" style="cursor: pointer;" @click='toClipboard(groupCode)' />
|
||||
</template>
|
||||
</n-input>
|
||||
<n-flex>
|
||||
<n-button @click="createGroup" :disabled="groupCode !== ''" :type="groupCode !== '' ? 'default' : 'primary'"
|
||||
ghost>
|
||||
创建群组
|
||||
</n-button>
|
||||
<n-button @click="joinGroup(groupCode)" :disabled="groupCode === ''"
|
||||
:type="groupCode === '' ? 'default' : 'primary'" ghost>
|
||||
加入群组
|
||||
</n-button>
|
||||
<n-button @click="shareWithQR()" :disabled="activeGID === null" v-show="activeGID !== null"
|
||||
:type="activeGID === null ? 'default' : 'primary'">
|
||||
分享群组
|
||||
</n-button>
|
||||
<n-select v-model:value="activeGID" :options="joinedGroup.map(gid => { return { label: gid, value: gid } })" v-show="activeGID !== null"
|
||||
placeholder="未加入群组" style="flex:1;user-select: none;" />
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<!-- <n-flex style="padding-top: 10px;padding-bottom: 10px;">
|
||||
<n-select v-model:value="activeGID" :options="joinedGroup.map(gid => { return { label: gid, value: gid } })"
|
||||
placeholder="选择当前查看群组" style="flex:1" />
|
||||
<n-button @click="shareWithQR()" :disabled="activeGID === null"
|
||||
:type="activeGID === null ? 'default' : 'primary'">
|
||||
分享群组
|
||||
</n-button>
|
||||
</n-flex> -->
|
||||
<!-- <n-data-table :columns="columns"
|
||||
:data="activeGID ? Messages[activeGID].map(v => { v.timestampString = new Date(v.timestamp).toLocaleString(); return v }).reverse() : []"
|
||||
:row-props="null" :bordered="false" :single-line="false" :max-height="400" /> -->
|
||||
<div style="flex:1; position: relative;min-height: none;">
|
||||
<chatui style="margin-bottom: 10px; height: 65vh"
|
||||
:messages="activeGID ? Messages[activeGID].map(v => { v.timestampString = new Date(v.timestamp).toLocaleString(); return v }).reverse() : []"/>
|
||||
|
||||
<n-input v-model:value="msgInput" type="textarea" placeholder="发送信息" :autosize="{
|
||||
minRows: 1,
|
||||
maxRows: 2
|
||||
}" @keydown="handleKeyDown" style="margin-top: 0px;">
|
||||
<template #suffix>
|
||||
<n-icon :component="DocumentsOutline" style="cursor: pointer;margin-right: 5px;" @click='filePicker({fn:sendFiles,multiple:true})'/>
|
||||
<n-icon :component="ImagesOutline" style="cursor: pointer;margin-right: 5px;" @click='filePicker({fn:sendImages,accept:"image/*",multiple:true})'/>
|
||||
<n-icon :component="SendOutline" style="cursor: pointer;" @click='sendMsg(activeGID, msgInput)'/>
|
||||
</template>
|
||||
</n-input>
|
||||
|
||||
<!-- <n-button @click="sendMsg(activeGID, msg)" :disabled="msg === '' || activeGID === null"
|
||||
:type="msg === '' || activeGID === null ? 'default' : 'primary'">
|
||||
发送消息
|
||||
</n-button> -->
|
||||
</div>
|
||||
</n-flex>
|
||||
<!-- {{ dataTest }} -->
|
||||
<!-- {{ Messages }} -->
|
||||
|
||||
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: fit-content;"
|
||||
title=""
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-qr-code :value="QRCodeData" style="padding: 0;" :size="256" error-correction-level="Q" :v-if="showModal"
|
||||
:icon-src='0?QRIconUrl:null'
|
||||
:icon-size="128"/>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref,onMounted } from 'vue'
|
||||
// import { useModal } from "naive-ui";
|
||||
import useClipboard from "vue-clipboard3";
|
||||
import { NIcon, NFlex, NDataTable, NButton, NInput, NSelect,NModal,NCard , NQrCode} from 'naive-ui'
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import { generateKey, encryptString, encrypt } from '@/func/upload';
|
||||
import { decryptString } from '@/func/download';
|
||||
import WebsocketHeartbeatJs from 'websocket-heartbeat-js';
|
||||
import { CopyOutline,SendOutline,ImagesOutline,DocumentsOutline } from '@vicons/ionicons5'
|
||||
import getQueryString from '@/func/getQuery'
|
||||
import chatui from "@/components/chatui.vue"
|
||||
import { Base64 } from 'js-base64';
|
||||
import { useMessage, useNotification } from "naive-ui";
|
||||
import filePicker from "@/func/filePicker"
|
||||
|
||||
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
// const modal = useModal();
|
||||
const { toClipboard } = useClipboard();
|
||||
|
||||
const setting = useSettingStore();
|
||||
const serverPort = setting.serverPort;
|
||||
const wsBaseUrl = 'ws' + "://" + location.hostname + ":" + serverPort;
|
||||
const baseUrl = location.protocol + "//" + location.hostname + ":" + serverPort + "/";
|
||||
const QRIconUrl = ref(location.protocol + "//" + location.hostname + ":" + serverPort + "/favicon.ico")
|
||||
|
||||
const columns = [
|
||||
{
|
||||
"title": "type",
|
||||
"key": "type",
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
"title": "from",
|
||||
"key": "from",
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
"title": "timestamp",
|
||||
"key": "timestampString",
|
||||
width: 150,
|
||||
// sorter: {
|
||||
// compare: (a, b) => {
|
||||
// // console.log(a,b,new Date(a),parseInt(new Date(a)) - parseInt(new Date(b)))
|
||||
// return parseInt(new Date(a.timestamp)) - parseInt(new Date(b.timestamp))
|
||||
// },
|
||||
// }
|
||||
},
|
||||
{
|
||||
"title": "msg",
|
||||
"key": "msg"
|
||||
}
|
||||
]
|
||||
const columnsTest = [
|
||||
{
|
||||
title: "from",
|
||||
key: "from"
|
||||
},
|
||||
{
|
||||
title: "message",
|
||||
key: "msg"
|
||||
},
|
||||
];
|
||||
function handleKeyDown(e){
|
||||
// console.log(e,e.ctrlKey,e.code)
|
||||
if(e.ctrlKey === true&&e.code === 'Enter'){
|
||||
sendMsg(activeGID.value, msgInput.value)
|
||||
}
|
||||
}
|
||||
|
||||
// 用于二维码分享
|
||||
const showModal = ref(false)
|
||||
const QRCodeData = ref('')
|
||||
function shareWithQR(){
|
||||
let code = activeGID.value+gID2key[activeGID.value].key
|
||||
QRCodeData.value = location.protocol + "//" + location.hostname + ":" + serverPort + `/?aP=chat&cG=${code}`
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
// 凭此code进入群组
|
||||
const groupCode = ref('')
|
||||
|
||||
// 当前活跃的群组ID
|
||||
const activeGID = ref(null)
|
||||
|
||||
// 临时生成的key,之后与返回的code结合形成groupCode
|
||||
const key = ref('')
|
||||
|
||||
// 存储所有消息,通过gID查询一个群组的消息,{msg,from,timestamp}
|
||||
const Messages = ref({})
|
||||
|
||||
const dataTest = ref([])
|
||||
const gID = ref('')
|
||||
// const codeInput = ref('')
|
||||
const msgInput = ref('')
|
||||
|
||||
const joinedGroup = ref([])
|
||||
// 由server随机分配uuid
|
||||
const uuid = ref('')
|
||||
|
||||
|
||||
const gID2key = {}
|
||||
|
||||
const options = {
|
||||
url: wsBaseUrl,
|
||||
pingTimeout: 20000,
|
||||
pongTimeout: 10000,
|
||||
reconnectTimeout: 3000,
|
||||
pingMsg: JSON.stringify({ type: 'heartbeat' })
|
||||
}
|
||||
let socket = new WebsocketHeartbeatJs(options);
|
||||
|
||||
// const socket = new WebSocket(baseUrl);
|
||||
// socket.addEventListener("open", (event) => {
|
||||
// // socket.send("");
|
||||
// });
|
||||
// setInterval(()=>console.log(activeGID.value),1000)
|
||||
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data)
|
||||
dataTest.value.push({ from: 'server', msg: event.data })
|
||||
if (msg.type) {
|
||||
let type = msg.type
|
||||
if (type === 'sMsg') {
|
||||
let type1 = msg.type1
|
||||
if (type1 === 'create') {
|
||||
gID.value = msg.gID
|
||||
groupCode.value = msg.gID + key.value
|
||||
joinGroup(groupCode.value)
|
||||
} else if (type1 === 'joinVerify') {
|
||||
let gID = msg.gID
|
||||
let key = gID2key[gID].key
|
||||
let res = decryptString(key, msg.verify)
|
||||
// console.log(res, key, msg.verify)
|
||||
|
||||
// 验证成功,正式加入群组
|
||||
if (res === 'miaoSpring') {
|
||||
socket.send(JSON.stringify({ type: 'join', type1: 'success', gID }))
|
||||
joinedGroup.value.push(msg.gID)
|
||||
Messages.value[msg.gID] = []
|
||||
// if(activeGID.value === null){
|
||||
activeGID.value = msg.gID
|
||||
// }
|
||||
}
|
||||
} else if (type1 === 'history') {
|
||||
let gID = msg.gID
|
||||
let key = gID2key[gID].key
|
||||
Messages.value[msg.gID].length = 0
|
||||
for (let m of JSON.parse(msg.history)) {
|
||||
Messages.value[msg.gID].push({ type: m.type,type1:m.type1, msg: decryptString(key, m.msg), from: m.from, timestamp: m.timestamp })
|
||||
dataTest.value.push({ from: 'decrypt', msg: decryptString(key, m.msg) })
|
||||
}
|
||||
} else if (type1 === 'uuid') {
|
||||
uuid.value = msg.uuid
|
||||
}
|
||||
} else if (type === 'gMsg') {
|
||||
let gID = msg.gID
|
||||
let key = gID2key[gID].key
|
||||
Messages.value[msg.gID].push({ type: msg.type,type1:msg.type1, msg: decryptString(key, msg.msg), from: msg.from, timestamp: msg.timestamp })
|
||||
dataTest.value.push({ from: 'decrypt', msg: decryptString(key, msg.msg) })
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onopen = () => {
|
||||
if (uuid.value) {
|
||||
socket.send(JSON.stringify({ type: 'uuid', type1: 'set', uuid: uuid.value }))
|
||||
} else {
|
||||
socket.send(JSON.stringify({ type: 'uuid', type1: 'get' }))
|
||||
}
|
||||
// console.log(joinedGroup)
|
||||
let toJoin = [...joinedGroup.value]
|
||||
joinedGroup.value.length = 0
|
||||
for (let gID of toJoin) {
|
||||
let code = gID + gID2key[gID].key
|
||||
console.log(code)
|
||||
joinGroup(code)
|
||||
}
|
||||
}
|
||||
|
||||
socket.onreconnect = (e) => {
|
||||
console.log('reconnecting...');
|
||||
}
|
||||
|
||||
function createGroup() {
|
||||
let keyt = generateKey(setting)
|
||||
key.value = keyt
|
||||
socket.send(JSON.stringify({ type: 'create', verify: encryptString(keyt, 'miaoSpring', setting) }))
|
||||
}
|
||||
|
||||
function joinGroup(code) {
|
||||
var gID = code.substring(0, 8);
|
||||
var key = code.substring(8);
|
||||
if (joinedGroup.value.includes(gID)) {
|
||||
return
|
||||
}
|
||||
gID2key[gID] = {}
|
||||
gID2key[gID].key = key
|
||||
joinGroup_Verify(gID)
|
||||
}
|
||||
|
||||
function joinGroup_Verify(gID) {
|
||||
socket.send(JSON.stringify({ type: 'join', type1: 'verify', gID }))
|
||||
}
|
||||
|
||||
function sendMsg(gID, msg) {
|
||||
if(msg === '' || msg === null || activeGID.value === null)return
|
||||
let key = gID2key[gID].key
|
||||
msg = encryptString(key, msg, setting)
|
||||
socket.send(JSON.stringify({ type: 'message',type1:'text', msg, gID }))
|
||||
msgInput.value = null
|
||||
}
|
||||
|
||||
// async function encryptImg(file){
|
||||
// var f = new Blob([file]);
|
||||
// let key = gID2key[gID].key
|
||||
// let eU8A = await encrypt(key, f, fileName, setting)
|
||||
// let eb = Base64.fromUint8Array(eU8A)
|
||||
// return eb
|
||||
// }
|
||||
|
||||
async function uploadFile(file){
|
||||
if(activeGID.value === null)return
|
||||
var fileName = file.name;
|
||||
let key = gID2key[activeGID.value].key
|
||||
const encryptedFileName = encryptString(key, fileName, setting);
|
||||
var f = new Blob([file]);
|
||||
let data = await encrypt(key, f, fileName, setting)
|
||||
const params = {
|
||||
fileName: encryptedFileName,
|
||||
downloadCredit:-1
|
||||
};
|
||||
message.info(`开始上传${fileName}`)
|
||||
const xhr = new XMLHttpRequest();
|
||||
// onProgress({ percent: 1 });
|
||||
xhr.open(
|
||||
"POST",
|
||||
baseUrl + "file/upload?" + new URLSearchParams(params)
|
||||
);
|
||||
// console.log(setting.token)
|
||||
xhr.setRequestHeader('Authorization', `Bearer ${setting.token}`);
|
||||
xhr.send(data);
|
||||
const code = await new Promise((resolve,reject)=>{
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
let id = xhr.responseText;
|
||||
let code = id + key
|
||||
message.success(`上传${fileName}成功`)
|
||||
resolve(code)
|
||||
} else {
|
||||
if(xhr.statusText !== 'OK'){
|
||||
console.error(xhr.statusText);
|
||||
message.error(`上传${fileName}失败`)
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
console.log(code)
|
||||
return code
|
||||
}
|
||||
|
||||
async function sendImages(files){
|
||||
if(activeGID.value === null || files.length === 0)return
|
||||
for(let file of files){
|
||||
const code = await uploadFile(file)
|
||||
let key = gID2key[activeGID.value].key
|
||||
socket.send(JSON.stringify({ type: 'message',type1:'img', msg: encryptString(key, code), gID:activeGID.value }))
|
||||
};
|
||||
}
|
||||
|
||||
async function sendFiles(files){
|
||||
if(activeGID.value === null || files.length === 0)return
|
||||
for(let file of files){
|
||||
let fileName = file.name;
|
||||
const code = await uploadFile(file)
|
||||
let key = gID2key[activeGID.value].key
|
||||
socket.send(JSON.stringify({ type: 'message',type1:'file', msg: encryptString(key, code), gID:activeGID.value }))
|
||||
};
|
||||
}
|
||||
onMounted(()=>{
|
||||
let aP = getQueryString('aP')
|
||||
let cG = getQueryString('cG')
|
||||
if(aP === 'chat' && cG){
|
||||
groupCode.value = cG
|
||||
var st = setInterval(()=>{
|
||||
if(joinedGroup.value.includes(cG.substring(0,8))){
|
||||
clearInterval(st)
|
||||
return
|
||||
}
|
||||
joinGroup(cG)
|
||||
},2000)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -22,17 +22,15 @@
|
||||
:max-height="250"
|
||||
:bordered="false"
|
||||
:single-line="false"
|
||||
style="user-select: none;"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import aesjs from "aes-js";
|
||||
import { Base64 } from "js-base64";
|
||||
import { saveAs } from "file-saver";
|
||||
import { ref, onMounted,computed } from "vue";
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import { sm4 } from "sm-crypto";
|
||||
import {decrypt, decryptString, downloadFromUint8Array} from '../func/download'
|
||||
import {NInput,NFlex,NDataTable,NButton} from 'naive-ui'
|
||||
|
||||
var downloadFileID = ref();
|
||||
@@ -46,83 +44,85 @@ var baseUrl =
|
||||
location.protocol + "//" + location.hostname + ":" + serverPort + "/";
|
||||
// console.log(baseUrl);
|
||||
|
||||
const blob2Uint8Array = function(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob);
|
||||
reader.onload = function() {
|
||||
let buffer = reader.result;
|
||||
let uint8array = new Uint8Array(buffer);
|
||||
resolve(uint8array);
|
||||
};
|
||||
reader.onerror = function() {
|
||||
reject(reader.error);
|
||||
};
|
||||
});
|
||||
};
|
||||
const decrypt = (key, data) => {
|
||||
return blob2Uint8Array(data).then(data => {
|
||||
// 无加密
|
||||
if(key.length === 0){
|
||||
return data
|
||||
}
|
||||
// SM4
|
||||
if (key.length >= 34) {
|
||||
key = key.substring(3);
|
||||
// console.log([...data])
|
||||
var decryptedBytes = new Uint8Array(
|
||||
sm4.decrypt([...data], key, { output: "array" })
|
||||
);
|
||||
return decryptedBytes;
|
||||
} else {
|
||||
// AES
|
||||
key = aesjs.utils.utf8.toBytes(key);
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(
|
||||
key,
|
||||
new aesjs.Counter(5)
|
||||
);
|
||||
var decryptedBytes = aesCtr.decrypt(data);
|
||||
return decryptedBytes;
|
||||
}
|
||||
});
|
||||
};
|
||||
const decryptString = (key, str) => {
|
||||
// 无加密
|
||||
if(key.length === 0){
|
||||
return sm4.decrypt(str, 'miaospringmiaospringmiaospringao');
|
||||
}
|
||||
// SM4
|
||||
// console.log(key,key.length)
|
||||
if (key.length >= 34) {
|
||||
key = key.substring(3);
|
||||
return sm4.decrypt(str, key);
|
||||
}
|
||||
// AES
|
||||
var data = new Uint8Array(
|
||||
Base64.decode(str)
|
||||
.split(",")
|
||||
.map(n => parseInt(n))
|
||||
);
|
||||
key = aesjs.utils.utf8.toBytes(key);
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
var decryptedBytes = aesCtr.decrypt(data);
|
||||
var decryptedStr = aesjs.utils.utf8.fromBytes(decryptedBytes);
|
||||
return decryptedStr;
|
||||
};
|
||||
// const blob2Uint8Array = function(blob) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// let reader = new FileReader();
|
||||
// reader.readAsArrayBuffer(blob);
|
||||
// reader.onload = function() {
|
||||
// let buffer = reader.result;
|
||||
// let uint8array = new Uint8Array(buffer);
|
||||
// resolve(uint8array);
|
||||
// };
|
||||
// reader.onerror = function() {
|
||||
// reject(reader.error);
|
||||
// };
|
||||
// });
|
||||
// };
|
||||
// const decrypt = (key, data) => {
|
||||
// return blob2Uint8Array(data).then(data => {
|
||||
// // 无加密
|
||||
// if(key.length === 0){
|
||||
// return data
|
||||
// }
|
||||
// // SM4
|
||||
// if (key.length >= 34) {
|
||||
// key = key.substring(3);
|
||||
// // console.log([...data])
|
||||
// var decryptedBytes = new Uint8Array(
|
||||
// sm4.decrypt([...data], key, { output: "array" })
|
||||
// );
|
||||
// return decryptedBytes;
|
||||
// } else {
|
||||
// // AES
|
||||
// key = aesjs.utils.utf8.toBytes(key);
|
||||
// var aesCtr = new aesjs.ModeOfOperation.ctr(
|
||||
// key,
|
||||
// new aesjs.Counter(5)
|
||||
// );
|
||||
// var decryptedBytes = aesCtr.decrypt(data);
|
||||
// return decryptedBytes;
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
// const decryptString = (key, str) => {
|
||||
// // 无加密
|
||||
// if(key.length === 0){
|
||||
// return sm4.decrypt(str, 'miaospringmiaospringmiaospringao');
|
||||
// }
|
||||
// // SM4
|
||||
// // console.log(key,key.length)
|
||||
// if (key.length >= 34) {
|
||||
// key = key.substring(3);
|
||||
// return sm4.decrypt(str, key);
|
||||
// }
|
||||
// // AES
|
||||
// var data = new Uint8Array(
|
||||
// Base64.decode(str)
|
||||
// .split(",")
|
||||
// .map(n => parseInt(n))
|
||||
// );
|
||||
// key = aesjs.utils.utf8.toBytes(key);
|
||||
// var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
// var decryptedBytes = aesCtr.decrypt(data);
|
||||
// var decryptedStr = aesjs.utils.utf8.fromBytes(decryptedBytes);
|
||||
// return decryptedStr;
|
||||
// };
|
||||
|
||||
// const downloadFromUint8Array = (data, fileName) => {
|
||||
// let uint8Array = data;
|
||||
// let blob = new Blob([uint8Array], { type: "text/plain" });
|
||||
// // 使用file-saver.js
|
||||
// saveAs(blob, fileName);
|
||||
|
||||
// //let url = URL.createObjectURL(blob);
|
||||
// //let a = document.createElement("a");
|
||||
// //a.href = url;
|
||||
// //a.download = fileName;
|
||||
// //a.click();
|
||||
// //URL.revokeObjectURL(url);
|
||||
// };
|
||||
|
||||
const downloadFromUint8Array = (data, fileName) => {
|
||||
let uint8Array = data;
|
||||
let blob = new Blob([uint8Array], { type: "text/plain" });
|
||||
// 使用file-saver.js
|
||||
saveAs(blob, fileName);
|
||||
|
||||
//let url = URL.createObjectURL(blob);
|
||||
//let a = document.createElement("a");
|
||||
//a.href = url;
|
||||
//a.download = fileName;
|
||||
//a.click();
|
||||
//URL.revokeObjectURL(url);
|
||||
};
|
||||
// 刷新展示的下载进度
|
||||
const updateDownloadQuene = (code, key, value) => {
|
||||
for (let index in downloadQuene.value) {
|
||||
@@ -158,7 +158,11 @@ const downloadFile = code => {
|
||||
fileName: id
|
||||
};
|
||||
const options = {
|
||||
method: "GET"
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Authorization': `Bearer ${setting.token}`
|
||||
// 'Content-Type':'application/json'
|
||||
},
|
||||
};
|
||||
fetch(baseUrl + "file/download?" + new URLSearchParams(params), options)
|
||||
.then(res => {
|
||||
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<n-card style="box-shadow: 10px 10px 5px #888888;margin-top: 100px; padding-left: 25px;padding-right: 25px; padding-bottom: 30px" size="small">
|
||||
<n-flex vertical justify="center">
|
||||
<n-flex justify="center">
|
||||
<h1 style="letter-spacing:25px;font-size: 40px;">登录</h1>
|
||||
</n-flex>
|
||||
<n-input-group>
|
||||
<n-input-group-label>账号</n-input-group-label>
|
||||
<n-input placeholder="userID" v-model:value="userIDInput" :loading="isLogining" :disabled="isLogining"></n-input>
|
||||
</n-input-group>
|
||||
<n-input-group>
|
||||
<n-input-group-label>密码</n-input-group-label>
|
||||
<n-input placeholder="userPassword" v-model:value="userPasswordInput" type="password"
|
||||
show-password-on="click" :loading="isLogining" :disabled="isLogining"></n-input>
|
||||
</n-input-group>
|
||||
<n-flex justify="end">
|
||||
<n-flex style="flex:1;align-items: center;">
|
||||
<n-checkbox size="large" label="保持登录状态" v-model:checked="keepTokenLocal" />
|
||||
</n-flex>
|
||||
<n-flex justify="end">
|
||||
<n-button @click="useGuestAccount" v-if="settingData.enableGuest">使用访客账号登录</n-button>
|
||||
<n-button @click="login">登录</n-button>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { NCheckbox, NInputGroupLabel, NInputGroup, NP, NFlex, NDataTable, NButton, NInput, NCard } from 'naive-ui'
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import { useMessage, useNotification } from "naive-ui";
|
||||
import sha256 from "crypto-js/sha256.js";
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const settingData = useSettingStore();
|
||||
|
||||
const serverPort = settingData.serverPort;
|
||||
|
||||
|
||||
const userIDInput = ref('')
|
||||
const userPasswordInput = ref('')
|
||||
const isLogining = ref(false)
|
||||
const baseUrl =
|
||||
location.protocol + "//" + location.hostname + ":" + serverPort + "/";
|
||||
|
||||
const Token = computed(() => settingData.token)
|
||||
const keepTokenLocal = ref(true)
|
||||
|
||||
|
||||
|
||||
function useGuestAccount() {
|
||||
userIDInput.value = 'guest'
|
||||
userPasswordInput.value = 'guest'
|
||||
login()
|
||||
}
|
||||
async function login() {
|
||||
try {
|
||||
isLogining.value = true
|
||||
const response = await fetch(`${baseUrl}user/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
// 'Authorization': `Bearer ${Token}`
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: userIDInput.value,
|
||||
pwd: sha256(userPasswordInput.value).toString()
|
||||
})
|
||||
})
|
||||
let data = await response.json()
|
||||
if (data.status === 'Error') {
|
||||
console.log('error')
|
||||
throw ('failed')
|
||||
}
|
||||
console.log(data.token, data)
|
||||
settingData.setJWT(data.token,keepTokenLocal)
|
||||
settingData.setUserID(userIDInput.value,keepTokenLocal)
|
||||
settingData.setUserGroup(data.userGroup,keepTokenLocal)
|
||||
message.success(`登录成功`);
|
||||
} catch (e) {
|
||||
isLogining.value = false
|
||||
message.error(`登录失败`);
|
||||
console.log('failed')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<n-list hoverable>
|
||||
<n-list-item>
|
||||
<p style="font-size:larger;position:absolute;left:5px;">选择加密算法:</p>
|
||||
<p style="font-size:larger;position:absolute;left:5px;user-select: none;">选择加密算法:</p>
|
||||
<br />
|
||||
<!-- <br /> -->
|
||||
<!-- <br /> -->
|
||||
@@ -16,7 +16,7 @@
|
||||
</n-flex>
|
||||
</n-list-item>
|
||||
<n-list-item>
|
||||
<p style="font-size:larger;position:absolute;left:5px;">选择密钥组成:</p>
|
||||
<p style="font-size:larger;position:absolute;left:5px;user-select: none;">选择密钥组成:</p>
|
||||
<br />
|
||||
<n-flex justify="end">
|
||||
<span v-for="k in keyButtons" style="margin:5px">
|
||||
@@ -11,10 +11,10 @@
|
||||
<!-- <div style="margin-bottom: 12px">
|
||||
<n-icon size="48" :depth="3"></n-icon>
|
||||
</div>-->
|
||||
<n-text style="font-size: 16px">点击此处或者拖动文件到该区域来上传</n-text>
|
||||
<n-p depth="3" style="margin: 8px 0 0 0">
|
||||
<n-text style="font-size: 16px;user-select: none;">点击此处或者拖动文件到该区域来上传</n-text>
|
||||
<n-p depth="3" style="margin: 8px 0 0 0;user-select: none;">
|
||||
最大支持200MB大小文件,其实只是懒得做分片上传而已
|
||||
<br />虽然理论上没有人有能力审查你上传的东西
|
||||
<!-- <br />虽然理论上没有人有能力审查你上传的东西 -->
|
||||
<br />反正没人用写这么多干什么。。。
|
||||
</n-p>
|
||||
</n-upload-dragger>
|
||||
@@ -31,58 +31,57 @@
|
||||
:max-height="250"
|
||||
:bordered="false"
|
||||
:single-line="false"
|
||||
style="user-select: none;"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import aesjs from "aes-js";
|
||||
import { Base64 } from "js-base64";
|
||||
import { ref, onMounted,computed } from "vue";
|
||||
// import aesjs from "aes-js";
|
||||
// import { Base64 } from "js-base64";
|
||||
import { useMessage, useNotification } from "naive-ui";
|
||||
import useClipboard from "vue-clipboard3";
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import { sm4 } from 'sm-crypto'
|
||||
// import { sm4 } from 'sm-crypto'
|
||||
import {NUpload,NUploadDragger,NText,NP,NFlex,NDataTable,NButton,NInputNumber} from 'naive-ui'
|
||||
import {encrypt,encryptString,generateKey} from '../func/upload'
|
||||
|
||||
const setting = useSettingStore();
|
||||
const { toClipboard } = useClipboard();
|
||||
const message = useMessage();
|
||||
const notification = useNotification();
|
||||
|
||||
// 目前使用加密
|
||||
const activeFuction = setting.activeFuction;
|
||||
// 启用的密钥组成
|
||||
const activeKeyButtons = setting.activeKeyMap;
|
||||
|
||||
// 服务端端口
|
||||
const serverPort = setting.serverPort;
|
||||
|
||||
// console.log(activeFuction,activeKeyButtons)
|
||||
var charMap = ""
|
||||
for(let i of setting.activeKeyMap){
|
||||
if(i === 'A-Z'){
|
||||
charMap = charMap + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
}else if(i === "a-z"){
|
||||
charMap = charMap + 'abcdefghijklmnopqrstuvwxyz'
|
||||
}else if(i === '0-9'){
|
||||
charMap = charMap + '0123456789'
|
||||
}
|
||||
}
|
||||
// 生成密钥
|
||||
const generateKey = function() {
|
||||
if(setting.activeFuction === "不加密"){
|
||||
return ''
|
||||
}
|
||||
var returnStr = "",
|
||||
length = setting.activeFuction === "AES128" ? 16 : 32
|
||||
// charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
// var charMap = ""
|
||||
// for(let i of setting.activeKeyMap){
|
||||
// if(i === 'A-Z'){
|
||||
// charMap = charMap + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
// }else if(i === "a-z"){
|
||||
// charMap = charMap + 'abcdefghijklmnopqrstuvwxyz'
|
||||
// }else if(i === '0-9'){
|
||||
// charMap = charMap + '0123456789'
|
||||
// }
|
||||
// }
|
||||
// // 生成密钥
|
||||
// const generateKey = function() {
|
||||
// if(setting.activeFuction === "不加密"){
|
||||
// return ''
|
||||
// }
|
||||
// var returnStr = "",
|
||||
// length = setting.activeFuction === "AES128" ? 16 : 32
|
||||
// // charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var index = Math.round(Math.random() * charMap.length);
|
||||
returnStr += charMap.substring(index, index + 1);
|
||||
}
|
||||
return returnStr;
|
||||
};
|
||||
// for (var i = 0; i < length; i++) {
|
||||
// var index = Math.round(Math.random() * charMap.length);
|
||||
// returnStr += charMap.substring(index, index + 1);
|
||||
// }
|
||||
// return returnStr;
|
||||
// };
|
||||
|
||||
// 上传文件用
|
||||
const uploadRef = ref(null);
|
||||
@@ -99,69 +98,68 @@ const baseUrl =
|
||||
location.protocol + "//" + location.hostname + ":" + serverPort + "/";
|
||||
|
||||
|
||||
const blob2Uint8Array = function(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob);
|
||||
reader.onload = function() {
|
||||
let buffer = reader.result;
|
||||
let uint8array = new Uint8Array(buffer);
|
||||
resolve(uint8array);
|
||||
};
|
||||
reader.onerror = function() {
|
||||
reject(reader.error);
|
||||
};
|
||||
});
|
||||
};
|
||||
// 包含AES128和AES256
|
||||
const AES = (key,data)=>{
|
||||
key = aesjs.utils.utf8.toBytes(key);
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
var encryptedBytes = aesCtr.encrypt(data);
|
||||
return encryptedBytes;
|
||||
}
|
||||
const SM4 = (key,data)=>{
|
||||
// console.log(key)
|
||||
var encryptedBytes = sm4.encrypt(data,key,{padding: 'pkcs#7',output:'array'});
|
||||
// console.log(encryptedBytes, sm4.decrypt(encryptedBytes, key,{output:'array'}))
|
||||
return new Uint8Array(encryptedBytes);
|
||||
}
|
||||
const encrypt = (key, data, fileName) => {
|
||||
return blob2Uint8Array(data).then(data => {
|
||||
let encrypted_data = null
|
||||
if(setting.activeFuction === "不加密"){
|
||||
encrypted_data = data
|
||||
}else if(setting.activeFuction != "SM4"){
|
||||
message.info(`开始加密${fileName}`);
|
||||
encrypted_data = AES(key,data)
|
||||
message.info(`${fileName}加密完成`);
|
||||
}else{
|
||||
message.info(`开始加密${fileName}`);
|
||||
encrypted_data = SM4(key,data)
|
||||
message.info(`${fileName}加密完成`);
|
||||
}
|
||||
return encrypted_data
|
||||
});
|
||||
};
|
||||
const encryptString = (key, str) => {
|
||||
// console.log(key)
|
||||
if(setting.activeFuction === "不加密"){
|
||||
return sm4.encrypt(str, 'miaospringmiaospringmiaospringao')
|
||||
}else if(setting.activeFuction !== "SM4"){
|
||||
key = aesjs.utils.utf8.toBytes(key);
|
||||
var data = aesjs.utils.utf8.toBytes(str);
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
var encryptedBytes = aesCtr.encrypt(data);
|
||||
return Base64.encode(encryptedBytes);
|
||||
}else{
|
||||
return sm4.encrypt(str, key)
|
||||
}
|
||||
};
|
||||
// const blob2Uint8Array = function(blob) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// let reader = new FileReader();
|
||||
// reader.readAsArrayBuffer(blob);
|
||||
// reader.onload = function() {
|
||||
// let buffer = reader.result;
|
||||
// let uint8array = new Uint8Array(buffer);
|
||||
// resolve(uint8array);
|
||||
// };
|
||||
// reader.onerror = function() {
|
||||
// reject(reader.error);
|
||||
// };
|
||||
// });
|
||||
// };
|
||||
// // 包含AES128和AES256
|
||||
// const AES = (key,data)=>{
|
||||
// key = aesjs.utils.utf8.toBytes(key);
|
||||
// var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
// var encryptedBytes = aesCtr.encrypt(data);
|
||||
// return encryptedBytes;
|
||||
// }
|
||||
// const SM4 = (key,data)=>{
|
||||
// // console.log(key)
|
||||
// var encryptedBytes = sm4.encrypt(data,key,{padding: 'pkcs#7',output:'array'});
|
||||
// // console.log(encryptedBytes, sm4.decrypt(encryptedBytes, key,{output:'array'}))
|
||||
// return new Uint8Array(encryptedBytes);
|
||||
// }
|
||||
// const encrypt = (key, data, fileName) => {
|
||||
// return blob2Uint8Array(data).then(data => {
|
||||
// let encrypted_data = null
|
||||
// if(setting.activeFuction === "不加密"){
|
||||
// encrypted_data = data
|
||||
// }else if(setting.activeFuction != "SM4"){
|
||||
// message.info(`开始加密${fileName}`);
|
||||
// encrypted_data = AES(key,data)
|
||||
// message.info(`${fileName}加密完成`);
|
||||
// }else{
|
||||
// message.info(`开始加密${fileName}`);
|
||||
// encrypted_data = SM4(key,data)
|
||||
// message.info(`${fileName}加密完成`);
|
||||
// }
|
||||
// return encrypted_data
|
||||
// });
|
||||
// };
|
||||
// const encryptString = (key, str) => {
|
||||
// // console.log(key)
|
||||
// if(setting.activeFuction === "不加密"){
|
||||
// return sm4.encrypt(str, 'miaospringmiaospringmiaospringao')
|
||||
// }else if(setting.activeFuction !== "SM4"){
|
||||
// key = aesjs.utils.utf8.toBytes(key);
|
||||
// var data = aesjs.utils.utf8.toBytes(str);
|
||||
// var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
|
||||
// var encryptedBytes = aesCtr.encrypt(data);
|
||||
// return Base64.encode(encryptedBytes);
|
||||
// }else{
|
||||
// return sm4.encrypt(str, key)
|
||||
// }
|
||||
// };
|
||||
|
||||
const clickUpload = function(){
|
||||
// for(let i of uploadRef.value)console.log(i)
|
||||
// console.log(uploadRef.value)
|
||||
message.info(`开始上传`);
|
||||
uploadRef.value.submit();
|
||||
}
|
||||
|
||||
@@ -184,13 +182,13 @@ const customRequest = ({
|
||||
onError,
|
||||
onProgress
|
||||
}) => {
|
||||
// console.log(file);
|
||||
file = file.file;
|
||||
var fileName = file.name;
|
||||
const key = generateKey();
|
||||
const encryptedFileName = encryptString(key, fileName);
|
||||
const key = generateKey(setting);
|
||||
const encryptedFileName = encryptString(key, fileName, setting);
|
||||
var f = new Blob([file]);
|
||||
encrypt(key, f, fileName).then(data => {
|
||||
encrypt(key, f, fileName, setting, message).then(data => {
|
||||
message.info(`开始上传${fileName}`);
|
||||
const params = {
|
||||
fileName: encryptedFileName,
|
||||
downloadCredit:downloadCredit.value == null ? -1 : downloadCredit.value
|
||||
@@ -206,14 +204,16 @@ const customRequest = ({
|
||||
"POST",
|
||||
baseUrl + "file/upload?" + new URLSearchParams(params)
|
||||
);
|
||||
console.log(setting.token)
|
||||
xhr.setRequestHeader('Authorization', `Bearer ${setting.token}`);
|
||||
xhr.send(data);
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
let id = xhr.responseText;
|
||||
let code = id + key
|
||||
if(setting.activeFuction === "SM4"){
|
||||
code = id + 'SM4' + key
|
||||
}
|
||||
// if(setting.activeFuction === "SM4"){
|
||||
// code = id + 'SM4' + key
|
||||
// }
|
||||
file_Code_Map.value.push({
|
||||
code: code,
|
||||
fileName: fileName
|
||||
@@ -0,0 +1,9 @@
|
||||
import aesjs from 'aes-js'
|
||||
|
||||
self.onmessage = async function (event) {
|
||||
const data = event.data.data
|
||||
const key = event.data.key
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(0))
|
||||
const encryptData = aesCtr.encrypt(data)
|
||||
self.postMessage(encryptData)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { sm4 } from 'sm-crypto'
|
||||
|
||||
self.onmessage = async function (event) {
|
||||
const data = event.data.data
|
||||
const key = event.data.key
|
||||
const encryptData = sm4.encrypt(data, key, {
|
||||
padding: 'pkcs#7',
|
||||
output: 'array'
|
||||
})
|
||||
self.postMessage(encryptData)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import sha256 from 'crypto-js/sha256.js';
|
||||
import fs from 'fs'
|
||||
|
||||
const JWT_SK = fs.readFileSync('JWT_SK.key').toString()
|
||||
|
||||
let login = {id:123456,pwd:sha256('nmsl').toString()}
|
||||
console.log(JWT_SK,login)
|
||||
|
||||
let token = jwt.sign(login,JWT_SK,{ expiresIn: '1h' })
|
||||
|
||||
let fakeToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTIzNDU2LCJwd2QiOiIwNmYxYTIzOTE2MGZhNTQ2ZTAyOWM5YWQxNTU4OGYwYTI0YWVjYWNhN2M1ZjkyZTE4YmFlNjMzMDk1ZmRjM2JlIiwiaWF0IjoxNzExNTEzMjIwLCJleHAiOjE3MTE1MTY4MjB9.O4-0DkT8Nn0jwZUj09DFK-dOhOfG0n1Mmx3QvsfU8x1`
|
||||
|
||||
// error
|
||||
let verify = jwt.verify(token,JWT_SK)
|
||||
|
||||
console.log(token,verify,parseInt(Date.now()/1000))
|
||||
+23
-2
@@ -14,8 +14,29 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
server:{
|
||||
host:'0.0.0.0',
|
||||
port:'17057',
|
||||
// host:'0.0.0.0',
|
||||
port:'17058',
|
||||
open:true,
|
||||
},
|
||||
esbuild: {
|
||||
drop: ['console', 'debugger'],
|
||||
},
|
||||
build: {
|
||||
minify: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// 打包输出的配置
|
||||
manualChunks: id => {
|
||||
// 这个ID,就是所有文件的绝对路径
|
||||
if (id.includes("node_modules")) {
|
||||
// 因为 node_modules 中的依赖通常是不会改变的
|
||||
// 所以直接单独打包出去
|
||||
// 这个return 的值就是打包的名称
|
||||
// return id.split('node_modules')[1]
|
||||
return "modules";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user