Files

69 lines
2.2 KiB
Python
Raw Permalink Normal View History

2026-04-07 02:04:22 +05:30
"""YakPanel - Web Terminal API (WebSocket)"""
import asyncio
import os
import sys
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
router = APIRouter(prefix="/terminal", tags=["terminal"])
@router.websocket("/ws")
async def terminal_websocket(websocket: WebSocket):
"""WebSocket terminal - spawns shell and streams I/O"""
await websocket.accept()
token = websocket.query_params.get("token")
if token:
from app.core.security import decode_token
if not decode_token(token):
await websocket.close(code=4001)
return
if sys.platform == "win32":
proc = await asyncio.create_subprocess_shell(
"cmd.exe",
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
)
else:
proc = await asyncio.create_subprocess_shell(
"/bin/bash",
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
env={**os.environ, "TERM": "xterm-256color"},
)
async def read_stdout():
try:
while proc.returncode is None and proc.stdout:
data = await proc.stdout.read(4096)
if data:
await websocket.send_text(data.decode("utf-8", errors="replace"))
except (WebSocketDisconnect, ConnectionResetError):
pass
finally:
try:
proc.kill()
except ProcessLookupError:
pass
async def read_websocket():
try:
while True:
msg = await websocket.receive()
data = msg.get("text") or (msg.get("bytes") or b"").decode("utf-8", errors="replace")
if data and proc.stdin and not proc.stdin.is_closing():
proc.stdin.write(data.encode("utf-8"))
await proc.stdin.drain()
except (WebSocketDisconnect, ConnectionResetError):
pass
finally:
try:
proc.kill()
except ProcessLookupError:
pass
await asyncio.gather(read_stdout(), read_websocket())