93 lines
3.1 KiB
JavaScript
93 lines
3.1 KiB
JavaScript
const { Server } = require('socket.io')
|
||
const { Client: Client } = require('ssh2')
|
||
const { readSSHRecord, verifyAuth, RSADecrypt, AESDecrypt } = require('../utils')
|
||
|
||
function createTerminal(socket, vps) {
|
||
vps.shell({ term: 'xterm-color' }, (err, stream) => {
|
||
if (err) return socket.emit('output', err.toString())
|
||
stream
|
||
.on('data', (data) => {
|
||
socket.emit('output', data.toString())
|
||
})
|
||
.on('close', () => {
|
||
console.log('关闭终端')
|
||
vps.end()
|
||
})
|
||
socket.on('input', key => {
|
||
if(vps._sock.writable === false) return console.log('终端连接已关闭')
|
||
stream.write(key)
|
||
})
|
||
socket.emit('connect_terminal')
|
||
|
||
socket.on('resize', ({ rows, cols }) => {
|
||
stream.setWindow(rows, cols)
|
||
})
|
||
})
|
||
}
|
||
|
||
module.exports = (httpServer) => {
|
||
const serverIo = new Server(httpServer, {
|
||
path: '/terminal',
|
||
cors: {
|
||
origin: '*'
|
||
}
|
||
})
|
||
serverIo.on('connection', (socket) => {
|
||
// 前者兼容nginx反代, 后者兼容nodejs自身服务
|
||
let clientIp = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||
let vps = new Client()
|
||
console.log('terminal websocket 已连接')
|
||
|
||
socket.on('create', ({ host: ip, token }) => {
|
||
const { code } = verifyAuth(token, clientIp)
|
||
if(code !== 1) {
|
||
socket.emit('token_verify_fail')
|
||
socket.disconnect()
|
||
return
|
||
}
|
||
// console.log('code:', code)
|
||
const sshRecord = readSSHRecord()
|
||
let loginInfo = sshRecord.find(item => item.host === ip)
|
||
if(!sshRecord.some(item => item.host === ip)) return socket.emit('create_fail', `未找到【${ ip }】凭证`)
|
||
let { type, host, port, username, randomKey } = loginInfo
|
||
try {
|
||
// 解密放到try里面,防止报错【公私钥必须配对, 否则需要重新添加服务器密钥】
|
||
randomKey = AESDecrypt(randomKey) // 先对称解密key
|
||
randomKey = RSADecrypt(randomKey) // 再非对称解密key
|
||
loginInfo[type] = AESDecrypt(loginInfo[type], randomKey) // 对称解密ssh密钥
|
||
console.log('准备连接服务器:', host)
|
||
vps
|
||
.on('ready', () => {
|
||
console.log('已连接到服务器:', host)
|
||
socket.emit('connect_success', `已连接到服务器:${ host }`)
|
||
createTerminal(socket, vps)
|
||
})
|
||
.on('error', (err) => {
|
||
console.log('连接失败:', err.level)
|
||
socket.emit('connect_fail', err.message)
|
||
})
|
||
.connect({
|
||
type: 'privateKey',
|
||
host,
|
||
port,
|
||
username,
|
||
[type]: loginInfo[type]
|
||
// debug: (info) => {
|
||
// console.log(info)
|
||
// }
|
||
})
|
||
} catch (err) {
|
||
console.log('创建失败:', err.message)
|
||
socket.emit('create_fail', err.message)
|
||
}
|
||
})
|
||
|
||
socket.on('disconnect', (reason) => {
|
||
console.log('终端连接断开:', reason)
|
||
vps.end()
|
||
vps.destroy()
|
||
vps = null
|
||
})
|
||
})
|
||
}
|