添加登录功能

This commit is contained in:
539943419 committed 2024-03-27 22:05:00 +08:00
1 parent 20e9f4940e
commit 530e54a596
17 files changed
+576 -104

No files matched your search

+4
View File
@@ -32,3 +32,7 @@ coverage
# files created with miaoShare service # files created with miaoShare service
temp temp
miaoShare.db miaoShare.db
addUser.js
# JWT SK
*.key
+9 -1
View File
@@ -1,3 +1,11 @@
{ {
"PORT":17057 "PORT":17057,
"userGroupSetting":{
"admin":{
"cloudCapacity":2147483648
},
"guest":{
"cloudCapacity":2147483648
}
}
} }
+126 -8
View File
@@ -13,6 +13,8 @@
"express": "^4.18.2", "express": "^4.18.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"js-base64": "^3.7.7", "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", "uuid": "^9.0.1",
@@ -24,12 +26,13 @@
"ws": "^8.16.0" "ws": "^8.16.0"
}, },
"devDependencies": { "devDependencies": {
"@vicons/fluent": "^0.12.0",
"@vicons/ionicons5": "^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", "better-sqlite3": "^8.7.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"cors": "^2.8.5", "cors": "^2.8.5",
"naive-ui": "^2.37.3", "naive-ui": "^2.38.1",
"vfonts": "^0.0.3", "vfonts": "^0.0.3",
"vite": "^5.0.11" "vite": "^5.0.11"
} }
@@ -656,6 +659,12 @@
"@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": { "node_modules/@vicons/ionicons5": {
"version": "0.12.0", "version": "0.12.0",
"resolved": "https://registry.npmmirror.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz", "resolved": "https://registry.npmmirror.com/@vicons/ionicons5/-/ionicons5-0.12.0.tgz",
@@ -889,6 +898,11 @@
"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",
@@ -1174,6 +1188,14 @@
"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",
@@ -1577,6 +1599,51 @@
"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",
@@ -1589,11 +1656,45 @@
"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",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": { "dependencies": {
"yallist": "^4.0.0" "yallist": "^4.0.0"
}, },
@@ -1690,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",
@@ -1756,6 +1857,25 @@
"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",
@@ -2056,7 +2176,6 @@
"version": "7.6.0", "version": "7.6.0",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz", "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dev": true,
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
}, },
@@ -2581,8 +2700,7 @@
"node_modules/yallist": { "node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
"dev": true
}, },
"node_modules/yargs": { "node_modules/yargs": {
"version": "17.7.2", "version": "17.7.2",
+4 -1
View File
@@ -17,6 +17,8 @@
"express": "^4.18.2", "express": "^4.18.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"js-base64": "^3.7.7", "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", "uuid": "^9.0.1",
@@ -28,12 +30,13 @@
"ws": "^8.16.0" "ws": "^8.16.0"
}, },
"devDependencies": { "devDependencies": {
"@vicons/fluent": "^0.12.0",
"@vicons/ionicons5": "^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", "better-sqlite3": "^8.7.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"cors": "^2.8.5", "cors": "^2.8.5",
"naive-ui": "^2.37.3", "naive-ui": "^2.38.1",
"vfonts": "^0.0.3", "vfonts": "^0.0.3",
"vite": "^5.0.11" "vite": "^5.0.11"
} }
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

