Update flask_terminal_routes.py

This commit is contained in:
MacRimi
2025-11-21 20:12:07 +01:00
parent 623aec495b
commit 4ddb5f14d9

View File

@@ -13,7 +13,8 @@ import select
import struct import struct
import fcntl import fcntl
import termios import termios
import signal import threading
import time
terminal_bp = Blueprint('terminal', __name__) terminal_bp = Blueprint('terminal', __name__)
sock = Sock() sock = Sock()
@@ -34,6 +35,25 @@ def set_winsize(fd, rows, cols):
except Exception as e: except Exception as e:
print(f"Error setting window size: {e}") print(f"Error setting window size: {e}")
def read_and_forward_output(master_fd, ws):
"""Read from PTY and send to WebSocket"""
while True:
try:
# Use select with timeout to check if data is available
r, _, _ = select.select([master_fd], [], [], 0.01)
if master_fd in r:
try:
data = os.read(master_fd, 4096)
if data:
ws.send(data.decode('utf-8', errors='ignore'))
else:
break
except OSError:
break
except Exception as e:
print(f"Error reading from PTY: {e}")
break
@sock.route('/ws/terminal') @sock.route('/ws/terminal')
def terminal_websocket(ws): def terminal_websocket(ws):
"""WebSocket endpoint for terminal sessions""" """WebSocket endpoint for terminal sessions"""
@@ -62,44 +82,41 @@ def terminal_websocket(ws):
fcntl.fcntl(master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) fcntl.fcntl(master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
# Set initial terminal size # Set initial terminal size
set_winsize(master_fd, 24, 80) set_winsize(master_fd, 30, 120)
# Start thread to read PTY output and forward to WebSocket
output_thread = threading.Thread(
target=read_and_forward_output,
args=(master_fd, ws),
daemon=True
)
output_thread.start()
try: try:
while True: while True:
# Use select to wait for data from either WebSocket or PTY # Receive data from WebSocket (blocking)
readable, _, _ = select.select([ws.sock, master_fd], [], [], 0.1) data = ws.receive(timeout=None)
# Read from WebSocket (user input) if data is None:
if ws.sock in readable: # Client closed connection
break
# Handle terminal resize (optional)
if data.startswith('\x1b[8;'):
try: try:
data = ws.receive(timeout=0) parts = data[4:-1].split(';')
if data is None: rows, cols = int(parts[0]), int(parts[1])
break set_winsize(master_fd, rows, cols)
continue
# Handle special commands (optional)
if data.startswith('\x1b[8;'): # Terminal resize
# Parse resize: ESC[8;{rows};{cols}t
try:
parts = data[4:-1].split(';')
rows, cols = int(parts[0]), int(parts[1])
set_winsize(master_fd, rows, cols)
except:
pass
else:
# Send input to bash
os.write(master_fd, data.encode('utf-8'))
except: except:
break pass
# Read from PTY (bash output) # Send input to bash
if master_fd in readable: try:
try: os.write(master_fd, data.encode('utf-8'))
output = os.read(master_fd, 4096) except OSError as e:
if output: print(f"Error writing to PTY: {e}")
ws.send(output.decode('utf-8', errors='ignore')) break
except OSError:
# PTY closed
break
# Check if process is still alive # Check if process is still alive
if shell_process.poll() is not None: if shell_process.poll() is not None:
@@ -113,16 +130,24 @@ def terminal_websocket(ws):
shell_process.terminate() shell_process.terminate()
shell_process.wait(timeout=1) shell_process.wait(timeout=1)
except: except:
shell_process.kill() try:
shell_process.kill()
except:
pass
os.close(master_fd) try:
os.close(slave_fd) os.close(master_fd)
except:
pass
try:
os.close(slave_fd)
except:
pass
if session_id in active_sessions: if session_id in active_sessions:
del active_sessions[session_id] del active_sessions[session_id]
ws.close()
def init_terminal_routes(app): def init_terminal_routes(app):
"""Initialize terminal routes with Flask app""" """Initialize terminal routes with Flask app"""
sock.init_app(app) sock.init_app(app)