Compare commits

..
26 Commits
Author SHA1 Message Date
539943419 82d9531c25 ... 2024-07-29 15:20:30 +08:00
539943419 ad8260b87e app上路径直接写死 2024-07-29 15:06:07 +08:00
539943419 4b9599ef8a fix 2024-07-29 15:00:05 +08:00
539943419 5ad3f87323 fix 2024-07-29 14:58:13 +08:00
539943419 5b5849744b ignore visualizer.html 2024-07-29 14:43:06 +08:00
539943419 44f3d57884 统一在store.js中管理baseUrl 2024-07-29 14:41:50 +08:00
539943419 dfc26aedea delete visualizer.html 2024-07-20 21:18:03 +08:00
539943419 1780a0bb8b 添加复制时提示 2024-07-20 21:16:10 +08:00
539943419 cf4297cc3c 修改chat界面样式,增加复制文本消息功能 2024-07-20 09:16:49 +08:00
539943419 b29add4ae5 修正SM4解密时产生错误结果的bug 2024-07-19 19:59:16 +08:00
539943419 f0e4a777d9 完善使用worker进行加密解密相关代码 2024-07-19 17:24:08 +08:00
539943419 3c2dc0f183 使用worker进行加解密,防止主线程卡死 2024-07-19 17:04:54 +08:00
539943419 8d08392cad 修复了chat中图片显示的相关问题 2024-07-07 12:40:25 +08:00
539943419 ba43ad2bbc 添加token过期检查 2024-07-02 20:13:07 +08:00
539943419 84025a83ab 优化vite配置 2024-07-02 19:27:26 +08:00
539943419 17caf491f1 create miaoworkerpool.js 2024-06-29 01:06:00 +08:00
539943419 133d0b7844 现在非开发环境下web端视为服务器端口与页面端口一致 2024-04-23 20:18:45 +08:00
539943419 5ca7125328 chat中可通过ctrl+enter发送消息 2024-04-03 13:00:18 +08:00
539943419 0389c8b7fe 优化细节表现 2024-04-03 12:45:13 +08:00
539943419 dd56403d93 chat中添加上传文件功能 2024-04-03 11:33:46 +08:00
539943419 709cac04f6 更新chat的ui界面 2024-04-02 23:18:54 +08:00
539943419 530e54a596 添加登录功能 2024-03-27 22:05:00 +08:00
539943419 20e9f4940e 修正AES加密字符串中对emoji处理的编码问题 2024-03-22 22:58:12 +08:00
539943419 ec5fcf1ee7 添加心跳包检测 2024-03-22 16:38:33 +08:00
539943419 127f58bd36 添加chat功能 2024-03-22 14:35:46 +08:00
539943419 08e7820f37 拆分部分文件 2024-03-21 20:37:10 +08:00
36 changed files with 4902 additions and 376 deletions

No files matched your search

