feat: add webshell

This commit is contained in:
2025-03-18 21:38:06 +08:00
parent aa1cee7c9f
commit 9970efccfd
19 changed files with 2976 additions and 39 deletions

View File

@@ -0,0 +1,12 @@
import { Server as HttpServer } from 'http';
import { SocketServer } from './socket';
import { terminal } from './terminal';
export const createSocketServer = (server: HttpServer) => {
const socketServer = new SocketServer(server, {
path: '/terminal',
pingTimeout: 1000 * 60 * 60 * 24,
});
socketServer.use('terminal', terminal);
};

View File

@@ -0,0 +1,23 @@
import { Server as HttpServer } from 'http';
import { ServerOptions, Server as SocketIoServer, Namespace, Socket } from 'socket.io';
export class SocketServer {
private io: SocketIoServer;
constructor(server: HttpServer, options?: Partial<ServerOptions>) {
this.io = new SocketIoServer(server, options);
}
use(name: string | ((socket: Socket) => void), fn?: (socket: Socket) => void): boolean {
if (!name) return false;
if (typeof name === 'string') {
if (!fn) return false;
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
this.io.of(name).on('connection', fn);
} else if (typeof name === 'function') {
this.io.on('connection', name);
}
return true;
}
}

View File

@@ -0,0 +1,49 @@
import * as pty from 'node-pty';
import * as os from 'os';
import { homedir } from 'os';
import { Socket } from 'socket.io';
const shell: string = os.platform() === 'win32' ? 'powershell.exe' : 'zsh';
interface Option {
name: string;
cols?: number;
rows?: number;
cwd?: string;
}
let ptyContainers: { [key: string]: pty.IPty } = {};
export const terminal = (socket: Socket) => {
socket.on('create', (option: Option) => {
let ptyProcess = pty.spawn(shell, ['--login'], {
name: 'xterm-color',
cols: option.cols || 80,
rows: option.rows || 24,
cwd: option.cwd || homedir(),
env: process.env,
}) as any;
console.log('create', option.name);
ptyProcess.on('data', (data: string) => socket.emit(option.name + '-output', data));
socket.on(option.name + '-input', (data: string) => ptyProcess.write(data));
socket.on(option.name + '-resize', (size: [number, number]) => {
ptyProcess.resize(size[0], size[1]);
});
socket.on(option.name + '-exit', () => {
console.log('exit', option.name)
ptyProcess.destroy();
});
socket.emit(option.name + '-pid', ptyProcess.pid);
ptyContainers[option.name] = ptyProcess;
});
socket.on('remove', (name: string) => {
socket.removeAllListeners(name + '-input');
socket.removeAllListeners(name + '-resize');
socket.removeAllListeners(name + '-exit');
if (name && ptyContainers[name] && ptyContainers[name].pid) {
const curentContainer = ptyContainers[name] as any;
curentContainer.destroy();
delete ptyContainers[name];
}
});
};