FastAPI-based REST API for managing tmux sessions, windows, panes, and ttyd (web terminal) instances. Uses MySQL for persistence.
cd fast-api
source venv/bin/activate
uvicorn main:app --host 0.0.0.0 --port 14444 --reloadOr via Docker:
cd fast-api
docker compose up -dRun all tests:
cd fast-api
bash run_tests.shRun single pytest test:
docker exec fast-api python -m pytest tests/test_create_window.py::TestCreateWindow::test_create_window -vRun single curl test:
bash tests/curl/test_health.shNo formal linter configured. Follow code style guidelines below.
- Use 4 spaces for indentation (no tabs)
- No trailing whitespace
- Max line length: 120 characters (soft limit)
- Use descriptive variable/function names
Standard library first, then third-party, then local:
import os
import subprocess
from typing import Optional
import pymysql
import yaml
from fastapi import APIRouter, HTTPException
from routers.tmux.router import run_tmux- Use Python 3.10+ union syntax:
str | None(notOptional[str]) - Use
dictfor generic dicts, notDict[str, Any] - Return type hints on functions when non-trivial:
def run_tmux(cmd: list[str], check_session: bool = False) -> str | None:
...| Type | Convention | Example |
|---|---|---|
| Functions | snake_case | get_db(), create_ttyd_pane_common() |
| Classes | PascalCase | WindowCreate, ServiceIn |
| Constants | UPPER_SNAKE | MYSQL_HOST, TTYD_PORT_RANGE_PROD |
| Variables | snake_case | session_check, pane_id |
| Private functions | prefix with _ |
_load_api_token() |
class WindowCreate(BaseModel):
win_name: str
dev: bool = False
workspace: Optional[str] = None
init_script: str = "pwd"
@field_validator('win_name')
@classmethod
def validate_win_name(cls, v):
if not re.match(r'^[a-zA-Z0-9_]+$', v):
raise ValueError('win_name must contain only alphanumeric and underscores')
return v- Use
Optional[str] = Nonefor optional fields with defaults - Use Pydantic v2 validators for input validation
- Always validate user input (regex, length, etc.)
- Use
HTTPException(status_code=400, detail="message")for API errors - Use
try/exceptwith specific exceptions, thenpassor re-raise - Return
Nonefor "not found" cases when appropriate:
def run_tmux(cmd, check_session=False):
result = subprocess.run(...)
if result.returncode != 0:
if check_session and "no server running" in err:
return None
raise HTTPException(status_code=400, detail=result.stderr.strip())
return result.stdout.strip()- Use pymysql with DictCursor for named column access
- Always close connections with
finallyblock or context manager - Use parameterized queries (
%splaceholders):
conn = pymysql.connect(host=MYSQL_HOST, port=MYSQL_PORT, ...)
try:
with conn.cursor() as c:
c.execute("SELECT pane_id FROM ttyd_config WHERE pane_id=%s", (pane_id,))
row = c.fetchone()
finally:
conn.close()- Use
async deffor endpoints - Include auth dependency on routers:
app.include_router(tmux_router, dependencies=[Depends(verify_token)])Use format_response() helper for dual JSON/YAML support:
def format_response(data: dict, request: Request = None):
if request and is_yaml(request):
yaml_str = yaml.dump(data, default_flow_style=False, allow_unicode=True)
return PlainTextResponse(yaml_str, media_type="application/yaml")
return datafast-api/
├── main.py # App entry, health endpoints
├── routers/
│ ├── tmux/router.py # Tmux session/window/pane management
│ ├── ttyd.py # TTYD service management
│ └── groups.py # Group management
├── tests/
│ ├── test_*.py # Pytest tests
│ └── curl/test_*.sh # Shell-based API tests
└── DOCS/
Required in .env (copy from .env.example):
TMUX_SOCKET=
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=...
MYSQL_DATABASE=tts_bot
TTYD_PORT_RANGE_DEV=16100-16200
TTYD_PORT_RANGE_PROD=15100-15300
- Tests must clean up created resources (tmux sessions, files)
- Use timestamps in test names:
test_win_{int(time.time())} - Wait for async operations:
time.sleep()after pane creation - Test both success and failure paths
Follow conventional commits: feat:, fix:, docs:, refactor:, etc.
- Never commit secrets (use
.envand.gitignore) - Validate all user input (regex, length checks)
- Use authentication on all production endpoints