From 39ac606c93947c5df7dc9af744df78376426d1a9 Mon Sep 17 00:00:00 2001 From: Cai Date: Mon, 15 Sep 2025 13:23:49 +0000 Subject: [PATCH] fix ws --- code-server-like/app/__init__.py | 6 +- code-server-like/app/extensions.py | 11 +- .../app/sockets/terminal_service.py | 155 +++++++++--------- code-server-like/static/js/terminal.js | 2 +- 4 files changed, 88 insertions(+), 86 deletions(-) diff --git a/code-server-like/app/__init__.py b/code-server-like/app/__init__.py index 93a36bc..b03c82a 100644 --- a/code-server-like/app/__init__.py +++ b/code-server-like/app/__init__.py @@ -1,9 +1,8 @@ from flask import Flask from .views.routes import main_bp -from .sockets.terminal_service import terminal_bp from .views.file import file_bp from .config import Config -from .extensions import init_extensions +from .extensions import init_extensions,register_namespaces def create_app(): app = Flask(__name__, template_folder="../templates", static_folder="../static",static_url_path="/vsc-like/static") @@ -14,9 +13,10 @@ def create_app(): # 注册蓝图 app.register_blueprint(main_bp) - app.register_blueprint(terminal_bp) app.register_blueprint(file_bp) # 初始化SocketIO init_extensions(app) + register_namespaces(app) + return app diff --git a/code-server-like/app/extensions.py b/code-server-like/app/extensions.py index 1d9b1c6..b17a397 100644 --- a/code-server-like/app/extensions.py +++ b/code-server-like/app/extensions.py @@ -3,9 +3,16 @@ from flask_socketio import SocketIO from .config import Config # 初始化扩展 cors = CORS() -socketio = SocketIO() +socketio = SocketIO(path='vsc-like-ws',cors_allowed_origins="*",max_payload_size=52428800, async_mode="eventlet") # 不直接传 app + session_paths = {} def init_extensions(app): cors.init_app(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True) socketio.init_app(app, cors_allowed_origins="*", max_payload_size=52428800, async_mode="threading") - app.extensions["session_paths"] = session_paths \ No newline at end of file + app.extensions["session_paths"] = session_paths + +def register_namespaces(app): + """把所有 Socket.IO namespaces 注册到 socketio""" + # 延迟导入以避免循环 + from .sockets.terminal_service import VSCLikeNameSpace, socketio + socketio.on_namespace(VSCLikeNameSpace('/vsc-like')) \ No newline at end of file diff --git a/code-server-like/app/sockets/terminal_service.py b/code-server-like/app/sockets/terminal_service.py index 7ab154a..c3ff252 100644 --- a/code-server-like/app/sockets/terminal_service.py +++ b/code-server-like/app/sockets/terminal_service.py @@ -3,16 +3,16 @@ import select import subprocess import threading from flask import Blueprint, request, current_app, session -from flask_socketio import SocketIO, emit, join_room +from flask_socketio import Namespace, join_room, leave_room, emit, SocketIO from threading import Thread import sys from ..extensions import socketio -terminal_bp = Blueprint('terminal', __name__, url_prefix='/vsc-like/terminal') +terminal_bp = Blueprint('terminal', __name__, url_prefix='/vsc-like') # 存储终端进程 terminals = {} - +terminal_locks = {} def start_terminal_process(socket_id): """ 监听多个子进程的输出,并通过 WebSocket 发给前端 @@ -52,84 +52,79 @@ def start_terminal_process(socket_id): if process.poll() is not None: break -@socketio.on('connect', namespace='/vsc-like/terminal') -def connect_terminal(): - """ - 处理 WebSocket 连接 - - 验证会话 - - 使用指定的用户启动 bash 终端 - - 启动监听子进程输出的线程 - """ - socket_id = session.get('uuid') - user_id = session.get('user_id') # 从 session 获取用户 ID - path = session.get('path') - if not path or not user_id: - emit('error', {'message': 'Authentication failed, no path or user found for this session'}) - return False # 拒绝连接 +class VSCLikeNameSpace(Namespace): + def on_connect(self, data): + """ + 处理 WebSocket 连接 + - 验证会话 + - 使用指定的用户启动 bash 终端 + - 启动监听子进程输出的线程 + """ + socket_id = session.get('uuid') + user_id = session.get('user_id') # 从 session 获取用户 ID + path = session.get('path') - # 使用 sudo 启动终端进程,以指定的 user_id 用户身份执行 - try: - process = subprocess.Popen( - ['sudo', '-u', user_id, 'bash'], # 使用指定的 user_id 启动 bash - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True - ) - terminals[socket_id] = process - - emit('connected', {'message': 'Terminal connected'}) - join_room(path, namespace='/vsc-like/terminal') - - # 启动监听终端输出的线程 - thread = Thread(target=start_terminal_process, args=(socket_id,)) - thread.daemon = True - thread.start() - - except Exception as e: - emit('error', {'message': f'Error starting terminal process: {str(e)}'}) - return False - -terminal_locks = {} # 存储终端的锁,用于线程同步 - -@socketio.on('send_input', namespace='/vsc-like/terminal') -def send_input(data): - socket_id = session.get('uuid') - input_data = data.get('input') - - # 检查当前 socket_id 是否有对应的终端 - if socket_id in terminals: - # 获取该终端的锁进行线程同步 - lock = terminal_locks.get(socket_id) - if not lock: - lock = threading.Lock() - terminal_locks[socket_id] = lock - - with lock: # 在锁的保护下操作终端 - try: - terminals[socket_id].stdin.write(input_data + '\n') - terminals[socket_id].stdin.flush() - emit('terminal_output', {'data': f'Command sent: {input_data}'}, room=socket_id) - except Exception as e: - emit('error', {'message': f'Error sending input: {str(e)}'}, room=socket_id) - else: - emit('error', {'message': 'No terminal found for this session'}, room=socket_id) - - -@socketio.on('disconnect', namespace='/vsc-like/terminal') -def disconnect_terminal(): - socket_id = session.get('uuid') - - # 检查是否有终端进程需要终止 - if socket_id in terminals: + if not path or not user_id: + emit('error', {'message': 'Authentication failed, no path or user found for this session'}) + return False # 拒绝连接 + # 使用 sudo 启动终端进程,以指定的 user_id 用户身份执行 try: - # 终止终端进程 - terminals[socket_id].terminate() - del terminals[socket_id] - # 删除锁 - if socket_id in terminal_locks: - del terminal_locks[socket_id] - emit('terminal_output', {'data': 'Terminal disconnected and terminated'}, room=socket_id) + process = subprocess.Popen( + ['sudo', '-u', user_id, 'bash'], # 使用指定的 user_id 启动 bash + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + terminals[socket_id] = process + + emit('connected', {'message': 'Terminal connected'}) + join_room(path, namespace='/vsc-like') + + # 启动监听终端输出的线程 + thread = Thread(target=start_terminal_process, args=(socket_id,)) + thread.daemon = True + thread.start() + except Exception as e: - emit('error', {'message': f'Error disconnecting terminal: {str(e)}'}, room=socket_id) \ No newline at end of file + emit('error', {'message': f'Error starting terminal process: {str(e)}'}) + return False + + def on_send_input(self, data): + socket_id = session.get('uuid') + input_data = data.get('input') + + # 检查当前 socket_id 是否有对应的终端 + if socket_id in terminals: + # 获取该终端的锁进行线程同步 + lock = terminal_locks.get(socket_id) + if not lock: + lock = threading.Lock() + terminal_locks[socket_id] = lock + + with lock: # 在锁的保护下操作终端 + try: + terminals[socket_id].stdin.write(input_data + '\n') + terminals[socket_id].stdin.flush() + emit('terminal_output', {'data': f'Command sent: {input_data}'}, room=socket_id) + except Exception as e: + emit('error', {'message': f'Error sending input: {str(e)}'}, room=socket_id) + else: + emit('error', {'message': 'No terminal found for this session'}, room=socket_id) + + def on_disconnect(): + socket_id = session.get('uuid') + + # 检查是否有终端进程需要终止 + if socket_id in terminals: + try: + # 终止终端进程 + terminals[socket_id].terminate() + del terminals[socket_id] + # 删除锁 + if socket_id in terminal_locks: + del terminal_locks[socket_id] + emit('terminal_output', {'data': 'Terminal disconnected and terminated'}, room=socket_id) + except Exception as e: + emit('error', {'message': f'Error disconnecting terminal: {str(e)}'}, room=socket_id) diff --git a/code-server-like/static/js/terminal.js b/code-server-like/static/js/terminal.js index 10a2063..65c8381 100644 --- a/code-server-like/static/js/terminal.js +++ b/code-server-like/static/js/terminal.js @@ -1,4 +1,4 @@ -const socket = io.connect('https://hsamooc.cn/vsc-like/terminal'); +const socket = io('https://hsamooc.cn/vsc-like',{path:'/vsc-like-ws'}); let term; document.addEventListener('DOMContentLoaded', () => {