104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
// ssh -L 8080:localhost:80 -L 9090:localhost:90 user@remote-server
|
|
import { ChildProcess, spawn } from 'child_process';
|
|
type Options = {
|
|
name: string;
|
|
values: SSHValue[];
|
|
};
|
|
export type SSHValue = {
|
|
localPort: number;
|
|
remoteHost: string; // localhost
|
|
remotePort: number;
|
|
remote?: string; //sky config配置
|
|
status?: 'active' | 'inactive';
|
|
};
|
|
export const demos: SSHValue[] = [
|
|
{
|
|
localPort: 3000,
|
|
remoteHost: 'localhost',
|
|
remotePort: 3000,
|
|
remote: 'diana',
|
|
},
|
|
{
|
|
localPort: 5244,
|
|
remoteHost: 'localhost',
|
|
remotePort: 5244,
|
|
remote: 'diana',
|
|
},
|
|
];
|
|
export class SSHServer {
|
|
name = 'diana';
|
|
values: SSHValue[] = [];
|
|
childProcess: ChildProcess | null = null;
|
|
isRunning = false;
|
|
constructor(opts?: Options) {
|
|
this.name = opts?.name || this.name;
|
|
this.values = opts?.values || demos;
|
|
}
|
|
start(options?: Options) {
|
|
try {
|
|
const remote = options?.name || this.name;
|
|
const demos = options?.values || this.values;
|
|
const _port = demos.filter((item) => item.status === 'active');
|
|
if (_port.length === 0) {
|
|
this.isRunning = false;
|
|
return;
|
|
}
|
|
const params = _port
|
|
.map((item) => {
|
|
return ['-L', `${item.localPort}:${item.remoteHost}:${item.remotePort}`];
|
|
})
|
|
.flat();
|
|
const childProcess = spawn(
|
|
'ssh',
|
|
[
|
|
'-N', // 不执行远程命令
|
|
// '-f', // 后台运行
|
|
'-o',
|
|
'ServerAliveInterval=60', // 每60秒发送一次心跳包
|
|
'-o',
|
|
'ServerAliveCountMax=3', // 尝试3次心跳失败后断开连接
|
|
'-T',
|
|
...params,
|
|
remote,
|
|
],
|
|
{
|
|
// stdio: 'ignore',
|
|
stdio: 'inherit',
|
|
killSignal: 'SIGINT',
|
|
},
|
|
);
|
|
this.childProcess = childProcess;
|
|
childProcess.stdout?.on('data', (data) => {
|
|
console.log('childProcess', data.toString());
|
|
});
|
|
childProcess.on('error', (err: any) => {
|
|
if (err.code === 'EADDRINUSE') {
|
|
console.error('端口被占用:', err);
|
|
return;
|
|
}
|
|
console.error('Failed to start SSH process:', err);
|
|
});
|
|
childProcess.on('exit', (code, signal) => {
|
|
console.log('SSH process exited:', code, signal);
|
|
this.isRunning = false;
|
|
});
|
|
this.isRunning = true;
|
|
console.log('当前ssh -L 服务启动', this.name);
|
|
// 监听的端口是
|
|
// console.log('监听的端口是:', _port.map((item) => `[${item.localPort},${item.remotePort}]`).join(','));
|
|
console.log('监听的端口是:', _port.map((item) => item.localPort).join(','));
|
|
} catch (error) {
|
|
console.error('启动失败:', error);
|
|
}
|
|
}
|
|
stop() {
|
|
if (this.childProcess) {
|
|
console.log('正在停止 ssh -L 服务:', this.name);
|
|
this.childProcess.kill(); // 发送默认信号 SIGTERM
|
|
this.childProcess = undefined;
|
|
} else {
|
|
console.log('没有正在运行的 ssh -L 服务.');
|
|
}
|
|
}
|
|
}
|