mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-12-15 00:26:23 +00:00
Update AppImage
This commit is contained in:
@@ -273,7 +273,7 @@ export function ProxmoxDashboard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background flex flex-col">
|
<div className="min-h-screen bg-background">
|
||||||
<OnboardingCarousel />
|
<OnboardingCarousel />
|
||||||
<ReleaseNotesModal open={showReleaseNotes} onClose={() => setShowReleaseNotes(false)} />
|
<ReleaseNotesModal open={showReleaseNotes} onClose={() => setShowReleaseNotes(false)} />
|
||||||
|
|
||||||
@@ -610,8 +610,8 @@ export function ProxmoxDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="container mx-auto px-4 md:px-6 py-4 md:py-6 flex-1 flex flex-col min-h-0">
|
<div className="container mx-auto px-4 md:px-6 py-4 md:py-6">
|
||||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col min-h-0">
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4 md:space-y-6">
|
||||||
<TabsContent value="overview" className="space-y-4 md:space-y-6 mt-0">
|
<TabsContent value="overview" className="space-y-4 md:space-y-6 mt-0">
|
||||||
<SystemOverview key={`overview-${componentKey}`} />
|
<SystemOverview key={`overview-${componentKey}`} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
@@ -632,11 +632,7 @@ export function ProxmoxDashboard() {
|
|||||||
<Hardware key={`hardware-${componentKey}`} />
|
<Hardware key={`hardware-${componentKey}`} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent
|
<TabsContent value="terminal" className="mt-0">
|
||||||
value="terminal"
|
|
||||||
className="flex flex-col overflow-hidden mt-0 w-full"
|
|
||||||
style={{ height: "calc(100vh - 300px)" }}
|
|
||||||
>
|
|
||||||
<TerminalPanel key={`terminal-${componentKey}`} />
|
<TerminalPanel key={`terminal-${componentKey}`} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
@@ -648,9 +644,8 @@ export function ProxmoxDashboard() {
|
|||||||
<Settings />
|
<Settings />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer className="container mx-auto px-4 md:px-6 py-4 border-t border-border text-center text-xs md:text-sm text-muted-foreground flex-shrink-0">
|
<footer className="mt-8 md:mt-12 pt-4 md:pt-6 border-t border-border text-center text-xs md:text-sm text-muted-foreground">
|
||||||
<p className="font-medium mb-2">ProxMenux Monitor v1.0.1</p>
|
<p className="font-medium mb-2">ProxMenux Monitor v1.0.1</p>
|
||||||
<p>
|
<p>
|
||||||
<a
|
<a
|
||||||
@@ -663,6 +658,7 @@ export function ProxmoxDashboard() {
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
<HealthStatusModal open={showHealthModal} onOpenChange={setShowHealthModal} getApiUrl={getApiUrl} />
|
<HealthStatusModal open={showHealthModal} onOpenChange={setShowHealthModal} getApiUrl={getApiUrl} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -288,6 +288,8 @@ export const TerminalPanel: React.FC<TerminalPanelProps> = ({ websocketUrl, onCl
|
|||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
scrollback: 2000,
|
scrollback: 2000,
|
||||||
disableStdin: false,
|
disableStdin: false,
|
||||||
|
cols: isMobile ? 40 : layout === "grid" ? 60 : 120,
|
||||||
|
rows: isMobile ? 20 : layout === "grid" ? 15 : 30,
|
||||||
theme: {
|
theme: {
|
||||||
background: "#000000",
|
background: "#000000",
|
||||||
foreground: "#ffffff",
|
foreground: "#ffffff",
|
||||||
@@ -316,47 +318,7 @@ export const TerminalPanel: React.FC<TerminalPanelProps> = ({ websocketUrl, onCl
|
|||||||
term.loadAddon(fitAddon)
|
term.loadAddon(fitAddon)
|
||||||
|
|
||||||
term.open(container)
|
term.open(container)
|
||||||
|
|
||||||
const performResize = () => {
|
|
||||||
// Ensure xterm viewport has no extra padding
|
|
||||||
const xtermViewport = container.querySelector(".xterm-viewport") as HTMLElement
|
|
||||||
const xtermScreen = container.querySelector(".xterm-screen") as HTMLElement
|
|
||||||
if (xtermViewport) xtermViewport.style.padding = "0"
|
|
||||||
if (xtermScreen) xtermScreen.style.padding = "0"
|
|
||||||
|
|
||||||
// Get actual container dimensions
|
|
||||||
const containerRect = container.getBoundingClientRect()
|
|
||||||
console.log(`[v0] Container dimensions: ${containerRect.width}x${containerRect.height}`)
|
|
||||||
|
|
||||||
// Only resize if container has valid dimensions
|
|
||||||
if (containerRect.width > 0 && containerRect.height > 0) {
|
|
||||||
fitAddon.fit()
|
fitAddon.fit()
|
||||||
const cols = term.cols
|
|
||||||
const rows = term.rows
|
|
||||||
console.log(`[v0] Terminal fitted to: ${cols}x${rows}`)
|
|
||||||
|
|
||||||
// Send resize to backend via HTTP
|
|
||||||
const apiUrl = getApiUrl()
|
|
||||||
fetch(`${apiUrl}/api/terminal/${terminal.id}/resize`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ cols, rows }),
|
|
||||||
})
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((data) => {
|
|
||||||
console.log(`[v0] Backend PTY resized to: ${data.cols}x${data.rows}`)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(`[v0] Error resizing backend PTY:`, err)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
console.log(`[v0] Container not ready yet, dimensions: ${containerRect.width}x${containerRect.height}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => performResize(), 150)
|
|
||||||
setTimeout(() => performResize(), 400)
|
|
||||||
setTimeout(() => performResize(), 800)
|
|
||||||
|
|
||||||
const wsUrl = websocketUrl || getWebSocketUrl()
|
const wsUrl = websocketUrl || getWebSocketUrl()
|
||||||
const ws = new WebSocket(wsUrl)
|
const ws = new WebSocket(wsUrl)
|
||||||
@@ -364,9 +326,6 @@ export const TerminalPanel: React.FC<TerminalPanelProps> = ({ websocketUrl, onCl
|
|||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
setTerminals((prev) => prev.map((t) => (t.id === terminal.id ? { ...t, isConnected: true, term, ws } : t)))
|
setTerminals((prev) => prev.map((t) => (t.id === terminal.id ? { ...t, isConnected: true, term, ws } : t)))
|
||||||
term.writeln("\x1b[32mConnected to ProxMenux terminal.\x1b[0m")
|
term.writeln("\x1b[32mConnected to ProxMenux terminal.\x1b[0m")
|
||||||
|
|
||||||
setTimeout(() => performResize(), 250)
|
|
||||||
setTimeout(() => performResize(), 600)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
@@ -392,7 +351,7 @@ export const TerminalPanel: React.FC<TerminalPanelProps> = ({ websocketUrl, onCl
|
|||||||
|
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
try {
|
try {
|
||||||
performResize()
|
fitAddon.fit()
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore resize errors
|
// Ignore resize errors
|
||||||
}
|
}
|
||||||
@@ -496,18 +455,8 @@ export const TerminalPanel: React.FC<TerminalPanelProps> = ({ websocketUrl, onCl
|
|||||||
const activeTerminal = terminals.find((t) => t.id === activeTerminalId)
|
const activeTerminal = terminals.find((t) => t.id === activeTerminalId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex flex-col h-full bg-zinc-950 rounded-md overflow-hidden">
|
||||||
<style jsx>{`
|
<div className="flex items-center justify-between px-4 py-2 bg-zinc-900 border-b border-zinc-800">
|
||||||
:global(.xterm .xterm-viewport) {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
:global(.xterm .xterm-screen) {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
|
|
||||||
<div className="h-full w-full flex flex-col bg-zinc-900 rounded-md overflow-hidden">
|
|
||||||
<div className="flex items-center justify-between px-4 py-2 bg-zinc-900 border-b border-zinc-800 flex-shrink-0">
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Activity className="h-5 w-5 text-blue-500" />
|
<Activity className="h-5 w-5 text-blue-500" />
|
||||||
<div
|
<div
|
||||||
@@ -588,57 +537,57 @@ export const TerminalPanel: React.FC<TerminalPanelProps> = ({ websocketUrl, onCl
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 overflow-hidden w-full">
|
<div className="flex-1 min-h-0">
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
<Tabs value={activeTerminalId} onValueChange={setActiveTerminalId} className="h-full w-full flex flex-col">
|
<Tabs value={activeTerminalId} onValueChange={setActiveTerminalId} className="h-full flex flex-col">
|
||||||
<TabsList className="bg-zinc-900 border-b border-zinc-800 rounded-none justify-start overflow-x-auto flex-shrink-0">
|
<TabsList className="w-full justify-start bg-zinc-900 rounded-none border-b border-zinc-800">
|
||||||
{terminals.map((terminal) => (
|
{terminals.map((terminal) => (
|
||||||
<TabsTrigger
|
<TabsTrigger key={terminal.id} value={terminal.id} className="relative">
|
||||||
key={terminal.id}
|
|
||||||
value={terminal.id}
|
|
||||||
className="data-[state=active]:bg-blue-500/20 data-[state=active]:border-b-2 data-[state=active]:border-blue-500 rounded-none"
|
|
||||||
>
|
|
||||||
{terminal.title}
|
{terminal.title}
|
||||||
</TabsTrigger>
|
|
||||||
))}
|
|
||||||
</TabsList>
|
|
||||||
{terminals.map((terminal) => (
|
|
||||||
<TabsContent
|
|
||||||
key={terminal.id}
|
|
||||||
value={terminal.id}
|
|
||||||
className="flex-1 m-0 data-[state=active]:flex data-[state=inactive]:hidden"
|
|
||||||
>
|
|
||||||
<div ref={setContainerRef(terminal.id)} className="w-full h-full bg-black" />
|
|
||||||
</TabsContent>
|
|
||||||
))}
|
|
||||||
</Tabs>
|
|
||||||
) : (
|
|
||||||
<div className={`${getLayoutClass()} gap-2 h-full w-full p-2`}>
|
|
||||||
{terminals.map((terminal) => (
|
|
||||||
<div
|
|
||||||
key={terminal.id}
|
|
||||||
className={`relative bg-black rounded flex flex-col overflow-hidden border ${
|
|
||||||
terminal.id === activeTerminalId ? "border-blue-500" : "border-zinc-700"
|
|
||||||
}`}
|
|
||||||
onClick={() => setActiveTerminalId(terminal.id)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between px-2 py-1 bg-zinc-800 border-b border-zinc-700">
|
|
||||||
<span className="text-xs font-medium text-white">{terminal.title}</span>
|
|
||||||
{terminals.length > 1 && (
|
{terminals.length > 1 && (
|
||||||
<Button
|
<button
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="h-5 w-5 p-0"
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
closeTerminal(terminal.id)
|
closeTerminal(terminal.id)
|
||||||
}}
|
}}
|
||||||
|
className="ml-2 hover:bg-zinc-700 rounded p-0.5"
|
||||||
>
|
>
|
||||||
<X className="h-3 w-3" />
|
<X className="h-3 w-3" />
|
||||||
</Button>
|
</button>
|
||||||
|
)}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
{terminals.map((terminal) => (
|
||||||
|
<TabsContent key={terminal.id} value={terminal.id} className="flex-1 m-0 p-0">
|
||||||
|
<div
|
||||||
|
ref={setContainerRef(terminal.id)}
|
||||||
|
className="w-full h-full bg-black"
|
||||||
|
style={{ height: "calc(100vh - 24rem)" }}
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
) : (
|
||||||
|
<div className={`${getLayoutClass()} h-full gap-0.5 bg-zinc-800 p-0.5`}>
|
||||||
|
{terminals.map((terminal) => (
|
||||||
|
<div key={terminal.id} className="relative bg-zinc-900 overflow-hidden">
|
||||||
|
<div className="absolute top-0 left-0 right-0 z-10 flex items-center justify-between px-2 py-1 bg-zinc-900/95 border-b border-zinc-800">
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTerminalId(terminal.id)}
|
||||||
|
className={`text-xs font-medium ${
|
||||||
|
activeTerminalId === terminal.id ? "text-blue-400" : "text-zinc-500"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{terminal.title}
|
||||||
|
</button>
|
||||||
|
{terminals.length > 1 && (
|
||||||
|
<button onClick={() => closeTerminal(terminal.id)} className="hover:bg-zinc-700 rounded p-0.5">
|
||||||
|
<X className="h-3 w-3" />
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div ref={setContainerRef(terminal.id)} className="flex-1 w-full bg-black" />
|
<div ref={setContainerRef(terminal.id)} className="w-full h-full bg-black pt-7" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -850,6 +799,5 @@ export const TerminalPanel: React.FC<TerminalPanelProps> = ({ websocketUrl, onCl
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,79 @@ import requests
|
|||||||
terminal_bp = Blueprint('terminal', __name__)
|
terminal_bp = Blueprint('terminal', __name__)
|
||||||
sock = Sock()
|
sock = Sock()
|
||||||
|
|
||||||
# Active terminal sessions - now stores session by session_id string
|
# Active terminal sessions
|
||||||
sessions = {}
|
active_sessions = {}
|
||||||
|
|
||||||
|
@terminal_bp.route('/api/terminal/health', methods=['GET'])
|
||||||
|
def terminal_health():
|
||||||
|
"""Health check for terminal service"""
|
||||||
|
return {'success': True, 'active_sessions': len(active_sessions)}
|
||||||
|
|
||||||
|
@terminal_bp.route('/api/terminal/search-command', methods=['GET'])
|
||||||
|
def search_command():
|
||||||
|
"""Proxy endpoint for cheat.sh API to avoid CORS issues"""
|
||||||
|
query = request.args.get('q', '')
|
||||||
|
|
||||||
|
if not query or len(query) < 2:
|
||||||
|
return jsonify({'error': 'Query too short'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
url = f'https://cht.sh/{query.replace(" ", "+")}?QT'
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'curl/7.68.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(url, headers=headers, timeout=10)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
content = response.text
|
||||||
|
examples = []
|
||||||
|
current_description = []
|
||||||
|
|
||||||
|
for line in content.split('\n'):
|
||||||
|
stripped = line.strip()
|
||||||
|
|
||||||
|
# Ignorar líneas vacías
|
||||||
|
if not stripped:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Si es un comentario
|
||||||
|
if stripped.startswith('#'):
|
||||||
|
# Acumular descripciones
|
||||||
|
current_description.append(stripped[1:].strip())
|
||||||
|
# Si no es comentario, es un comando
|
||||||
|
elif stripped and not stripped.startswith('http'):
|
||||||
|
# Unir las descripciones acumuladas
|
||||||
|
description = ' '.join(current_description) if current_description else ''
|
||||||
|
|
||||||
|
examples.append({
|
||||||
|
'description': description,
|
||||||
|
'command': stripped
|
||||||
|
})
|
||||||
|
|
||||||
|
# Resetear descripciones para el siguiente comando
|
||||||
|
current_description = []
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'examples': examples
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'API returned status {response.status_code}'
|
||||||
|
}), response.status_code
|
||||||
|
|
||||||
|
except requests.Timeout:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Request timeout'
|
||||||
|
}), 504
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
def set_winsize(fd, rows, cols):
|
def set_winsize(fd, rows, cols):
|
||||||
"""Set terminal window size"""
|
"""Set terminal window size"""
|
||||||
@@ -50,33 +121,6 @@ def read_and_forward_output(master_fd, ws):
|
|||||||
print(f"Error reading from PTY: {e}")
|
print(f"Error reading from PTY: {e}")
|
||||||
break
|
break
|
||||||
|
|
||||||
@terminal_bp.route('/api/terminal/health', methods=['GET'])
|
|
||||||
def terminal_health():
|
|
||||||
"""Health check for terminal service"""
|
|
||||||
return {'success': True, 'active_sessions': len(sessions)}
|
|
||||||
|
|
||||||
@terminal_bp.route('/api/terminal/<session_id>/resize', methods=['POST'])
|
|
||||||
def resize_terminal(session_id):
|
|
||||||
"""Resize the PTY for a given terminal session."""
|
|
||||||
if session_id not in sessions:
|
|
||||||
return jsonify({'error': 'Session not found'}), 404
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = request.get_json()
|
|
||||||
cols = int(data.get('cols', 120))
|
|
||||||
rows = int(data.get('rows', 30))
|
|
||||||
|
|
||||||
# Resize the PTY to match the frontend terminal dimensions
|
|
||||||
master_fd = sessions[session_id]['master_fd']
|
|
||||||
set_winsize(master_fd, rows, cols)
|
|
||||||
|
|
||||||
print(f"[v0] Terminal {session_id} resized to {cols}x{rows}")
|
|
||||||
|
|
||||||
return jsonify({'status': 'success', 'cols': cols, 'rows': rows})
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error resizing terminal {session_id}: {e}")
|
|
||||||
return jsonify({'error': str(e)}), 500
|
|
||||||
|
|
||||||
@sock.route('/ws/terminal')
|
@sock.route('/ws/terminal')
|
||||||
def terminal_websocket(ws):
|
def terminal_websocket(ws):
|
||||||
"""WebSocket endpoint for terminal sessions"""
|
"""WebSocket endpoint for terminal sessions"""
|
||||||
@@ -95,8 +139,8 @@ def terminal_websocket(ws):
|
|||||||
env=dict(os.environ, TERM='xterm-256color', PS1='\\u@\\h:\\w\\$ ')
|
env=dict(os.environ, TERM='xterm-256color', PS1='\\u@\\h:\\w\\$ ')
|
||||||
)
|
)
|
||||||
|
|
||||||
session_id = str(int(time.time() * 1000))
|
session_id = id(ws)
|
||||||
sessions[session_id] = {
|
active_sessions[session_id] = {
|
||||||
'process': shell_process,
|
'process': shell_process,
|
||||||
'master_fd': master_fd
|
'master_fd': master_fd
|
||||||
}
|
}
|
||||||
@@ -105,10 +149,8 @@ def terminal_websocket(ws):
|
|||||||
flags = fcntl.fcntl(master_fd, fcntl.F_GETFL)
|
flags = fcntl.fcntl(master_fd, fcntl.F_GETFL)
|
||||||
fcntl.fcntl(master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
fcntl.fcntl(master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
||||||
|
|
||||||
set_winsize(master_fd, 30, 80)
|
# Set initial terminal size
|
||||||
|
set_winsize(master_fd, 30, 120)
|
||||||
# Send session_id to frontend
|
|
||||||
ws.send(f"\x1b]0;SESSION_ID:{session_id}\x07")
|
|
||||||
|
|
||||||
# Start thread to read PTY output and forward to WebSocket
|
# Start thread to read PTY output and forward to WebSocket
|
||||||
output_thread = threading.Thread(
|
output_thread = threading.Thread(
|
||||||
@@ -127,17 +169,14 @@ def terminal_websocket(ws):
|
|||||||
# Client closed connection
|
# Client closed connection
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Handle terminal resize (optional)
|
||||||
if data.startswith('\x1b[8;'):
|
if data.startswith('\x1b[8;'):
|
||||||
try:
|
try:
|
||||||
# Parse: \x1b[8;{rows};{cols}t
|
|
||||||
parts = data[4:-1].split(';')
|
parts = data[4:-1].split(';')
|
||||||
if len(parts) >= 2:
|
|
||||||
rows, cols = int(parts[0]), int(parts[1])
|
rows, cols = int(parts[0]), int(parts[1])
|
||||||
set_winsize(master_fd, rows, cols)
|
set_winsize(master_fd, rows, cols)
|
||||||
print(f"[v0] Terminal resized via WebSocket to {rows}x{cols}")
|
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except:
|
||||||
print(f"Error parsing resize: {e}")
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Send input to bash
|
# Send input to bash
|
||||||
@@ -174,8 +213,8 @@ def terminal_websocket(ws):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if session_id in sessions:
|
if session_id in active_sessions:
|
||||||
del sessions[session_id]
|
del active_sessions[session_id]
|
||||||
|
|
||||||
def init_terminal_routes(app):
|
def init_terminal_routes(app):
|
||||||
"""Initialize terminal routes with Flask app"""
|
"""Initialize terminal routes with Flask app"""
|
||||||
|
|||||||
Reference in New Issue
Block a user