+7
View File
@@ -32,3 +32,10 @@ coverage
# files created with miaoShare service # files created with miaoShare service
temp temp
miaoShare.db miaoShare.db
# addUser.js
# removeUser.js
# JWT SK
*.key
visualizer.html
+16
View File
@@ -0,0 +1,16 @@
import sha256 from 'crypto-js/sha256.js';
import Database from "better-sqlite3";
const dbPath = "./miaoShare.db";
const db = new Database(dbPath, { verbose: null });
const stmt_insertId2FileName = db.prepare(`INSERT INTO USERS (user_id, password_hash, user_group, uploadFilesSizeCount) VALUES (?, ?, ?, 0)`);
let userList = [
['539943419','zjh030628','admin'],
// ['guest','guest','guest']
].map(a=>{a[1]=sha256(a[0]+sha256(a[1]).toString()).toString();return a})
for(let i of userList){
stmt_insertId2FileName.run(...i)
}
+2
View File
@@ -14,6 +14,8 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
height: 100%; height: 100%;
width:100%;
max-width: 100%;
display: flex; display: flex;
} }
</style> </style>
+13
View File
@@ -0,0 +1,13 @@
{
"PORT":17057,
"userGroupSetting":{
"admin":{
"cloudCapacity":2147483648
},
"guest":{
"cloudCapacity":2147483648
}
},
"enableGuest":false,
"tokenExpiresIn":168
}
+231 -24
View File
@@ -9,22 +9,30 @@
"version": "1.2.0", "version": "1.2.0",
"dependencies": { "dependencies": {
"aes-js": "^3.1.2", "aes-js": "^3.1.2",
"better-sqlite3": "^8.7.0", "crypto-js": "^4.2.0",
"cors": "^2.8.5",
"express": "^4.18.2", "express": "^4.18.2",
"file-saver": "^2.0.5", "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", "pinia": "^2.1.7",
"sm-crypto": "^0.3.13", "sm-crypto": "^0.3.13",
"uuid": "^9.0.1",
"vicons": "^0.0.1", "vicons": "^0.0.1",
"vue": "^3.4.15", "vue": "^3.4.15",
"vue-clipboard3": "^2.0.0", "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": { "devDependencies": {
"@vicons/fluent": "^0.12.0",
"@vicons/ionicons5": "^0.12.0",
"@vitejs/plugin-vue": "^5.0.3", "@vitejs/plugin-vue": "^5.0.3",
"better-sqlite3": "^8.7.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"naive-ui": "^2.37.3", "cors": "^2.8.5",
"naive-ui": "^2.38.1",
"vfonts": "^0.0.3", "vfonts": "^0.0.3",
"vite": "^5.0.11" "vite": "^5.0.11"
} }
@@ -651,6 +659,18 @@
"@types/lodash": "*" "@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": { "node_modules/@vitejs/plugin-vue": {
"version": "5.0.3", "version": "5.0.3",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz", "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz",
@@ -811,12 +831,14 @@
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", "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": { "node_modules/better-sqlite3": {
"version": "8.7.0", "version": "8.7.0",
"resolved": "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-8.7.0.tgz", "resolved": "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-8.7.0.tgz",
"integrity": "sha512-99jZU4le+f3G6aIl6PmmV0cxUIWqKieHxsiF7G34CVFiE+/UabpYqkU0NJIkY/96mQKikHeBjtR27vFfs5JpEw==", "integrity": "sha512-99jZU4le+f3G6aIl6PmmV0cxUIWqKieHxsiF7G34CVFiE+/UabpYqkU0NJIkY/96mQKikHeBjtR27vFfs5JpEw==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"bindings": "^1.5.0", "bindings": "^1.5.0",
@@ -827,6 +849,7 @@
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz", "resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"dependencies": { "dependencies": {
"file-uri-to-path": "1.0.0" "file-uri-to-path": "1.0.0"
} }
@@ -835,6 +858,7 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dev": true,
"dependencies": { "dependencies": {
"buffer": "^5.5.0", "buffer": "^5.5.0",
"inherits": "^2.0.4", "inherits": "^2.0.4",
@@ -868,11 +892,17 @@
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dev": true,
"dependencies": { "dependencies": {
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"ieee754": "^1.1.13" "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": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
@@ -923,7 +953,8 @@
"node_modules/chownr": { "node_modules/chownr": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", "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": { "node_modules/clipboard": {
"version": "2.0.11", "version": "2.0.11",
@@ -1027,6 +1058,7 @@
"version": "2.8.5", "version": "2.8.5",
"resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz", "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dev": true,
"dependencies": { "dependencies": {
"object-assign": "^4", "object-assign": "^4",
"vary": "^1" "vary": "^1"
@@ -1035,6 +1067,11 @@
"node": ">= 0.10" "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": { "node_modules/css-render": {
"version": "0.15.12", "version": "0.15.12",
"resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz", "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.12.tgz",
@@ -1089,6 +1126,7 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dev": true,
"dependencies": { "dependencies": {
"mimic-response": "^3.1.0" "mimic-response": "^3.1.0"
}, },
@@ -1100,6 +1138,7 @@
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz", "resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true,
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=4.0.0"
} }
@@ -1144,10 +1183,19 @@
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz", "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz",
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "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": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
@@ -1171,6 +1219,7 @@
"version": "1.4.4", "version": "1.4.4",
"resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz", "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dev": true,
"dependencies": { "dependencies": {
"once": "^1.4.0" "once": "^1.4.0"
} }
@@ -1266,6 +1315,7 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz", "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -1319,7 +1369,8 @@
"node_modules/file-uri-to-path": { "node_modules/file-uri-to-path": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "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": { "node_modules/finalhandler": {
"version": "1.2.0", "version": "1.2.0",
@@ -1357,7 +1408,8 @@
"node_modules/fs-constants": { "node_modules/fs-constants": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", "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": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
@@ -1405,7 +1457,8 @@
"node_modules/github-from-package": { "node_modules/github-from-package": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz", "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": { "node_modules/good-listener": {
"version": "1.2.2", "version": "1.2.2",
@@ -1505,7 +1558,8 @@
"node_modules/ieee754": { "node_modules/ieee754": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", "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": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
@@ -1515,7 +1569,8 @@
"node_modules/ini": { "node_modules/ini": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", "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": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
@@ -1535,15 +1590,60 @@
} }
}, },
"node_modules/js-base64": { "node_modules/js-base64": {
"version": "3.7.6", "version": "3.7.7",
"resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.6.tgz", "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.7.tgz",
"integrity": "sha512-NPrWuHFxFUknr1KqJRDgUQPexQF0uIJWjeT+2KjEePhitQxQEx5EJBG1lVn5/hc8aLycTpXrDOgPQ6Zq+EDiTA==" "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="
}, },
"node_modules/jsbn": { "node_modules/jsbn": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" "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": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@@ -1556,6 +1656,41 @@
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"dev": true "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": { "node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -1633,6 +1768,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz", "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
} }
@@ -1640,12 +1776,14 @@
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", "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": { "node_modules/mkdirp-classic": {
"version": "0.5.3", "version": "0.5.3",
"resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "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": { "node_modules/ms": {
"version": "2.0.0", "version": "2.0.0",
@@ -1653,9 +1791,9 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
}, },
"node_modules/naive-ui": { "node_modules/naive-ui": {
"version": "2.37.3", "version": "2.38.1",
"resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.37.3.tgz", "resolved": "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.38.1.tgz",
"integrity": "sha512-aUkHFXVIluSi8Me+npbcsdv1NYhVMj5t9YaruoCESlqmfqspj+R2QHEVXkTtUI1kQwVrABMCtAGq/wountqjZA==", "integrity": "sha512-AnU1FQ7K/CbhguAX++V4kCFjk7h7RvWt4nvZPRjORMpq+fUIlzD+EcQ5Cv1VqDloNF8+eMv4Akc2Ogacc9S+5A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@css-render/plugin-bem": "^0.15.12", "@css-render/plugin-bem": "^0.15.12",
@@ -1696,7 +1834,8 @@
"node_modules/napi-build-utils": { "node_modules/napi-build-utils": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz", "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": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
@@ -1710,6 +1849,7 @@
"version": "3.54.0", "version": "3.54.0",
"resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.54.0.tgz", "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.54.0.tgz",
"integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==", "integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==",
"dev": true,
"dependencies": { "dependencies": {
"semver": "^7.3.5" "semver": "^7.3.5"
}, },
@@ -1717,10 +1857,30 @@
"node": ">=10" "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": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -1745,6 +1905,7 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": { "dependencies": {
"wrappy": "1" "wrappy": "1"
} }
@@ -1828,6 +1989,7 @@
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.1.tgz", "resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.1.tgz",
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
"dev": true,
"dependencies": { "dependencies": {
"detect-libc": "^2.0.0", "detect-libc": "^2.0.0",
"expand-template": "^2.0.3", "expand-template": "^2.0.3",
@@ -1865,6 +2027,7 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"dependencies": { "dependencies": {
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
"once": "^1.3.1" "once": "^1.3.1"
@@ -1907,6 +2070,7 @@
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz", "resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"dependencies": { "dependencies": {
"deep-extend": "^0.6.0", "deep-extend": "^0.6.0",
"ini": "~1.3.0", "ini": "~1.3.0",
@@ -1921,6 +2085,7 @@
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"dependencies": { "dependencies": {
"inherits": "^2.0.3", "inherits": "^2.0.3",
"string_decoder": "^1.1.1", "string_decoder": "^1.1.1",
@@ -2107,12 +2272,14 @@
"node_modules/simple-concat": { "node_modules/simple-concat": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz", "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": { "node_modules/simple-get": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz", "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"dev": true,
"dependencies": { "dependencies": {
"decompress-response": "^6.0.0", "decompress-response": "^6.0.0",
"once": "^1.3.1", "once": "^1.3.1",
@@ -2153,6 +2320,7 @@
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"dependencies": { "dependencies": {
"safe-buffer": "~5.2.0" "safe-buffer": "~5.2.0"
} }
@@ -2187,6 +2355,7 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -2207,6 +2376,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.1.tgz", "resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"dev": true,
"dependencies": { "dependencies": {
"chownr": "^1.1.1", "chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2", "mkdirp-classic": "^0.5.2",
@@ -2218,6 +2388,7 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true,
"dependencies": { "dependencies": {
"bl": "^4.0.3", "bl": "^4.0.3",
"end-of-stream": "^1.4.1", "end-of-stream": "^1.4.1",
@@ -2267,6 +2438,7 @@
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dev": true,
"dependencies": { "dependencies": {
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
}, },
@@ -2297,7 +2469,8 @@
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "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": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
@@ -2307,6 +2480,14 @@
"node": ">= 0.4.0" "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": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
@@ -2462,6 +2643,11 @@
"vue": "^3.0.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": { "node_modules/wrap-ansi": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -2479,7 +2665,28 @@
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", "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": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
+19 -7
View File
@@ -4,30 +4,42 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "vite": "vite",
"server": "concurrently \"node server.js\" \"node server_page.js\"", "dev": "concurrently \"node server.js\" \"vite\"",
"server": "concurrently \"node server.js\"",
"miaoShare": "concurrently \"node server.js\" \"vite\"", "miaoShare": "concurrently \"node server.js\" \"vite\"",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"aes-js": "^3.1.2", "aes-js": "^3.1.2",
"crypto-js": "^4.2.0",
"express": "^4.18.2", "express": "^4.18.2",
"file-saver": "^2.0.5", "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", "pinia": "^2.1.7",
"rollup-plugin-visualizer": "^5.12.0",
"sm-crypto": "^0.3.13", "sm-crypto": "^0.3.13",
"uuid": "^9.0.1",
"vicons": "^0.0.1", "vicons": "^0.0.1",
"vue": "^3.4.15", "vue": "^3.4.15",
"vue-clipboard3": "^2.0.0", "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": { "devDependencies": {
"better-sqlite3": "^8.7.0", "@vicons/fluent": "^0.12.0",
"cors": "^2.8.5", "@vicons/ionicons5": "^0.12.0",
"@vitejs/plugin-vue": "^5.0.3", "@vitejs/plugin-vue": "^5.0.3",
"better-sqlite3": "^8.7.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"naive-ui": "^2.37.3", "cors": "^2.8.5",
"naive-ui": "^2.38.1",
"sass": "^1.77.8",
"vfonts": "^0.0.3", "vfonts": "^0.0.3",
"vite": "^5.0.11" "vite": "^5.0.11"
} }
+2386
View File
File diff suppressed because it is too large. Load diff
BIN
View File
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

+15
View File
@@ -0,0 +1,15 @@
import sha256 from 'crypto-js/sha256.js';
import Database from "better-sqlite3";
const dbPath = "./miaoShare.db";
const db = new Database(dbPath, { verbose: null });
const stmt_ = db.prepare(`DELETE FROM USERS WHERE user_id = ?`);
let userList = [
['admin'],["guest"]
]
for(let i of userList){
stmt_.run(...i)
}
+508 -106
View File
@@ -1,131 +1,236 @@
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");
// 屏蔽console.log
console.log=()=>{}
const HOST = "0.0.0.0";
const PORT = Config.PORT;
const HOST = '0.0.0.0' // const PORT = 17057;
const PORT = '17058'
const fileDir = "./temp"; const fileDir = "./temp";
const dbPath = './miaoShare.db' const dbPath = "./miaoShare.db";
const PATH = "./dist";
const JWT_SK = fs.readFileSync("JWT_SK.key").toString();
// 上传文件最大大小:200MB // 上传文件最大大小:200MB
const uploadMaxSize=200*1024*1024 const uploadMaxSize = 200 * 1024 * 1024;
// 一天的毫秒数 // 一天的毫秒数
const millisecondsOfTheDay = parseInt(24*3600*1000) const millisecondsOfOneDay = parseInt(24 * 3600 * 1000);
// 文件3天过期 // 文件3天过期
const LifeOfFile=millisecondsOfTheDay*3 const LifeOfFile = millisecondsOfOneDay * 3;
// const LifeOfFile=0 // const LifeOfFile=0
// 数据库相关 // 数据库相关
const db = new Database(dbPath, { verbose: null }); 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, id CHAR(8) PRIMARY KEY NOT NULL UNIQUE,
fileName TEXT NOT NULL, fileName TEXT NOT NULL,
timestamp INT NOT NULL, timestamp INT NOT NULL,
freeze tinyint(1) NOT NULL, freeze tinyint(1) NOT NULL,
downloadCredit INT NOT NULL downloadCredit INT NOT NULL,
);`).run() fileSize INT NOT NULL,
const stmt_getFileName = db.prepare(`SELECT fileName FROM id2fileName WHERE id = ?`) sender_id CHAR(32) NOT NULL
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 = ?`) db.exec(`
const stmt_getFreezenIDs = db.prepare(`SELECT id FROM id2fileName WHERE freeze = 1`) CREATE TABLE IF NOT EXISTS USERS(
const stmt_insertId2FileName = db.prepare(`INSERT INTO id2fileName (id, fileName, timestamp, freeze, downloadCredit) VALUES (?, ?, ?, ?, ?)`) user_id CHAR(32) PRIMARY KEY UNIQUE,
const stmt_deleteExpireData = db.prepare(`DELETE FROM id2fileName WHERE freeze=1`) password_hash CHAR(32) NOT NULL,
const stmt_freezeExpireData = db.prepare(`UPDATE id2fileName SET freeze=1 WHERE timestamp < ? OR downloadCredit < 1`) user_group VARCHAR(32) NOT NULL,
const stmt_updateDownloadCredit = db.prepare(`UPDATE id2fileName SET downloadCredit=downloadCredit-1 WHERE id = ?`) 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)=>{ const db_insert = (id, fileName, downloadCredit, fileSize, senderId) => {
stmt_insertId2FileName.run(id,fileName,parseInt(Date.now()),0,downloadCredit) stmt_insertId2FileName.run(id, fileName, parseInt(Date.now()), 0, downloadCredit, fileSize, senderId);
} };
const db_getFileName = (id)=>{ const db_getFileName = id => {
try{ try {
if(stmt_getFreeze.get(id)['freeze'] == 1)return null if (stmt_getFreeze.get(id)["freeze"] == 1) return null;
return stmt_getFileName.get(id)['fileName'] return stmt_getFileName.get(id)["fileName"];
}catch(e){ } catch (e) {
return null 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)=>{ const db_downloadable = id => {
try{ try {
// 懒 // 懒
if(stmt_getDownloadCredit.get(id)['downloadCredit'] < 1)return false if (stmt_getDownloadCredit.get(id)["downloadCredit"] < 1) return false;
if(stmt_getFreeze.get(id)['freeze'] == 1)return false if (stmt_getFreeze.get(id)["freeze"] == 1) return false;
return true return true;
}catch(e){ } catch (e) {
return false return false;
} }
} };
const db_updateDownloadCredit = (id)=>{ const db_updateDownloadCredit = id => {
try{ try {
stmt_updateDownloadCredit.run(id) stmt_updateDownloadCredit.run(id);
return true return true;
}catch(e){ } catch (e) {
return false 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 = ()=>{ const updateDBAndFiles = () => {
try{ try {
let rows = stmt_getFreezenIDs.all() let rows = stmt_getFreezenIDs.all();
stmt_deleteExpireData.run() stmt_deleteExpireData.run();
for(let row of rows){ for (let row of rows) {
let fileName = row['id'] let fileName = row["id"];
let path = fileDir + '/' + fileName let sid = row["sender_id"];
try{ let fsize = row["fileSize"];
fs.unlink(path,(e)=>{if(e)throw e}) db_updateUserUploadFilesSizeCount_d(sid, fsize);
}catch(e){ let path = fileDir + "/" + fileName;
console.log(e) try {
continue fs.unlink(path, e => {
if (e) throw e;
});
} catch (e) {
console.log(e);
continue;
} }
} }
stmt_freezeExpireData.run(parseInt(Date.now()) - LifeOfFile) stmt_freezeExpireData.run(parseInt(Date.now()) - LifeOfFile);
}catch(e){ } catch (e) {
console.log(e) console.log(e);
return return;
} }
} };
updateFiles() updateDBAndFiles();
// 3小时更新一次 // 3小时更新一次
setInterval(updateFiles,1000*3600*3) setInterval(updateDBAndFiles, 1000 * 3600 * 3);
// // 测试环境一分钟更新一次,记得注释掉
// setInterval(updateDBAndFiles, 1000 * 60);
const app = express();
var app = express();
app.use(cors()); app.use(cors());
var tempMap = {} app.options("*", cors());
// var tempMap = {}
if (!fs.existsSync(fileDir)) { if (!fs.existsSync(fileDir)) {
fs.mkdirSync(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 = "", var returnStr = "",
range = 8, range = 8,
charStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; charStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for(var i=0; i<range; i++){ for (var i = 0; i < range; i++) {
var index = Math.round(Math.random() * 35); if (isGID === true && i == 0) {
returnStr += charStr.substring(index,index+1); var index = parseInt(Math.random() * 26);
} else {
var index = parseInt(Math.random() * 36);
}
returnStr += charStr.substring(index, index + 1);
} }
return returnStr; return returnStr;
} }
// 上传文件接口 // 上传文件接口
function uploadFile(req, res, fileName, downloadCredit){ function uploadFile(req, res, fileName, downloadCredit, fileSize) {
let id = generateId(); let id = generateId();
const fileStream = fs.createWriteStream(fileDir + '/' + id); const fileStream = fs.createWriteStream(fileDir + "/" + id);
db_insert(id,fileName,downloadCredit) db_insert(id, fileName, downloadCredit, fileSize, req.sender_id);
db_updateUserUploadFilesSizeCount(req.sender_id, fileSize);
// tempMap[id] = fileName; // tempMap[id] = fileName;
// console.log(tempMap); // console.log(tempMap);
req.pipe(fileStream); req.pipe(fileStream);
res.on('data',(c)=>{console.log(c)}) res.on("data", c => {
console.log(c);
});
req.on("end", function () { req.on("end", function () {
fileStream.end(); fileStream.end();
res.statusCode = 200; res.statusCode = 200;
@@ -138,25 +243,25 @@ function uploadFile(req, res, fileName, downloadCredit){
}); });
} }
function downloadFile(req, res, fileId){ function downloadFile(req, res, fileId) {
try { try {
if(db_downloadable(fileId) == false){ if (db_downloadable(fileId) == false) {
res.writeHead(404); res.writeHead(404);
res.end(null); res.end(null);
return return;
} }
db_updateDownloadCredit(fileId) db_updateDownloadCredit(fileId);
var fileName = db_getFileName(fileId) var fileName = db_getFileName(fileId);
if(fileName == null){ if (fileName == null) {
res.writeHead(404); res.writeHead(404);
res.end(null); res.end(null);
return return;
} }
let data = fs.readFileSync(fileDir + '/' + fileId); let data = fs.readFileSync(fileDir + "/" + fileId);
res.writeHead(200, { res.writeHead(200, {
"Content-Type": "application/octet-stream ", "Content-Type": "application/octet-stream ",
"File-Name": fileName, "File-Name": fileName,
'Content-Length':data.length, "Content-Length": data.length
}); });
res.end(data); res.end(data);
} catch (err) { } catch (err) {
@@ -166,48 +271,345 @@ function downloadFile(req, res, fileId){
} }
} }
// 设置一些通用的中间件 function getFileInfo(req, res, fileId) {
// app.use(bodyParser.json()); try {
// app.use(bodyParser.urlencoded({extended: true})); 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.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-Origin", "*");
res.header("Access-Control-Allow-Headers", "*"); res.header("Access-Control-Allow-Headers", "*");
res.header("Access-Control-Allow-Methods", "POST"); res.header("Access-Control-Allow-Methods", "POST");
res.header("Access-Control-Expose-Headers", "File-Name"); 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 fileName = req.query.fileName;
var downloadCredit = parseInt(req.query.downloadCredit); var downloadCredit = parseInt(req.query.downloadCredit);
if(downloadCredit == -1)downloadCredit = 2147483647 if (downloadCredit == -1) downloadCredit = 2147483647;
if(!fileName){ if (!fileName) {
res.statusCode = 500; res.statusCode = 500;
res.end("FileName Not Found"); 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.statusCode = 500;
res.end("Maximum size limit exceeded"); res.end("Maximum size limit exceeded");
} } else {
else{ uploadFile(req, res, fileName, downloadCredit, fileSize);
uploadFile(req, res, fileName, downloadCredit);
} }
}); });
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-Origin", "*");
res.header("Access-Control-Allow-Headers", "*"); res.header("Access-Control-Allow-Headers", "*");
res.header("Access-Control-Allow-Methods", "GET"); res.header("Access-Control-Allow-Methods", "GET");
res.header("Access-Control-Expose-Headers", "File-Name"); res.header("Access-Control-Expose-Headers", "File-Name");
var fileId = req.query.fileName; var fileId = req.query.fileName;
if(!fileId){ if (!fileId) {
res.statusCode = 500; res.statusCode = 500;
res.end("FileName Not Found"); res.end("FileName Not Found");
}else{ } else {
downloadFile(req, res, fileId); downloadFile(req, res, fileId);
} }
}); });
// 启动服务器 app.get("/file/info", verifyToken, function (req, res) {
app.listen(PORT, HOST, function(){ res.header("Access-Control-Allow-Origin", "*");
// console.log('Example app listening on port ' + PORT + '.'); 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, () => {});
+2
View File
@@ -1,3 +1,5 @@
// 已废弃
import express from 'express' import express from 'express'
const HOST = '0.0.0.0' const HOST = '0.0.0.0'
+159 -18
View File
@@ -1,29 +1,159 @@
<script setup> <script setup>
import uploadFileMiao from "./components/uploadFileMiao.vue"; import upload from "./views/uploadFileMiao.vue";
import downloadFileMiao from "./components/downloadFileMiao.vue"; import download from "./views/downloadFileMiao.vue";
import settingMiao from "./components/setting.vue"; import setting from "./views/setting.vue";
import {NNotificationProvider,NMessageProvider,NCard,NTabs,NTabPane} from 'naive-ui' 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 = settingData.baseUrl
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(() => {
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> </script>
<template> <template>
<div id="windows"> <div id="windows">
<n-notification-provider placement="bottom-right" :max="3"> <n-notification-provider placement="bottom-right" :max="3">
<n-message-provider> <n-message-provider>
<n-card content-style="padding: 0; margin:0;" id="theCard"> <n-modal-provider>
<n-tabs type="line" size="large" :tabs-padding="20" pane-style="padding: 20px;"> <n-card content-style="padding: 0; margin:0" id="theCard">
<n-tab-pane name="上传文件"> <div id="menu">
<uploadFileMiao style /> <n-menu mode="horizontal" :responsive="true" v-model:value="activePage"
</n-tab-pane> :options="menuOptions" :icon-size="26"></n-menu>
<n-tab-pane name="下载文件"> </div>
<downloadFileMiao style/>
</n-tab-pane> <div style="padding: 20px;">
<n-tab-pane name="设置"> <KeepAlive>
<settingMiao style="max-width:100%" /> <component :is="isComponent"></component>
</n-tab-pane> </KeepAlive>
</n-tabs> </div>
</n-card> </n-card>
</n-modal-provider>
</n-message-provider> </n-message-provider>
</n-notification-provider> </n-notification-provider>
</div> </div>
@@ -39,10 +169,21 @@ import {NNotificationProvider,NMessageProvider,NCard,NTabs,NTabPane} from 'naive
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
#theCard { #theCard {
width: 50%; width: 40vw;
height: 80%; 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) { @media screen and (max-width:768px) {
#theCard { #theCard {
width: 100vw; width: 100vw;
+320
View File
@@ -0,0 +1,320 @@
<template>
<!-- <div style="border: 1px solid rgb(239, 239, 245);border-radius: 5px;position: relative;"> -->
<n-scrollbar class="scrollbar">
<n-image-group>
<div v-for="msg of props.messages">
<div class="message-title">{{ `${msg.from.split('-')[0]} ${msg.timestampString}` }}</div>
<div class="message-text" v-if="msg.type1 === 'text'">
<span>{{ msg.msg }}</span>
<n-icon :component="CopyOutline" class="message-text-copy" @click='_toClipboard(msg.msg)' />
</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'" class="message-file">
<n-flex class="message-file-content" @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, CopyOutline } from '@vicons/ionicons5'
import fileSize2String from '@/func/fileSize2String'
import useClipboard from "vue-clipboard3";
import { useMessage } from "naive-ui";
const message = useMessage()
const { toClipboard } = useClipboard();
const _toClipboard = async (text) => {
await toClipboard(text)
message.info(`已复制 ${text.length < 10 ? text : text.substring(0,7) + '...'}`)
}
const setting = useSettingStore();
const serverPort = setting.serverPort;
const baseUrl = setting.baseUrl
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 scoped lang="scss">
.scrollbar {
padding: 10px;
padding-right: 15px;
border: 1px solid rgb(239, 239, 245);
border-radius: 5px;
position: relative;
.message-title {
color: slategrey;
}
.message-text {
position: relative;
background-color: rgb(239, 239, 245);
padding: 2px 7px;
// padding-right: 5px;
width: fit-content;
max-width: calc(100% - 28px);
border-radius: 5px;
white-space: pre-wrap;
.message-text-copy {
cursor: pointer;
position: absolute;
bottom: 8px;
right: -10px;
height: 7px;
width: 7px;
opacity: 0;
}
}
.message-text:hover .message-text-copy {
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
.message-text:not(:hover) .message-text-copy {
opacity: 0;
transition: opacity 0.3s ease-in-out;
transition-delay: 0.5s;
}
.message-file {
background-color: rgb(239, 239, 245);
padding: 2px 7px;
width: fit-content;
border-radius: 5px;
.message-file-content {
justify-content: center;
align-items: center;
user-select: none;
cursor: pointer;
}
}
}
</style>
+13
View File
@@ -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 })
})
}
+42
View File
@@ -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
+27
View File
@@ -0,0 +1,27 @@
import sm4_encrypt_worker from '@/worker/sm4_encrypt?worker'
import sm4_decrypt_worker from '@/worker/sm4_decrypt?worker'
const SM4 = {
encrypt: async (key, data) => {
const encryptedBytes = await new Promise((resolve) => {
const sm4Worker = new sm4_encrypt_worker()
sm4Worker.onmessage = (event) => {
resolve(event.data)
}
sm4Worker.postMessage({ data, key })
})
return encryptedBytes
},
decrypt: async (key, data) => {
const decryptedBytes = await new Promise((resolve) => {
const sm4Worker = new sm4_decrypt_worker()
sm4Worker.onmessage = (event) => {
resolve(event.data)
}
sm4Worker.postMessage({ data, key })
})
return decryptedBytes
}
}
export default SM4
+96
View File
@@ -0,0 +1,96 @@
import aesjs from "aes-js";
import { Base64 } from "js-base64";
import { saveAs } from "file-saver";
import { sm4 } from "sm-crypto";
import AES from './AES'
import SM4 from './SM4'
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(async 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" }));
let decryptedBytes = await SM4.decrypt(key, data)
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 decryptedBytes = await AES(key, 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 };
+14
View File
@@ -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();
}
+12
View File
@@ -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'
}
+8
View File
@@ -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;
}
+138
View File
@@ -0,0 +1,138 @@
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.encrypt(key, data);
if (message) message.info(`${fileName}加密完成`);
}
console.log(encrypted_data);
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
View File
@@ -3,7 +3,7 @@ import 'vfonts/Lato.css'
import { createApp } from "vue"; import { createApp } from "vue";
import App from "./App.vue"; import App from "./App.vue";
import router from "./router"; // import router from "./router";
// import naive from "naive-ui"; // import naive from "naive-ui";
import { createPinia } from "pinia"; import { createPinia } from "pinia";
@@ -14,7 +14,7 @@ import { createPinia } from "pinia";
const pinia = createPinia() const pinia = createPinia()
const app = createApp(App); const app = createApp(App);
app.use(router); // app.use(router);
app.use(pinia); app.use(pinia);
// app.use(naive); // app.use(naive);
+64 -10
View File
@@ -1,30 +1,84 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia'
import config from '../../miaoShareConfig.json'
console.log(location)
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')
var activeFuctionLocal = localStorage.getItem('activeFuction') var activeFuctionLocal = localStorage.getItem('activeFuction')
if (activeFuctionLocal == null){ if (activeFuctionLocal == null) {
activeFuctionLocal = 'AES128' activeFuctionLocal = 'AES128'
localStorage.setItem('activeFuction',activeFuctionLocal) localStorage.setItem('activeFuction', activeFuctionLocal)
} }
// localStorage.removeItem('activeKeyMap') // localStorage.removeItem('activeKeyMap')
var activeKeyMap = localStorage.getItem('activeKeyMap') var activeKeyMap = localStorage.getItem('activeKeyMap')
if (activeKeyMap == null ){ if (activeKeyMap == null) {
activeKeyMap = JSON.stringify(['A-Z','0-9']) activeKeyMap = JSON.stringify(['A-Z', '0-9'])
localStorage.setItem('activeKeyMap',activeKeyMap) localStorage.setItem('activeKeyMap', activeKeyMap)
} }
export const useSettingStore = defineStore("setting", {
// 开发环境下服务器端口使用设置的端口,否则使用与页面相同的端口
const serverPort =
process.env.NODE_ENV === 'development' ? config.PORT : location.port
const baseUrl = 'http://110.238.117.95:17057/'
// location.protocol + '//' + location.hostname + ':' + serverPort + '/'
// const wsBaseUrl = 'ws' + '://' + location.hostname + ':' + serverPort
const wsBaseUrl = 'ws://110.238.117.95:17057/'
// console.log(process.env.NODE_ENV,config.DEVPORT,location.port,serverPort)
export const useSettingStore = defineStore('setting', {
state: () => ({ state: () => ({
activeFuction: activeFuctionLocal, activeFuction: activeFuctionLocal,
activeKeyMap:JSON.parse(activeKeyMap), activeKeyMap: JSON.parse(activeKeyMap),
serverPort:"17058" enableGuest: config.enableGuest,
serverPort,
token,
userID,
userGroup,
baseUrl,
wsBaseUrl
}), }),
getters: { getters: {
// double: state => state.count * 2 // double: state => state.count * 2
}, },
actions: { 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() { // increment() {
// this.count++; // this.count++;
// } // }
} }
}); })
+42 -14
View File
@@ -1,22 +1,50 @@
// 未使用
import { createRouter, createWebHistory } from 'vue-router' 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({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [ routes: [
// { {
// path: '/', path: '/',
// name: 'home', redirect: '/upload',
// component: HomeView },
// }, {
// { path: '/upload',
// path: '/about', name: 'upload',
// name: 'about', component: uploadFileMiao,
// // route level code-splitting meta: {
// // this generates a separate chunk (About.[hash].js) for this route keepAlive: true,
// // which is lazy-loaded when the route is visited. }
// component: () => import('../views/AboutView.vue') },
// } {
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,
}
},
] ]
}) })
+18
View File
@@ -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>
+385
View File
@@ -0,0 +1,385 @@
<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 _toClipboard = async (text) => {
await toClipboard(text)
message.info(`已复制 ${text.length < 10 ? text : text.substring(0,7) + '...'}`)
}
const setting = useSettingStore();
const serverPort = setting.serverPort;
// const wsBaseUrl = 'ws' + "://" + location.hostname + ":" + serverPort;
const wsBaseUrl = setting.wsBaseUrl
// const baseUrl = location.protocol + "//" + location.hostname + ":" + serverPort + "/";
const baseUrl = setting.baseUrl
const QRIconUrl = ref(baseUrl + "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" :max-height="250"
:bordered="false" :bordered="false"
:single-line="false" :single-line="false"
style="user-select: none;"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted,computed } from "vue";
import aesjs from "aes-js";
import { Base64 } from "js-base64";
import { saveAs } from "file-saver";
import { useSettingStore } from "@/pinia/store"; 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' import {NInput,NFlex,NDataTable,NButton} from 'naive-ui'
var downloadFileID = ref(); var downloadFileID = ref();
@@ -42,87 +40,89 @@ const setting = useSettingStore();
// 服务端端口 // 服务端端口
const serverPort = setting.serverPort; const serverPort = setting.serverPort;
var baseUrl = // const baseUrl = location.protocol + "//" + location.hostname + ":" + serverPort + "/";
location.protocol + "//" + location.hostname + ":" + serverPort + "/"; const baseUrl = setting.baseUrl
// console.log(baseUrl); // console.log(baseUrl);
const blob2Uint8Array = function(blob) { // const blob2Uint8Array = function(blob) {
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
let reader = new FileReader(); // let reader = new FileReader();
reader.readAsArrayBuffer(blob); // reader.readAsArrayBuffer(blob);
reader.onload = function() { // reader.onload = function() {
let buffer = reader.result; // let buffer = reader.result;
let uint8array = new Uint8Array(buffer); // let uint8array = new Uint8Array(buffer);
resolve(uint8array); // resolve(uint8array);
}; // };
reader.onerror = function() { // reader.onerror = function() {
reject(reader.error); // reject(reader.error);
}; // };
}); // });
}; // };
const decrypt = (key, data) => { // const decrypt = (key, data) => {
return blob2Uint8Array(data).then(data => { // return blob2Uint8Array(data).then(data => {
// 无加密 // // 无加密
if(key.length === 0){ // if(key.length === 0){
return data // return data
} // }
// SM4 // // SM4
if (key.length >= 34) { // if (key.length >= 34) {
key = key.substring(3); // key = key.substring(3);
// console.log([...data]) // // console.log([...data])
var decryptedBytes = new Uint8Array( // var decryptedBytes = new Uint8Array(
sm4.decrypt([...data], key, { output: "array" }) // sm4.decrypt([...data], key, { output: "array" })
); // );
return decryptedBytes; // return decryptedBytes;
} else { // } else {
// AES // // AES
key = aesjs.utils.utf8.toBytes(key); // key = aesjs.utils.utf8.toBytes(key);
var aesCtr = new aesjs.ModeOfOperation.ctr( // var aesCtr = new aesjs.ModeOfOperation.ctr(
key, // key,
new aesjs.Counter(5) // new aesjs.Counter(5)
); // );
var decryptedBytes = aesCtr.decrypt(data); // var decryptedBytes = aesCtr.decrypt(data);
return decryptedBytes; // return decryptedBytes;
} // }
}); // });
}; // };
const decryptString = (key, str) => { // const decryptString = (key, str) => {
// 无加密 // // 无加密
if(key.length === 0){ // if(key.length === 0){
return sm4.decrypt(str, 'miaospringmiaospringmiaospringao'); // return sm4.decrypt(str, 'miaospringmiaospringmiaospringao');
} // }
// SM4 // // SM4
// console.log(key,key.length) // // console.log(key,key.length)
if (key.length >= 34) { // if (key.length >= 34) {
key = key.substring(3); // key = key.substring(3);
return sm4.decrypt(str, key); // return sm4.decrypt(str, key);
} // }
// AES // // AES
var data = new Uint8Array( // var data = new Uint8Array(
Base64.decode(str) // Base64.decode(str)
.split(",") // .split(",")
.map(n => parseInt(n)) // .map(n => parseInt(n))
); // );
key = aesjs.utils.utf8.toBytes(key); // key = aesjs.utils.utf8.toBytes(key);
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); // var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
var decryptedBytes = aesCtr.decrypt(data); // var decryptedBytes = aesCtr.decrypt(data);
var decryptedStr = aesjs.utils.utf8.fromBytes(decryptedBytes); // var decryptedStr = aesjs.utils.utf8.fromBytes(decryptedBytes);
return decryptedStr; // 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) => { const updateDownloadQuene = (code, key, value) => {
for (let index in downloadQuene.value) { for (let index in downloadQuene.value) {
@@ -158,7 +158,11 @@ const downloadFile = code => {
fileName: id fileName: id
}; };
const options = { const options = {
method: "GET" method: "GET",
headers: {
'Authorization': `Bearer ${setting.token}`
// 'Content-Type':'application/json'
},
}; };
fetch(baseUrl + "file/download?" + new URLSearchParams(params), options) fetch(baseUrl + "file/download?" + new URLSearchParams(params), options)
.then(res => { .then(res => {
+90
View File
@@ -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 baseUrl = settingData.baseUrl
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> <div>
<n-list hoverable> <n-list hoverable>
<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 /> <br />
<!-- <br /> --> <!-- <br /> -->
<!-- <br /> --> <!-- <br /> -->
@@ -16,7 +16,7 @@
</n-flex> </n-flex>
</n-list-item> </n-list-item>
<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 /> <br />
<n-flex justify="end"> <n-flex justify="end">
<span v-for="k in keyButtons" style="margin:5px"> <span v-for="k in keyButtons" style="margin:5px">
@@ -11,10 +11,10 @@
<!-- <div style="margin-bottom: 12px"> <!-- <div style="margin-bottom: 12px">
<n-icon size="48" :depth="3"></n-icon> <n-icon size="48" :depth="3"></n-icon>
</div>--> </div>-->
<n-text style="font-size: 16px">点击此处或者拖动文件到该区域来上传</n-text> <n-text style="font-size: 16px;user-select: none;">点击此处或者拖动文件到该区域来上传</n-text>
<n-p depth="3" style="margin: 8px 0 0 0"> <n-p depth="3" style="margin: 8px 0 0 0;user-select: none;">
最大支持200MB大小文件,其实只是懒得做分片上传而已 最大支持200MB大小文件,其实只是懒得做分片上传而已
<br />虽然理论上没有人有能力审查你上传的东西 <!-- <br />虽然理论上没有人有能力审查你上传的东西 -->
<br />反正没人用写这么多干什么。。。 <br />反正没人用写这么多干什么。。。
</n-p> </n-p>
</n-upload-dragger> </n-upload-dragger>
@@ -31,58 +31,57 @@
:max-height="250" :max-height="250"
:bordered="false" :bordered="false"
:single-line="false" :single-line="false"
style="user-select: none;"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted,computed } from "vue";
import aesjs from "aes-js"; // import aesjs from "aes-js";
import { Base64 } from "js-base64"; // import { Base64 } from "js-base64";
import { useMessage, useNotification } from "naive-ui"; import { useMessage, useNotification } from "naive-ui";
import useClipboard from "vue-clipboard3"; import useClipboard from "vue-clipboard3";
import { useSettingStore } from "@/pinia/store"; 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 {NUpload,NUploadDragger,NText,NP,NFlex,NDataTable,NButton,NInputNumber} from 'naive-ui'
import {encrypt,encryptString,generateKey} from '../func/upload'
const setting = useSettingStore(); const setting = useSettingStore();
const { toClipboard } = useClipboard(); const { toClipboard } = useClipboard();
const message = useMessage(); const message = useMessage();
const notification = useNotification(); const notification = useNotification();
// 目前使用加密
const activeFuction = setting.activeFuction;
// 启用的密钥组成
const activeKeyButtons = setting.activeKeyMap;
// 服务端端口 // 服务端端口
const serverPort = setting.serverPort; const serverPort = setting.serverPort;
// console.log(activeFuction,activeKeyButtons) // console.log(activeFuction,activeKeyButtons)
var charMap = "" // var charMap = ""
for(let i of setting.activeKeyMap){ // for(let i of setting.activeKeyMap){
if(i === 'A-Z'){ // if(i === 'A-Z'){
charMap = charMap + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' // charMap = charMap + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
}else if(i === "a-z"){ // }else if(i === "a-z"){
charMap = charMap + 'abcdefghijklmnopqrstuvwxyz' // charMap = charMap + 'abcdefghijklmnopqrstuvwxyz'
}else if(i === '0-9'){ // }else if(i === '0-9'){
charMap = charMap + '0123456789' // charMap = charMap + '0123456789'
} // }
} // }
// 生成密钥 // // 生成密钥
const generateKey = function() { // const generateKey = function() {
if(setting.activeFuction === "不加密"){ // if(setting.activeFuction === "不加密"){
return '' // return ''
} // }
var returnStr = "", // var returnStr = "",
length = setting.activeFuction === "AES128" ? 16 : 32 // length = setting.activeFuction === "AES128" ? 16 : 32
// charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // // charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (var i = 0; i < length; i++) { // for (var i = 0; i < length; i++) {
var index = Math.round(Math.random() * charMap.length); // var index = Math.round(Math.random() * charMap.length);
returnStr += charMap.substring(index, index + 1); // returnStr += charMap.substring(index, index + 1);
} // }
return returnStr; // return returnStr;
}; // };
// 上传文件用 // 上传文件用
const uploadRef = ref(null); const uploadRef = ref(null);
@@ -95,73 +94,73 @@ var file_Code_Map = ref([]);
var uploadProcess = ref(0); var uploadProcess = ref(0);
const baseUrl = // const baseUrl = location.protocol + "//" + location.hostname + ":" + serverPort + "/";
location.protocol + "//" + location.hostname + ":" + serverPort + "/"; const baseUrl = setting.baseUrl
console.log(baseUrl)
const blob2Uint8Array = function(blob) { // const blob2Uint8Array = function(blob) {
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
let reader = new FileReader(); // let reader = new FileReader();
reader.readAsArrayBuffer(blob); // reader.readAsArrayBuffer(blob);
reader.onload = function() { // reader.onload = function() {
let buffer = reader.result; // let buffer = reader.result;
let uint8array = new Uint8Array(buffer); // let uint8array = new Uint8Array(buffer);
resolve(uint8array); // resolve(uint8array);
}; // };
reader.onerror = function() { // reader.onerror = function() {
reject(reader.error); // reject(reader.error);
}; // };
}); // });
}; // };
// 包含AES128和AES256 // // 包含AES128和AES256
const AES = (key,data)=>{ // const AES = (key,data)=>{
key = aesjs.utils.utf8.toBytes(key); // key = aesjs.utils.utf8.toBytes(key);
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); // var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
var encryptedBytes = aesCtr.encrypt(data); // var encryptedBytes = aesCtr.encrypt(data);
return encryptedBytes; // return encryptedBytes;
} // }
const SM4 = (key,data)=>{ // const SM4 = (key,data)=>{
// console.log(key) // // console.log(key)
var encryptedBytes = sm4.encrypt(data,key,{padding: 'pkcs#7',output:'array'}); // var encryptedBytes = sm4.encrypt(data,key,{padding: 'pkcs#7',output:'array'});
// console.log(encryptedBytes, sm4.decrypt(encryptedBytes, key,{output:'array'})) // // console.log(encryptedBytes, sm4.decrypt(encryptedBytes, key,{output:'array'}))
return new Uint8Array(encryptedBytes); // return new Uint8Array(encryptedBytes);
} // }
const encrypt = (key, data, fileName) => { // const encrypt = (key, data, fileName) => {
return blob2Uint8Array(data).then(data => { // return blob2Uint8Array(data).then(data => {
let encrypted_data = null // let encrypted_data = null
if(setting.activeFuction === "不加密"){ // if(setting.activeFuction === "不加密"){
encrypted_data = data // encrypted_data = data
}else if(setting.activeFuction != "SM4"){ // }else if(setting.activeFuction != "SM4"){
message.info(`开始加密${fileName}`); // message.info(`开始加密${fileName}`);
encrypted_data = AES(key,data) // encrypted_data = AES(key,data)
message.info(`${fileName}加密完成`); // message.info(`${fileName}加密完成`);
}else{ // }else{
message.info(`开始加密${fileName}`); // message.info(`开始加密${fileName}`);
encrypted_data = SM4(key,data) // encrypted_data = SM4(key,data)
message.info(`${fileName}加密完成`); // message.info(`${fileName}加密完成`);
} // }
return encrypted_data // return encrypted_data
}); // });
}; // };
const encryptString = (key, str) => { // const encryptString = (key, str) => {
// console.log(key) // // console.log(key)
if(setting.activeFuction === "不加密"){ // if(setting.activeFuction === "不加密"){
return sm4.encrypt(str, 'miaospringmiaospringmiaospringao') // return sm4.encrypt(str, 'miaospringmiaospringmiaospringao')
}else if(setting.activeFuction !== "SM4"){ // }else if(setting.activeFuction !== "SM4"){
key = aesjs.utils.utf8.toBytes(key); // key = aesjs.utils.utf8.toBytes(key);
var data = aesjs.utils.utf8.toBytes(str); // var data = aesjs.utils.utf8.toBytes(str);
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); // var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
var encryptedBytes = aesCtr.encrypt(data); // var encryptedBytes = aesCtr.encrypt(data);
return Base64.encode(encryptedBytes); // return Base64.encode(encryptedBytes);
}else{ // }else{
return sm4.encrypt(str, key) // return sm4.encrypt(str, key)
} // }
}; // };
const clickUpload = function(){ const clickUpload = function(){
// for(let i of uploadRef.value)console.log(i) // for(let i of uploadRef.value)console.log(i)
// console.log(uploadRef.value) // console.log(uploadRef.value)
message.info(`开始上传`);
uploadRef.value.submit(); uploadRef.value.submit();
} }
@@ -184,13 +183,13 @@ const customRequest = ({
onError, onError,
onProgress onProgress
}) => { }) => {
// console.log(file);
file = file.file; file = file.file;
var fileName = file.name; var fileName = file.name;
const key = generateKey(); const key = generateKey(setting);
const encryptedFileName = encryptString(key, fileName); const encryptedFileName = encryptString(key, fileName, setting);
var f = new Blob([file]); var f = new Blob([file]);
encrypt(key, f, fileName).then(data => { encrypt(key, f, fileName, setting, message).then(data => {
message.info(`开始上传${fileName}`);
const params = { const params = {
fileName: encryptedFileName, fileName: encryptedFileName,
downloadCredit:downloadCredit.value == null ? -1 : downloadCredit.value downloadCredit:downloadCredit.value == null ? -1 : downloadCredit.value
@@ -206,14 +205,16 @@ const customRequest = ({
"POST", "POST",
baseUrl + "file/upload?" + new URLSearchParams(params) baseUrl + "file/upload?" + new URLSearchParams(params)
); );
console.log(setting.token)
xhr.setRequestHeader('Authorization', `Bearer ${setting.token}`);
xhr.send(data); xhr.send(data);
xhr.onreadystatechange = () => { xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) { if (xhr.readyState === 4 && xhr.status === 200) {
let id = xhr.responseText; let id = xhr.responseText;
let code = id + key let code = id + key
if(setting.activeFuction === "SM4"){ // if(setting.activeFuction === "SM4"){
code = id + 'SM4' + key // code = id + 'SM4' + key
} // }
file_Code_Map.value.push({ file_Code_Map.value.push({
code: code, code: code,
fileName: fileName fileName: fileName
+9
View File
@@ -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(7058))
const encryptData = aesCtr.encrypt(data)
self.postMessage(encryptData)
}
+9
View File
@@ -0,0 +1,9 @@
import { sm4 } from 'sm-crypto'
self.onmessage = async function (event) {
const data = event.data.data
const key = event.data.key
self.postMessage(
new Uint8Array(sm4.decrypt([...data], key, { output: 'array' }))
)
}
+11
View File
@@ -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(new Uint8Array(encryptData))
}
+17
View File
@@ -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))
+34 -7
View File
@@ -1,21 +1,48 @@
import { fileURLToPath, URL } from 'node:url' import { fileURLToPath, URL } from "node:url";
import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from 'vite' import { defineConfig } from "vite";
import vue from '@vitejs/plugin-vue' import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
vue(), vue(),
visualizer({
open: true,
filename: 'visualizer.html' //分析图生成的文件名
})
], ],
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) '@': fileURLToPath(new URL('./src', import.meta.url))
} }
}, },
server:{ server: {
host:'0.0.0.0', // host:'0.0.0.0',
port:'17057', port: '17058',
open:true, 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'
}
}
}
}
},
base: './'
}) })