fix ws
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
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'))
|
||||
@@ -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)
|
||||
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)
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user