diff --git a/.gitignore b/.gitignore index cd65b26..48350ce 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ plan.md .env.local .env-encrypt-key *clear.js -local-script \ No newline at end of file +local-script +版本发布.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f6549a8..f761e8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [3.0.1](https://github.com/chaos-zhu/easynode/releases) (2024-11-18) + +* 修复同IP实例SFTP连接到其他的实例的bug +* 修复一些UI问题 + + ## [3.0.0](https://github.com/chaos-zhu/easynode/releases) (2024-11-09) * 新增跳板机功能,支持选择多台机器跳转 diff --git a/README.md b/README.md index cd57ea8..07f1030 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,9 @@ _✨ 一个多功能Linux服务器WEB终端面板(webSSH&webSFTP) ✨_ docker run -d -p 8082:8082 --restart=always -v /root/easynode/db:/easynode/app/db chaoszhu/easynode ``` 环境变量: -- `PLUS_KEY`: 激活PLUS功能的授权码 - `DEBUG`: 启动debug日志 0:关闭 1:开启, 默认关闭 - `ALLOWED_IPS`: 可以访问服务的IP白名单, 多个使用逗号分隔, 支持填写部分ip前缀, 例如: `-e ALLOWED_IPS=127.0.0.1,196.168` - ## 监控服务安装 - 监控服务用于实时向服务端&web端推送**系统、公网IP、CPU、内存、硬盘、网卡**等基础信息 diff --git a/server/app/controller/user.js b/server/app/controller/user.js index 6a673e4..bda28bd 100644 --- a/server/app/controller/user.js +++ b/server/app/controller/user.js @@ -2,6 +2,8 @@ const jwt = require('jsonwebtoken') const axios = require('axios') const speakeasy = require('speakeasy') const QRCode = require('qrcode') +const version = require('../../package.json').version +const { plusServer1, plusServer2 } = require('../utils/plus-server') const { sendNoticeAsync } = require('../utils/notify') const { RSADecryptAsync, AESEncryptAsync, SHA1Encrypt } = require('../utils/encrypt') const { getNetIPInfo } = require('../utils/tools') @@ -86,7 +88,7 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => { let token = jwt.sign({ date: Date.now() }, commonKey, { expiresIn: jwtExpires }) // 生成token token = await AESEncryptAsync(token) // 对称加密token后再传输给前端 - // 记录客户端登录IP(用于判断是否异地且只保留最近10条) + // 记录客户端登录IP(用于判断是否异地且只保留最近10��) const clientIPInfo = await getNetIPInfo(clientIp) const { ip, country, city } = clientIPInfo || {} consola.info('登录成功:', new Date(), { ip, country, city }) @@ -172,6 +174,27 @@ const getPlusInfo = async ({ res }) => { res.success({ data, msg: 'success' }) } +const getPlusDiscount = async ({ res } = {}) => { + const servers = [plusServer1, plusServer2] + for (const server of servers) { + try { + const url = `${ server }/api/announcement/public?version=${ version }` + const response = await fetch(url) + if (!response.ok) { + throw new Error(`HTTP error! status: ${ response.status }`) + } + const data = await response.json() + return res.success({ data, msg: 'success' }) + } catch (error) { + if (server === servers[servers.length - 1]) { + consola.error('All servers failed:', error.message) + return res.success({ discount: false }) + } + continue + } + } +} + module.exports = { login, getpublicKey, @@ -181,5 +204,6 @@ module.exports = { getMFA2Code, enableMFA2, disableMFA2, - getPlusInfo + getPlusInfo, + getPlusDiscount } diff --git a/server/app/router/routes.js b/server/app/router/routes.js index 042f34d..e55a906 100644 --- a/server/app/router/routes.js +++ b/server/app/router/routes.js @@ -1,6 +1,6 @@ const { getSSHList, addSSH, updateSSH, removeSSH, getCommand, decryptPrivateKey } = require('../controller/ssh') const { getHostList, addHost, updateHost, batchUpdateHost, removeHost, importHost } = require('../controller/host') -const { login, getpublicKey, updatePwd, getEasynodeVersion, getMFA2Status, getMFA2Code, enableMFA2, disableMFA2, getPlusInfo } = require('../controller/user') +const { login, getpublicKey, updatePwd, getEasynodeVersion, getMFA2Status, getMFA2Code, enableMFA2, disableMFA2, getPlusInfo, getPlusDiscount } = 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, batchRemoveScript, importScript } = require('../controller/scripts') @@ -116,6 +116,11 @@ const user = [ method: 'get', path: '/plus-info', controller: getPlusInfo + }, + { + method: 'get', + path: '/plus-discount', + controller: getPlusDiscount } ] const notify = [ diff --git a/server/app/socket/sftp.js b/server/app/socket/sftp.js index 444c09f..2c01fc8 100644 --- a/server/app/socket/sftp.js +++ b/server/app/socket/sftp.js @@ -224,18 +224,18 @@ module.exports = (httpServer) => { let sftpClient = new SFTPClient() consola.success('terminal websocket 已连接') - socket.on('create', async ({ host: ip, token }) => { + socket.on('create', async ({ hostId, token }) => { const { code } = await verifyAuthSync(token, requestIP) + consola.log('code:', code) if (code !== 1) { socket.emit('token_verify_fail') socket.disconnect() return } - - const hostList = await hostListDB.findAsync({}) - const targetHostInfo = hostList.find(item => item.host === ip) || {} + const targetHostInfo = await hostListDB.findOneAsync({ _id: hostId }) + if (!targetHostInfo) throw new Error(`Host with ID ${ hostId } not found`) let { authType, host, port, username } = targetHostInfo - if (!host) return socket.emit('create_fail', `查找【${ ip }】凭证信息失败`) + if (!host) return socket.emit('create_fail', `查找id【${ hostId }】凭证信息失败`) let authInfo = { host, port, username } // 解密放到try里面,防止报错【commonKey必须配对, 否则需要重新添加服务器密钥】 diff --git a/server/app/utils/get-plus.js b/server/app/utils/get-plus.js index 70d6093..5184915 100644 --- a/server/app/utils/get-plus.js +++ b/server/app/utils/get-plus.js @@ -2,6 +2,7 @@ const schedule = require('node-schedule') const { getLocalNetIP } = require('./tools') const { AESEncryptAsync } = require('./encrypt') const version = require('../../package.json').version +const { plusServer1, plusServer2 } = require('./plus-server') async function getLicenseInfo() { let key = process.env.PLUS_KEY @@ -28,7 +29,7 @@ async function getLicenseInfo() { let headers = { 'Content-Type': 'application/json' } let timeout = 10000 try { - response = await fetch('https://en1.221022.xyz/api/licenses/activate', { + response = await fetch(plusServer1 + '/api/licenses/activate', { method, headers, body, @@ -41,7 +42,7 @@ async function getLicenseInfo() { } catch (error) { consola.log('retry to activate plus by backup server') - response = await fetch('https://en2.221022.xyz/api/licenses/activate', { + response = await fetch(plusServer2 + '/api/licenses/activate', { method, headers, body, diff --git a/server/app/utils/plus-server.js b/server/app/utils/plus-server.js new file mode 100644 index 0000000..7f28516 --- /dev/null +++ b/server/app/utils/plus-server.js @@ -0,0 +1,4 @@ +module.exports = { + plusServer1: 'https://en1.221022.xyz', + plusServer2: 'https://en2.221022.xyz' +} diff --git a/server/package.json b/server/package.json index 02a4726..3181cd8 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "3.0.0", + "version": "3.0.1", "description": "easynode-server", "bin": "./bin/www", "scripts": { diff --git a/web/package.json b/web/package.json index fa33191..07568a8 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "web", - "version": "3.0.0", + "version": "3.0.1", "description": "easynode-web", "private": true, "scripts": { diff --git a/web/src/api/index.js b/web/src/api/index.js index 5f4c8c3..2c15ee9 100644 --- a/web/src/api/index.js +++ b/web/src/api/index.js @@ -22,6 +22,9 @@ export default { getPlusInfo() { return axios({ url: '/plus-info', method: 'get' }) }, + getPlusDiscount() { + return axios({ url: '/plus-discount', method: 'get' }) + }, getCommand(hostId) { return axios({ url: '/command', method: 'get', params: { hostId } }) }, diff --git a/web/src/assets/discount.png b/web/src/assets/discount.png new file mode 100644 index 0000000..5e24efe Binary files /dev/null and b/web/src/assets/discount.png differ diff --git a/web/src/components/top-bar.vue b/web/src/components/top-bar.vue index e721eb6..9635c88 100644 --- a/web/src/components/top-bar.vue +++ b/web/src/components/top-bar.vue @@ -20,7 +20,7 @@ link @click="visible = true" > - 关于 {{ isNew ? `(新版本可用)` : '' }} + 版本更新 {{ isNew ? `(新版本可用)` : '' }} @@ -40,12 +40,20 @@ @@ -122,15 +136,16 @@
-

EasyNode

+

当前版本: {{ currentVersion }} (最新)

-

Error:版本更新检测失败(版本检测API需要外网环境)

+

Error:版本更新检测失败(版本检测API需要外网环境),请手动访问GitHub查看

新版本可用: {{ latestVersion }} -> https://github.com/chaos-zhu/easynode/releases

- 更新日志:https://github.com/chaos-zhu/easynode/blob/main/CHANGELOG.md

- tg更新通知:https://t.me/easynode_notify + TG更新通知频道:https://t.me/easynode_notify

PLUS说明:
@@ -156,11 +171,12 @@        为了项目的可持续发展,从3.0.0版本开始推出了PLUS版本,具体特性鼠标悬浮右上角PLUS图标查看,后续特性功能开发也会优先在PLUS版本中实现,但即使不升级到PLUS,也不会影响到EasyNode的基础功能使用【注意: 暂不支持纯内网用户激活PLUS功能】。
        - 为了感谢前期赞赏过的用户, 在PLUS功能正式发布前,所有进行过赞赏的用户,无论金额大小,均可联系作者TG: @chaoszhu 凭打赏记录获取永久PLUS授权码。 + 为了感谢前期赞赏过的用户, 在PLUS功能正式发布前,所有进行过赞赏的用户,无论金额大小,均可联系作者TG: @chaoszhu 凭打赏记录免费获取永久PLUS授权码。

- @@ -181,18 +197,20 @@ \ No newline at end of file diff --git a/web/src/views/terminal/components/sftp.vue b/web/src/views/terminal/components/sftp.vue index 1e880a5..6e82a2d 100644 --- a/web/src/views/terminal/components/sftp.vue +++ b/web/src/views/terminal/components/sftp.vue @@ -168,7 +168,7 @@ import unknowIcon from '@/assets/image/system/unknow.png' const { io } = socketIo const props = defineProps({ - host: { + hostId: { required: true, type: String } @@ -270,7 +270,7 @@ const connectSftp = () => { socket.value.on('connect', () => { console.log('/sftp socket已连接:', socket.value.id) listenSftp() - socket.value.emit('create', { host: props.host, token: token.value }) + socket.value.emit('create', { hostId: props.hostId, token: token.value }) socket.value.on('root_ls', (tree) => { let temp = sortDirTree(tree).filter((item) => isDir(item.type)) temp.unshift({ name: '/', type: 'd' }) diff --git a/web/src/views/terminal/components/terminal.vue b/web/src/views/terminal/components/terminal.vue index aad38bd..53e0715 100644 --- a/web/src/views/terminal/components/terminal.vue +++ b/web/src/views/terminal/components/terminal.vue @@ -167,7 +167,7 @@