Files
nas-tools/dashboard/backend/routers/terminal.py
T
Gan, Jimmy 88a45b9779
Deploy Dashboard / deploy (push) Failing after 3m26s
Fix terminal: use create_process with binary encoding for PTY I/O
2026-02-19 20:32:36 +08:00

68 lines
2.2 KiB
Python

import asyncio
import struct
from fastapi import WebSocket, WebSocketDisconnect, Query
import asyncssh
import jwt
import config
async def ws_endpoint(websocket: WebSocket, token: str = Query("")):
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()
try:
conn = await asyncssh.connect(
config.SSH_HOST, username=config.SSH_USER,
client_keys=[config.SSH_KEY_PATH], known_hosts=None,
)
except Exception:
await websocket.close(code=1011, reason="SSH connection failed")
return
try:
process = await conn.create_process(
term_type="xterm-256color", term_size=(80, 24),
encoding=None,
)
async def read_ssh():
try:
while True:
data = await process.stdout.read(4096)
if not data:
break
await websocket.send_bytes(data)
except (asyncssh.Error, WebSocketDisconnect, asyncio.CancelledError):
pass
read_task = asyncio.create_task(read_ssh())
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]
process.channel.change_terminal_size(cols, rows)
else:
process.stdin.write(data)
elif "text" in msg and msg["text"]:
process.stdin.write(msg["text"].encode())
except WebSocketDisconnect:
pass
finally:
read_task.cancel()
process.close()
finally:
conn.close()