fix: raise terminal per-user session cap
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 2m44s
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 2m44s
Allow more concurrent dashboard terminal tabs per user and use the current access token for websocket reconnects so refreshed sessions keep working. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
import struct
|
||||
import logging
|
||||
import shlex
|
||||
from collections import Counter
|
||||
from fastapi import WebSocket, Query
|
||||
import asyncssh
|
||||
@@ -10,19 +11,28 @@ from auth import get_current_user_ws
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MAX_SESSIONS = 5
|
||||
MAX_SESSIONS_PER_USER = 2
|
||||
MAX_SESSIONS_PER_USER = 5
|
||||
_session_lock = asyncio.Lock()
|
||||
_active_sessions: dict[int, str] = {}
|
||||
PERSISTENCE_STATUS_PREFIX = "__DASHBOARD_PERSISTENCE__:"
|
||||
|
||||
HOSTS = {
|
||||
"nas": {"host": config.SSH_HOST, "user": config.SSH_USER, "key": config.SSH_KEY_PATH},
|
||||
"vps": {"host": config.VPS_SSH_HOST, "user": config.VPS_SSH_USER, "key": config.VPS_SSH_KEY_PATH},
|
||||
"caddy-vps": {"host": config.CADDY_VPS_SSH_HOST, "user": config.CADDY_VPS_SSH_USER, "key": config.VPS_SSH_KEY_PATH},
|
||||
"mac": {"host": config.MAC_SSH_HOST, "user": config.MAC_SSH_USER, "key": config.MAC_SSH_KEY_PATH},
|
||||
"claude-dev": {"host": config.SSH_HOST, "user": config.SSH_USER, "key": config.SSH_KEY_PATH,
|
||||
"command": "/volume1/@appstore/ContainerManager/usr/bin/docker exec -it claude-dev bash"},
|
||||
"claude-dev": {
|
||||
"host": config.SSH_HOST,
|
||||
"user": config.SSH_USER,
|
||||
"key": config.SSH_KEY_PATH,
|
||||
"persistent_session": {
|
||||
"container": "claude-dev",
|
||||
"session_name": "dashboard-terminal",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# Load known_hosts once at import time — fail closed if missing
|
||||
_known_hosts_available = False
|
||||
try:
|
||||
@@ -33,6 +43,35 @@ except Exception:
|
||||
logger.error("SSH known_hosts file not found at %s — terminal connections will be refused", config.SSH_KNOWN_HOSTS)
|
||||
|
||||
|
||||
def build_host_command(host_config: dict) -> str | None:
|
||||
persistent = host_config.get("persistent_session")
|
||||
if not persistent:
|
||||
return host_config.get("command")
|
||||
|
||||
docker_bin = "/volume1/@appstore/ContainerManager/usr/bin/docker"
|
||||
container = persistent["container"]
|
||||
session_name = persistent["session_name"]
|
||||
quoted_container = shlex.quote(container)
|
||||
quoted_session = shlex.quote(session_name)
|
||||
quoted_marker = shlex.quote(PERSISTENCE_STATUS_PREFIX)
|
||||
attach_cmd = (
|
||||
f"{docker_bin} exec -it {quoted_container} "
|
||||
f"tmux new-session -A -s {quoted_session}"
|
||||
)
|
||||
|
||||
script = " ".join([
|
||||
"if",
|
||||
f"{docker_bin} exec {quoted_container} tmux has-session -t {quoted_session} >/dev/null 2>&1;",
|
||||
"then",
|
||||
f"printf '%sattached\\n' {quoted_marker};",
|
||||
"else",
|
||||
f"printf '%screated\\n' {quoted_marker};",
|
||||
"fi;",
|
||||
f"exec {attach_cmd}",
|
||||
])
|
||||
return f"bash -lc {shlex.quote(script)}"
|
||||
|
||||
|
||||
async def _reserve_session(session_id: int, username: str) -> tuple[bool, str | None, int | None]:
|
||||
async with _session_lock:
|
||||
if len(_active_sessions) >= MAX_SESSIONS:
|
||||
@@ -98,7 +137,7 @@ async def ws_endpoint(websocket: WebSocket, host: str = Query("nas"), token: str
|
||||
|
||||
try:
|
||||
process = await conn.create_process(
|
||||
h.get("command"), term_type="xterm-256color", term_size=(80, 24),
|
||||
build_host_command(h), term_type="xterm-256color", term_size=(80, 24),
|
||||
encoding=None,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user