feat: aligner le dashboard production sur recette
This commit is contained in:
179
backend/src/ssh.js
Normal file
179
backend/src/ssh.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Module SSH Terminal - Gestion des connexions SSH via WebSocket
|
||||
* Utilise la bibliothèque ssh2 pour établir des connexions SSH
|
||||
*/
|
||||
const { Client } = require('ssh2');
|
||||
const WebSocket = require('ws');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const config = require('./config');
|
||||
|
||||
/**
|
||||
* Initialise le serveur WebSocket SSH sur /ws-ssh
|
||||
* @param {http.Server} server - Le serveur HTTP Express
|
||||
*/
|
||||
function initSSHWebSocket(server) {
|
||||
const wssSsh = new WebSocket.Server({ server, path: '/ws-ssh' });
|
||||
|
||||
wssSsh.on('connection', (ws, req) => {
|
||||
// Vérifier l'authentification via le token dans l'URL
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
const token = url.searchParams.get('token');
|
||||
|
||||
if (!token) {
|
||||
ws.send(JSON.stringify({ type: 'error', message: 'Token manquant' }));
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
jwt.verify(token, config.jwtSecret);
|
||||
} catch (err) {
|
||||
ws.send(JSON.stringify({ type: 'error', message: 'Token invalide' }));
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Nouvelle connexion WebSocket SSH');
|
||||
|
||||
let sshClient = null;
|
||||
let sshStream = null;
|
||||
let connected = false;
|
||||
|
||||
ws.on('message', (data) => {
|
||||
try {
|
||||
const msg = JSON.parse(data.toString());
|
||||
|
||||
switch (msg.type) {
|
||||
case 'connect':
|
||||
// Établir la connexion SSH
|
||||
handleConnect(ws, msg, (client, stream) => {
|
||||
sshClient = client;
|
||||
sshStream = stream;
|
||||
connected = true;
|
||||
});
|
||||
break;
|
||||
|
||||
case 'input':
|
||||
// Envoyer des données au terminal SSH
|
||||
if (sshStream && connected) {
|
||||
sshStream.write(msg.data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'resize':
|
||||
// Redimensionner le terminal
|
||||
if (sshStream && connected) {
|
||||
sshStream.setWindow(msg.rows, msg.cols, 0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'disconnect':
|
||||
// Fermer la connexion SSH
|
||||
if (sshClient) {
|
||||
sshClient.end();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn('Type de message SSH inconnu:', msg.type);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur parsing message SSH:', err.message);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('Connexion WebSocket SSH fermée');
|
||||
if (sshClient) {
|
||||
sshClient.end();
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('error', (err) => {
|
||||
console.error('Erreur WebSocket SSH:', err.message);
|
||||
if (sshClient) {
|
||||
sshClient.end();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('Serveur WebSocket SSH initialisé sur /ws-ssh');
|
||||
return wssSsh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère la connexion SSH
|
||||
*/
|
||||
function handleConnect(ws, msg, onConnected) {
|
||||
const { host, port, username, password, privateKey } = msg;
|
||||
|
||||
if (!host || !username) {
|
||||
ws.send(JSON.stringify({ type: 'error', message: 'Hôte et utilisateur requis' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const sshClient = new Client();
|
||||
|
||||
const connConfig = {
|
||||
host: host,
|
||||
port: port || 22,
|
||||
username: username,
|
||||
readyTimeout: 15000,
|
||||
keepaliveInterval: 10000,
|
||||
};
|
||||
|
||||
if (privateKey) {
|
||||
connConfig.privateKey = privateKey;
|
||||
} else if (password) {
|
||||
connConfig.password = password;
|
||||
} else {
|
||||
ws.send(JSON.stringify({ type: 'error', message: 'Mot de passe ou clé privée requis' }));
|
||||
return;
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify({ type: 'status', message: `Connexion à ${username}@${host}:${port || 22}...` }));
|
||||
|
||||
sshClient.on('ready', () => {
|
||||
ws.send(JSON.stringify({ type: 'connected', message: `Connecté à ${host}` }));
|
||||
|
||||
sshClient.shell({ term: 'xterm-256color', rows: 24, cols: 80 }, (err, stream) => {
|
||||
if (err) {
|
||||
ws.send(JSON.stringify({ type: 'error', message: `Erreur shell: ${err.message}` }));
|
||||
sshClient.end();
|
||||
return;
|
||||
}
|
||||
|
||||
onConnected(sshClient, stream);
|
||||
|
||||
// Transmettre les données du terminal SSH vers le WebSocket
|
||||
stream.on('data', (data) => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'output', data: data.toString('base64') }));
|
||||
}
|
||||
});
|
||||
|
||||
stream.stderr.on('data', (data) => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'output', data: data.toString('base64') }));
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('close', () => {
|
||||
ws.send(JSON.stringify({ type: 'disconnected', message: 'Session SSH terminée' }));
|
||||
sshClient.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sshClient.on('error', (err) => {
|
||||
ws.send(JSON.stringify({ type: 'error', message: `Erreur SSH: ${err.message}` }));
|
||||
});
|
||||
|
||||
sshClient.on('end', () => {
|
||||
ws.send(JSON.stringify({ type: 'disconnected', message: 'Connexion SSH fermée' }));
|
||||
});
|
||||
|
||||
sshClient.connect(connConfig);
|
||||
}
|
||||
|
||||
module.exports = { initSSHWebSocket };
|
||||
Reference in New Issue
Block a user