diff --git a/CHANGELOG.md b/CHANGELOG.md index 1916887..efe3c3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,23 @@ ## TODO * 批量下发后台定时任务 -* AI对话收藏 * 移动端Ctrl、Tab的按键面板 * 数据导出(2fa强制) * 进程管理 * 当前会话连接|w * 日志打印优化 -* rdp * 探针接入 * 通知模块重构,支持webhook * 支持csv模板特定格式导入 +## [3.5.0](https://github.com/chaos-zhu/easynode/releases) (2025-10-18) +* 🖥️ 支持 RDP 远程 Windows 桌面连接(支持移动端交互 & 剪贴板互动) +* 🧩 脚本库增强:新增脚本执行模式 — 多行脚本@zhanghao-njmu +* 💻 终端增强:输出高亮自定义、配置持久化管理、样式优化、全屏下 bug 修复@zhanghao-njmu +* 📂 SFTP增强:宽度拖拽、个性化显示文件信息列(大小、修改时间、权限、拥有者)@zhanghao-njmu +* ⚙️ 其他优化与bug修复 +* ❤️ 特别感谢 @zhanghao-njmu 的功能PR + ## [3.4.2](https://github.com/chaos-zhu/easynode/releases) (2025-08-24) * 🔒鉴权增强 * SFTP连接优化、支持搜索文件(夹)、新建文件(夹)功能名称缓存建议 diff --git a/README.md b/README.md index 986140f..c38ed77 100644 --- a/README.md +++ b/README.md @@ -77,11 +77,29 @@ docker-compose up -d ### docker镜像 +**注意!!!** + +**v3.5.0版本新增RDP连接windows服务器功能,此功能依赖单独的guacd服务** + +- 如果你不知道guacd服务,请使用上面的 docker-compose.yml 进行部署 + +- 如果你不想使用 docker-compose.yml 进行部署,请配置环境变量 `GUACD_HOST` 和 `GUACD_PORT` + ```shell -docker run -d -p 8082:8082 --restart=always -v /root/easynode/db:/easynode/app/db chaoszhu/easynode +# GUACD_HOST: 自建 guacd 服务 IP【此处127.0.0.1仅为示例,需自建服务】 +# GUACD_PORT: 自建 guacd 服务端口 +docker run -d \ + -p 8082:8082 \ + --restart=always \ + -v /root/easynode/db:/easynode/app/db \ + -e GUACD_HOST=127.0.0.1 \ + -e GUACD_PORT=4822 \ + chaoszhu/easynode ``` 环境变量: +- `GUACD_HOST`: 自建guacd服务IP +- `GUACD_PORT`: 自建guacd服务PORT - `DEBUG`: 启动debug日志 0:关闭 1:开启, 默认关闭 注意: **docker默认不启用ipv6,请自行配置或者使用支持ipv6的跳板机中转.** @@ -110,11 +128,7 @@ webssh与监控服务都将以`该服务器作为中转`。中国大陆用户建 CDN acceleration and security protection for this project are sponsored by Tencent EdgeOne: EdgeOne offers a long-term free plan with unlimited traffic and requests, covering Mainland China nodes, with no overage charges. Interested friends can click the link below to claim it. [Best Asian CDN, Edge, and Secure Solutions - Tencent EdgeOne](https://edgeone.ai/zh?from=github) [![EdgeOne Logo](https://edgeone.ai/media/34fe3a45-492d-4ea4-ae5d-ea1087ca7b4b.png)](https://edgeone.ai/?from=github) -[![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source") - - -[![image](https://img.shields.io/badge/NodeSupport-YXVM-red)](https://yxvm.com/) - -![Image](https://github.com/user-attachments/assets/a50409e4-9394-4a59-a125-18ffe64c5fb0) + +![Image](https://github.com/user-attachments/assets/a50409e4-9394-4a59-a125-18ffe64c5fb0) [![image](https://img.shields.io/badge/NodeSupport-YXVM-red)](https://yxvm.com/) diff --git a/docker-compose.yml b/docker-compose.yml index 0f881a2..d94f3dd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,7 @@ version: '3' services: easynode: + container_name: easynode image: chaoszhu/easynode restart: always ports: @@ -11,14 +12,42 @@ services: environment: - TZ=Asia/Shanghai - DEBUG=0 + - GUACD_HOST=easynode-guacd + - GUACD_PORT=4822 + depends_on: + easynode-guacd: + condition: service_healthy + networks: + - easynode-network labels: - "com.centurylinklabs.watchtower.enable=true" + easynode-guacd: + container_name: easynode-guacd + image: docker.1ms.run/guacamole/guacd + restart: always + expose: + - "4822" + healthcheck: + test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 4822"] + interval: 5s + timeout: 2s + retries: 10 + networks: + - easynode-network + labels: + - "com.centurylinklabs.watchtower.enable=false" + watchtower: + container_name: easynode-watchtower image: containrrr/watchtower + restart: always volumes: - /var/run/docker.sock:/var/run/docker.sock command: --schedule "0 8 * * *" --label-enable - restart: always environment: - TZ=Asia/Shanghai + +networks: + easynode-network: + driver: bridge diff --git a/server/app/controller/host.js b/server/app/controller/host.js index 91a5a8e..a66bd2c 100644 --- a/server/app/controller/host.js +++ b/server/app/controller/host.js @@ -52,7 +52,7 @@ async function updateHost({ res, request }) { delete updateFiled.privateKey delete updateFiled.credential } - console.log('updateFiled: ', updateFiled) + // console.log('updateFiled: ', updateFiled) await hostListDB.updateAsync({ _id: id }, { $set: { ...updateFiled } }) res.success({ msg: '修改成功' }) } diff --git a/server/app/controller/plus.js b/server/app/controller/plus.js index 6db878f..ff7266f 100644 --- a/server/app/controller/plus.js +++ b/server/app/controller/plus.js @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/server/app/controller/ssh.js b/server/app/controller/ssh.js index e29d95f..d9d805e 100644 --- a/server/app/controller/ssh.js +++ b/server/app/controller/ssh.js @@ -101,11 +101,21 @@ const decryptPrivateKey = async ({ res, request }) => { } } +const getRdpToken = async ({ res, request }) => { + let { genRdpToken } = (await decryptAndExecuteAsync(path.join(__dirname, 'plus.js'))) || {} + if (genRdpToken) { + await genRdpToken({ res, request }) + } else { + return res.fail({ data: false, msg: 'Plus专属功能,无法获取RDP Token!' }) + } +} + module.exports = { getSSHList, addSSH, updateSSH, removeSSH, getCommand, - decryptPrivateKey + decryptPrivateKey, + getRdpToken } diff --git a/server/app/middlewares/auth.js b/server/app/middlewares/auth.js index 6dd94f8..e15962a 100644 --- a/server/app/middlewares/auth.js +++ b/server/app/middlewares/auth.js @@ -21,8 +21,7 @@ const useAuth = async ({ request, res }, next) => { case enumLoginCode.EXPIRES: return res.fail({ msg: '登录态已过期, 请重新登录', status: 401 }) case enumLoginCode.ERROR_TOKEN: - consola.warn('TOKEN校验错误(可能存在外部攻击): ', path) - return res.fail({ msg: 'TOKEN错误!!!', status: 403 }) + return res.fail({ msg: 'TOKEN校验失败, 请重新登录', status: 401 }) case enumLoginCode.ERROR_UID: consola.warn('用户id校验失败(可能存在外部攻击): ', path) return res.fail({ msg: 'UID错误!!!', status: 403 }) diff --git a/server/app/router/routes.js b/server/app/router/routes.js index 9595f61..7d70f71 100644 --- a/server/app/router/routes.js +++ b/server/app/router/routes.js @@ -1,4 +1,4 @@ -const { getSSHList, addSSH, updateSSH, removeSSH, getCommand, decryptPrivateKey } = require('../controller/ssh') +const { getSSHList, addSSH, updateSSH, removeSSH, getCommand, decryptPrivateKey, getRdpToken } = require('../controller/ssh') const { getHostList, addHost, updateHost, batchUpdateHost, removeHost, importHost } = require('../controller/host') const { login, getpublicKey, updatePwd, getEasynodeVersion, getMFA2Status, getMFA2Code, enableMFA2, disableMFA2, getPlusInfo, getPlusDiscount, getPlusConf, updatePlusKey } = require('../controller/user') const { getNotifyConfig, updateNotifyConfig, getNotifyList, updateNotifyList } = require('../controller/notify') @@ -41,6 +41,11 @@ const ssh = [ method: 'post', path: '/decrypt-private-key', controller: decryptPrivateKey + }, + { + method: 'get', + path: '/get-rdp-token', + controller: getRdpToken } ] const host = [ diff --git a/server/app/server.js b/server/app/server.js index e66b6cf..8e13ec2 100644 --- a/server/app/server.js +++ b/server/app/server.js @@ -3,6 +3,7 @@ const compose = require('koa-compose') // 组合中间件,简化写法 const http = require('http') const { httpPort } = require('./config') const middlewares = require('./middlewares') +const wsRdp = require('./socket/rdp') const wsTerminal = require('./socket/terminal') const wsSftpV2 = require('./socket/sftp-v2') const wsDocker = require('./socket/docker') @@ -24,6 +25,7 @@ const httpServer = () => { // 服务 function serverHandler(app, server) { app.proxy = true // 用于nginx反代时获取真实客户端ip + wsRdp(server) // RDP wsTerminal(server) // 终端 wsSftpV2(server) // sftp-v2 wsDocker(server) // docker diff --git a/server/app/socket/plus.js b/server/app/socket/plus.js index ffd094a..2af8ee6 100644 --- a/server/app/socket/plus.js +++ b/server/app/socket/plus.js @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/server/app/socket/rdp.js b/server/app/socket/rdp.js new file mode 100644 index 0000000..a74ca13 --- /dev/null +++ b/server/app/socket/rdp.js @@ -0,0 +1,40 @@ +const GuacamoleLite = require('guacamole-lite') + +const GUACD_HOST = process.env.GUACD_HOST || 'guacd' +const GUACD_PORT = Number(process.env.GUACD_PORT) || 4822 + +const getOptions = (server) => { + const websocketOptions = { + server, + path: '/guac' + } + + const guacdOptions = { + host: GUACD_HOST, + port: GUACD_PORT + } + + const clientOptions = { + crypt: { + cypher: 'AES-256-CBC', + key: global.rpdToken + } + } + return { websocketOptions, guacdOptions, clientOptions } +} +module.exports = (httpServer) => { + const { websocketOptions, guacdOptions, clientOptions } = getOptions(httpServer) + let guacamole = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions) + try { + guacamole.on('connection', () => { + consola.success('✔ RDP guacamole连接成功') + }) + guacamole.on('error', (err) => { + consola.error('❌ RDP guacamole连接错误', err) + }) + } catch (error) { + guacamole.close() + guacamole = null + consola.error('❌ RDP 初始化失败:', error.message) + } +} \ No newline at end of file diff --git a/server/app/utils/plus.js b/server/app/utils/plus.js index c448f02..00d391e 100644 --- a/server/app/utils/plus.js +++ b/server/app/utils/plus.js @@ -1 +1 @@ -U2FsdGVkX1/ZvAPYoB/CtzeytYO1I2b3w9dMsJCC7Ij/bQSMkkrORQSfc2kzqp6ZOuRvrYaEj8/SoMyukViiB14z5DVhX+Pjfm8VjQ3sNtPfN/Kc8nxJkipB8bjWVuiK9VuTxxBVatGqQ+ESDF8Vzq1ygL3NTGz0QqnfMiY3NXecc/PGi/dP99feMPpL/5XmbqMhDz8FG/MlGhuXIjd+Zl2qkxhGgI2bgOwcyJBLCOuugaAVNUUSZIq4rrsJPgnj/kFf6rh1qgXkTMa3dE7XACoNyjnYPUCX0brIenKUqd7aehFKiRczudmzSjrPJXxb3txUbzjWJzhjW0FA0odNQCWog0Qvh+WXDc76QpMOD/Bq/yxmQvKwx8AniTTn8zhYUnwSWO+b4+H6v/kGcY9LSocQhbK7Ef5cfn7SRnTE5fKcjTrLnXQ2sc3j77VQFcJZ9lCQk0WgfZhtR37ZjzDeMkV/og9Kw4hJpluAFti+4cD2vpqF8c8rs45CtzqpIfc1evQPbxKH4kKtM0gWXrMuyQN3bIuLydsSFX95GZahg8PGdJkRngcHOKjjR/VMsAibdaGHuuQ3UlAqehWGJodSO81+ULVz5qt9VeSB1qZkdopy6GAuZsRbXibTbCv1E9CduFCiZQ8+eWpQexf83jieYevTEpQoS4L5DTxrYK7zavS+3qlECcpDbPiQ8Ji6AkmUokbe5Mnm9WSR+WaIY3gp1T0Of5f6GDvxce1HrM2ysWt5LjLd4sOACL8mVzbqXoMKaU5HC69CtaXsOee68GSr0P2V0glojMS2awRImA3BMUCGABn8SfVn45w1vf2jcwnITD4QrKTXeARuvs1vltvRXeoKyXa+HesQkgBidgIyIBk= \ No newline at end of file +U2FsdGVkX1+ILBpE7oSk+UTXJz7RbpPv3UNrL+5f1NfCyRjBvdtSnyeR3UAwyY3JwjE1o/AFHGcCA3Cb259Hkrxkz3HjGv9tVIdwE5mt8AoV7na7jKIPXRIcbhsgAeiPa6TO+WKr8Y0Mcnt2AG5UaX0579UnGBNx312wRanSiZsOX5sNUafxEMikp2/RxM7Mmbh9M0Jf75EfXCWIwruYJiZ4/7i5cDlVxv8MlhgUOc61M8OZKqoO5IHlBGAY/sZmznaLiaZHkwBlFmCwIoJfJ4K/iVHTsacIev/W+raeK5iieIwyuO/xCNUGMc0cGii30T4JvGTxBDHTfHjUWFX83nv4aAoEl//i0OgQ7pP8rUl7YhTPgMVpko2XbtFHi6MsrQ+qGsNntHngVYIO4zb4USp4Ulm+WRr9ZGco3+ij8qgee/+789WtidpLojfXAPSfmi1aEBWIVb9jiXOOr4+8kZwHafZjnrvRG9xfKE79KGLqZrAEbi4ycmG57hwsCm4UrbkTtE1w1KQDJRwrDnghOGDEdH21oadR+trHwSh983NVL7jxAf9rrbgs6YupyfccKbS7/srffI7kS0abFXbspcDr4YrB9MwCaLIXW6xHOdHtSSeajPJaRLbrHiPhONbBQSrQ2v1ZI5kOYlVGjtNVCP91DvQ0kIaH8q6jdEhL2fH1AU4/l7ao25/Zu0bgehTMlaXTy/QH0uFvCIS13+aMXgczTnSM891N28g2QgDgp4Krut7MZAT6c9NgI+hTI9j0IpAant+CbsWO3LDsnCLXpbbZfyL5HkluY6ahyYLI7TRAwTZGU581OgjHMjx4S2Dc6DBvCL2qZjLmysRo8UspZp4SUJqEW7LI9ye+1UMtHKE= \ No newline at end of file diff --git a/server/index.js b/server/index.js index b1e4be9..33722be 100644 --- a/server/index.js +++ b/server/index.js @@ -1,4 +1,5 @@ const consola = require('consola') global.consola = consola +global.rpdToken = Array.from({ length:32 },()=>Math.random().toString(36)[2]).join('') require('dotenv').config() require('./app/main.js') diff --git a/server/package.json b/server/package.json index fefd838..4a0ae5c 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "3.4.2", + "version": "3.5.0", "description": "easynode-server", "bin": "./bin/www", "scripts": { @@ -21,13 +21,14 @@ "dependencies": { "@koa/cors": "^5.0.0", "@seald-io/nedb": "^4.0.4", - "axios": "^1.8.3", + "axios": "^1.12.0", "consola": "^3.2.3", "cross-env": "^7.0.3", "crypto-js": "^4.2.0", "dotenv": "^16.4.5", "fs-extra": "^11.2.0", "global": "^4.4.0", + "guacamole-lite": "^1.2.0", "iconv-lite": "^0.6.3", "jsonwebtoken": "^9.0.2", "koa": "2.16.2", @@ -44,7 +45,7 @@ "node-rsa": "^1.1.1", "node-schedule": "^2.1.1", "node-telegram-bot-api": "^0.66.0", - "nodemailer": "^6.9.14", + "nodemailer": "^7.0.7", "qrcode": "^1.5.4", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", diff --git a/server/version.json b/server/version.json index 498391d..26a4137 100644 --- a/server/version.json +++ b/server/version.json @@ -1,4 +1,16 @@ [ + { + "version": "v3.5.0", + "date": "2025-10-18", + "features": [ + "🖥️ 支持 RDP 远程 Windows 桌面连接(支持移动端交互 & 剪贴板互动)", + "🧩 脚本库增强:新增脚本执行模式 — 多行脚本 @zhanghao-njmu", + "💻 终端增强:输出高亮自定义、配置持久化管理、样式优化、全屏下 bug 修复 @zhanghao-njmu", + "📂 SFTP增强:宽度拖拽、个性化显示文件信息列(大小、修改时间、权限、拥有者) @zhanghao-njmu", + "⚙️ 其他优化与bug修复", + "❤️ 特别感谢 @zhanghao-njmu 的功能PR" + ] + }, { "version": "v3.4.2", "date": "2025-08-24", diff --git a/web/.eslintrc.js b/web/.eslintrc.js index c6f2271..6fb771d 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -34,6 +34,7 @@ module.exports = { 'vue/singleline-html-element-content-newline': 0, // js + 'no-case-declarations': 'off', 'space-before-blocks': ['error', 'always',], 'space-in-parens': ['error', 'never',], 'keyword-spacing': ['error', { 'before': true, 'after': true },], diff --git a/web/index.html b/web/index.html index 362d8e3..83d2d1e 100644 --- a/web/index.html +++ b/web/index.html @@ -6,7 +6,7 @@ EasyNode - + diff --git a/web/package.json b/web/package.json index 57477eb..277639f 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "web", - "version": "3.4.2", + "version": "3.5.0", "description": "easynode-web", "private": true, "scripts": { @@ -37,7 +37,8 @@ "crypto-js": "^4.2.0", "csv-parse": "^5.6.0", "dayjs": "^1.11.13", - "element-plus": "^2.9.7", + "element-plus": "^2.11.4", + "guacamole-common-js": "^1.5.0", "highlight.js": "^11.11.1", "jsencrypt": "^3.3.2", "markdown-it": "^14.1.0", diff --git a/web/src/api/index.js b/web/src/api/index.js index a2251c8..8ac0b8a 100644 --- a/web/src/api/index.js +++ b/web/src/api/index.js @@ -19,6 +19,9 @@ export default { removeSSH(id) { return axios({ url: `/remove-ssh/${ id }`, method: 'delete' }) }, + getRdpToken(config = {}) { + return axios({ url: '/get-rdp-token', method: 'get', params: config }) + }, getPlusInfo() { return axios({ url: '/plus-info', method: 'get' }) }, diff --git a/web/src/components/menuList.vue b/web/src/components/menuList.vue index 1d2bb3e..af7cac0 100644 --- a/web/src/components/menuList.vue +++ b/web/src/components/menuList.vue @@ -48,15 +48,20 @@ const route = useRoute() const list = reactive([ { - name: '实例配置', + name: '实例列表', icon: markRaw(IconMenu), index: '/server' }, { - name: '连接终端', + name: '远程终端', icon: markRaw(ScaleToOriginal), index: '/terminal' }, + { + name: '远程桌面', + icon: markRaw(ScaleToOriginal), + index: '/rdp' + }, { name: '文件对传', icon: markRaw(FolderOpened), diff --git a/web/src/components/plus-table.vue b/web/src/components/plus-table.vue index f347974..ca439b1 100644 --- a/web/src/components/plus-table.vue +++ b/web/src/components/plus-table.vue @@ -95,6 +95,7 @@ const basicFeatures = [ const plusFeatures = [ 'AI Chat对话组件', '服务器代理&跳板机功能', + 'RDP远程win桌面连接', '文件对传', '终端单窗口模式', '批量修改实例配置', diff --git a/web/src/components/server-selector.vue b/web/src/components/server-selector.vue index 3051cda..5d2b3a2 100644 --- a/web/src/components/server-selector.vue +++ b/web/src/components/server-selector.vue @@ -38,7 +38,7 @@ const emit = defineEmits(['update:modelValue', 'change',]) const { proxy: { $store } } = getCurrentInstance() -const serverList = computed(() => $store.hostList?.filter(item => item.isConfig)) +const serverList = computed(() => $store.hostList?.filter(item => item.connectType !== 'rdp' && item.isConfig)) const isPlusActive = computed(() => $store.isPlusActive) const selectedServer = computed({ diff --git a/web/src/main.js b/web/src/main.js index 336946f..518493e 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -18,6 +18,9 @@ globalComponents(app) app.use(createPinia()) app.use(router) +const isDev = import.meta.env.DEV +app.config.globalProperties.$isDev = isDev + app.config.globalProperties.$api = api app.config.globalProperties.$tools = tools app.config.globalProperties.$http = axios diff --git a/web/src/router/index.js b/web/src/router/index.js index 732a7cb..d051196 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -4,6 +4,7 @@ import Login from '@views/login/index.vue' import Container from '@views/index.vue' import Server from '@views/server/index.vue' import Terminal from '@views/terminal/index.vue' +import Rdp from '@views/rdp/index.vue' import Credentials from '@views/credentials/index.vue' import File from '@views/file/index.vue' import Onekey from '@views/onekey/index.vue' @@ -27,6 +28,7 @@ const routes = [ children: [ { path: '/server', component: Server }, { path: '/terminal', component: Terminal }, + { path: '/rdp', component: Rdp }, { path: '/credentials', component: Credentials }, { path: '/file', component: File }, { path: '/onekey', component: Onekey }, diff --git a/web/src/utils/enum.js b/web/src/utils/enum.js index 8ef2a5b..fcbdb4b 100644 --- a/web/src/utils/enum.js +++ b/web/src/utils/enum.js @@ -1,14 +1,41 @@ // 终端连接状态 export const terminalStatus = { + CONNECT_READY: 'connect_ready', CONNECTING: 'connecting', CONNECT_FAIL: 'connect_fail', CONNECT_SUCCESS: 'connect_success' } + export const terminalStatusList = [ + { value: terminalStatus.CONNECT_READY, label: '待连接', color: 'gray' }, { value: terminalStatus.CONNECTING, label: '连接中', color: '#FFA500' }, { value: terminalStatus.CONNECT_FAIL, label: '连接失败', color: '#DC3545' }, { value: terminalStatus.CONNECT_SUCCESS, label: '已连接', color: '#28A745' }, ] + +// RDP连接状态 +export const rdpStatus = { + IDLE: 'idle', + CONNECTING: 'connecting', + WAITING: 'waiting', + CONNECTED: 'connected', + DISCONNECTING: 'disconnecting', + DISCONNECTED: 'disconnected', + TIMEOUT: 'timeout', + ERROR: 'error' +} + +export const rdpStatusList = [ + { value: rdpStatus.IDLE, label: '准备连接', color: '#909399' }, + { value: rdpStatus.CONNECTING, label: '正在连接...', color: '#E6A23C' }, + { value: rdpStatus.WAITING, label: '等待响应...', color: '#409EFF' }, + { value: rdpStatus.CONNECTED, label: '已连接', color: '#67C23A' }, + { value: rdpStatus.DISCONNECTING, label: '正在断开...', color: '#E6A23C' }, + { value: rdpStatus.DISCONNECTED, label: '已断开', color: '#909399' }, + { value: rdpStatus.TIMEOUT, label: '连接超时', color: '#F56C6C' }, + { value: rdpStatus.ERROR, label: '连接错误', color: '#F56C6C' }, +] + export const virtualKeyType = { LONG_PRESS: 'long-press', SINGLE_PRESS: 'single-press' diff --git a/web/src/views/rdp/components/rdp.vue b/web/src/views/rdp/components/rdp.vue new file mode 100644 index 0000000..2afc58c --- /dev/null +++ b/web/src/views/rdp/components/rdp.vue @@ -0,0 +1,927 @@ + + + + + + + \ No newline at end of file diff --git a/web/src/views/rdp/index.vue b/web/src/views/rdp/index.vue new file mode 100644 index 0000000..42b4db0 --- /dev/null +++ b/web/src/views/rdp/index.vue @@ -0,0 +1,227 @@ + + + + + diff --git a/web/src/views/server/components/host-form.vue b/web/src/views/server/components/host-form.vue index eed6cbf..f486362 100644 --- a/web/src/views/server/components/host-form.vue +++ b/web/src/views/server/components/host-form.vue @@ -29,6 +29,14 @@ label-width="100px" :show-message="false" > + + + SSH + + RDP + + + - + 密钥 密码 凭据 - + props.batchHosts) const defaultData = computed(() => props.defaultData) const rules = computed(() => { return { + connectType: { required: true, message: '选择一个连接类型' }, group: { required: !isBatchModify.value, message: '选择一个分组' }, name: { required: !isBatchModify.value, message: '输入实例别名', trigger: 'change' }, host: { required: !isBatchModify.value, message: '输入IP/域名', trigger: 'change' }, @@ -431,12 +452,32 @@ const title = computed(() => { return isBatchModify.value ? '批量修改实例' : (defaultData.value ? '修改实例' : '添加实例') }) +// 连接类型计算属性 +const isSSH = computed(() => hostForm.value.connectType === 'ssh') +const isRDP = computed(() => hostForm.value.connectType === 'rdp') + const groupList = computed(() => $store.groupList) const sshList = computed(() => $store.sshList) const hostList = computed(() => $store.hostList) const confHostList = computed(() => hostList.value?.filter(item => item.isConfig)) const proxyList = computed(() => $store.proxyList) +// 监听连接类型变化,自动修正port&Username +watch( + () => hostForm.value.connectType, + (newVal) => { + if (defaultData.value || isBatchModify.value) return + if (newVal === 'rdp') { + hostForm.value.port = 3389 + hostForm.value.username = 'Administrator' + } + if (newVal === 'ssh') { + hostForm.value.port = 22 + hostForm.value.username = 'root' + } + } +) + // 监听折叠状态变化,保存到localStorage watch(advancedSettingsCollapsed, (newVal) => { localStorage.setItem('hostFormAdvancedSettingsCollapsed', JSON.stringify(newVal.includes('advanced'))) @@ -461,6 +502,7 @@ const setBatchDefaultData = () => { if (!isBatchModify.value) return Object.assign(hostForm.value, { ...formField }, { group: '', port: '', username: '', authType: '', proxyType: '', jumpHosts: [], proxyServer: '' }) } + const handleOpen = async () => { if (isBatchModify.value) { setBatchDefaultData() @@ -502,10 +544,20 @@ const defaultUsers = [ { value: 'opc' }, { value: 'admin' }, ] + +const defaultWindowsUsers = [ + { value: 'Administrator' }, + { value: 'admin' }, + { value: 'user' }, + { value: 'Guest' }, +] + const userSearch = (keyword, cb) => { + // 根据连接类型选择不同的用户名建议 + const users = isRDP.value ? defaultWindowsUsers : defaultUsers let res = keyword - ? defaultUsers.filter((item) => item.value.includes(keyword)) - : defaultUsers + ? users.filter((item) => item.value.toLowerCase().includes(keyword.toLowerCase())) + : users cb(res) } @@ -529,6 +581,7 @@ const handleSave = () => { if (Array.isArray(value)) return value.length > 0 return Boolean(value) })) // 剔除掉未更改的值 + if (isRDP.value) updateFieldData.authType = 'password' let { authType = '' } = updateFieldData if (authType && !updateFieldData[authType]) { delete updateFieldData.authType @@ -547,6 +600,7 @@ const handleSave = () => { let { msg } = await $api.batchUpdateHost({ updateIds, updateFieldData }) $message({ type: 'success', center: true, message: msg }) } else { + if (isRDP.value) formData.authType = 'password' let { authType } = formData if (formData[authType]) { let tempKey = randomStr(16) diff --git a/web/src/views/server/components/host-table.vue b/web/src/views/server/components/host-table.vue index 5302654..0bfb54f 100644 --- a/web/src/views/server/components/host-table.vue +++ b/web/src/views/server/components/host-table.vue @@ -23,7 +23,16 @@ sortable :sort-method="(a, b) => a.name - b.name" > - + @@ -173,8 +182,8 @@ const handleToConsole = ({ consoleUrl }) => { } const handleSSH = async (row) => { - let { id } = row - $router.push({ path: '/terminal', query: { hostIds: id } }) + let { id, connectType } = row + $router.push({ path: connectType === 'rdp' ? '/rdp' : '/terminal', query: { hostIds: id } }) } const defaultSortLocal = localStorage.getItem('host_table_sort') diff --git a/web/src/views/server/index.vue b/web/src/views/server/index.vue index 19ee904..fc6bdc3 100644 --- a/web/src/views/server/index.vue +++ b/web/src/views/server/index.vue @@ -9,7 +9,7 @@
- - - -
+ + + +
@@ -2379,10 +2379,10 @@ const performFileUpload = async (task) => { throw new Error('WebSocket连接未建立') } - // 检查文件大小限制 (1GB) - const maxFileSize = 1024 * 1024 * 1024 // 1GB + // 检查文件大小限制 (3GB) + const maxFileSize = 1024 * 1024 * 1024 * 3 // 3GB if (task.totalSize > maxFileSize) { - throw new Error(`文件过大(${ formatSize(task.totalSize) }),单个文件不能超过1GB`) + throw new Error(`文件过大(${ formatSize(task.totalSize) }),单个文件不能超过3GB`) } // 发送上传开始事件 diff --git a/web/src/views/terminal/components/terminal-wrapper.vue b/web/src/views/terminal/components/terminal-wrapper.vue index 5ad2214..8fec780 100644 --- a/web/src/views/terminal/components/terminal-wrapper.vue +++ b/web/src/views/terminal/components/terminal-wrapper.vue @@ -666,7 +666,10 @@ const isPlusActive = computed(() => $store.isPlusActive) const terminalTabs = computed(() => props.terminalTabs) const terminalTabsLen = computed(() => props.terminalTabs.length) const hostGroupList = computed(() => $store.groupList) -const hostList = computed(() => $store.hostList) +const hostList = computed(() => { + if (!Array.isArray($store.hostList)) return [] + return $store.hostList.filter(item => item.connectType !== 'rdp') +}) // const curHost = computed(() => // hostList.value.find( // (item) => item.host === terminalTabs.value[activeTabIndex.value]?.host diff --git a/yarn.lock b/yarn.lock index 7db002a..cf4bf3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1309,18 +1309,23 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/lodash-es@^4.17.6": +"@types/lodash-es@^4.17.12": version "4.17.12" resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== dependencies: "@types/lodash" "*" -"@types/lodash@*", "@types/lodash@^4.14.182": +"@types/lodash@*": version "4.17.7" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== +"@types/lodash@^4.17.20": + version "4.17.20" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93" + integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA== + "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -1746,22 +1751,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== -axios@^1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.3.tgz#9ebccd71c98651d547162a018a1a95a4b4ed4de8" - integrity sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A== +axios@^1.12.0, axios@^1.8.4: + version "1.12.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.0.tgz#11248459be05a5ee493485628fa0e4323d0abfc3" + integrity sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg== dependencies: follow-redirects "^1.15.6" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -axios@^1.8.4: - version "1.8.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.4.tgz#78990bb4bc63d2cae072952d374835950a82f447" - integrity sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw== - dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.0" + form-data "^4.0.4" proxy-from-env "^1.1.0" balanced-match@^1.0.0: @@ -2320,6 +2316,11 @@ deep-equal@~1.0.1: resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -2447,24 +2448,24 @@ electron-to-chromium@^1.5.73: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz#8ea537b369c32527b3cc47df7973bffe5d3c2980" integrity sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ== -element-plus@^2.9.7: - version "2.9.7" - resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-2.9.7.tgz#05bcc35de1d98192d25ebfd06fff7d6d2de9f911" - integrity sha512-6vjZh5SXBncLhUwJGTVKS5oDljfgGMh6J4zVTeAZK3YdMUN76FgpvHkwwFXocpJpMbii6rDYU3sgie64FyPerQ== +element-plus@^2.11.4: + version "2.11.4" + resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-2.11.4.tgz#00c7cdec627e8f83f9175aa9a55b00846c58803a" + integrity sha512-sLq+Ypd0cIVilv8wGGMEGvzRVBBsRpJjnAS5PsI/1JU1COZXqzH3N1UYMUc/HCdvdjf6dfrBy80Sj7KcACsT7w== dependencies: "@ctrl/tinycolor" "^3.4.1" "@element-plus/icons-vue" "^2.3.1" "@floating-ui/dom" "^1.0.1" "@popperjs/core" "npm:@sxzz/popperjs-es@^2.11.7" - "@types/lodash" "^4.14.182" - "@types/lodash-es" "^4.17.6" + "@types/lodash" "^4.17.20" + "@types/lodash-es" "^4.17.12" "@vueuse/core" "^9.1.0" async-validator "^4.2.5" dayjs "^1.11.13" escape-html "^1.0.3" lodash "^4.17.21" lodash-es "^4.17.21" - lodash-unified "^1.0.2" + lodash-unified "^1.0.3" memoize-one "^6.0.0" normalize-wheel-es "^1.2.0" @@ -2960,7 +2961,7 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== -form-data@^4.0.0, form-data@~4.0.0: +form-data@^4.0.4, form-data@~4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== @@ -3186,6 +3187,19 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +guacamole-common-js@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/guacamole-common-js/-/guacamole-common-js-1.5.0.tgz#50d814c09628b8a65451973d1acc68bc1612558d" + integrity sha512-zxztif3GGhKbg1RgOqwmqot8kXgv2HmHFg1EvWwd4q7UfEKvBcYZ0f+7G8HzvU+FUxF0Psqm9Kl5vCbgfrRgJg== + +guacamole-lite@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/guacamole-lite/-/guacamole-lite-1.2.0.tgz#90d00d9f42585724ff957c6dff8ab552a81dad42" + integrity sha512-NeSYgbT5s5rxF0SE/kzJsV5Gg0IvnqoTOCbNIUMl23z1+SshaVfLExpxrEXSGTG0cdvY5lfZC1fOAepYriaXGg== + dependencies: + deep-extend "^0.6.0" + ws "^8.15.1" + has-bigints@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" @@ -3960,7 +3974,7 @@ lodash-es@^4.17.21: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash-unified@^1.0.2: +lodash-unified@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/lodash-unified/-/lodash-unified-1.0.3.tgz#80b1eac10ed2eb02ed189f08614a29c27d07c894" integrity sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ== @@ -4251,10 +4265,10 @@ node-telegram-bot-api@^0.66.0: mime "^1.6.0" pump "^2.0.0" -nodemailer@^6.9.14: - version "6.9.14" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.14.tgz#845fda981f9fd5ac264f4446af908a7c78027f75" - integrity sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA== +nodemailer@^7.0.7: + version "7.0.7" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-7.0.7.tgz#91a16235c08abb7805a4ec1537ca63edca79687f" + integrity sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA== nodemon@^3.1.4: version "3.1.4" @@ -5856,6 +5870,11 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +ws@^8.15.1: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + ws@~8.17.1: version "8.17.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"