refactor: organize API routers and services into subpackages
Service organization (19 files moved into 6 subpackages): - services/instance/ — event_bus, health_monitor, lifecycle_hooks - services/config/ — config_profile_resolver - services/git/ — clone, git_operations, git_service - services/build/ — docker_build, manifest_compiler - services/terminal/ — terminal_manager, terminal_session - services/shared/ — correlation, file_service, notification_service, permission_fixer, readiness_probe, ssh_keys, tunnel, workspace_manager API router organization (16 files moved into 6 subpackages): - api/tool/ — tool_instances, tool_types, tool_definitions, tool_types_validation, sessions (extracted from tool_instances) - api/config/ — config_profiles, user_config - api/workspace/ — workspaces, workspace_files, workspace_git, workspace_instances - api/user/ — users, auth, ssh_keys - api/project/ — projects, git_repositories - api/system/ — health, events, notifications, dashboard, terminal, instance_proxy Updated main.py imports and all __init__.py re-exports. Sessions router extracted from tool_instances.py into api/tool/sessions.py. Quality gates: py_compile passed, ruff passed.
This commit is contained in:
@@ -1,6 +1 @@
|
||||
from src.api.auth import router as auth_router
|
||||
from src.api.events import router as events_router
|
||||
from src.api.notifications import router as notifications_router
|
||||
from src.api.users import router as users_router
|
||||
|
||||
__all__ = ["auth_router", "events_router", "notifications_router", "users_router"]
|
||||
"""API routers package."""
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
"""Config module."""
|
||||
"""Config API routers module."""
|
||||
|
||||
from src.api.config.config_profiles import router as config_profiles_router
|
||||
from src.api.config.user_config import router as user_config_router
|
||||
|
||||
__all__ = ["config_profiles_router", "user_config_router"]
|
||||
|
||||
@@ -25,7 +25,7 @@ from src.schemas.config import (
|
||||
ValidateGitUrlRequest,
|
||||
ValidateGitUrlResponse,
|
||||
)
|
||||
from src.services.config_profile_resolver import (
|
||||
from src.services.config.config_profile_resolver import (
|
||||
ConfigProfileCycleError,
|
||||
check_include_cycle,
|
||||
resolve_profile,
|
||||
@@ -726,7 +726,7 @@ async def validate_git_url(
|
||||
key_path = None
|
||||
if data.ssh_key_id:
|
||||
from src.models import SSHKey
|
||||
from src.services.ssh_keys import _get_fernet
|
||||
from src.services.shared.ssh_keys import _get_fernet
|
||||
|
||||
try:
|
||||
ssh_key_uuid = uuid.UUID(data.ssh_key_id)
|
||||
@@ -1 +1,6 @@
|
||||
"""Project module."""
|
||||
"""Project API routers module."""
|
||||
|
||||
from src.api.project.git_repositories import router as git_repositories_router
|
||||
from src.api.project.projects import router as projects_router
|
||||
|
||||
__all__ = ["git_repositories_router", "projects_router"]
|
||||
|
||||
@@ -44,7 +44,7 @@ from src.utils.git_control import (
|
||||
)
|
||||
from src.utils.git_history import get_commit_detail, get_commit_history
|
||||
from src.utils.git_url_parser import parse_git_url
|
||||
from src.services.ssh_keys import _get_fernet
|
||||
from src.services.shared.ssh_keys import _get_fernet
|
||||
|
||||
router = APIRouter(prefix="/projects", tags=["git-repositories"])
|
||||
|
||||
@@ -1 +1,17 @@
|
||||
"""System module."""
|
||||
"""System API routers module."""
|
||||
|
||||
from src.api.system.dashboard import router as dashboard_router
|
||||
from src.api.system.events import router as events_router
|
||||
from src.api.system.health import router as health_router
|
||||
from src.api.system.instance_proxy import router as instance_proxy_router
|
||||
from src.api.system.notifications import router as notifications_router
|
||||
from src.api.system.terminal import router as terminal_router
|
||||
|
||||
__all__ = [
|
||||
"dashboard_router",
|
||||
"events_router",
|
||||
"health_router",
|
||||
"instance_proxy_router",
|
||||
"notifications_router",
|
||||
"terminal_router",
|
||||
]
|
||||
|
||||
@@ -10,7 +10,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from src.auth.dependencies import get_current_user_id
|
||||
from src.services.event_bus import InstanceEventBus, InstanceEventPayload
|
||||
from src.services.instance.event_bus import InstanceEventBus, InstanceEventPayload
|
||||
|
||||
router = APIRouter(prefix="/events", tags=["events"])
|
||||
|
||||
@@ -10,7 +10,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from src.auth.dependencies import get_current_user, get_db_session
|
||||
from src.models.user import User
|
||||
from src.models import UserConfig
|
||||
from src.services.notification_service import notification_service
|
||||
from src.services.shared.notification_service import notification_service
|
||||
|
||||
router = APIRouter(prefix="/notifications", tags=["notifications"])
|
||||
|
||||
@@ -15,7 +15,7 @@ from src.auth.dependencies import get_current_user_id, get_db_session
|
||||
from src.models import TerminalSessionModel
|
||||
from src.models import ToolInstance
|
||||
from src.models import ToolType
|
||||
from src.services.terminal_manager import MaxSessionsExceededError, terminal_manager
|
||||
from src.services.terminal.terminal_manager import MaxSessionsExceededError, terminal_manager
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -1 +1,13 @@
|
||||
"""Tool module."""
|
||||
"""Tool API routers module."""
|
||||
|
||||
from src.api.tool.sessions import sessions_router
|
||||
from src.api.tool.tool_definitions import router as tool_definitions_router
|
||||
from src.api.tool.tool_instances import router as tool_instances_router
|
||||
from src.api.tool.tool_types import router as tool_types_router
|
||||
|
||||
__all__ = [
|
||||
"sessions_router",
|
||||
"tool_definitions_router",
|
||||
"tool_instances_router",
|
||||
"tool_types_router",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Sessions API endpoints (running instances for current user)."""
|
||||
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.auth.dependencies import _get_user, get_current_user_id, get_db_session
|
||||
from src.models import GitRepository
|
||||
from src.models import Project
|
||||
from src.models import ToolInstance
|
||||
from src.models import ToolType
|
||||
|
||||
sessions_router = APIRouter(prefix="/users", tags=["sessions"])
|
||||
|
||||
|
||||
@sessions_router.get(
|
||||
"/me/sessions",
|
||||
summary="Get user sessions",
|
||||
description="Get all active sessions (running instances) for the current user.",
|
||||
)
|
||||
async def get_user_sessions(
|
||||
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||
session: AsyncSession = Depends(get_db_session),
|
||||
) -> dict:
|
||||
"""Get all active sessions for the current user.
|
||||
|
||||
Args:
|
||||
user_id: ID of the authenticated user.
|
||||
session: Database session.
|
||||
|
||||
Returns:
|
||||
Dictionary containing list of active sessions with instance details.
|
||||
"""
|
||||
_user = await _get_user(session, user_id)
|
||||
|
||||
result = await session.execute(
|
||||
select(ToolInstance)
|
||||
.where(ToolInstance.owner_id == user_id)
|
||||
.where(
|
||||
ToolInstance.status.in_(
|
||||
["running", "building", "pending", "stopped", "error"]
|
||||
)
|
||||
)
|
||||
.order_by(ToolInstance.created_at.desc())
|
||||
)
|
||||
instances = result.scalars().all()
|
||||
|
||||
sessions = []
|
||||
for instance in instances:
|
||||
tool_type = await session.get(ToolType, instance.tool_type_id)
|
||||
repo = await session.get(GitRepository, instance.repository_id)
|
||||
project = await session.get(Project, instance.project_id)
|
||||
|
||||
sessions.append(
|
||||
{
|
||||
"id": str(instance.id),
|
||||
"display_name": instance.display_name,
|
||||
"tool_type_name": tool_type.name if tool_type else "unknown",
|
||||
"tool_icon": tool_type.name if tool_type else "code",
|
||||
"tool_type_interfaces": [tool_type.interface_type] if tool_type else [],
|
||||
"repository_name": repo.name if repo else "unknown",
|
||||
"repository_id": str(instance.repository_id),
|
||||
"project_name": project.name if project else "unknown",
|
||||
"project_id": str(instance.project_id),
|
||||
"status": instance.status,
|
||||
"url": instance.url,
|
||||
"clone_mode": instance.clone_mode,
|
||||
"branch": instance.branch,
|
||||
"selected_config_profile_id": str(instance.selected_config_profile_id)
|
||||
if instance.selected_config_profile_id
|
||||
else None,
|
||||
"created_at": instance.created_at.isoformat()
|
||||
if instance.created_at
|
||||
else None,
|
||||
}
|
||||
)
|
||||
|
||||
return {"sessions": sessions}
|
||||
@@ -11,7 +11,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from src.auth.dependencies import get_current_user_id, get_db_session
|
||||
from src.models import ToolDefinitionManifest
|
||||
from src.models import ToolType
|
||||
from src.services.manifest_compiler import (
|
||||
from src.services.build.manifest_compiler import (
|
||||
compile_compose,
|
||||
compile_dockerfile,
|
||||
compile_entrypoint,
|
||||
@@ -11,7 +11,6 @@ from datetime import datetime
|
||||
import httpx
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
APIRouter as FastAPIRouter,
|
||||
Depends,
|
||||
HTTPException,
|
||||
Request,
|
||||
@@ -27,16 +26,16 @@ from src.auth.dependencies import (
|
||||
get_current_user_id,
|
||||
get_db_session,
|
||||
)
|
||||
from src.services.event_bus import InstanceEventBus
|
||||
from src.services.lifecycle_hooks import publish_lifecycle_event
|
||||
from src.services.instance.event_bus import InstanceEventBus
|
||||
from src.services.instance.lifecycle_hooks import publish_lifecycle_event
|
||||
from src.models import ConfigProfile
|
||||
from src.models import GitRepository
|
||||
from src.models.project import Project
|
||||
from src.models import SSHKey
|
||||
from src.models import ToolInstance
|
||||
from src.models import ToolType
|
||||
from src.services.clone import check_dirty_state, clone_repository
|
||||
from src.services.config_profile_resolver import (
|
||||
from src.services.git.clone import check_dirty_state, clone_repository
|
||||
from src.services.config.config_profile_resolver import (
|
||||
ConfigProfileCycleError,
|
||||
ResolvedProfile,
|
||||
apply_resolved_profile,
|
||||
@@ -61,14 +60,14 @@ from src.services.docker import (
|
||||
write_config_files,
|
||||
write_env_file,
|
||||
)
|
||||
from src.services.tunnel import (
|
||||
from src.services.shared.tunnel import (
|
||||
check_tunnel_health,
|
||||
recreate_tunnel,
|
||||
start_tunnel,
|
||||
stop_tunnel,
|
||||
)
|
||||
from src.services.docker_build import build_image
|
||||
from src.services.manifest_compiler import (
|
||||
from src.services.build.docker_build import build_image
|
||||
from src.services.build.manifest_compiler import (
|
||||
compile_compose,
|
||||
compile_dockerfile,
|
||||
compile_entrypoint,
|
||||
@@ -78,9 +77,9 @@ from src.services.manifest_compiler import (
|
||||
merge_with_config,
|
||||
resolve_base,
|
||||
)
|
||||
from src.services.permission_fixer import apply_mount_permissions, apply_ssh_permissions
|
||||
from src.services.readiness_probe import execute_probe
|
||||
from src.services.ssh_keys import cleanup_ssh_key_files, prepare_ssh_key_files
|
||||
from src.services.shared.permission_fixer import apply_mount_permissions, apply_ssh_permissions
|
||||
from src.services.shared.readiness_probe import execute_probe
|
||||
from src.services.shared.ssh_keys import cleanup_ssh_key_files, prepare_ssh_key_files
|
||||
from src.schemas.tool import CreateInstanceRequest, StartInstanceRequest
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -1585,7 +1584,7 @@ async def start_instance(
|
||||
|
||||
# Mount selected SSH keys into container home dir
|
||||
if instance.ssh_key_ids:
|
||||
from src.services.ssh_keys import write_ssh_config, _sanitize_filename
|
||||
from src.services.shared.ssh_keys import write_ssh_config, _sanitize_filename
|
||||
|
||||
# Collect all valid keys first
|
||||
ssh_keys_to_mount = []
|
||||
@@ -2885,71 +2884,3 @@ async def proxy_to_instance(
|
||||
status_code=response.status_code,
|
||||
headers=response_headers,
|
||||
)
|
||||
|
||||
|
||||
sessions_router = FastAPIRouter(prefix="/users", tags=["sessions"])
|
||||
|
||||
|
||||
@sessions_router.get(
|
||||
"/me/sessions",
|
||||
summary="Get user sessions",
|
||||
description="Get all active sessions (running instances) for the current user.",
|
||||
)
|
||||
async def get_user_sessions(
|
||||
user_id: uuid.UUID = Depends(get_current_user_id),
|
||||
session: AsyncSession = Depends(get_db_session),
|
||||
) -> dict:
|
||||
"""Get all active sessions for the current user.
|
||||
|
||||
Args:
|
||||
user_id: ID of the authenticated user.
|
||||
session: Database session.
|
||||
|
||||
Returns:
|
||||
Dictionary containing list of active sessions with instance details.
|
||||
"""
|
||||
_user = await _get_user(session, user_id)
|
||||
|
||||
result = await session.execute(
|
||||
select(ToolInstance)
|
||||
.where(ToolInstance.owner_id == user_id)
|
||||
.where(
|
||||
ToolInstance.status.in_(
|
||||
["running", "building", "pending", "stopped", "error"]
|
||||
)
|
||||
)
|
||||
.order_by(ToolInstance.created_at.desc())
|
||||
)
|
||||
instances = result.scalars().all()
|
||||
|
||||
sessions = []
|
||||
for instance in instances:
|
||||
tool_type = await session.get(ToolType, instance.tool_type_id)
|
||||
repo = await session.get(GitRepository, instance.repository_id)
|
||||
project = await session.get(Project, instance.project_id)
|
||||
|
||||
sessions.append(
|
||||
{
|
||||
"id": str(instance.id),
|
||||
"display_name": instance.display_name,
|
||||
"tool_type_name": tool_type.name if tool_type else "unknown",
|
||||
"tool_icon": tool_type.name if tool_type else "code",
|
||||
"tool_type_interfaces": [tool_type.interface_type] if tool_type else [],
|
||||
"repository_name": repo.name if repo else "unknown",
|
||||
"repository_id": str(instance.repository_id),
|
||||
"project_name": project.name if project else "unknown",
|
||||
"project_id": str(instance.project_id),
|
||||
"status": instance.status,
|
||||
"url": instance.url,
|
||||
"clone_mode": instance.clone_mode,
|
||||
"branch": instance.branch,
|
||||
"selected_config_profile_id": str(instance.selected_config_profile_id)
|
||||
if instance.selected_config_profile_id
|
||||
else None,
|
||||
"created_at": instance.created_at.isoformat()
|
||||
if instance.created_at
|
||||
else None,
|
||||
}
|
||||
)
|
||||
|
||||
return {"sessions": sessions}
|
||||
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.api.tool_types_validation import (
|
||||
from src.api.tool.tool_types_validation import (
|
||||
check_port_exposed,
|
||||
validate_compose_yaml,
|
||||
validate_required_variables,
|
||||
@@ -1 +1,7 @@
|
||||
"""User module."""
|
||||
"""User API routers module."""
|
||||
|
||||
from src.api.user.auth import router as auth_router
|
||||
from src.api.user.ssh_keys import router as ssh_keys_router
|
||||
from src.api.user.users import router as users_router
|
||||
|
||||
__all__ = ["auth_router", "ssh_keys_router", "users_router"]
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
"""Workspace module."""
|
||||
"""Workspace API routers module."""
|
||||
|
||||
from src.api.workspace.workspace_files import router as workspace_files_router
|
||||
from src.api.workspace.workspace_git import router as workspace_git_router
|
||||
from src.api.workspace.workspace_instances import router as workspace_instances_router
|
||||
from src.api.workspace.workspaces import all_workspaces_router, router as workspaces_router
|
||||
|
||||
__all__ = [
|
||||
"all_workspaces_router",
|
||||
"workspace_files_router",
|
||||
"workspace_git_router",
|
||||
"workspace_instances_router",
|
||||
"workspaces_router",
|
||||
]
|
||||
|
||||
@@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.auth.dependencies import get_current_user_id, get_db_session
|
||||
from src.models import Workspace
|
||||
from src.services.file_service import FileService
|
||||
from src.services.shared.file_service import FileService
|
||||
|
||||
router = APIRouter(prefix="/workspaces/{workspace_id}/files")
|
||||
|
||||
@@ -103,7 +103,7 @@ async def write_file(
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
if commit_message:
|
||||
from src.services.git_operations import GitOperations
|
||||
from src.services.git.git_operations import GitOperations
|
||||
|
||||
git = GitOperations(workspace)
|
||||
try:
|
||||
@@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.auth.dependencies import get_current_user_id, get_db_session
|
||||
from src.models import Workspace
|
||||
from src.services.git_operations import GitOperations
|
||||
from src.services.git.git_operations import GitOperations
|
||||
|
||||
router = APIRouter(prefix="/workspaces/{workspace_id}/git")
|
||||
|
||||
@@ -12,7 +12,7 @@ from src.auth.dependencies import get_current_user_id, get_db_session
|
||||
from src.models import GitRepository
|
||||
from src.models import ToolInstance
|
||||
from src.models import Workspace
|
||||
from src.services.workspace_manager import WorkspaceHasInstancesError, WorkspaceManager
|
||||
from src.services.shared.workspace_manager import WorkspaceHasInstancesError, WorkspaceManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
Reference in New Issue
Block a user