Files
nas-tools/dashboard/backend/routers/terminal.py
T
Gan, Jimmy 69bd41b5a1
Deploy Dashboard / deploy (push) Failing after 2m11s
Fix terminal: pass JWT via query param, remove Docker-incompatible IP check
2026-02-19 17:44:55 +08:00

75 lines
2.2 KiB
Python

import asyncio
import os
import pty
import signal
import struct
import fcntl
import termios
import select
from fastapi import WebSocket, WebSocketDisconnect, Query
import jwt
import config
async def ws_endpoint(websocket: WebSocket, token: str = Query("")):
# Verify auth token
try:
payload = jwt.decode(token, config.SECRET_KEY, algorithms=[config.ALGORITHM])
if payload.get("sub") != config.ADMIN_USER:
await websocket.close(code=1008, reason="Unauthorized")
return
except Exception:
await websocket.close(code=1008, reason="Unauthorized")
return
await websocket.accept()
master_fd, slave_fd = pty.openpty()
pid = os.fork()
if pid == 0:
os.close(master_fd)
os.setsid()
os.dup2(slave_fd, 0)
os.dup2(slave_fd, 1)
os.dup2(slave_fd, 2)
os.close(slave_fd)
os.execvp("/bin/bash", ["/bin/bash"])
os.close(slave_fd)
async def read_pty():
try:
while True:
await asyncio.sleep(0.01)
r, _, _ = select.select([master_fd], [], [], 0)
if r:
data = os.read(master_fd, 4096)
if not data:
break
await websocket.send_bytes(data)
except (OSError, WebSocketDisconnect):
pass
read_task = asyncio.create_task(read_pty())
try:
while True:
msg = await websocket.receive()
if "bytes" in msg and msg["bytes"]:
data = msg["bytes"]
if data[0:1] == b"\x01" and len(data) == 5:
cols = struct.unpack("!H", data[1:3])[0]
rows = struct.unpack("!H", data[3:5])[0]
fcntl.ioctl(master_fd, termios.TIOCSWINSZ, struct.pack("HHHH", rows, cols, 0, 0))
else:
os.write(master_fd, data)
elif "text" in msg and msg["text"]:
os.write(master_fd, msg["text"].encode())
except WebSocketDisconnect:
pass
finally:
read_task.cancel()
os.close(master_fd)
os.kill(pid, signal.SIGTERM)
os.waitpid(pid, 0)