♻️ 重构本地数据库-host模块

This commit is contained in:
chaos-zhu 2024-10-22 00:48:26 +08:00
parent a72ab84cee
commit 5724ede172
17 changed files with 178 additions and 219 deletions

View File

@ -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 }) { async function getGroupList({ res }) {
let data = await readGroupList() let data = await readGroupList()
@ -41,16 +44,17 @@ const removeGroup = async ({ res, request }) => {
if (idx === -1) return res.fail({ msg: '分组不存在' }) if (idx === -1) return res.fail({ msg: '分组不存在' })
// 移除分组将所有该分组下host分配到default中去 // 移除分组将所有该分组下host分配到default中去
let hostList = await readHostList() let hostList = await hostListDB.findAsync({})
hostList = hostList?.map((item) => { if (Array.isArray(hostList) && hostList.length > 0) {
if (item.group === groupList[idx]._id) item.group = 'default' for (let item of hostList) {
return item if (item.group === groupList[idx]._id) {
}) item.group = 'default'
await writeHostList(hostList) await hostListDB.updateAsync({ _id: item._id }, item)
}
}
}
groupList.splice(idx, 1) groupList.splice(idx, 1)
await writeGroupList(groupList) await writeGroupList(groupList)
res.success({ data: '移除成功' }) res.success({ data: '移除成功' })
} }

View File

@ -1,15 +1,16 @@
const { readHostList, writeHostList } = require('../utils/storage') const { RSADecryptAsync, AESEncryptAsync, AESDecryptAsync } = require('../utils/encrypt')
const { RSADecryptSync, AESEncryptSync, AESDecryptSync } = require('../utils/encrypt') const { HostListDB } = require('../utils/db-class')
const hostListDB = new HostListDB().getInstance()
async function getHostList({ res }) { async function getHostList({ res }) {
// console.log('get-host-list') // 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)) data?.sort((a, b) => Number(b.index || 0) - Number(a.index || 0))
for (const item of data) { for (const item of data) {
try { try {
let { username, port, authType, _id: id, credential } = item let { username, port, authType, _id: id, credential } = item
// console.log('解密凭证title: ', credential) // console.log('解密凭证title: ', credential)
if (credential) credential = await AESDecryptSync(credential) if (credential) credential = await AESDecryptAsync(credential)
const isConfig = Boolean(username && port && (item[authType])) const isConfig = Boolean(username && port && (item[authType]))
Object.assign(item, { id, isConfig, password: '', privateKey: '', credential }) Object.assign(item, { id, isConfig, password: '', privateKey: '', credential })
} catch (error) { } catch (error) {
@ -19,9 +20,7 @@ async function getHostList({ res }) {
res.success({ data }) res.success({ data })
} }
async function addHost({ async function addHost({ res, request }) {
res, request
}) {
let { let {
body: { body: {
name, host, index, expired, expiredNotify, group, consoleUrl, remark, name, host, index, expired, expiredNotify, group, consoleUrl, remark,
@ -30,21 +29,19 @@ async function addHost({
} = request } = request
// console.log(request) // console.log(request)
if (!host || !name) return res.fail({ msg: 'missing params: name or host' }) if (!host || !name) return res.fail({ msg: 'missing params: name or host' })
let hostList = await readHostList()
let record = { let record = {
name, host, index, expired, expiredNotify, group, consoleUrl, remark, name, host, index, expired, expiredNotify, group, consoleUrl, remark,
port: newPort, clientPort, username, authType, password, privateKey, credential, command port: newPort, clientPort, username, authType, password, privateKey, credential, command
} }
if (record[authType]) { if (record[authType]) {
const clearTempKey = await RSADecryptSync(tempKey) const clearTempKey = await RSADecryptAsync(tempKey)
console.log('clearTempKey:', clearTempKey) console.log('clearTempKey:', clearTempKey)
const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) const clearSSHKey = await AESDecryptAsync(record[authType], clearTempKey)
console.log(`${ authType }原密文: `, clearSSHKey) console.log(`${ authType }原密文: `, clearSSHKey)
record[authType] = await AESEncryptSync(clearSSHKey) record[authType] = await AESEncryptAsync(clearSSHKey)
// console.log(`${ authType }__commonKey加密存储: `, record[authType]) // console.log(`${ authType }__commonKey加密存储: `, record[authType])
} }
hostList.push(record) await hostListDB.insertAsync(record)
await writeHostList(hostList)
res.success() res.success()
} }
@ -60,78 +57,65 @@ async function updateHost({ res, request }) {
let isBatch = Array.isArray(hosts) let isBatch = Array.isArray(hosts)
if (isBatch) { if (isBatch) {
if (!hosts.length) return res.fail({ msg: 'hosts为空' }) if (!hosts.length) return res.fail({ msg: 'hosts为空' })
let hostList = await readHostList() let hostList = await hostListDB.findAsync({})
let newHostList = []
for (let oldRecord of hostList) { for (let oldRecord of hostList) {
let record = hosts.find(item => item.id === oldRecord._id) let target = hosts.find(item => item.id === oldRecord._id)
if (!record) { if (!target) continue
newHostList.push(oldRecord) let { authType } = target
continue
}
let { authType } = record
// 如果存在原认证方式则保存下来 // 如果存在原认证方式则保存下来
if (!record[authType] && oldRecord[authType]) { if (!target[authType]) {
record[authType] = oldRecord[authType] target[authType] = oldRecord[authType]
} else { } else {
const clearTempKey = await RSADecryptSync(record.tempKey) const clearTempKey = await RSADecryptAsync(target.tempKey)
// console.log('批量解密tempKey:', clearTempKey) // console.log('批量解密tempKey:', clearTempKey)
const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) const clearSSHKey = await AESDecryptAsync(target[authType], clearTempKey)
// console.log(`${ authType }原密文: `, clearSSHKey) // console.log(`${ authType }原密文: `, clearSSHKey)
record[authType] = await AESEncryptSync(clearSSHKey) target[authType] = await AESEncryptAsync(clearSSHKey)
// console.log(`${ authType }__commonKey加密存储: `, record[authType]) // console.log(`${ authType }__commonKey加密存储: `, target[authType])
} }
delete oldRecord.monitorData delete target._id
delete record.monitorData delete target.monitorData
newHostList.push(Object.assign(oldRecord, record)) delete target.tempKey
Object.assign(oldRecord, target)
await hostListDB.updateAsync({ _id: oldRecord._id }, oldRecord)
} }
await writeHostList(newHostList)
return res.success({ msg: '批量修改成功' }) return res.success({ msg: '批量修改成功' })
} }
if (!newHost || !newName || !oldHost) return res.fail({ msg: '参数错误' }) if (!newHost || !newName || !oldHost) return res.fail({ msg: '参数错误' })
let hostList = await readHostList() let updateRecord = {
if (!hostList.some(({ host }) => host === oldHost)) return res.fail({ msg: `原实例[${ oldHost }]不存在,请尝试添加实例` })
let record = {
name: newName, host: newHost, index, expired, expiredNotify, group, consoleUrl, remark, name: newName, host: newHost, index, expired, expiredNotify, group, consoleUrl, remark,
port, clientPort, username, authType, password, privateKey, credential, command port, clientPort, username, authType, password, privateKey, credential, command
} }
let idx = hostList.findIndex(({ _id }) => _id === id) let oldRecord = await hostListDB.findOneAsync({ _id: id })
const oldRecord = hostList[idx]
// 如果存在原认证方式则保存下来 // 如果存在原认证方式则保存下来
if (!record[authType] && oldRecord[authType]) { if (!updateRecord[authType] && oldRecord[authType]) {
record[authType] = oldRecord[authType] updateRecord[authType] = oldRecord[authType]
} else { } else {
const clearTempKey = await RSADecryptSync(tempKey) const clearTempKey = await RSADecryptAsync(tempKey)
// console.log('clearTempKey:', clearTempKey) // console.log('clearTempKey:', clearTempKey)
const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) const clearSSHKey = await AESDecryptAsync(updateRecord[authType], clearTempKey)
// console.log(`${ authType }原密文: `, clearSSHKey) // console.log(`${ authType }原密文: `, clearSSHKey)
record[authType] = await AESEncryptSync(clearSSHKey) updateRecord[authType] = await AESEncryptAsync(clearSSHKey)
// console.log(`${ authType }__commonKey加密存储: `, record[authType]) // console.log(`${ authType }__commonKey加密存储: `, updateRecord[authType])
} }
hostList.splice(idx, 1, record) await hostListDB.updateAsync({ _id: oldRecord._id }, updateRecord)
await writeHostList(hostList) res.success({ msg: '修改成功' })
res.success()
} }
async function removeHost({ async function removeHost({ res, request }) {
res, request
}) {
let { body: { ids } } = request let { body: { ids } } = request
let hostList = await readHostList()
if (!Array.isArray(ids)) return res.fail({ msg: '参数错误' }) if (!Array.isArray(ids)) return res.fail({ msg: '参数错误' })
hostList = hostList.filter(({ _id }) => !ids.includes(_id)) const numRemoved = await hostListDB.removeAsync({ _id: { $in: ids } }, { multi: true })
await writeHostList(hostList) // console.log('numRemoved: ', numRemoved)
res.success({ data: '已移除' }) res.success({ data: `已移除,数量: ${ numRemoved }` })
} }
async function importHost({ async function importHost({ res, request }) {
res, request
}) {
let { body: { importHost, isEasyNodeJson = false } } = request let { body: { importHost, isEasyNodeJson = false } } = request
if (!Array.isArray(importHost)) return res.fail({ msg: '参数错误' }) if (!Array.isArray(importHost)) return res.fail({ msg: '参数错误' })
let hostList = await readHostList() let hostList = await hostListDB.findAsync({})
// 考虑到批量导入可能会重复太多,先过滤已存在的host:port // 考虑到批量导入可能会重复太多,先过滤已存在的host:port
let hostListSet = new Set(hostList.map(({ host, port }) => `${ host }:${ port }`)) let hostListSet = new Set(hostList.map(({ host, port }) => `${ host }:${ port }`))
let newHostList = importHost.filter(({ host, port }) => !hostListSet.has(`${ host }:${ port }`)) let newHostList = importHost.filter(({ host, port }) => !hostListSet.has(`${ host }:${ port }`))
@ -157,8 +141,7 @@ async function importHost({
return Object.assign(item, { ...extraFiels }) return Object.assign(item, { ...extraFiels })
}) })
} }
hostList.push(...newHostList) await hostListDB.insertAsync(newHostList)
await writeHostList(hostList)
res.success({ data: { len: newHostList.length } }) res.success({ data: { len: newHostList.length } })
} }

View File

@ -1,5 +1,7 @@
const { readSSHRecord, writeSSHRecord, readHostList, writeHostList } = require('../utils/storage') const { readSSHRecord, writeSSHRecord } = 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 getSSHList({ res }) { async function getSSHList({ res }) {
// console.log('get-host-list') // console.log('get-host-list')
@ -19,11 +21,11 @@ const addSSH = async ({ res, request }) => {
let sshRecord = await readSSHRecord() let sshRecord = await readSSHRecord()
if (sshRecord.some(item => item.name === name)) return res.fail({ data: false, msg: '已存在同名凭证' }) 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) console.log('clearTempKey:', clearTempKey)
const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) const clearSSHKey = await AESDecryptAsync(record[authType], clearTempKey)
// console.log(`${ authType }原密文: `, clearSSHKey) // console.log(`${ authType }原密文: `, clearSSHKey)
record[authType] = await AESEncryptSync(clearSSHKey) record[authType] = await AESEncryptAsync(clearSSHKey)
// console.log(`${ authType }__commonKey加密存储: `, record[authType]) // console.log(`${ authType }__commonKey加密存储: `, record[authType])
sshRecord.push({ ...record, date: Date.now() }) sshRecord.push({ ...record, date: Date.now() })
@ -46,11 +48,11 @@ const updateSSH = async ({ res, request }) => {
if (!record[authType] && oldRecord[authType]) { if (!record[authType] && oldRecord[authType]) {
record[authType] = oldRecord[authType] record[authType] = oldRecord[authType]
} else { } else {
const clearTempKey = await RSADecryptSync(tempKey) const clearTempKey = await RSADecryptAsync(tempKey)
console.log('clearTempKey:', clearTempKey) console.log('clearTempKey:', clearTempKey)
const clearSSHKey = await AESDecryptSync(record[authType], clearTempKey) const clearSSHKey = await AESDecryptAsync(record[authType], clearTempKey)
// console.log(`${ authType }原密文: `, clearSSHKey) // console.log(`${ authType }原密文: `, clearSSHKey)
record[authType] = await AESEncryptSync(clearSSHKey) record[authType] = await AESEncryptAsync(clearSSHKey)
// console.log(`${ authType }__commonKey加密存储: `, record[authType]) // console.log(`${ authType }__commonKey加密存储: `, record[authType])
} }
record._id = sshRecord[idx]._id record._id = sshRecord[idx]._id
@ -67,12 +69,15 @@ const removeSSH = async ({ res, request }) => {
if (idx === -1) return res.fail({ msg: '凭证不存在' }) if (idx === -1) return res.fail({ msg: '凭证不存在' })
sshRecord.splice(idx, 1) sshRecord.splice(idx, 1)
// 将删除的凭证id从host中删除 // 将删除的凭证id从host中删除
let hostList = await readHostList() let hostList = await hostListDB.findAsync({})
hostList = hostList.map(item => { if (Array.isArray(hostList) && hostList.length > 0) {
if (item.credential === id) item.credential = '' for (let item of hostList) {
return item if (item.credential === id) {
}) item.credential = ''
await writeHostList(hostList) await hostListDB.updateAsync({ _id: item._id }, item)
}
}
}
consola.info('移除凭证:', id) consola.info('移除凭证:', id)
await writeSSHRecord(sshRecord) await writeSSHRecord(sshRecord)
res.success({ data: '移除成功' }) res.success({ data: '移除成功' })
@ -81,7 +86,7 @@ const removeSSH = async ({ res, request }) => {
const getCommand = async ({ res, request }) => { const getCommand = async ({ res, request }) => {
let { hostId } = request.query let { hostId } = request.query
if (!hostId) return res.fail({ data: false, msg: '参数错误' }) 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) let record = hostInfo?.find(item => item._id === hostId)
consola.info('查询登录后执行的指令:', hostId) consola.info('查询登录后执行的指令:', hostId)
if (!record) return res.fail({ data: false, msg: 'host not found' }) // host不存在 if (!record) return res.fail({ data: false, msg: 'host not found' }) // host不存在

View File

@ -2,7 +2,7 @@ const jwt = require('jsonwebtoken')
const axios = require('axios') const axios = require('axios')
const { asyncSendNotice } = require('../utils/notify') const { asyncSendNotice } = require('../utils/notify')
const { readKey, writeKey, writeLog } = require('../utils/storage') 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 { getNetIPInfo } = require('../utils/tools')
const getpublicKey = async ({ res }) => { const getpublicKey = async ({ res }) => {
@ -52,7 +52,7 @@ const login = async ({ res, request }) => {
// 登录流程 // 登录流程
try { try {
// console.log('ciphertext', ciphertext) // console.log('ciphertext', ciphertext)
let loginPwd = await RSADecryptSync(ciphertext) let loginPwd = await RSADecryptAsync(ciphertext)
// console.log('Decrypt解密password:', loginPwd) // console.log('Decrypt解密password:', loginPwd)
let { user, pwd } = await readKey() let { user, pwd } = await readKey()
if (loginName === user && loginPwd === 'admin' && pwd === 'admin') { if (loginName === user && loginPwd === 'admin' && pwd === 'admin') {
@ -76,7 +76,7 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => {
// 生产token // 生产token
let { commonKey } = await readKey() let { commonKey } = await readKey()
let token = jwt.sign({ date: Date.now() }, commonKey, { expiresIn: jwtExpires }) // 生成token let token = jwt.sign({ date: Date.now() }, commonKey, { expiresIn: jwtExpires }) // 生成token
token = await AESEncryptSync(token) // 对称加密token后再传输给前端 token = await AESEncryptAsync(token) // 对称加密token后再传输给前端
// 记录客户端登录IP(用于判断是否异地且只保留最近10条) // 记录客户端登录IP(用于判断是否异地且只保留最近10条)
const clientIPInfo = await getNetIPInfo(clientIp) const clientIPInfo = await getNetIPInfo(clientIp)
@ -92,13 +92,13 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => {
const updatePwd = async ({ res, request }) => { const updatePwd = async ({ res, request }) => {
let { body: { oldLoginName, oldPwd, newLoginName, newPwd } } = request let { body: { oldLoginName, oldPwd, newLoginName, newPwd } } = request
let rsaOldPwd = await RSADecryptSync(oldPwd) let rsaOldPwd = await RSADecryptAsync(oldPwd)
oldPwd = rsaOldPwd === 'admin' ? 'admin' : SHA1Encrypt(rsaOldPwd) oldPwd = rsaOldPwd === 'admin' ? 'admin' : SHA1Encrypt(rsaOldPwd)
let keyObj = await readKey() let keyObj = await readKey()
let { user, pwd } = keyObj let { user, pwd } = keyObj
if (oldLoginName !== user || oldPwd !== pwd) return res.fail({ data: false, msg: '原用户名或密码校验失败' }) 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.user = newLoginName
keyObj.pwd = newPwd keyObj.pwd = newPwd
await writeKey(keyObj) await writeKey(keyObj)

View File

@ -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**
> 批量指令记录

View File

@ -1,7 +1,7 @@
const NodeRSA = require('node-rsa') const NodeRSA = require('node-rsa')
const { readKey, writeKey } = require('./utils/storage') const { readKey, writeKey } = require('./utils/storage')
const { randomStr } = require('./utils/tools') const { randomStr } = require('./utils/tools')
const { AESEncryptSync } = require('./utils/encrypt') const { AESEncryptAsync } = require('./utils/encrypt')
// 初始化公私钥, 供登录、保存ssh密钥/密码等加解密 // 初始化公私钥, 供登录、保存ssh密钥/密码等加解密
async function initRsa() { async function initRsa() {
@ -11,7 +11,7 @@ async function initRsa() {
key.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' }) key.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' })
let privateKey = key.exportKey('pkcs1-private-pem') let privateKey = key.exportKey('pkcs1-private-pem')
let publicKey = key.exportKey('pkcs8-public-pem') let publicKey = key.exportKey('pkcs8-public-pem')
keyObj.privateKey = await AESEncryptSync(privateKey) // 加密私钥 keyObj.privateKey = await AESEncryptAsync(privateKey) // 加密私钥
keyObj.publicKey = publicKey // 公开公钥 keyObj.publicKey = publicKey // 公开公钥
await writeKey(keyObj) await writeKey(keyObj)
consola.info('Task: 已生成新的非对称加密公私钥') consola.info('Task: 已生成新的非对称加密公私钥')

View File

@ -1,25 +1,26 @@
const schedule = require('node-schedule') const schedule = require('node-schedule')
const { asyncSendNotice } = require('../utils/notify') const { asyncSendNotice } = require('../utils/notify')
const { readHostList } = require('../utils/storage')
const { formatTimestamp } = require('../utils/tools') const { formatTimestamp } = require('../utils/tools')
const { HostListDB } = require('../utils/db-class')
const hostListDB = new HostListDB().getInstance()
const expiredNotifyJob = async () => { const expiredNotifyJob = async () => {
consola.info('=====开始检测服务器到期时间=====', new Date()) consola.info('=====开始检测服务器到期时间=====', new Date())
const hostList = await readHostList() const hostList = await hostListDB.findAsync({})
for (const item of hostList) { for (const item of hostList) {
if(!item.expiredNotify) continue if (!item.expiredNotify) continue
const { host, name, expired, consoleUrl } = item const { host, name, expired, consoleUrl } = item
const restDay = Number(((expired - Date.now()) / (1000 * 60 * 60 * 24)).toFixed(1)) const restDay = Number(((expired - Date.now()) / (1000 * 60 * 60 * 24)).toFixed(1))
console.log(Date.now(), restDay) console.log(Date.now(), restDay)
let title = '服务器到期提醒' let title = '服务器到期提醒'
let content = `别名: ${ name }\nIP: ${ host }\n到期时间:${ formatTimestamp(expired, 'week') }\n控制台: ${ consoleUrl || '未填写' }` let content = `别名: ${ name }\nIP: ${ host }\n到期时间:${ formatTimestamp(expired, 'week') }\n控制台: ${ consoleUrl || '未填写' }`
if(0 <= restDay && restDay <= 1) { if (0 <= restDay && restDay <= 1) {
let temp = '有服务器将在一天后到期,请关注\n' let temp = '有服务器将在一天后到期,请关注\n'
asyncSendNotice('host_expired', title, temp + content) asyncSendNotice('host_expired', title, temp + content)
}else if(3 <= restDay && restDay < 4) { } else if (3 <= restDay && restDay < 4) {
let temp = '有服务器将在三天后到期,请关注\n' let temp = '有服务器将在三天后到期,请关注\n'
asyncSendNotice('host_expired', title, temp + content) asyncSendNotice('host_expired', title, temp + content)
}else if(7 <= restDay && restDay < 8) { } else if (7 <= restDay && restDay < 8) {
let temp = '有服务器将在七天后到期,请关注\n' let temp = '有服务器将在七天后到期,请关注\n'
asyncSendNotice('host_expired', title, temp + content) asyncSendNotice('host_expired', title, temp + content)
} }

View File

@ -1,15 +1,16 @@
const { Server: ServerIO } = require('socket.io') const { Server: ServerIO } = require('socket.io')
const { io: ClientIO } = require('socket.io-client') const { io: ClientIO } = require('socket.io-client')
const { readHostList } = require('../utils/storage')
const { defaultClientPort } = require('../config') const { defaultClientPort } = require('../config')
const { verifyAuthSync } = require('../utils/verify-auth') const { verifyAuthSync } = require('../utils/verify-auth')
const { isAllowedIp } = require('../utils/tools') const { isAllowedIp } = require('../utils/tools')
const { HostListDB } = require('../utils/db-class')
const hostListDB = new HostListDB().getInstance()
let clientSockets = [] let clientSockets = []
let clientsData = {} let clientsData = {}
async function getClientsInfo(clientSockets) { async function getClientsInfo(clientSockets) {
let hostList = await readHostList() let hostList = await hostListDB.findAsync({})
clientSockets.forEach((clientItem) => { clientSockets.forEach((clientItem) => {
// 被删除的客户端断开连接 // 被删除的客户端断开连接
if (!hostList.some(item => item.host === clientItem.host)) clientItem.close && clientItem.close() if (!hostList.some(item => item.host === clientItem.host)) clientItem.close && clientItem.close()

View File

@ -1,11 +1,13 @@
const { Server } = require('socket.io') const { Server } = require('socket.io')
const { Client: SSHClient } = require('ssh2') const { Client: SSHClient } = require('ssh2')
const { asyncSendNotice } = require('../utils/notify') 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 { verifyAuthSync } = require('../utils/verify-auth')
const { shellThrottle } = require('../utils/tools') const { shellThrottle } = require('../utils/tools')
const { AESDecryptSync } = require('../utils/encrypt') const { AESDecryptAsync } = require('../utils/encrypt')
const { isAllowedIp } = require('../utils/tools') const { isAllowedIp } = require('../utils/tools')
const { HostListDB } = require('../utils/db-class')
const hostListDB = new HostListDB().getInstance()
const execStatusEnum = { const execStatusEnum = {
connecting: '连接中', connecting: '连接中',
@ -129,7 +131,7 @@ module.exports = (httpServer) => {
console.log('hostIds:', hostIds) console.log('hostIds:', hostIds)
// console.log('token:', token) // console.log('token:', token)
console.log('command:', command) console.log('command:', command)
const hostList = await readHostList() const hostList = await hostListDB.findAsync({})
const targetHostsInfo = hostList.filter(item => hostIds.some(id => item._id === id)) || {} const targetHostsInfo = hostList.filter(item => hostIds.some(id => item._id === id)) || {}
// console.log('targetHostsInfo:', targetHostsInfo) // console.log('targetHostsInfo:', targetHostsInfo)
if (!targetHostsInfo.length) return socket.emit('create_fail', `未找到【${ hostIds }】服务器信息`) if (!targetHostsInfo.length) return socket.emit('create_fail', `未找到【${ hostIds }】服务器信息`)
@ -145,13 +147,13 @@ module.exports = (httpServer) => {
execResult.push(curRes) execResult.push(curRes)
try { try {
if (authType === 'credential') { if (authType === 'credential') {
let credentialId = await AESDecryptSync(hostInfo['credential']) let credentialId = await AESDecryptAsync(hostInfo['credential'])
const sshRecordList = await readSSHRecord() const sshRecordList = await readSSHRecord()
const sshRecord = sshRecordList.find(item => item._id === credentialId) const sshRecord = sshRecordList.find(item => item._id === credentialId)
authInfo.authType = sshRecord.authType authInfo.authType = sshRecord.authType
authInfo[authInfo.authType] = await AESDecryptSync(sshRecord[authInfo.authType]) authInfo[authInfo.authType] = await AESDecryptAsync(sshRecord[authInfo.authType])
} else { } else {
authInfo[authType] = await AESDecryptSync(hostInfo[authType]) authInfo[authType] = await AESDecryptAsync(hostInfo[authType])
} }
consola.info('准备连接终端执行一次性指令:', host) consola.info('准备连接终端执行一次性指令:', host)
consola.log('连接信息', { username, port, authType }) consola.log('连接信息', { username, port, authType })

View File

@ -5,9 +5,11 @@ const CryptoJS = require('crypto-js')
const { Server } = require('socket.io') const { Server } = require('socket.io')
const { sftpCacheDir } = require('../config') const { sftpCacheDir } = require('../config')
const { verifyAuthSync } = require('../utils/verify-auth') const { verifyAuthSync } = require('../utils/verify-auth')
const { AESDecryptSync } = require('../utils/encrypt') const { AESDecryptAsync } = require('../utils/encrypt')
const { readSSHRecord, readHostList } = require('../utils/storage') const { readSSHRecord } = require('../utils/storage')
const { isAllowedIp } = require('../utils/tools') const { isAllowedIp } = require('../utils/tools')
const { HostListDB } = require('../utils/db-class')
const hostListDB = new HostListDB().getInstance()
// 读取切片 // 读取切片
const pipeStream = (path, writeStream) => { const pipeStream = (path, writeStream) => {
@ -230,7 +232,7 @@ module.exports = (httpServer) => {
return return
} }
const hostList = await readHostList() const hostList = await hostListDB.findAsync({})
const targetHostInfo = hostList.find(item => item.host === ip) || {} const targetHostInfo = hostList.find(item => item.host === ip) || {}
let { authType, host, port, username } = targetHostInfo let { authType, host, port, username } = targetHostInfo
if (!host) return socket.emit('create_fail', `查找【${ ip }】凭证信息失败`) if (!host) return socket.emit('create_fail', `查找【${ ip }】凭证信息失败`)
@ -238,16 +240,16 @@ module.exports = (httpServer) => {
// 解密放到try里面防止报错【commonKey必须配对, 否则需要重新添加服务器密钥】 // 解密放到try里面防止报错【commonKey必须配对, 否则需要重新添加服务器密钥】
if (authType === 'credential') { if (authType === 'credential') {
let credentialId = await AESDecryptSync(targetHostInfo[authType]) let credentialId = await AESDecryptAsync(targetHostInfo[authType])
const sshRecordList = await readSSHRecord() const sshRecordList = await readSSHRecord()
const sshRecord = sshRecordList.find(item => item._id === credentialId) const sshRecord = sshRecordList.find(item => item._id === credentialId)
authInfo.authType = sshRecord.authType authInfo.authType = sshRecord.authType
authInfo[authInfo.authType] = await AESDecryptSync(sshRecord[authInfo.authType]) authInfo[authInfo.authType] = await AESDecryptAsync(sshRecord[authInfo.authType])
} else { } else {
authInfo[authType] = await AESDecryptSync(targetHostInfo[authType]) authInfo[authType] = await AESDecryptAsync(targetHostInfo[authType])
} }
consola.info('准备连接Sftp面板', host) 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 }) consola.log('连接信息', { username, port, authType })
sftpClient sftpClient

View File

@ -1,10 +1,12 @@
const { Server } = require('socket.io') const { Server } = require('socket.io')
const { Client: SSHClient } = require('ssh2') const { Client: SSHClient } = require('ssh2')
const { verifyAuthSync } = require('../utils/verify-auth') const { verifyAuthSync } = require('../utils/verify-auth')
const { AESDecryptSync } = require('../utils/encrypt') const { AESDecryptAsync } = require('../utils/encrypt')
const { readSSHRecord, readHostList } = require('../utils/storage') const { readSSHRecord } = require('../utils/storage')
const { asyncSendNotice } = require('../utils/notify') const { asyncSendNotice } = require('../utils/notify')
const { isAllowedIp, ping } = require('../utils/tools') const { isAllowedIp, ping } = require('../utils/tools')
const { HostListDB } = require('../utils/db-class')
const hostListDB = new HostListDB().getInstance()
function createInteractiveShell(socket, sshClient) { function createInteractiveShell(socket, sshClient) {
return new Promise((resolve) => { return new Promise((resolve) => {
@ -51,7 +53,7 @@ function createInteractiveShell(socket, sshClient) {
async function createTerminal(hostId, socket, sshClient) { async function createTerminal(hostId, socket, sshClient) {
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
const hostList = await readHostList() const hostList = await hostListDB.findAsync({})
const targetHostInfo = hostList.find(item => item._id === hostId) || {} const targetHostInfo = hostList.find(item => item._id === hostId) || {}
let { authType, host, port, username, name } = targetHostInfo let { authType, host, port, username, name } = targetHostInfo
if (!host) return socket.emit('create_fail', `查找hostId【${ hostId }】凭证信息失败`) if (!host) return socket.emit('create_fail', `查找hostId【${ hostId }】凭证信息失败`)
@ -60,16 +62,16 @@ async function createTerminal(hostId, socket, sshClient) {
try { try {
// 解密放到try里面防止报错【commonKey必须配对, 否则需要重新添加服务器密钥】 // 解密放到try里面防止报错【commonKey必须配对, 否则需要重新添加服务器密钥】
if (authType === 'credential') { if (authType === 'credential') {
let credentialId = await AESDecryptSync(targetHostInfo[authType]) let credentialId = await AESDecryptAsync(targetHostInfo[authType])
const sshRecordList = await readSSHRecord() const sshRecordList = await readSSHRecord()
const sshRecord = sshRecordList.find(item => item._id === credentialId) const sshRecord = sshRecordList.find(item => item._id === credentialId)
authInfo.authType = sshRecord.authType authInfo.authType = sshRecord.authType
authInfo[authInfo.authType] = await AESDecryptSync(sshRecord[authInfo.authType]) authInfo[authInfo.authType] = await AESDecryptAsync(sshRecord[authInfo.authType])
} else { } else {
authInfo[authType] = await AESDecryptSync(targetHostInfo[authType]) authInfo[authType] = await AESDecryptAsync(targetHostInfo[authType])
} }
consola.info('准备连接终端:', host) consola.info('准备连接终端:', host)
// targetHostInfo[targetHostInfo.authType] = await AESDecryptSync(targetHostInfo[targetHostInfo.authType]) // targetHostInfo[targetHostInfo.authType] = await AESDecryptAsync(targetHostInfo[targetHostInfo.authType])
consola.log('连接信息', { username, port, authType }) consola.log('连接信息', { username, port, authType })
sshClient sshClient
.on('ready', async() => { .on('ready', async() => {

View File

@ -15,6 +15,7 @@ module.exports.KeyDB = class KeyDB {
constructor() { constructor() {
if (!KeyDB.instance) { if (!KeyDB.instance) {
KeyDB.instance = new Datastore({ filename: keyDBPath, autoload: true }) KeyDB.instance = new Datastore({ filename: keyDBPath, autoload: true })
KeyDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -26,6 +27,7 @@ module.exports.HostListDB = class HostListDB {
constructor() { constructor() {
if (!HostListDB.instance) { if (!HostListDB.instance) {
HostListDB.instance = new Datastore({ filename: hostListDBPath, autoload: true }) HostListDB.instance = new Datastore({ filename: hostListDBPath, autoload: true })
HostListDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -37,6 +39,7 @@ module.exports.SshRecordDB = class SshRecordDB {
constructor() { constructor() {
if (!SshRecordDB.instance) { if (!SshRecordDB.instance) {
SshRecordDB.instance = new Datastore({ filename: credentialsDBPath, autoload: true }) SshRecordDB.instance = new Datastore({ filename: credentialsDBPath, autoload: true })
SshRecordDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -48,6 +51,8 @@ module.exports.NotifyDB = class NotifyDB {
constructor() { constructor() {
if (!NotifyDB.instance) { if (!NotifyDB.instance) {
NotifyDB.instance = new Datastore({ filename: notifyDBPath, autoload: true }) NotifyDB.instance = new Datastore({ filename: notifyDBPath, autoload: true })
NotifyDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -59,6 +64,7 @@ module.exports.NotifyConfigDB = class NotifyConfigDB {
constructor() { constructor() {
if (!NotifyConfigDB.instance) { if (!NotifyConfigDB.instance) {
NotifyConfigDB.instance = new Datastore({ filename: notifyConfigDBPath, autoload: true }) NotifyConfigDB.instance = new Datastore({ filename: notifyConfigDBPath, autoload: true })
NotifyConfigDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -70,6 +76,7 @@ module.exports.GroupDB = class GroupDB {
constructor() { constructor() {
if (!GroupDB.instance) { if (!GroupDB.instance) {
GroupDB.instance = new Datastore({ filename: groupConfDBPath, autoload: true }) GroupDB.instance = new Datastore({ filename: groupConfDBPath, autoload: true })
GroupDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -81,6 +88,7 @@ module.exports.ScriptsDB = class ScriptsDB {
constructor() { constructor() {
if (!ScriptsDB.instance) { if (!ScriptsDB.instance) {
ScriptsDB.instance = new Datastore({ filename: scriptsDBPath, autoload: true }) ScriptsDB.instance = new Datastore({ filename: scriptsDBPath, autoload: true })
ScriptsDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -92,6 +100,7 @@ module.exports.OnekeyDB = class OnekeyDB {
constructor() { constructor() {
if (!OnekeyDB.instance) { if (!OnekeyDB.instance) {
OnekeyDB.instance = new Datastore({ filename: onekeyDBPath, autoload: true }) OnekeyDB.instance = new Datastore({ filename: onekeyDBPath, autoload: true })
OnekeyDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {
@ -103,6 +112,7 @@ module.exports.LogDB = class LogDB {
constructor() { constructor() {
if (!LogDB.instance) { if (!LogDB.instance) {
LogDB.instance = new Datastore({ filename: logDBPath, autoload: true }) LogDB.instance = new Datastore({ filename: logDBPath, autoload: true })
LogDB.instance.setAutocompactionInterval(5000)
} }
} }
getInstance() { getInstance() {

View File

@ -4,10 +4,10 @@ const NodeRSA = require('node-rsa')
const { readKey } = require('./storage.js') const { readKey } = require('./storage.js')
// rsa非对称 私钥解密 // rsa非对称 私钥解密
const RSADecryptSync = async (ciphertext) => { const RSADecryptAsync = async (ciphertext) => {
if (!ciphertext) return if (!ciphertext) return
let { privateKey } = await readKey() let { privateKey } = await readKey()
privateKey = await AESDecryptSync(privateKey) // 先解密私钥 privateKey = await AESDecryptAsync(privateKey) // 先解密私钥
const rsakey = new NodeRSA(privateKey) const rsakey = new NodeRSA(privateKey)
rsakey.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' }) // Must Set It When Frontend Use jsencrypt rsakey.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' }) // Must Set It When Frontend Use jsencrypt
const plaintext = rsakey.decrypt(ciphertext, 'utf8') const plaintext = rsakey.decrypt(ciphertext, 'utf8')
@ -15,16 +15,16 @@ const RSADecryptSync = async (ciphertext) => {
} }
// aes对称 加密(default commonKey) // aes对称 加密(default commonKey)
const AESEncryptSync = async (text, key) => { const AESEncryptAsync = async (text, key) => {
if(!text) return if (!text) return
let { commonKey } = await readKey() let { commonKey } = await readKey()
let ciphertext = CryptoJS.AES.encrypt(text, key || commonKey).toString() let ciphertext = CryptoJS.AES.encrypt(text, key || commonKey).toString()
return ciphertext return ciphertext
} }
// aes对称 解密(default commonKey) // aes对称 解密(default commonKey)
const AESDecryptSync = async (ciphertext, key) => { const AESDecryptAsync = async (ciphertext, key) => {
if(!ciphertext) return if (!ciphertext) return
let { commonKey } = await readKey() let { commonKey } = await readKey()
let bytes = CryptoJS.AES.decrypt(ciphertext, key || commonKey) let bytes = CryptoJS.AES.decrypt(ciphertext, key || commonKey)
let originalText = bytes.toString(CryptoJS.enc.Utf8) let originalText = bytes.toString(CryptoJS.enc.Utf8)
@ -37,8 +37,8 @@ const SHA1Encrypt = (clearText) => {
} }
module.exports = { module.exports = {
RSADecryptSync, RSADecryptAsync,
AESEncryptSync, AESEncryptAsync,
AESDecryptSync, AESDecryptAsync,
SHA1Encrypt SHA1Encrypt
} }

View File

@ -64,43 +64,43 @@ const writeSSHRecord = async (record = []) => {
}) })
} }
const readHostList = async () => { // const readHostList = async () => {
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
const hostListDB = new HostListDB().getInstance() // const hostListDB = new HostListDB().getInstance()
hostListDB.find({}, (err, docs) => { // hostListDB.find({}, (err, docs) => {
if (err) { // if (err) {
consola.error('读取host-list-db错误:', err) // consola.error('读取host-list-db错误:', err)
reject(err) // reject(err)
} else { // } else {
resolve(docs) // resolve(docs)
} // }
}) // })
}) // })
} // }
const writeHostList = async (record = []) => { // const writeHostList = async (record = []) => {
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
const hostListDB = new HostListDB().getInstance() // const hostListDB = new HostListDB().getInstance()
hostListDB.remove({}, { multi: true }, (err) => { // hostListDB.remove({}, { multi: true }, (err) => {
if (err) { // if (err) {
consola.error('清空HostList出错:', err) // consola.error('清空HostList出错:', err)
reject(err) // reject(err)
} else { // } else {
hostListDB.compactDatafile() // hostListDB.compactDatafile()
// 插入新的数据列表 // // 插入新的数据列表
hostListDB.insert(record, (err, newDocs) => { // hostListDB.insert(record, (err, newDocs) => {
if (err) { // if (err) {
consola.error('写入新的HostList出错:', err) // consola.error('写入新的HostList出错:', err)
reject(err) // reject(err)
} else { // } else {
hostListDB.compactDatafile() // hostListDB.compactDatafile()
resolve(newDocs) // resolve(newDocs)
} // }
}) // })
} // }
}) // })
}) // })
} // }
const readNotifyConfig = async () => { const readNotifyConfig = async () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -329,7 +329,7 @@ const writeLog = async (records = {}) => {
module.exports = { module.exports = {
readSSHRecord, writeSSHRecord, readSSHRecord, writeSSHRecord,
readHostList, writeHostList, // readHostList, writeHostList,
readKey, writeKey, readKey, writeKey,
readNotifyList, writeNotifyList, readNotifyList, writeNotifyList,
readNotifyConfig, writeNotifyConfig, getNotifySwByType, readNotifyConfig, writeNotifyConfig, getNotifySwByType,

View File

@ -1,5 +1,5 @@
const { AESDecryptSync } = require('./encrypt') const { AESDecryptAsync } = require('./encrypt')
const { readKey } = require('./storage') const { readKey } = require('./storage')
const jwt = require('jsonwebtoken') const jwt = require('jsonwebtoken')
@ -13,7 +13,7 @@ const enumLoginCode = {
const verifyAuthSync = async (token, clientIp) => { const verifyAuthSync = async (token, clientIp) => {
consola.info('verifyAuthSync IP', clientIp) consola.info('verifyAuthSync IP', clientIp)
try { try {
token = await AESDecryptSync(token) // 先aes解密 token = await AESDecryptAsync(token) // 先aes解密
const { commonKey } = await readKey() const { commonKey } = await readKey()
const { exp } = jwt.verify(token, commonKey) const { exp } = jwt.verify(token, commonKey)
if (Date.now() > (exp * 1000)) return { code: -1, msg: 'token expires' } // 过期 if (Date.now() > (exp * 1000)) return { code: -1, msg: 'token expires' } // 过期

View File

@ -356,7 +356,7 @@ const setDefaultData = () => {
const setBatchDefaultData = () => { const setBatchDefaultData = () => {
if (!isBatchModify.value) return 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 () => { const handleOpen = async () => {
setDefaultData() setDefaultData()
@ -416,7 +416,7 @@ const handleSave = () => {
// eslint-disable-next-line // eslint-disable-next-line
let updateFileData = Object.fromEntries(Object.entries(formData).filter(([key, value]) => Boolean(value))) // let updateFileData = Object.fromEntries(Object.entries(formData).filter(([key, value]) => Boolean(value))) //
if (Object.keys(updateFileData).length === 0) return $message.warning('没有任何修改') if (Object.keys(updateFileData).length === 0) return $message.warning('没有任何修改')
// console.log(updateFileData) console.log(updateFileData)
let newHosts = batchHosts.value let newHosts = batchHosts.value
.map(item => ({ ...item, ...updateFileData })) .map(item => ({ ...item, ...updateFileData }))
.map(item => { .map(item => {

View File

@ -168,7 +168,6 @@ let handleBatchOnekey = async () => {
let handleBatchExport = () => { let handleBatchExport = () => {
collectSelectHost() collectSelectHost()
if (!selectHosts.value.length) return $message.warning('请选择要批量操作的实例') if (!selectHosts.value.length) return $message.warning('请选择要批量操作的实例')
console.log(selectHosts.value)
let exportData = JSON.parse(JSON.stringify(selectHosts.value)) let exportData = JSON.parse(JSON.stringify(selectHosts.value))
exportData = exportData.map(item => { exportData = exportData.map(item => {
delete item.monitorData delete item.monitorData