+133 -35
View File
@@ -5,15 +5,23 @@ import fs from "fs";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
import http from "http"; import http from "http";
import { v4 as uuid4 } from "uuid"; import { v4 as uuid4 } from "uuid";
// import Config from "./miaoShareConfig.json" import jwt from "jsonwebtoken";
import sha256 from "crypto-js/sha256.js";
import Config from './miaoShareConfig.json' assert { type: 'json' };
// function loadJSON(path){
// return JSON.parse(fs.readFileSync(path).toString());
// }
// const Config = loadJSON("./miaoShareConfig.json");
const HOST = "0.0.0.0"; const HOST = "0.0.0.0";
// const PORT = Config.PORT const PORT = Config.PORT
const PORT = 17057; // const PORT = 17057;
const fileDir = "./temp"; const fileDir = "./temp";
const dbPath = "./miaoShare.db"; const dbPath = "./miaoShare.db";
const PATH = "./dist"; const PATH = "./dist";
const JWT_SK = fs.readFileSync("JWT_SK.key").toString();
// 上传文件最大大小:200MB // 上传文件最大大小:200MB
const uploadMaxSize = 200 * 1024 * 1024; const uploadMaxSize = 200 * 1024 * 1024;
// 一天的毫秒数 // 一天的毫秒数
@@ -24,27 +32,43 @@ const LifeOfFile = millisecondsOfOneDay * 3;
// 数据库相关 // 数据库相关
const db = new Database(dbPath, { verbose: null }); const db = new Database(dbPath, { verbose: null });
db.prepare( db.exec(
`CREATE TABLE IF NOT EXISTS id2fileName ( `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,
fileSize INT NOT NULL,
sender_id CHAR(32) NOT NULL
);` );`
).run(); );
db.exec(`
CREATE TABLE IF NOT EXISTS USERS(
user_id CHAR(32) PRIMARY KEY UNIQUE,
password_hash CHAR(32) NOT NULL,
user_group VARCHAR(32) NOT NULL,
uploadFilesSizeCount BIGINT NOT NULL,
email CHAR(64)
);`);
const stmt_getPermissionGroup = db.prepare("SELECT user_group FROM USERS WHERE user_id = ? AND password_hash = ?");
const stmt_getFileName = db.prepare(`SELECT fileName FROM id2fileName WHERE id = ?`); const stmt_getFileName = db.prepare(`SELECT fileName 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_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_getFreeze = db.prepare(`SELECT freeze FROM id2fileName WHERE id = ?`);
const stmt_getDownloadCredit = db.prepare(`SELECT downloadCredit FROM id2fileName WHERE id = ?`); const stmt_getDownloadCredit = db.prepare(`SELECT downloadCredit FROM id2fileName WHERE id = ?`);
const stmt_getFreezenIDs = db.prepare(`SELECT id FROM id2fileName WHERE freeze = 1`); const stmt_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) VALUES (?, ?, ?, ?, ?)`); 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_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_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_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 {
@@ -78,6 +102,22 @@ const db_getTimestamp = id => {
return stmt_getTimestamp.get(id)["timestamp"]; 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)
}
// 减
function db_updateUserUploadFilesSizeCount_d(id,size){
return stmt_updateUserUploadFilesSizeCount_d.run(size,id)
}
// 更新存储的文件,删除已经被冻结的文件以及相关记录,然后冻结过期文件 // 更新存储的文件,删除已经被冻结的文件以及相关记录,然后冻结过期文件
const updateDBAndFiles = () => { const updateDBAndFiles = () => {
try { try {
@@ -85,6 +125,9 @@ const updateDBAndFiles = () => {
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 sid = row['sender_id']
let fsize = row['fileSize']
db_updateUserUploadFilesSizeCount_d(sid,fsize)
let path = fileDir + "/" + fileName; let path = fileDir + "/" + fileName;
try { try {
fs.unlink(path, e => { fs.unlink(path, e => {
@@ -117,6 +160,15 @@ if (!fs.existsSync(fileDir)) {
fs.mkdirSync(fileDir); fs.mkdirSync(fileDir);
} }
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) { function generateId(isGID) {
var returnStr = "", var returnStr = "",
range = 8, range = 8,
@@ -134,10 +186,11 @@ function generateId(isGID) {
} }
// 上传文件接口 // 上传文件接口
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);
@@ -184,9 +237,54 @@ function downloadFile(req, res, fileId) {
} }
} }
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.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: "168h" });
res.end(JSON.stringify({ status: "Success", userGroup, token }));
} else {
res.end(JSON.stringify({ status: "Error" }));
}
// console.log(req)
});
// 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");
@@ -198,15 +296,15 @@ app.post("/file/upload", function (req, res) {
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); uploadFile(req, res, fileName, downloadCredit, fileSize);
} }
}); });
app.get("/file/download", function (req, res) { app.get("/file/download", verifyToken, function (req, res) {
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-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");
@@ -234,29 +332,29 @@ const groupsMap = {};
const LifeOfGroup = millisecondsOfOneDay * 1; const LifeOfGroup = millisecondsOfOneDay * 1;
// const LifeOfGroup = 0; // const LifeOfGroup = 0;
// 根据uuid查询加入的群组 // 根据uuid查询加入的群组
const uuid2Groups={} const uuid2Groups = {};
db.exec( db.exec(
`CREATE TABLE IF NOT EXISTS GROUPS ( `CREATE TABLE IF NOT EXISTS CHATGROUPS (
gID CHAR(8) PRIMARY KEY UNIQUE NOT NULL, gID CHAR(8) PRIMARY KEY UNIQUE NOT NULL,
verify TEXT NOT NULL, verify TEXT NOT NULL,
activeTimestamp INT NOT NULL activeTimestamp INT NOT NULL
);` );`
); );
const stmt_getExpiredGroups = db.prepare(`SELECT gID FROM GROUPS WHERE activeTimestamp < ? `); const stmt_getExpiredGroups = db.prepare(`SELECT gID FROM CHATGROUPS WHERE activeTimestamp < ? `);
const stmt_getGroupVerify = db.prepare(`SELECT verify FROM GROUPS WHERE gID = ? `); const stmt_getGroupVerify = db.prepare(`SELECT verify FROM CHATGROUPS WHERE gID = ? `);
const stmt_insertGROUPS = db.prepare(`INSERT INTO GROUPS (gID,verify, activeTimestamp) VALUES (?, ?, ?)`); const stmt_insertCHATGROUPS = db.prepare(`INSERT INTO CHATGROUPS (gID,verify, activeTimestamp) VALUES (?, ?, ?)`);
const stmt_updateGROUPS_ActiveTimestamp = db.prepare(`UPDATE GROUPS SET activeTimestamp=? WHERE gID=?`); const stmt_updateCHATGROUPS_ActiveTimestamp = db.prepare(`UPDATE CHATGROUPS SET activeTimestamp=? WHERE gID=?`);
const stmt_deleteGroup = db.prepare(`DELETE FROM GROUPS WHERE gID=?`); const stmt_deleteGroup = db.prepare(`DELETE FROM CHATGROUPS WHERE gID=?`);
const generateStmt_insertMessage_map = {}; const generateStmt_insertMessage_map = {};
const updateGROUPS = () => { const updateCHATGROUPS = () => {
// console.log(1) // console.log(1)
try { try {
let gIDs = stmt_getExpiredGroups.all(parseInt(Date.now()) - LifeOfGroup); let gIDs = stmt_getExpiredGroups.all(parseInt(Date.now()) - LifeOfGroup);
@@ -270,8 +368,8 @@ const updateGROUPS = () => {
return; return;
} }
}; };
updateGROUPS(); updateCHATGROUPS();
setInterval(updateGROUPS, 1000 * 3600 * 3); setInterval(updateCHATGROUPS, 1000 * 3600 * 3);
// 创建群组的数据库 // 创建群组的数据库
function createGroupTableDB(gID, verify) { function createGroupTableDB(gID, verify) {
@@ -283,7 +381,7 @@ function createGroupTableDB(gID, verify) {
timestamp INT NOT NULL timestamp INT NOT NULL
);` );`
); );
stmt_insertGROUPS.run(gID, verify, parseInt(Date.now())); stmt_insertCHATGROUPS.run(gID, verify, parseInt(Date.now()));
} }
// 插入信息 // 插入信息
@@ -295,7 +393,7 @@ function insertMessageByGID(gID, msg, from, timestamp) {
} }
function updateActiveTimestamp(gID) { function updateActiveTimestamp(gID) {
stmt_updateGROUPS_ActiveTimestamp.run(parseInt(Date.now()), gID); stmt_updateCHATGROUPS_ActiveTimestamp.run(parseInt(Date.now()), gID);
} }
function createGroup(verify) { function createGroup(verify) {
@@ -307,13 +405,13 @@ function createGroup(verify) {
} }
function getGroupVerify(gID) { function getGroupVerify(gID) {
initMsgHistory(gID) initMsgHistory(gID);
return groupsMap[gID].verify; return groupsMap[gID].verify;
} }
// 正式加入群组 // 正式加入群组
function joinGroup(socket, gID, callback) { function joinGroup(socket, gID, callback) {
initMsgHistory(gID) initMsgHistory(gID);
groupsMap[gID].member.push(socket); groupsMap[gID].member.push(socket);
updateActiveTimestamp(gID); updateActiveTimestamp(gID);
callback(); callback();
@@ -321,13 +419,13 @@ function joinGroup(socket, gID, callback) {
// 暂时这么写 // 暂时这么写
function getHistory(gID) { function getHistory(gID) {
initMsgHistory(gID) initMsgHistory(gID);
return groupsMap[gID].msgHistory; return groupsMap[gID].msgHistory;
} }
function broadcastMsg(gID, msg, from) { function broadcastMsg(gID, msg, from) {
const timestamp = parseInt(Date.now()) const timestamp = parseInt(Date.now());
initMsgHistory(gID) initMsgHistory(gID);
updateActiveTimestamp(gID); updateActiveTimestamp(gID);
insertMessageByGID(gID, msg, from, timestamp); insertMessageByGID(gID, msg, from, timestamp);
@@ -360,7 +458,7 @@ wss.on("connection", socket => {
const joinedGroup = []; const joinedGroup = [];
var uuid = uuid4(); var uuid = uuid4();
function setUuid(new_uuid) { function setUuid(new_uuid) {
uuid = new_uuid uuid = new_uuid;
} }
socket.on("message", message => { socket.on("message", message => {
try { try {
@@ -399,7 +497,7 @@ wss.on("connection", socket => {
if (msg.type1 === "get") { if (msg.type1 === "get") {
socket.send(JSON.stringify({ type: "sMsg", type1: "uuid", uuid })); socket.send(JSON.stringify({ type: "sMsg", type1: "uuid", uuid }));
} else if (msg.type1 === "set") { } else if (msg.type1 === "set") {
uuid = msg.uuid uuid = msg.uuid;
} }
} }
} }
+61 -25
View File
@@ -3,58 +3,110 @@ import upload from "./views/uploadFileMiao.vue";
import download from "./views/downloadFileMiao.vue"; import download from "./views/downloadFileMiao.vue";
import setting from "./views/setting.vue"; import setting from "./views/setting.vue";
import chat from "./views/chat.vue"; import chat from "./views/chat.vue";
import { NNotificationProvider, NMessageProvider, NCard, NMenu, NIcon } from 'naive-ui' import login from "./views/login.vue";
import { ref, watch, h ,shallowRef} from "vue"; import account from "./views/accountInfo.vue";
// import { RouterView, useRouter } from "vue-router"; 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 { import {
ArrowUpCircleOutline as uploadIcon, ArrowUpCircleOutline as uploadIcon,
ArrowDownCircleOutline as downloadIcon, ArrowDownCircleOutline as downloadIcon,
ChatboxEllipsesOutline as chatIcon, ChatboxEllipsesOutline as chatIcon,
SettingsOutline as settingIcon SettingsOutline as settingIcon,
PersonOutline as AccountIcon
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import { useSettingStore } from "@/pinia/store";
const settingData = useSettingStore();
const activePage = ref('upload') const activePage = ref('upload')
const isComponent = shallowRef(upload) const isComponent = shallowRef(upload)
watch(activePage, () => { watch(activePage, () => {
isComponent.value={upload,download,setting,chat}[activePage.value] isComponent.value={upload,download,setting,chat,login,account}[activePage.value]
}) })
function renderIcon(icon) { function renderIcon(icon) {
return () => h(NIcon, null, { default: () => h(icon) }) return () => h(NIcon, null, { default: () => h(icon) })
} }
const menuOptions = [ const menuOptions = ref([
{ {
label: '上传', label: '上传',
key: 'upload', key: 'upload',
icon: renderIcon(uploadIcon), icon: renderIcon(uploadIcon),
disabled:computed(()=>{
// console.log(settingData)
return settingData.token === null
})
}, },
{ {
label: '下载', label: '下载',
key: 'download', key: 'download',
icon: renderIcon(downloadIcon), icon: renderIcon(downloadIcon),
disabled:computed(()=>{
return settingData.token === null
})
}, },
{ {
label: 'Chat', label: 'Chat',
key: 'chat', key: 'chat',
icon: renderIcon(chatIcon), icon: renderIcon(chatIcon),
disabled:computed(()=>{
return settingData.token === null
})
}, },
{ {
label: '设置', label: '设置',
key: 'setting', key: 'setting',
icon: renderIcon(settingIcon), 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(()=>{
let aP = getQueryString('aP')
// console.log(aP)
if(aP){
activePage.value = aP
}
if(settingData.token === null){
aP = activePage.value
activePage.value = 'login'
// 登录后跳原来的activePage
const w = watch(()=>settingData.token,()=>{
activePage.value = aP
w()
})
}
})
</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-modal-provider>
<n-card content-style="padding: 0; margin:0" id="theCard"> <n-card content-style="padding: 0; margin:0" id="theCard">
<!-- <n-scrollbar style="max-height: 100%"> -->
<div id="menu"> <div id="menu">
<n-menu mode="horizontal" :responsive="true" v-model:value="activePage" :options="menuOptions" :icon-size="26"></n-menu> <n-menu mode="horizontal" :responsive="true" v-model:value="activePage" :options="menuOptions" :icon-size="26"></n-menu>
</div> </div>
@@ -64,24 +116,8 @@ const menuOptions = [
<component :is="isComponent"></component> <component :is="isComponent"></component>
</KeepAlive> </KeepAlive>
</div> </div>
<!-- </n-scrollbar> -->
<!-- <n-tabs type="line" size="large" :tabs-padding="20" pane-style="padding: 20px;"
display-directive="show">
<n-tab-pane name="上传文件">
<uploadFileMiao style />
</n-tab-pane>
<n-tab-pane name="下载文件">
<downloadFileMiao style />
</n-tab-pane>
<n-tab-pane name="miaoChat">
<chat style />
</n-tab-pane>
<n-tab-pane name="设置">
<settingMiao style="max-width:100%" />
</n-tab-pane>
</n-tabs> -->
</n-card> </n-card>
</n-modal-provider>
</n-message-provider> </n-message-provider>
</n-notification-provider> </n-notification-provider>
</div> </div>
+1 -1
View File
@@ -72,7 +72,7 @@ const decryptString = (key, str) => {
const downloadFromUint8Array = (data, fileName) => { const downloadFromUint8Array = (data, fileName) => {
let uint8Array = data; let uint8Array = data;
let blob = new Blob([uint8Array], { type: "text/plain" }); let blob = new Blob([uint8Array], { type: "application/octet-stream" });
// 使用file-saver.js // 使用file-saver.js
saveAs(blob, fileName); saveAs(blob, fileName);
}; };
+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;
}
+22 -1
View File
@@ -1,6 +1,11 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import config from "../../miaoShareConfig.json" import config from "../../miaoShareConfig.json"
const userID = localStorage.getItem('userID');
const userGroup = localStorage.getItem('userGroup');
const token = localStorage.getItem('token');
// console.log(token)
var activeFuctionLocal = localStorage.getItem('activeFuction') var activeFuctionLocal = localStorage.getItem('activeFuction')
if (activeFuctionLocal == null){ if (activeFuctionLocal == null){
@@ -18,12 +23,28 @@ export const useSettingStore = defineStore("setting", {
state: () => ({ state: () => ({
activeFuction: activeFuctionLocal, activeFuction: activeFuctionLocal,
activeKeyMap:JSON.parse(activeKeyMap), activeKeyMap:JSON.parse(activeKeyMap),
serverPort:config.PORT serverPort:config.PORT,
token,userID,userGroup
}), }),
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)
},
setUserID( id,keepTokenLocal ){
this.userID = id
if(keepTokenLocal)
localStorage.setItem('userID',id)
},
setUserGroup( g,keepTokenLocal ){
this.userGroup = g
if(keepTokenLocal)
localStorage.setItem('userGroup',g)
}
// increment() { // increment() {
// this.count++; // this.count++;
// } // }
+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>
+75 -29
View File
@@ -1,15 +1,17 @@
<template> <template>
<n-flex style="padding-top: 10px;padding-bottom: 10px;"> <n-flex style="padding-top: 10px;padding-bottom: 10px;">
<n-input v-model:value="groupCode" type="text" placeholder="输入进群码" style="flex:1"> <n-input v-model:value="groupCode" type="text" placeholder="输入邀请码" style="flex:1">
<template #suffix> <template #suffix>
<n-icon :component="CopyOutline" style="cursor: pointer;" @click='toClipboard(groupCode)' /> <n-icon :component="CopyOutline" style="cursor: pointer;" @click='toClipboard(groupCode)' />
</template> </template>
</n-input> </n-input>
<n-flex> <n-flex>
<n-button @click="createGroup" :disabled="groupCode !== ''" :type="groupCode !== ''?'default':'primary'" ghost > <n-button @click="createGroup" :disabled="groupCode !== ''" :type="groupCode !== '' ? 'default' : 'primary'"
ghost>
创建群组 创建群组
</n-button> </n-button>
<n-button @click="joinGroup(groupCode)" :disabled="groupCode === ''" :type="groupCode === ''?'default':'primary'" ghost> <n-button @click="joinGroup(groupCode)" :disabled="groupCode === ''"
:type="groupCode === '' ? 'default' : 'primary'" ghost>
加入群组 加入群组
</n-button> </n-button>
</n-flex> </n-flex>
@@ -23,54 +25,65 @@
<n-button @click="joinGroup(codeInput)"> <n-button @click="joinGroup(codeInput)">
joinTest joinTest
</n-button> --> </n-button> -->
<n-input <n-input v-model:value="msg" type="textarea" placeholder="测试发送信息" :autosize="{
v-model:value="msg"
type="textarea"
placeholder="测试发送信息"
:autosize="{
minRows: 3, minRows: 3,
maxRows: 3 maxRows: 3
}" }" />
/>
<n-flex style="padding-top: 10px;padding-bottom: 10px;"> <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="选择当前查看群组" <n-select v-model:value="activeGID" :options="joinedGroup.map(gid => { return { label: gid, value: gid } })"
style="flex:1"/> placeholder="选择当前查看群组" style="flex:1" />
<n-button @click="sendMsg(activeGID,msg)" :disabled="msg === '' || activeGID === null" :type="msg === '' || activeGID === null?'default':'primary'"> <n-button @click="sendMsg(activeGID, msg)" :disabled="msg === '' || activeGID === null"
:type="msg === '' || activeGID === null ? 'default' : 'primary'">
发送消息 发送消息
</n-button> </n-button>
<n-button @click="shareWithQR()" :disabled="activeGID === null"
:type="activeGID === null ? 'default' : 'primary'">
分享群组
</n-button>
</n-flex> </n-flex>
<!-- <n-button @click="dataTest.length = 0"> <!-- <n-button @click="dataTest.length = 0">
clearMsg clearMsg
</n-button> --> </n-button> -->
<n-data-table <n-data-table :columns="columns"
:columns="columns" :data="activeGID ? Messages[activeGID].map(v => { v.timestampString = new Date(v.timestamp).toLocaleString(); return v }).reverse() : []"
:data="activeGID?Messages[activeGID].map(v=>{v.timestampString = new Date(v.timestamp).toLocaleString();return v}):[]" :row-props="null" :bordered="false" :single-line="false" :max-height="400" />
:row-props="null"
<n-modal v-model:show="showModal">
<n-card
style="width: fit-content;"
title=""
:bordered="false" :bordered="false"
:single-line="false" size="huge"
:max-height="400" role="dialog"
/> aria-modal="true"
>
<n-qr-code :value="QRCodeData" style="padding: 0;" :size="256" error-correction-level="Q" :v-if="showModal" :icon-src='QRIconUrl' :icon-size="128"/>
</n-card>
</n-modal>
</template> </template>
<script setup> <script setup>
import {ref} from 'vue' import { ref,onMounted } from 'vue'
import { useNotification } from "naive-ui"; // import { useModal } from "naive-ui";
import useClipboard from "vue-clipboard3"; import useClipboard from "vue-clipboard3";
import {NIcon,NFlex,NDataTable,NButton,NInput,NSelect} from 'naive-ui' import { NIcon, NFlex, NDataTable, NButton, NInput, NSelect,NModal,NCard , NQrCode} from 'naive-ui'
import { useSettingStore } from "@/pinia/store"; import { useSettingStore } from "@/pinia/store";
import { generateKey, encryptString } from '@/func/upload'; import { generateKey, encryptString } from '@/func/upload';
import { decryptString } from '@/func/download'; import { decryptString } from '@/func/download';
import WebsocketHeartbeatJs from 'websocket-heartbeat-js'; import WebsocketHeartbeatJs from 'websocket-heartbeat-js';
import { CopyOutline } from '@vicons/ionicons5' import { CopyOutline } from '@vicons/ionicons5'
import getQueryString from '@/func/getQuery'
const notification = useNotification(); // const modal = useModal();
const { toClipboard } = useClipboard(); const { toClipboard } = useClipboard();
const setting = useSettingStore(); const setting = useSettingStore();
const serverPort = setting.serverPort; const serverPort = setting.serverPort;
const baseUrl = 'ws' + "://" + location.hostname + ":" + serverPort; const baseUrl = 'ws' + "://" + location.hostname + ":" + serverPort;
const QRIconUrl = ref(location.protocol + "//" + location.hostname + ":" + serverPort + "/favicon.ico")
const columns = [ const columns = [
{ {
@@ -110,6 +123,15 @@ const columnsTest = [
}, },
]; ];
// 用于二维码分享
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进入群组 // 凭此code进入群组
const groupCode = ref('') const groupCode = ref('')
@@ -165,16 +187,16 @@ socket.onmessage= (event) => {
let gID = msg.gID let gID = msg.gID
let key = gID2key[gID].key let key = gID2key[gID].key
let res = decryptString(key, msg.verify) let res = decryptString(key, msg.verify)
console.log(res,key,msg.verify) // console.log(res, key, msg.verify)
// 验证成功,正式加入群组 // 验证成功,正式加入群组
if (res === 'miaoSpring') { if (res === 'miaoSpring') {
socket.send(JSON.stringify({ type: 'join', type1: 'success', gID })) socket.send(JSON.stringify({ type: 'join', type1: 'success', gID }))
joinedGroup.value.push(msg.gID) joinedGroup.value.push(msg.gID)
Messages.value[msg.gID] = [] Messages.value[msg.gID] = []
if(activeGID.value === null){ // if(activeGID.value === null){
activeGID.value = msg.gID activeGID.value = msg.gID
} // }
} }
} else if (type1 === 'history') { } else if (type1 === 'history') {
let gID = msg.gID let gID = msg.gID
@@ -245,7 +267,31 @@ function sendMsg(gID,msg){
} }
// 通过地址传参并加入传入的群组
// function getQueryString(key) {
// var reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i');
// var r = window.location.search.substring(1).match(reg);
// if (r != null) {
// return unescape(r[2]);
// }
// return null;
// }
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> </script>
<style> <style></style>
</style>
+6 -2
View File
@@ -28,7 +28,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted,computed } from "vue";
import { useSettingStore } from "@/pinia/store"; import { useSettingStore } from "@/pinia/store";
import {decrypt, decryptString, downloadFromUint8Array} from '../func/download' import {decrypt, decryptString, downloadFromUint8Array} from '../func/download'
import {NInput,NFlex,NDataTable,NButton} from 'naive-ui' import {NInput,NFlex,NDataTable,NButton} from 'naive-ui'
@@ -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 => {
+89
View File
@@ -0,0 +1,89 @@
<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">使用访客账号登录</n-button>
<n-button @click="login">登录</n-button>
</n-flex>
</n-flex>
</n-flex>
</n-card>
</template>
<script setup>
import { computed, ref } from 'vue'
import { NCheckbox, NInputGroupLabel, NInputGroup, NP, NFlex, NDataTable, NButton, NInput, NCard } from 'naive-ui'
import { useSettingStore } from "@/pinia/store";
import { useMessage, useNotification } from "naive-ui";
import sha256 from "crypto-js/sha256.js";
const message = useMessage()
const settingData = useSettingStore();
const serverPort = settingData.serverPort;
const userIDInput = ref('')
const userPasswordInput = ref('')
const isLogining = ref(false)
const baseUrl =
location.protocol + "//" + location.hostname + ":" + serverPort + "/";
const Token = computed(() => settingData.token)
const keepTokenLocal = ref(true)
function useGuestAccount() {
userIDInput.value = 'guest'
userPasswordInput.value = 'guest'
login()
}
async function login() {
try {
isLogining.value = true
const response = await fetch(`${baseUrl}user/login`, {
method: 'POST',
headers: {
// 'Authorization': `Bearer ${Token}`
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: userIDInput.value,
pwd: sha256(userPasswordInput.value).toString()
})
})
let data = await response.json()
if (data.status === 'Error') {
console.log('error')
throw ('failed')
}
console.log(data.token, data)
settingData.setJWT(data.token,keepTokenLocal)
settingData.setUserID(userIDInput.value,keepTokenLocal)
settingData.setUserGroup(data.userGroup,keepTokenLocal)
} catch (e) {
isLogining.value = false
message.error(`登录失败`);
console.log('failed')
console.log(e)
}
}
</script>
+3 -1
View File
@@ -37,7 +37,7 @@
</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";
@@ -204,6 +204,8 @@ 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) {
+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))