Files
alex a01e6252f5 docs: add responsive terminal documentation
- Add docs/features/terminal.md with user guide, connection states,
  keyboard shortcuts, protocol details, and troubleshooting
- Update docs/architecture/frontend.md with terminal component stack,
  connection hook behavior, and data flow diagrams
- Update docs/architecture/backend.md with terminal system architecture,
  protocol reference, message batching, and reconnect behavior
- Update docs/README.md to include terminal in feature list
2026-05-27 21:46:57 +02:00

278 lines
9.6 KiB
Markdown

# Backend Architecture
## Overview
The Headquarter backend is built with **FastAPI** and follows a layered architecture pattern.
## Architecture Diagram
```
┌─────────────────────────────────────────────────────────────┐
│ FastAPI App │
├─────────────────────────────────────────────────────────────┤
│ Middleware: CORS → Request Logging → Exception Logging │
├─────────────────────────────────────────────────────────────┤
│ API Layer (src/api/) │
│ ┌─────────┐ ┌─────────┐ ┌────────┐ ┌──────────┐ │
│ │ Auth │ │Terminal │ │Projects│ │ Git │ │
│ │ Routes │ │ WS │ │ Routes │ │ Repos │ │
│ └────┬────┘ └────┬────┘ └───┬────┘ └────┬─────┘ │
├───────┼───────────┼──────────┼───────────┼──────────────────┤
│ │ │ │ │ │
│ Auth │ Terminal │ Project │ Git │ │
│ Layer │ Manager │ Service │ Service │ │
│ │ + Session│ │ │ │
├───────┴───────────┴──────────┴───────────┴──────────────────┤
│ Data Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Models │ │ Database │ │ Config │ │
│ │(SQLAlch) │ │(AsyncPG) │ │(Pydantic)│ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Directory Structure
```
src/
├── api/ # API Routes
│ ├── auth.py # Authentication endpoints
│ ├── terminal.py # WebSocket terminal endpoint
│ ├── projects.py # Project endpoints
│ ├── git_repositories.py # Repository endpoints
│ ├── users.py # User endpoints
│ ├── tool_types.py # Tool type endpoints
│ ├── ssh_keys.py # SSH key endpoints
│ └── dashboard.py # Dashboard endpoints
├── auth/ # Authentication
│ ├── session.py # Session management
│ ├── oidc.py # OAuth2 client
│ ├── dependencies.py # Auth dependencies
│ └── cookies.py # Cookie utilities
├── models/ # Database Models
│ ├── user.py # User model
│ ├── project.py # Project model
│ ├── git_repository.py # Repository model
│ ├── tool_type.py # Tool type model
│ ├── ssh_key.py # SSH key model
│ └── user_config.py # User config model
├── services/ # Business Logic
│ ├── terminal_manager.py # Terminal session manager
│ ├── terminal_session.py # PTY + docker exec session
│ ├── docker.py # Docker operations
│ └── profile_resolver.py # Profile resolution
├── utils/ # Utilities
│ ├── git_url_parser.py # URL parsing
│ ├── git_files.py # Git file operations
│ └── git_history.py # Git history operations
├── config.py # Configuration
├── database.py # Database setup
└── main.py # Application entry point
```
## Terminal System
The terminal system provides interactive shell access to running tool instances via WebSocket.
### Architecture
```
Client (WebSocket)
terminal.py (FastAPI WS endpoint)
├─ Auth validation (session cookie)
├─ Instance ownership check
├─ Session lifecycle (create / monitor / cleanup)
└─ Echo state detection (termios)
TerminalManager
├─ create_session() → spawns TerminalSession
├─ _read_loop() → batches PTY output → WebSocket
├─ _write_loop() → WebSocket input → PTY
└─ _heartbeat_loop() → closes idle connections (60s)
TerminalSession
├─ start() → pty.openpty() + docker exec
├─ read_output() → select.select() + os.read()
├─ write_input() → os.write() to PTY master
├─ resize() → TIOCSWINSZ ioctl
└─ check_echo_state() → termios.ECHO flag
```
### Protocol
**Binary frames**: Raw terminal I/O (hot path)
**Text (JSON) frames**: Control messages
**Control messages:**
| Direction | Type | Purpose |
|-----------|------|---------|
| Client → Server | `ping` | Heartbeat (every 15s idle) |
| Server → Client | `pong` | Heartbeat response |
| Client → Server | `resize` | Terminal dimensions changed |
| Server → Client | `set_echo_state` | Enable/disable local echo |
| Server → Client | `session_ended` | Container process exited |
### Message Batching
The read loop batches small PTY reads into single WebSocket frames:
- Buffer accumulates data for up to 16ms
- Flushed immediately when no new data is available
- Reduces WebSocket frame overhead for rapid output
### Reconnect Behavior
The server cannot resume a `docker exec` PTY across connections. On reconnect:
1. Old session is terminated
2. New `docker exec` is spawned
3. Client restores scrollback from `sessionStorage`
4. New shell appears seamlessly to the user
## Layers
### 1. API Layer (`src/api/`)
**Responsibilities:**
- Define HTTP endpoints
- Parse request parameters
- Return HTTP responses
- Use dependencies for auth and DB
**Pattern:**
```python
@router.get("/projects")
async def list_projects(
session: AsyncSession = Depends(get_db_session),
user_id: str = Depends(get_current_user_id),
):
# Call service layer
projects = await project_service.list(session, user_id)
return projects
```
### 2. Auth Layer (`src/auth/`)
**Responsibilities:**
- Session management (create, verify, expire)
- OAuth2 flow (login, callback)
- User authentication dependencies
**Key Components:**
- `session.py`: HMAC-SHA256 signed cookies
- `oidc.py`: OAuth2 token exchange
- `dependencies.py`: FastAPI dependencies for auth
### 3. Data Layer (`src/models/`, `src/database.py`)
**Responsibilities:**
- Database schema definition
- Async database sessions
- Connection management
**Technology:**
- SQLAlchemy 2.0 with async support
- asyncpg driver for PostgreSQL
- Alembic for migrations
### 4. Utility Layer (`src/utils/`)
**Responsibilities:**
- Git operations (file browsing, history)
- URL parsing
- Helper functions
## Data Flow
### Request Lifecycle
```
1. Request arrives at FastAPI
2. Middleware processes (CORS, logging)
3. Auth dependency verifies session
4. Route handler processes request
5. Database session executes queries
6. Response returned to client
```
### Authentication Flow
```
1. User clicks login
2. Backend redirects to Authentik
3. User authenticates
4. Authentik redirects with code
5. Backend exchanges code for token
6. Backend fetches user info
7. Backend creates session cookie
8. User is authenticated
```
## Dependencies
### Database Session
```python
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
async with SessionLocal() as session:
yield session
```
### Current User
```python
async def get_current_user_id(
request: Request,
settings: Settings = Depends(get_settings),
) -> str:
# Verify session cookie
# Return user_id
```
## Configuration
Configuration is managed via Pydantic Settings:
```python
class Settings(BaseSettings):
app_env: str = "development"
database_url: str = "..."
authentik_domain: str = "..."
# ...
```
Environment variables are automatically loaded from `.env` files.
## Error Handling
Errors are handled at multiple levels:
1. **Validation**: Pydantic validates request bodies
2. **HTTP Exceptions**: FastAPI HTTPException for client errors
3. **Middleware**: ExceptionLoggingMiddleware logs server errors
4. **Database**: SQLAlchemy errors converted to HTTP responses
## Testing
- **Unit tests**: SQLite in-memory database
- **Integration tests**: PostgreSQL with transaction rollback
- **Fixtures**: Shared in `conftest.py`
## Technology Stack
| Component | Technology | Version |
|-----------|-----------|---------|
| Web Framework | FastAPI | ^0.104 |
| ORM | SQLAlchemy | ^2.0 |
| Database Driver | asyncpg | ^0.29 |
| Validation | Pydantic | ^2.0 |
| Migrations | Alembic | ^1.12 |
| HTTP Client | httpx | ^0.25 |
| Testing | pytest | ^7.4 |
## Related Documentation
- [Database Schema](database.md)
- [Frontend Architecture](../frontend.md)
- [API Documentation](../api/)