feat: container monitoring integration + polish (PR-3)
- Integration tests: SSE auth, connection limits, lifecycle hooks, event persistence (6 tests)
- Instance events history API: GET /instances/{id}/events
- Documentation updates: terminal.md, backend.md, frontend.md
- Performance: SSE max 5 connections, health monitor write-on-change
Quality gates: pytest 21 monitoring passed, 172 unit passed (4 pre-existing), vitest 14 passed, tsc clean, eslint clean, ruff clean
This commit is contained in:
@@ -2284,6 +2284,65 @@ async def check_instance_tunnel_health(
|
||||
return response
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/events",
|
||||
summary="Get instance events history",
|
||||
description="Get lifecycle event history for a tool instance.",
|
||||
)
|
||||
async def get_instance_events(
|
||||
project_id: uuid.UUID,
|
||||
repo_id: uuid.UUID,
|
||||
instance_id: uuid.UUID,
|
||||
limit: int = 50,
|
||||
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||
session: AsyncSession = Depends(get_db_session),
|
||||
) -> list[dict]:
|
||||
"""Get lifecycle event history for an instance.
|
||||
|
||||
Args:
|
||||
project_id: UUID of the project.
|
||||
repo_id: UUID of the repository.
|
||||
instance_id: UUID of the instance.
|
||||
limit: Maximum number of events to return (default: 50).
|
||||
user_id: ID of the authenticated user.
|
||||
session: Database session.
|
||||
|
||||
Returns:
|
||||
List of event dictionaries.
|
||||
"""
|
||||
from sqlalchemy import select
|
||||
from src.models.instance_event import InstanceEvent
|
||||
|
||||
_user = await _get_user(session, user_id)
|
||||
_project = await _get_owned_project(project_id, user_id, session)
|
||||
|
||||
instance = await session.get(ToolInstance, instance_id)
|
||||
if instance is None or instance.repository_id != repo_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="instance not found"
|
||||
)
|
||||
|
||||
result = await session.execute(
|
||||
select(InstanceEvent)
|
||||
.where(InstanceEvent.instance_id == instance_id)
|
||||
.order_by(InstanceEvent.created_at.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
rows = result.scalars().all()
|
||||
|
||||
return [
|
||||
{
|
||||
"id": str(row.id),
|
||||
"event_type": row.event_type,
|
||||
"status": row.status,
|
||||
"message": row.message,
|
||||
"metadata": row.event_metadata,
|
||||
"created_at": row.created_at.isoformat() if row.created_at else None,
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}",
|
||||
summary="Proxy to instance",
|
||||
|
||||
Reference in New Issue
Block a user