Files
webssh/backend/socket/socketHandler.js
T
2026-02-10 13:29:45 +08:00

262 lines
7.2 KiB
JavaScript

const { Client } = require('ssh2');
const jwt = require('jsonwebtoken');
const db = require('../config/database');
const encryptionService = require('../utils/encryption');
// 存储活跃的SSH连接
const activeConnections = new Map();
const socketHandler = (io) => {
io.on('connection', (socket) => {
console.log('用户连接:', socket.id);
// 验证用户身份
socket.on('authenticate', async (token) => {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'webssh-secret-key');
socket.userId = decoded.userId;
socket.emit('authenticated', { success: true });
console.log('用户认证成功:', decoded.userId);
} catch (error) {
socket.emit('authenticated', { success: false, error: '认证失败' });
socket.disconnect();
}
});
// 建立SSH连接
socket.on('connect-ssh', async (serverId) => {
if (!socket.userId) {
socket.emit('ssh-error', { error: '未认证' });
return;
}
try {
// 获取服务器信息和认证信息
const [servers] = await db.pool.execute(
`SELECT id, name, host, port, username, password_encrypted,
private_key_encrypted, auth_type
FROM servers WHERE id = ? AND user_id = ?`,
[serverId, socket.userId]
);
if (servers.length === 0) {
socket.emit('ssh-error', { error: '服务器不存在' });
return;
}
const server = servers[0];
// 解密认证信息
let credentials = {};
if (server.auth_type === 'password' && server.password_encrypted) {
credentials.password = encryptionService.decryptPassword(
server.password_encrypted,
socket.userId
);
} else if (server.auth_type === 'key' && server.private_key_encrypted) {
credentials.privateKey = encryptionService.decryptPrivateKey(
server.private_key_encrypted,
socket.userId
);
}
// 创建SSH连接
const conn = new Client();
conn.on('ready', () => {
console.log('SSH连接已建立:', server.host);
socket.emit('ssh-connected');
// 存储连接
activeConnections.set(socket.id, {
conn,
serverId: server.id,
userId: socket.userId
});
// 创建shell会话
conn.shell({}, (err, stream) => {
if (err) {
socket.emit('ssh-error', { error: err.message });
return;
}
// 转发终端输出到前端
stream.on('data', (data) => {
socket.emit('ssh-data', data.toString());
});
// 处理来自前端的输入
socket.on('ssh-input', (input) => {
stream.write(input);
});
// 处理窗口大小调整
socket.on('resize', (size) => {
stream.setWindow(size.rows, size.cols, size.height, size.width);
});
// 处理连接关闭
stream.on('close', () => {
socket.emit('ssh-closed');
activeConnections.delete(socket.id);
});
socket.sshStream = stream;
});
});
conn.on('error', (err) => {
console.error('SSH连接错误:', err.message);
socket.emit('ssh-error', { error: err.message });
});
conn.on('close', () => {
console.log('SSH连接已关闭:', server.host);
socket.emit('ssh-closed');
activeConnections.delete(socket.id);
});
// 连接配置
const connConfig = {
host: server.host,
port: server.port || 22,
username: server.username,
readyTimeout: 20000,
keepaliveInterval: 30000
};
// 添加认证信息
if (server.auth_type === 'password') {
connConfig.password = credentials.password;
} else if (server.auth_type === 'key') {
connConfig.privateKey = credentials.privateKey;
}
// 开始连接
conn.connect(connConfig);
} catch (error) {
console.error('SSH连接准备错误:', error);
socket.emit('ssh-error', { error: '连接失败' });
}
});
// 快速连接(不保存服务器信息)
socket.on('quick-connect', (connectionInfo) => {
if (!socket.userId) {
socket.emit('ssh-error', { error: '未认证' });
return;
}
const { host, port, username, password, privateKey } = connectionInfo;
const conn = new Client();
conn.on('ready', () => {
console.log('快速SSH连接已建立:', host);
socket.emit('ssh-connected');
// 存储连接
activeConnections.set(socket.id, {
conn,
quickConnect: true,
userId: socket.userId
});
// 创建shell会话
conn.shell({}, (err, stream) => {
if (err) {
socket.emit('ssh-error', { error: err.message });
return;
}
stream.on('data', (data) => {
socket.emit('ssh-data', data.toString());
});
socket.on('ssh-input', (input) => {
stream.write(input);
});
socket.on('resize', (size) => {
stream.setWindow(size.rows, size.cols, size.height, size.width);
});
stream.on('close', () => {
socket.emit('ssh-closed');
activeConnections.delete(socket.id);
});
socket.sshStream = stream;
});
});
conn.on('error', (err) => {
console.error('快速SSH连接错误:', err.message);
socket.emit('ssh-error', { error: err.message });
});
conn.on('close', () => {
console.log('快速SSH连接已关闭:', host);
socket.emit('ssh-closed');
activeConnections.delete(socket.id);
});
// 连接配置
const connConfig = {
host,
port: port || 22,
username,
readyTimeout: 20000,
keepaliveInterval: 30000
};
if (password) {
connConfig.password = password;
} else if (privateKey) {
connConfig.privateKey = privateKey;
}
conn.connect(connConfig);
});
// 发送命令到终端
socket.on('send-command', (command) => {
if (socket.sshStream) {
socket.sshStream.write(command + '\n');
}
});
// 断开SSH连接
socket.on('disconnect-ssh', () => {
const connection = activeConnections.get(socket.id);
if (connection) {
connection.conn.end();
activeConnections.delete(socket.id);
}
});
// 处理连接断开
socket.on('disconnect', () => {
console.log('用户断开连接:', socket.id);
const connection = activeConnections.get(socket.id);
if (connection) {
connection.conn.end();
activeConnections.delete(socket.id);
}
});
// 心跳检测
socket.on('ping', () => {
socket.emit('pong');
});
});
// 全局错误处理
io.on('error', (error) => {
console.error('Socket.IO错误:', error);
});
};
module.exports = socketHandler;