diff --git a/server/app/controller/group.js b/server/app/controller/group.js index fabc709..09a09ce 100644 --- a/server/app/controller/group.js +++ b/server/app/controller/group.js @@ -1,4 +1,7 @@ -const { readGroupList, writeGroupList, readHostList, writeHostList } = require('../utils/storage') +const { readGroupList, writeGroupList } = require('../utils/storage') +const { HostListDB } = require('../utils/db-class') + +const hostListDB = new HostListDB().getInstance() async function getGroupList({ res }) { let data = await readGroupList() @@ -41,16 +44,17 @@ const removeGroup = async ({ res, request }) => { if (idx === -1) return res.fail({ msg: '分组不存在' }) // 移除分组将所有该分组下host分配到default中去 - let hostList = await readHostList() - hostList = hostList?.map((item) => { - if (item.group === groupList[idx]._id) item.group = 'default' - return item - }) - await writeHostList(hostList) - + let hostList = await hostListDB.findAsync({}) + if (Array.isArray(hostList) && hostList.length > 0) { + for (let item of hostList) { + if (item.group === groupList[idx]._id) { + item.group = 'default' + await hostListDB.updateAsync({ _id: item._id }, item) + } + } + } groupList.splice(idx, 1) await writeGroupList(groupList) - res.success({ data: '移除成功' }) } diff --git a/server/app/controller/host.js b/server/app/controller/host.js index 8a110c2..193df60 100644 --- a/server/app/controller/host.js +++ b/server/app/controller/host.js @@ -1,15 +1,16 @@ -const { readHostList, writeHostList } = require('../utils/storage') -const { RSADecryptSync, AESEncryptSync, AESDecryptSync } = require('../utils/encrypt') +const { RSADecryptAsync, AESEncryptAsync, AESDecryptAsync } = require('../utils/encrypt') +const { HostListDB } = require('../utils/db-class') +const hostListDB = new HostListDB().getInstance() async function getHostList({ res }) { // console.log('get-host-list') - let data = await readHostList() + let data = await hostListDB.findAsync({}) data?.sort((a, b) => Number(b.index || 0) - Number(a.index || 0)) for (const item of data) { try { let { username, port, authType, _id: id, credential } = item // console.log('解密凭证title: ', credential) - if (credential) credential = await AESDecryptSync(credential) + if (credential) credential = await AESDecryptAsync(credential) const isConfig = Boolean(username && port && (item[authType])) Object.assign(item, { id, isConfig, password: '', privateKey: '', credential }) } catch (error) { @@ -19,9 +20,7 @@ async function getHostList({ res }) { res.success({ data }) } -async function addHost({ - res, request -}) { +async function addHost({ res, request }) { let { body: { name, host, index, expired, expiredNotify, group, consoleUrl, remark, @@ -30,21 +29,19 @@ async function addHost({ } = request // console.log(request) if (!host || !name) return res.fail({ msg: 'missing params: name or host' }) - let hostList = await readHostList() let record = { name, host, index, expired, expiredNotify, group, consoleUrl, remark, port: newPort, clientPort, username, authType, password, privateKey, credential, command } if (record[authType]) { - const clearTempKey = await RSADecryptSync(tempKey) + const clearTempKey = await RSADecryptAsync(tempKey) console.log('clearTempKey:', clearTempKey) - const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) + const clearSSHKey = await AESDecryptAsync(record[authType], clearTempKey) console.log(`${ authType }原密文: `, clearSSHKey) - record[authType] = await AESEncryptSync(clearSSHKey) + record[authType] = await AESEncryptAsync(clearSSHKey) // console.log(`${ authType }__commonKey加密存储: `, record[authType]) } - hostList.push(record) - await writeHostList(hostList) + await hostListDB.insertAsync(record) res.success() } @@ -60,78 +57,65 @@ async function updateHost({ res, request }) { let isBatch = Array.isArray(hosts) if (isBatch) { if (!hosts.length) return res.fail({ msg: 'hosts为空' }) - let hostList = await readHostList() - let newHostList = [] + let hostList = await hostListDB.findAsync({}) for (let oldRecord of hostList) { - let record = hosts.find(item => item.id === oldRecord._id) - if (!record) { - newHostList.push(oldRecord) - continue - } - let { authType } = record + let target = hosts.find(item => item.id === oldRecord._id) + if (!target) continue + let { authType } = target // 如果存在原认证方式则保存下来 - if (!record[authType] && oldRecord[authType]) { - record[authType] = oldRecord[authType] + if (!target[authType]) { + target[authType] = oldRecord[authType] } else { - const clearTempKey = await RSADecryptSync(record.tempKey) + const clearTempKey = await RSADecryptAsync(target.tempKey) // console.log('批量解密tempKey:', clearTempKey) - const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) + const clearSSHKey = await AESDecryptAsync(target[authType], clearTempKey) // console.log(`${ authType }原密文: `, clearSSHKey) - record[authType] = await AESEncryptSync(clearSSHKey) - // console.log(`${ authType }__commonKey加密存储: `, record[authType]) + target[authType] = await AESEncryptAsync(clearSSHKey) + // console.log(`${ authType }__commonKey加密存储: `, target[authType]) } - delete oldRecord.monitorData - delete record.monitorData - newHostList.push(Object.assign(oldRecord, record)) + delete target._id + delete target.monitorData + delete target.tempKey + Object.assign(oldRecord, target) + await hostListDB.updateAsync({ _id: oldRecord._id }, oldRecord) } - await writeHostList(newHostList) return res.success({ msg: '批量修改成功' }) } if (!newHost || !newName || !oldHost) return res.fail({ msg: '参数错误' }) - let hostList = await readHostList() - if (!hostList.some(({ host }) => host === oldHost)) return res.fail({ msg: `原实例[${ oldHost }]不存在,请尝试添加实例` }) - - let record = { + let updateRecord = { name: newName, host: newHost, index, expired, expiredNotify, group, consoleUrl, remark, port, clientPort, username, authType, password, privateKey, credential, command } - let idx = hostList.findIndex(({ _id }) => _id === id) - const oldRecord = hostList[idx] + let oldRecord = await hostListDB.findOneAsync({ _id: id }) // 如果存在原认证方式则保存下来 - if (!record[authType] && oldRecord[authType]) { - record[authType] = oldRecord[authType] + if (!updateRecord[authType] && oldRecord[authType]) { + updateRecord[authType] = oldRecord[authType] } else { - const clearTempKey = await RSADecryptSync(tempKey) + const clearTempKey = await RSADecryptAsync(tempKey) // console.log('clearTempKey:', clearTempKey) - const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) + const clearSSHKey = await AESDecryptAsync(updateRecord[authType], clearTempKey) // console.log(`${ authType }原密文: `, clearSSHKey) - record[authType] = await AESEncryptSync(clearSSHKey) - // console.log(`${ authType }__commonKey加密存储: `, record[authType]) + updateRecord[authType] = await AESEncryptAsync(clearSSHKey) + // console.log(`${ authType }__commonKey加密存储: `, updateRecord[authType]) } - hostList.splice(idx, 1, record) - await writeHostList(hostList) - res.success() + await hostListDB.updateAsync({ _id: oldRecord._id }, updateRecord) + res.success({ msg: '修改成功' }) } -async function removeHost({ - res, request -}) { +async function removeHost({ res, request }) { let { body: { ids } } = request - let hostList = await readHostList() if (!Array.isArray(ids)) return res.fail({ msg: '参数错误' }) - hostList = hostList.filter(({ _id }) => !ids.includes(_id)) - await writeHostList(hostList) - res.success({ data: '已移除' }) + const numRemoved = await hostListDB.removeAsync({ _id: { $in: ids } }, { multi: true }) + // console.log('numRemoved: ', numRemoved) + res.success({ data: `已移除,数量: ${ numRemoved }` }) } -async function importHost({ - res, request -}) { +async function importHost({ res, request }) { let { body: { importHost, isEasyNodeJson = false } } = request if (!Array.isArray(importHost)) return res.fail({ msg: '参数错误' }) - let hostList = await readHostList() + let hostList = await hostListDB.findAsync({}) // 考虑到批量导入可能会重复太多,先过滤已存在的host:port let hostListSet = new Set(hostList.map(({ host, port }) => `${ host }:${ port }`)) let newHostList = importHost.filter(({ host, port }) => !hostListSet.has(`${ host }:${ port }`)) @@ -157,8 +141,7 @@ async function importHost({ return Object.assign(item, { ...extraFiels }) }) } - hostList.push(...newHostList) - await writeHostList(hostList) + await hostListDB.insertAsync(newHostList) res.success({ data: { len: newHostList.length } }) } diff --git a/server/app/controller/ssh.js b/server/app/controller/ssh.js index c1df398..8b52654 100644 --- a/server/app/controller/ssh.js +++ b/server/app/controller/ssh.js @@ -1,5 +1,7 @@ -const { readSSHRecord, writeSSHRecord, readHostList, writeHostList } = require('../utils/storage') -const { RSADecryptSync, AESEncryptSync, AESDecryptSync } = require('../utils/encrypt') +const { readSSHRecord, writeSSHRecord } = require('../utils/storage') +const { RSADecryptAsync, AESEncryptAsync, AESDecryptAsync } = require('../utils/encrypt') +const { HostListDB } = require('../utils/db-class') +const hostListDB = new HostListDB().getInstance() async function getSSHList({ res }) { // console.log('get-host-list') @@ -19,11 +21,11 @@ const addSSH = async ({ res, request }) => { let sshRecord = await readSSHRecord() if (sshRecord.some(item => item.name === name)) return res.fail({ data: false, msg: '已存在同名凭证' }) - const clearTempKey = await RSADecryptSync(tempKey) + const clearTempKey = await RSADecryptAsync(tempKey) console.log('clearTempKey:', clearTempKey) - const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) + const clearSSHKey = await AESDecryptAsync(record[authType], clearTempKey) // console.log(`${ authType }原密文: `, clearSSHKey) - record[authType] = await AESEncryptSync(clearSSHKey) + record[authType] = await AESEncryptAsync(clearSSHKey) // console.log(`${ authType }__commonKey加密存储: `, record[authType]) sshRecord.push({ ...record, date: Date.now() }) @@ -46,11 +48,11 @@ const updateSSH = async ({ res, request }) => { if (!record[authType] && oldRecord[authType]) { record[authType] = oldRecord[authType] } else { - const clearTempKey = await RSADecryptSync(tempKey) + const clearTempKey = await RSADecryptAsync(tempKey) console.log('clearTempKey:', clearTempKey) - const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) + const clearSSHKey = await AESDecryptAsync(record[authType], clearTempKey) // console.log(`${ authType }原密文: `, clearSSHKey) - record[authType] = await AESEncryptSync(clearSSHKey) + record[authType] = await AESEncryptAsync(clearSSHKey) // console.log(`${ authType }__commonKey加密存储: `, record[authType]) } record._id = sshRecord[idx]._id @@ -67,12 +69,15 @@ const removeSSH = async ({ res, request }) => { if (idx === -1) return res.fail({ msg: '凭证不存在' }) sshRecord.splice(idx, 1) // 将删除的凭证id从host中删除 - let hostList = await readHostList() - hostList = hostList.map(item => { - if (item.credential === id) item.credential = '' - return item - }) - await writeHostList(hostList) + let hostList = await hostListDB.findAsync({}) + if (Array.isArray(hostList) && hostList.length > 0) { + for (let item of hostList) { + if (item.credential === id) { + item.credential = '' + await hostListDB.updateAsync({ _id: item._id }, item) + } + } + } consola.info('移除凭证:', id) await writeSSHRecord(sshRecord) res.success({ data: '移除成功' }) @@ -81,7 +86,7 @@ const removeSSH = async ({ res, request }) => { const getCommand = async ({ res, request }) => { let { hostId } = request.query if (!hostId) return res.fail({ data: false, msg: '参数错误' }) - let hostInfo = await readHostList() + let hostInfo = await hostListDB.findAsync({}) let record = hostInfo?.find(item => item._id === hostId) consola.info('查询登录后执行的指令:', hostId) if (!record) return res.fail({ data: false, msg: 'host not found' }) // host不存在 diff --git a/server/app/controller/user.js b/server/app/controller/user.js index 5525169..2e5a876 100644 --- a/server/app/controller/user.js +++ b/server/app/controller/user.js @@ -2,7 +2,7 @@ const jwt = require('jsonwebtoken') const axios = require('axios') const { asyncSendNotice } = require('../utils/notify') const { readKey, writeKey, writeLog } = require('../utils/storage') -const { RSADecryptSync, AESEncryptSync, SHA1Encrypt } = require('../utils/encrypt') +const { RSADecryptAsync, AESEncryptAsync, SHA1Encrypt } = require('../utils/encrypt') const { getNetIPInfo } = require('../utils/tools') const getpublicKey = async ({ res }) => { @@ -52,7 +52,7 @@ const login = async ({ res, request }) => { // 登录流程 try { // console.log('ciphertext', ciphertext) - let loginPwd = await RSADecryptSync(ciphertext) + let loginPwd = await RSADecryptAsync(ciphertext) // console.log('Decrypt解密password:', loginPwd) let { user, pwd } = await readKey() if (loginName === user && loginPwd === 'admin' && pwd === 'admin') { @@ -76,7 +76,7 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => { // 生产token let { commonKey } = await readKey() let token = jwt.sign({ date: Date.now() }, commonKey, { expiresIn: jwtExpires }) // 生成token - token = await AESEncryptSync(token) // 对称加密token后再传输给前端 + token = await AESEncryptAsync(token) // 对称加密token后再传输给前端 // 记录客户端登录IP(用于判断是否异地且只保留最近10条) const clientIPInfo = await getNetIPInfo(clientIp) @@ -92,13 +92,13 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => { const updatePwd = async ({ res, request }) => { let { body: { oldLoginName, oldPwd, newLoginName, newPwd } } = request - let rsaOldPwd = await RSADecryptSync(oldPwd) + let rsaOldPwd = await RSADecryptAsync(oldPwd) oldPwd = rsaOldPwd === 'admin' ? 'admin' : SHA1Encrypt(rsaOldPwd) let keyObj = await readKey() let { user, pwd } = keyObj if (oldLoginName !== user || oldPwd !== pwd) return res.fail({ data: false, msg: '原用户名或密码校验失败' }) // 旧密钥校验通过,加密保存新密码 - newPwd = await RSADecryptSync(newPwd) === 'admin' ? 'admin' : SHA1Encrypt(await RSADecryptSync(newPwd)) + newPwd = await RSADecryptAsync(newPwd) === 'admin' ? 'admin' : SHA1Encrypt(await RSADecryptAsync(newPwd)) keyObj.user = newLoginName keyObj.pwd = newPwd await writeKey(keyObj) diff --git a/server/app/db/README.md b/server/app/db/README.md deleted file mode 100644 index cf6c022..0000000 --- a/server/app/db/README.md +++ /dev/null @@ -1,50 +0,0 @@ -db目录,初始化后自动生成 - -**host.db** - -> 存储服务器基本信息 - -**key.db** - -> 用于加密的密钥相关 - -**credentials.db** - -> ssh密钥记录(加密存储) - -**email.db** - -> 邮件配置 - -- port: 587 --> secure: false -```db -// Gmail调试不通过, 暂缓 -{ - "name": "Google邮箱", - "target": "google", - "host": "smtp.gmail.com", - "port": 465, - "secure": true, - "tls": { - "rejectUnauthorized": false - } -} -``` - -**notify.db** - -> 通知配置 - -**group.db** - -> 服务器分组配置 - - -**scripts.db** - -> 脚本库 - - -**onekey.db** - -> 批量指令记录 diff --git a/server/app/init.js b/server/app/init.js index 3c34b7a..fcfb51c 100644 --- a/server/app/init.js +++ b/server/app/init.js @@ -1,7 +1,7 @@ const NodeRSA = require('node-rsa') const { readKey, writeKey } = require('./utils/storage') const { randomStr } = require('./utils/tools') -const { AESEncryptSync } = require('./utils/encrypt') +const { AESEncryptAsync } = require('./utils/encrypt') // 初始化公私钥, 供登录、保存ssh密钥/密码等加解密 async function initRsa() { @@ -11,7 +11,7 @@ async function initRsa() { key.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' }) let privateKey = key.exportKey('pkcs1-private-pem') let publicKey = key.exportKey('pkcs8-public-pem') - keyObj.privateKey = await AESEncryptSync(privateKey) // 加密私钥 + keyObj.privateKey = await AESEncryptAsync(privateKey) // 加密私钥 keyObj.publicKey = publicKey // 公开公钥 await writeKey(keyObj) consola.info('Task: 已生成新的非对称加密公私钥') diff --git a/server/app/schedule/expired-notify.js b/server/app/schedule/expired-notify.js index 67499e6..6149e08 100644 --- a/server/app/schedule/expired-notify.js +++ b/server/app/schedule/expired-notify.js @@ -1,25 +1,26 @@ const schedule = require('node-schedule') const { asyncSendNotice } = require('../utils/notify') -const { readHostList } = require('../utils/storage') const { formatTimestamp } = require('../utils/tools') +const { HostListDB } = require('../utils/db-class') +const hostListDB = new HostListDB().getInstance() const expiredNotifyJob = async () => { consola.info('=====开始检测服务器到期时间=====', new Date()) - const hostList = await readHostList() + const hostList = await hostListDB.findAsync({}) for (const item of hostList) { - if(!item.expiredNotify) continue + if (!item.expiredNotify) continue const { host, name, expired, consoleUrl } = item const restDay = Number(((expired - Date.now()) / (1000 * 60 * 60 * 24)).toFixed(1)) console.log(Date.now(), restDay) let title = '服务器到期提醒' let content = `别名: ${ name }\nIP: ${ host }\n到期时间:${ formatTimestamp(expired, 'week') }\n控制台: ${ consoleUrl || '未填写' }` - if(0 <= restDay && restDay <= 1) { + if (0 <= restDay && restDay <= 1) { let temp = '有服务器将在一天后到期,请关注\n' asyncSendNotice('host_expired', title, temp + content) - }else if(3 <= restDay && restDay < 4) { + } else if (3 <= restDay && restDay < 4) { let temp = '有服务器将在三天后到期,请关注\n' asyncSendNotice('host_expired', title, temp + content) - }else if(7 <= restDay && restDay < 8) { + } else if (7 <= restDay && restDay < 8) { let temp = '有服务器将在七天后到期,请关注\n' asyncSendNotice('host_expired', title, temp + content) } diff --git a/server/app/socket/clients.js b/server/app/socket/clients.js index 8b4d59f..db168b7 100644 --- a/server/app/socket/clients.js +++ b/server/app/socket/clients.js @@ -1,15 +1,16 @@ const { Server: ServerIO } = require('socket.io') const { io: ClientIO } = require('socket.io-client') -const { readHostList } = require('../utils/storage') const { defaultClientPort } = require('../config') const { verifyAuthSync } = require('../utils/verify-auth') const { isAllowedIp } = require('../utils/tools') +const { HostListDB } = require('../utils/db-class') +const hostListDB = new HostListDB().getInstance() let clientSockets = [] let clientsData = {} async function getClientsInfo(clientSockets) { - let hostList = await readHostList() + let hostList = await hostListDB.findAsync({}) clientSockets.forEach((clientItem) => { // 被删除的客户端断开连接 if (!hostList.some(item => item.host === clientItem.host)) clientItem.close && clientItem.close() diff --git a/server/app/socket/onekey.js b/server/app/socket/onekey.js index 2cdbb92..a366338 100644 --- a/server/app/socket/onekey.js +++ b/server/app/socket/onekey.js @@ -1,11 +1,13 @@ const { Server } = require('socket.io') const { Client: SSHClient } = require('ssh2') const { asyncSendNotice } = require('../utils/notify') -const { readSSHRecord, readHostList, writeOneKeyRecord } = require('../utils/storage') +const { readSSHRecord, writeOneKeyRecord } = require('../utils/storage') const { verifyAuthSync } = require('../utils/verify-auth') const { shellThrottle } = require('../utils/tools') -const { AESDecryptSync } = require('../utils/encrypt') +const { AESDecryptAsync } = require('../utils/encrypt') const { isAllowedIp } = require('../utils/tools') +const { HostListDB } = require('../utils/db-class') +const hostListDB = new HostListDB().getInstance() const execStatusEnum = { connecting: '连接中', @@ -129,7 +131,7 @@ module.exports = (httpServer) => { console.log('hostIds:', hostIds) // console.log('token:', token) console.log('command:', command) - const hostList = await readHostList() + const hostList = await hostListDB.findAsync({}) const targetHostsInfo = hostList.filter(item => hostIds.some(id => item._id === id)) || {} // console.log('targetHostsInfo:', targetHostsInfo) if (!targetHostsInfo.length) return socket.emit('create_fail', `未找到【${ hostIds }】服务器信息`) @@ -145,13 +147,13 @@ module.exports = (httpServer) => { execResult.push(curRes) try { if (authType === 'credential') { - let credentialId = await AESDecryptSync(hostInfo['credential']) + let credentialId = await AESDecryptAsync(hostInfo['credential']) const sshRecordList = await readSSHRecord() const sshRecord = sshRecordList.find(item => item._id === credentialId) authInfo.authType = sshRecord.authType - authInfo[authInfo.authType] = await AESDecryptSync(sshRecord[authInfo.authType]) + authInfo[authInfo.authType] = await AESDecryptAsync(sshRecord[authInfo.authType]) } else { - authInfo[authType] = await AESDecryptSync(hostInfo[authType]) + authInfo[authType] = await AESDecryptAsync(hostInfo[authType]) } consola.info('准备连接终端执行一次性指令:', host) consola.log('连接信息', { username, port, authType }) diff --git a/server/app/socket/sftp.js b/server/app/socket/sftp.js index 7914621..c7e203e 100644 --- a/server/app/socket/sftp.js +++ b/server/app/socket/sftp.js @@ -5,9 +5,11 @@ const CryptoJS = require('crypto-js') const { Server } = require('socket.io') const { sftpCacheDir } = require('../config') const { verifyAuthSync } = require('../utils/verify-auth') -const { AESDecryptSync } = require('../utils/encrypt') -const { readSSHRecord, readHostList } = require('../utils/storage') +const { AESDecryptAsync } = require('../utils/encrypt') +const { readSSHRecord } = require('../utils/storage') const { isAllowedIp } = require('../utils/tools') +const { HostListDB } = require('../utils/db-class') +const hostListDB = new HostListDB().getInstance() // 读取切片 const pipeStream = (path, writeStream) => { @@ -230,7 +232,7 @@ module.exports = (httpServer) => { return } - const hostList = await readHostList() + const hostList = await hostListDB.findAsync({}) const targetHostInfo = hostList.find(item => item.host === ip) || {} let { authType, host, port, username } = targetHostInfo if (!host) return socket.emit('create_fail', `查找【${ ip }】凭证信息失败`) @@ -238,16 +240,16 @@ module.exports = (httpServer) => { // 解密放到try里面,防止报错【commonKey必须配对, 否则需要重新添加服务器密钥】 if (authType === 'credential') { - let credentialId = await AESDecryptSync(targetHostInfo[authType]) + let credentialId = await AESDecryptAsync(targetHostInfo[authType]) const sshRecordList = await readSSHRecord() const sshRecord = sshRecordList.find(item => item._id === credentialId) authInfo.authType = sshRecord.authType - authInfo[authInfo.authType] = await AESDecryptSync(sshRecord[authInfo.authType]) + authInfo[authInfo.authType] = await AESDecryptAsync(sshRecord[authInfo.authType]) } else { - authInfo[authType] = await AESDecryptSync(targetHostInfo[authType]) + authInfo[authType] = await AESDecryptAsync(targetHostInfo[authType]) } consola.info('准备连接Sftp面板:', host) - targetHostInfo[targetHostInfo.authType] = await AESDecryptSync(targetHostInfo[targetHostInfo.authType]) + targetHostInfo[targetHostInfo.authType] = await AESDecryptAsync(targetHostInfo[targetHostInfo.authType]) consola.log('连接信息', { username, port, authType }) sftpClient diff --git a/server/app/socket/terminal.js b/server/app/socket/terminal.js index 1bef4b9..f30b416 100644 --- a/server/app/socket/terminal.js +++ b/server/app/socket/terminal.js @@ -1,10 +1,12 @@ const { Server } = require('socket.io') const { Client: SSHClient } = require('ssh2') const { verifyAuthSync } = require('../utils/verify-auth') -const { AESDecryptSync } = require('../utils/encrypt') -const { readSSHRecord, readHostList } = require('../utils/storage') +const { AESDecryptAsync } = require('../utils/encrypt') +const { readSSHRecord } = require('../utils/storage') const { asyncSendNotice } = require('../utils/notify') const { isAllowedIp, ping } = require('../utils/tools') +const { HostListDB } = require('../utils/db-class') +const hostListDB = new HostListDB().getInstance() function createInteractiveShell(socket, sshClient) { return new Promise((resolve) => { @@ -51,7 +53,7 @@ function createInteractiveShell(socket, sshClient) { async function createTerminal(hostId, socket, sshClient) { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve) => { - const hostList = await readHostList() + const hostList = await hostListDB.findAsync({}) const targetHostInfo = hostList.find(item => item._id === hostId) || {} let { authType, host, port, username, name } = targetHostInfo if (!host) return socket.emit('create_fail', `查找hostId【${ hostId }】凭证信息失败`) @@ -60,16 +62,16 @@ async function createTerminal(hostId, socket, sshClient) { try { // 解密放到try里面,防止报错【commonKey必须配对, 否则需要重新添加服务器密钥】 if (authType === 'credential') { - let credentialId = await AESDecryptSync(targetHostInfo[authType]) + let credentialId = await AESDecryptAsync(targetHostInfo[authType]) const sshRecordList = await readSSHRecord() const sshRecord = sshRecordList.find(item => item._id === credentialId) authInfo.authType = sshRecord.authType - authInfo[authInfo.authType] = await AESDecryptSync(sshRecord[authInfo.authType]) + authInfo[authInfo.authType] = await AESDecryptAsync(sshRecord[authInfo.authType]) } else { - authInfo[authType] = await AESDecryptSync(targetHostInfo[authType]) + authInfo[authType] = await AESDecryptAsync(targetHostInfo[authType]) } consola.info('准备连接终端:', host) - // targetHostInfo[targetHostInfo.authType] = await AESDecryptSync(targetHostInfo[targetHostInfo.authType]) + // targetHostInfo[targetHostInfo.authType] = await AESDecryptAsync(targetHostInfo[targetHostInfo.authType]) consola.log('连接信息', { username, port, authType }) sshClient .on('ready', async() => { diff --git a/server/app/utils/db-class.js b/server/app/utils/db-class.js index 07d096c..4ec0fb4 100644 --- a/server/app/utils/db-class.js +++ b/server/app/utils/db-class.js @@ -15,6 +15,7 @@ module.exports.KeyDB = class KeyDB { constructor() { if (!KeyDB.instance) { KeyDB.instance = new Datastore({ filename: keyDBPath, autoload: true }) + KeyDB.instance.setAutocompactionInterval(5000) } } getInstance() { @@ -26,6 +27,7 @@ module.exports.HostListDB = class HostListDB { constructor() { if (!HostListDB.instance) { HostListDB.instance = new Datastore({ filename: hostListDBPath, autoload: true }) + HostListDB.instance.setAutocompactionInterval(5000) } } getInstance() { @@ -37,6 +39,7 @@ module.exports.SshRecordDB = class SshRecordDB { constructor() { if (!SshRecordDB.instance) { SshRecordDB.instance = new Datastore({ filename: credentialsDBPath, autoload: true }) + SshRecordDB.instance.setAutocompactionInterval(5000) } } getInstance() { @@ -48,6 +51,8 @@ module.exports.NotifyDB = class NotifyDB { constructor() { if (!NotifyDB.instance) { NotifyDB.instance = new Datastore({ filename: notifyDBPath, autoload: true }) + NotifyDB.instance.setAutocompactionInterval(5000) + } } getInstance() { @@ -59,6 +64,7 @@ module.exports.NotifyConfigDB = class NotifyConfigDB { constructor() { if (!NotifyConfigDB.instance) { NotifyConfigDB.instance = new Datastore({ filename: notifyConfigDBPath, autoload: true }) + NotifyConfigDB.instance.setAutocompactionInterval(5000) } } getInstance() { @@ -70,6 +76,7 @@ module.exports.GroupDB = class GroupDB { constructor() { if (!GroupDB.instance) { GroupDB.instance = new Datastore({ filename: groupConfDBPath, autoload: true }) + GroupDB.instance.setAutocompactionInterval(5000) } } getInstance() { @@ -81,6 +88,7 @@ module.exports.ScriptsDB = class ScriptsDB { constructor() { if (!ScriptsDB.instance) { ScriptsDB.instance = new Datastore({ filename: scriptsDBPath, autoload: true }) + ScriptsDB.instance.setAutocompactionInterval(5000) } } getInstance() { @@ -92,6 +100,7 @@ module.exports.OnekeyDB = class OnekeyDB { constructor() { if (!OnekeyDB.instance) { OnekeyDB.instance = new Datastore({ filename: onekeyDBPath, autoload: true }) + OnekeyDB.instance.setAutocompactionInterval(5000) } } getInstance() { @@ -103,6 +112,7 @@ module.exports.LogDB = class LogDB { constructor() { if (!LogDB.instance) { LogDB.instance = new Datastore({ filename: logDBPath, autoload: true }) + LogDB.instance.setAutocompactionInterval(5000) } } getInstance() { diff --git a/server/app/utils/encrypt.js b/server/app/utils/encrypt.js index 76fce17..a33d55b 100644 --- a/server/app/utils/encrypt.js +++ b/server/app/utils/encrypt.js @@ -4,10 +4,10 @@ const NodeRSA = require('node-rsa') const { readKey } = require('./storage.js') // rsa非对称 私钥解密 -const RSADecryptSync = async (ciphertext) => { +const RSADecryptAsync = async (ciphertext) => { if (!ciphertext) return let { privateKey } = await readKey() - privateKey = await AESDecryptSync(privateKey) // 先解密私钥 + privateKey = await AESDecryptAsync(privateKey) // 先解密私钥 const rsakey = new NodeRSA(privateKey) rsakey.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' }) // Must Set It When Frontend Use jsencrypt const plaintext = rsakey.decrypt(ciphertext, 'utf8') @@ -15,16 +15,16 @@ const RSADecryptSync = async (ciphertext) => { } // aes对称 加密(default commonKey) -const AESEncryptSync = async (text, key) => { - if(!text) return +const AESEncryptAsync = async (text, key) => { + if (!text) return let { commonKey } = await readKey() let ciphertext = CryptoJS.AES.encrypt(text, key || commonKey).toString() return ciphertext } // aes对称 解密(default commonKey) -const AESDecryptSync = async (ciphertext, key) => { - if(!ciphertext) return +const AESDecryptAsync = async (ciphertext, key) => { + if (!ciphertext) return let { commonKey } = await readKey() let bytes = CryptoJS.AES.decrypt(ciphertext, key || commonKey) let originalText = bytes.toString(CryptoJS.enc.Utf8) @@ -37,8 +37,8 @@ const SHA1Encrypt = (clearText) => { } module.exports = { - RSADecryptSync, - AESEncryptSync, - AESDecryptSync, + RSADecryptAsync, + AESEncryptAsync, + AESDecryptAsync, SHA1Encrypt } \ No newline at end of file diff --git a/server/app/utils/storage.js b/server/app/utils/storage.js index 81a19c2..6f36fac 100644 --- a/server/app/utils/storage.js +++ b/server/app/utils/storage.js @@ -64,43 +64,43 @@ const writeSSHRecord = async (record = []) => { }) } -const readHostList = async () => { - return new Promise((resolve, reject) => { - const hostListDB = new HostListDB().getInstance() - hostListDB.find({}, (err, docs) => { - if (err) { - consola.error('读取host-list-db错误:', err) - reject(err) - } else { - resolve(docs) - } - }) - }) -} +// const readHostList = async () => { +// return new Promise((resolve, reject) => { +// const hostListDB = new HostListDB().getInstance() +// hostListDB.find({}, (err, docs) => { +// if (err) { +// consola.error('读取host-list-db错误:', err) +// reject(err) +// } else { +// resolve(docs) +// } +// }) +// }) +// } -const writeHostList = async (record = []) => { - return new Promise((resolve, reject) => { - const hostListDB = new HostListDB().getInstance() - hostListDB.remove({}, { multi: true }, (err) => { - if (err) { - consola.error('清空HostList出错:', err) - reject(err) - } else { - hostListDB.compactDatafile() - // 插入新的数据列表 - hostListDB.insert(record, (err, newDocs) => { - if (err) { - consola.error('写入新的HostList出错:', err) - reject(err) - } else { - hostListDB.compactDatafile() - resolve(newDocs) - } - }) - } - }) - }) -} +// const writeHostList = async (record = []) => { +// return new Promise((resolve, reject) => { +// const hostListDB = new HostListDB().getInstance() +// hostListDB.remove({}, { multi: true }, (err) => { +// if (err) { +// consola.error('清空HostList出错:', err) +// reject(err) +// } else { +// hostListDB.compactDatafile() +// // 插入新的数据列表 +// hostListDB.insert(record, (err, newDocs) => { +// if (err) { +// consola.error('写入新的HostList出错:', err) +// reject(err) +// } else { +// hostListDB.compactDatafile() +// resolve(newDocs) +// } +// }) +// } +// }) +// }) +// } const readNotifyConfig = async () => { return new Promise((resolve, reject) => { @@ -329,7 +329,7 @@ const writeLog = async (records = {}) => { module.exports = { readSSHRecord, writeSSHRecord, - readHostList, writeHostList, + // readHostList, writeHostList, readKey, writeKey, readNotifyList, writeNotifyList, readNotifyConfig, writeNotifyConfig, getNotifySwByType, diff --git a/server/app/utils/verify-auth.js b/server/app/utils/verify-auth.js index 0ae3522..014ed97 100644 --- a/server/app/utils/verify-auth.js +++ b/server/app/utils/verify-auth.js @@ -1,5 +1,5 @@ -const { AESDecryptSync } = require('./encrypt') +const { AESDecryptAsync } = require('./encrypt') const { readKey } = require('./storage') const jwt = require('jsonwebtoken') @@ -13,7 +13,7 @@ const enumLoginCode = { const verifyAuthSync = async (token, clientIp) => { consola.info('verifyAuthSync IP:', clientIp) try { - token = await AESDecryptSync(token) // 先aes解密 + token = await AESDecryptAsync(token) // 先aes解密 const { commonKey } = await readKey() const { exp } = jwt.verify(token, commonKey) if (Date.now() > (exp * 1000)) return { code: -1, msg: 'token expires' } // 过期 diff --git a/web/src/views/server/components/host-form.vue b/web/src/views/server/components/host-form.vue index 36b8023..293a7ff 100644 --- a/web/src/views/server/components/host-form.vue +++ b/web/src/views/server/components/host-form.vue @@ -356,7 +356,7 @@ const setDefaultData = () => { const setBatchDefaultData = () => { if (!isBatchModify.value) return - Object.assign(hostForm.value, { ...formField }, { group: '', port: '', username: '', authType: '' }) + Object.assign(hostForm.value, { ...formField }, { group: '', port: '', username: '', authType: '', clientPort: '' }) } const handleOpen = async () => { setDefaultData() @@ -416,7 +416,7 @@ const handleSave = () => { // eslint-disable-next-line let updateFileData = Object.fromEntries(Object.entries(formData).filter(([key, value]) => Boolean(value))) // 剔除掉未更改的值 if (Object.keys(updateFileData).length === 0) return $message.warning('没有任何修改') - // console.log(updateFileData) + console.log(updateFileData) let newHosts = batchHosts.value .map(item => ({ ...item, ...updateFileData })) .map(item => { diff --git a/web/src/views/server/index.vue b/web/src/views/server/index.vue index 0b7970d..78705b0 100644 --- a/web/src/views/server/index.vue +++ b/web/src/views/server/index.vue @@ -168,7 +168,6 @@ let handleBatchOnekey = async () => { let handleBatchExport = () => { collectSelectHost() if (!selectHosts.value.length) return $message.warning('请选择要批量操作的实例') - console.log(selectHosts.value) let exportData = JSON.parse(JSON.stringify(selectHosts.value)) exportData = exportData.map(item => { delete item.monitorData