From fe5e75878aee68354b38f25a63d74d265c0d8cab Mon Sep 17 00:00:00 2001 From: chaos-zhu Date: Sun, 20 Oct 2024 22:31:27 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E7=99=BB=E5=BD=95=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=9C=AC=E5=9C=B0=E5=8C=96=E5=82=A8=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + server/app/config/index.js | 1 + server/app/controller/log.js | 16 +++++++++ server/app/controller/user.js | 10 ++---- server/app/init.js | 6 ++-- server/app/router/routes.js | 18 ++++++---- server/app/utils/db-class.js | 14 +++++++- server/app/utils/storage.js | 36 +++++++++++++++++-- web/src/api/index.js | 2 +- .../views/server/components/host-table.vue | 2 +- web/src/views/setting/components/record.vue | 22 ++++++++++-- 11 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 server/app/controller/log.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 860a48d..60784d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * 兼容移动端UI * 新增移动端虚拟功能按键映射 * 调整终端功能菜单 +* 登录日志本地化储存 * 修复终端选中文本无法复制的bug * 修复无法展示服务端ping客户端延迟ms的bug * 修复暗黑模式下的一些样式问题 diff --git a/server/app/config/index.js b/server/app/config/index.js index 915859a..173d291 100644 --- a/server/app/config/index.js +++ b/server/app/config/index.js @@ -16,6 +16,7 @@ module.exports = { notifyDBPath: path.join(process.cwd(),'app/db/notify.db'), notifyConfigDBPath: path.join(process.cwd(),'app/db/notify-config.db'), onekeyDBPath: path.join(process.cwd(),'app/db/onekey.db'), + logDBPath: path.join(process.cwd(),'app/db/log.db'), apiPrefix: '/api/v1', logConfig: { outDir: path.join(process.cwd(),'./app/logs'), diff --git a/server/app/controller/log.js b/server/app/controller/log.js new file mode 100644 index 0000000..dcf06f9 --- /dev/null +++ b/server/app/controller/log.js @@ -0,0 +1,16 @@ +const { readLog } = require('../utils/storage') + +let whiteList = process.env.ALLOWED_IPS ? process.env.ALLOWED_IPS.split(',') : [] + +async function getLog({ res }) { + let list = await readLog() + list = list.map(item => { + return { ...item, id: item._id } + }) + list?.sort((a, b) => Number(b.date) - Number(a.date)) + res.success({ data: { list, whiteList } }) +} + +module.exports = { + getLog +} diff --git a/server/app/controller/user.js b/server/app/controller/user.js index 9cf0fcb..5525169 100644 --- a/server/app/controller/user.js +++ b/server/app/controller/user.js @@ -1,7 +1,7 @@ const jwt = require('jsonwebtoken') const axios = require('axios') const { asyncSendNotice } = require('../utils/notify') -const { readKey, writeKey } = require('../utils/storage') +const { readKey, writeKey, writeLog } = require('../utils/storage') const { RSADecryptSync, AESEncryptSync, SHA1Encrypt } = require('../utils/encrypt') const { getNetIPInfo } = require('../utils/tools') @@ -86,8 +86,7 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => { // 邮件登录通知 asyncSendNotice('login', '登录提醒', `地点:${ country + city }\nIP: ${ ip }`) - global.loginRecord.unshift(clientIPInfo) - if (global.loginRecord.length > 10) global.loginRecord = global.loginRecord.slice(0, 10) + await writeLog({ ip, country, city, date: Date.now(), type: 'login' }) return token } @@ -109,10 +108,6 @@ const updatePwd = async ({ res, request }) => { res.success({ data: true, msg: 'success' }) } -const getLoginRecord = async ({ res }) => { - res.success({ data: global.loginRecord, msg: 'success' }) -} - const getEasynodeVersion = async ({ res }) => { try { // const { data } = await axios.get('https://api.github.com/repos/chaos-zhu/easynode/releases/latest') @@ -129,6 +124,5 @@ module.exports = { login, getpublicKey, updatePwd, - getLoginRecord, getEasynodeVersion } diff --git a/server/app/init.js b/server/app/init.js index 4d315e6..3c34b7a 100644 --- a/server/app/init.js +++ b/server/app/init.js @@ -6,7 +6,7 @@ const { AESEncryptSync } = require('./utils/encrypt') // 初始化公私钥, 供登录、保存ssh密钥/密码等加解密 async function initRsa() { let keyObj = await readKey() - if(keyObj.privateKey && keyObj.publicKey) return consola.info('公私钥已存在[重新生成会导致已保存的ssh密钥信息失效]') + if (keyObj.privateKey && keyObj.publicKey) return consola.info('公私钥已存在[重新生成会导致已保存的ssh密钥信息失效]') let key = new NodeRSA({ b: 1024 }) key.setOptions({ encryptionScheme: 'pkcs1', environment: 'browser' }) let privateKey = key.exportKey('pkcs1-private-pem') @@ -20,7 +20,7 @@ async function initRsa() { // 随机的commonKey secret async function randomJWTSecret() { let keyObj = await readKey() - if(keyObj?.commonKey) return consola.info('commonKey密钥已存在') + if (keyObj?.commonKey) return consola.info('commonKey密钥已存在') keyObj.commonKey = randomStr(16) await writeKey(keyObj) @@ -30,6 +30,4 @@ async function randomJWTSecret() { module.exports = async () => { await randomJWTSecret() // 全局密钥 await initRsa() // 全局公钥密钥 - // 用于记录客户端登录IP的列表 - global.loginRecord = [] } diff --git a/server/app/router/routes.js b/server/app/router/routes.js index a41e9de..6fd6711 100644 --- a/server/app/router/routes.js +++ b/server/app/router/routes.js @@ -1,10 +1,11 @@ const { getSSHList, addSSH, updateSSH, removeSSH, getCommand } = require('../controller/ssh') const { getHostList, addHost, updateHost, removeHost, importHost } = require('../controller/host') -const { login, getpublicKey, updatePwd, getLoginRecord, getEasynodeVersion } = require('../controller/user') +const { login, getpublicKey, updatePwd, getEasynodeVersion } = require('../controller/user') const { getNotifyConfig, updateNotifyConfig, getNotifyList, updateNotifyList } = require('../controller/notify') const { getGroupList, addGroupList, updateGroupList, removeGroup } = require('../controller/group') const { getScriptList, getLocalScriptList, addScript, updateScriptList, removeScript } = require('../controller/scripts') const { getOnekeyRecord, removeOnekeyRecord } = require('../controller/onekey') +const { getLog } = require('../controller/log') const ssh = [ { @@ -76,11 +77,6 @@ const user = [ path: '/pwd', controller: updatePwd }, - { - method: 'get', - path: '/get-login-record', - controller: getLoginRecord - }, { method: 'get', path: '/version', @@ -173,4 +169,12 @@ const onekey = [ controller: removeOnekeyRecord } ] -module.exports = [].concat(ssh, host, user, notify, group, scripts, onekey) + +const log = [ + { + method: 'get', + path: '/log', + controller: getLog + } +] +module.exports = [].concat(ssh, host, user, notify, group, scripts, onekey, log) diff --git a/server/app/utils/db-class.js b/server/app/utils/db-class.js index 105b401..07d096c 100644 --- a/server/app/utils/db-class.js +++ b/server/app/utils/db-class.js @@ -7,7 +7,8 @@ const { notifyConfigDBPath, groupConfDBPath, scriptsDBPath, - onekeyDBPath + onekeyDBPath, + logDBPath } = require('../config') module.exports.KeyDB = class KeyDB { @@ -97,3 +98,14 @@ module.exports.OnekeyDB = class OnekeyDB { return OnekeyDB.instance } } + +module.exports.LogDB = class LogDB { + constructor() { + if (!LogDB.instance) { + LogDB.instance = new Datastore({ filename: logDBPath, autoload: true }) + } + } + getInstance() { + return LogDB.instance + } +} \ No newline at end of file diff --git a/server/app/utils/storage.js b/server/app/utils/storage.js index b2d84e5..81a19c2 100644 --- a/server/app/utils/storage.js +++ b/server/app/utils/storage.js @@ -1,4 +1,4 @@ -const { KeyDB, HostListDB, SshRecordDB, NotifyDB, NotifyConfigDB, ScriptsDB, GroupDB, OnekeyDB } = require('./db-class') +const { KeyDB, HostListDB, SshRecordDB, NotifyDB, NotifyConfigDB, ScriptsDB, GroupDB, OnekeyDB, LogDB } = require('./db-class') const readKey = async () => { return new Promise((resolve, reject) => { @@ -281,6 +281,7 @@ const writeOneKeyRecord = async (records =[]) => { }) }) } + const deleteOneKeyRecord = async (ids =[]) => { return new Promise((resolve, reject) => { const onekeyDB = new OnekeyDB().getInstance() @@ -296,6 +297,36 @@ const deleteOneKeyRecord = async (ids =[]) => { }) } +const readLog = async () => { + return new Promise((resolve, reject) => { + const logDB = new LogDB().getInstance() + logDB.find({}, (err, docs) => { + if (err) { + consola.error('读取log DB错误: ', err) + reject(err) + } else { + logDB.compactDatafile() + resolve(docs) + } + }) + }) +} + +const writeLog = async (records = {}) => { + return new Promise((resolve, reject) => { + const logDB = new LogDB().getInstance() + logDB.insert(records, (err, newDocs) => { + if (err) { + consola.error('写入新的onekey记录出错:', err) + reject(err) + } else { + logDB.compactDatafile() + resolve(newDocs) + } + }) + }) +} + module.exports = { readSSHRecord, writeSSHRecord, readHostList, writeHostList, @@ -304,5 +335,6 @@ module.exports = { readNotifyConfig, writeNotifyConfig, getNotifySwByType, readGroupList, writeGroupList, readScriptList, writeScriptList, - readOneKeyRecord, writeOneKeyRecord, deleteOneKeyRecord + readOneKeyRecord, writeOneKeyRecord, deleteOneKeyRecord, + readLog, writeLog } \ No newline at end of file diff --git a/web/src/api/index.js b/web/src/api/index.js index 9778d27..c318456 100644 --- a/web/src/api/index.js +++ b/web/src/api/index.js @@ -47,7 +47,7 @@ export default { return axios({ url: '/login', method: 'post', data }) }, getLoginRecord() { - return axios({ url: '/get-login-record', method: 'get' }) + return axios({ url: '/log', method: 'get' }) }, updatePwd(data) { return axios({ url: '/pwd', method: 'put', data }) diff --git a/web/src/views/server/components/host-table.vue b/web/src/views/server/components/host-table.vue index 8740eb1..cdb9693 100644 --- a/web/src/views/server/components/host-table.vue +++ b/web/src/views/server/components/host-table.vue @@ -88,7 +88,7 @@