Bruno Charest 5bc12d0729
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Add terminal session management with heartbeat monitoring, idle timeout detection, session reuse logic, and command history panel UI with search and filtering capabilities
2025-12-18 13:49:40 -05:00

158 lines
4.0 KiB
Python

"""
Pydantic schemas for terminal session API.
"""
from datetime import datetime
from typing import List, Optional, Literal, Any
from pydantic import BaseModel, Field
# ============================================================================
# Command History Schemas
# ============================================================================
class CommandHistoryItem(BaseModel):
"""A single command from history."""
id: int
command: str
created_at: datetime
host_name: Optional[str] = None
username: Optional[str] = None
execution_count: Optional[int] = None
class Config:
from_attributes = True
class CommandHistoryResponse(BaseModel):
"""Response containing command history."""
commands: List[CommandHistoryItem]
total: int
host_id: Optional[str] = None
query: Optional[str] = None
class UniqueCommandItem(BaseModel):
"""A unique command with usage stats."""
command: str
command_hash: str
last_used: datetime
execution_count: int
class UniqueCommandsResponse(BaseModel):
"""Response containing unique commands."""
commands: List[UniqueCommandItem]
total: int
host_id: str
# ============================================================================
# Terminal Session Schemas
# ============================================================================
class TerminalSessionRequest(BaseModel):
"""Request to create a new terminal session."""
mode: Literal["embedded", "popout"] = Field(
default="embedded",
description="Terminal display mode: embedded in drawer or popout window"
)
class TerminalSessionHost(BaseModel):
"""Host information included in session response."""
id: str
name: str
ip: str
status: str
bootstrap_ok: bool
class TerminalSessionResponse(BaseModel):
"""Response after creating a terminal session."""
session_id: str
url: str
websocket_url: str
expires_at: datetime
ttl_seconds: int
mode: str
host: TerminalSessionHost
reused: bool = False # True if an existing session was reused
token: Optional[str] = None # Only provided for new sessions
class TerminalSessionStatus(BaseModel):
"""Current status of a terminal session."""
session_id: str
status: str
host_id: str
host_name: str
mode: str = "embedded"
created_at: datetime
expires_at: datetime
last_seen_at: Optional[datetime] = None
remaining_seconds: int
age_seconds: int = 0
last_seen_seconds: int = 0
class TerminalSessionList(BaseModel):
"""List of active terminal sessions."""
sessions: list[TerminalSessionStatus]
total: int
max_per_user: int
class ActiveSessionInfo(BaseModel):
"""Info about an active session for limit error response."""
session_id: str
host_id: str
host_name: str
mode: str
age_seconds: int
last_seen_seconds: int
class SessionLimitError(BaseModel):
"""Rich error response when session limit is exceeded."""
error: str = "SESSION_LIMIT"
message: str
max_active: int
current_count: int
active_sessions: List[ActiveSessionInfo]
suggested_actions: List[str]
can_reuse: bool = False
reusable_session_id: Optional[str] = None
class HeartbeatRequest(BaseModel):
"""Request to update session heartbeat."""
pass # Empty body, session_id comes from path
class HeartbeatResponse(BaseModel):
"""Response from heartbeat endpoint."""
session_id: str
status: str
last_seen_at: datetime
remaining_seconds: int
healthy: bool = True
class CloseBeaconRequest(BaseModel):
"""Request from sendBeacon to close session."""
reason: str = "client_close"
class SessionMetrics(BaseModel):
"""Terminal session service metrics."""
sessions_created: int
sessions_reused: int
sessions_closed_user: int
sessions_gc_expired: int
sessions_gc_idle: int
session_limit_hits: int
active_processes: int
allocated_ports: int
gc_running: bool