Fix terminal: pass JWT via query param, remove Docker-incompatible IP check
Deploy Dashboard / deploy (push) Failing after 2m11s

This commit is contained in:
Gan, Jimmy
2026-02-19 17:44:55 +08:00
parent bf49e13e56
commit 69bd41b5a1
2 changed files with 18 additions and 17 deletions
+16 -16
View File
@@ -5,16 +5,22 @@ import signal
import struct
import fcntl
import termios
from fastapi import WebSocket, WebSocketDisconnect, Depends
import auth
import select
from fastapi import WebSocket, WebSocketDisconnect, Query
import jwt
import config
async def ws_endpoint(websocket: WebSocket, user: str = Depends(auth.get_current_user_ws)):
# Block non-Tailscale IPs (Defense in depth)
client_ip = websocket.client.host if websocket.client else ""
if not client_ip.startswith("100."):
await websocket.close(code=1008, reason="Tailscale only")
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()
@@ -30,13 +36,13 @@ async def ws_endpoint(websocket: WebSocket, user: str = Depends(auth.get_current
os.execvp("/bin/bash", ["/bin/bash"])
os.close(slave_fd)
loop = asyncio.get_event_loop()
async def read_pty():
try:
while True:
await asyncio.sleep(0.01)
if select_readable(master_fd):
r, _, _ = select.select([master_fd], [], [], 0)
if r:
data = os.read(master_fd, 4096)
if not data:
break
@@ -44,11 +50,6 @@ async def ws_endpoint(websocket: WebSocket, user: str = Depends(auth.get_current
except (OSError, WebSocketDisconnect):
pass
def select_readable(fd):
import select
r, _, _ = select.select([fd], [], [], 0)
return bool(r)
read_task = asyncio.create_task(read_pty())
try:
@@ -56,7 +57,6 @@ async def ws_endpoint(websocket: WebSocket, user: str = Depends(auth.get_current
msg = await websocket.receive()
if "bytes" in msg and msg["bytes"]:
data = msg["bytes"]
# Handle resize: first byte 0x01 means resize message
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]
@@ -38,7 +38,8 @@
fit.fit();
const proto = location.protocol === "https:" ? "wss:" : "ws:";
const ws = new WebSocket(`${proto}//${location.host}/ws/terminal`);
const token = localStorage.getItem("token") || "";
const ws = new WebSocket(`${proto}//${location.host}/ws/terminal?token=${encodeURIComponent(token)}`);
ws.binaryType = "arraybuffer";
ws.onopen = () => {