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 struct
import fcntl import fcntl
import termios import termios
from fastapi import WebSocket, WebSocketDisconnect, Depends import select
import auth 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) async def ws_endpoint(websocket: WebSocket, token: str = Query("")):
client_ip = websocket.client.host if websocket.client else "" # Verify auth token
if not client_ip.startswith("100."): try:
await websocket.close(code=1008, reason="Tailscale only") 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 return
await websocket.accept() await websocket.accept()
master_fd, slave_fd = pty.openpty() 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.execvp("/bin/bash", ["/bin/bash"])
os.close(slave_fd) os.close(slave_fd)
loop = asyncio.get_event_loop()
async def read_pty(): async def read_pty():
try: try:
while True: while True:
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
if select_readable(master_fd): r, _, _ = select.select([master_fd], [], [], 0)
if r:
data = os.read(master_fd, 4096) data = os.read(master_fd, 4096)
if not data: if not data:
break break
@@ -44,11 +50,6 @@ async def ws_endpoint(websocket: WebSocket, user: str = Depends(auth.get_current
except (OSError, WebSocketDisconnect): except (OSError, WebSocketDisconnect):
pass pass
def select_readable(fd):
import select
r, _, _ = select.select([fd], [], [], 0)
return bool(r)
read_task = asyncio.create_task(read_pty()) read_task = asyncio.create_task(read_pty())
try: try:
@@ -56,7 +57,6 @@ async def ws_endpoint(websocket: WebSocket, user: str = Depends(auth.get_current
msg = await websocket.receive() msg = await websocket.receive()
if "bytes" in msg and msg["bytes"]: if "bytes" in msg and msg["bytes"]:
data = msg["bytes"] data = msg["bytes"]
# Handle resize: first byte 0x01 means resize message
if data[0:1] == b"\x01" and len(data) == 5: if data[0:1] == b"\x01" and len(data) == 5:
cols = struct.unpack("!H", data[1:3])[0] cols = struct.unpack("!H", data[1:3])[0]
rows = struct.unpack("!H", data[3:5])[0] rows = struct.unpack("!H", data[3:5])[0]
@@ -38,7 +38,8 @@
fit.fit(); fit.fit();
const proto = location.protocol === "https:" ? "wss:" : "ws:"; 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.binaryType = "arraybuffer";
ws.onopen = () => { ws.onopen = () => {