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:
2026-06-04 12:24:14 +02:00
parent 8816ee02ce
commit 37ccaa4fdc
57 changed files with 315 additions and 163 deletions
+1 -6
View File
@@ -1,6 +1 @@
from src.api.auth import router as auth_router """API routers package."""
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"]
+6 -1
View File
@@ -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, ValidateGitUrlRequest,
ValidateGitUrlResponse, ValidateGitUrlResponse,
) )
from src.services.config_profile_resolver import ( from src.services.config.config_profile_resolver import (
ConfigProfileCycleError, ConfigProfileCycleError,
check_include_cycle, check_include_cycle,
resolve_profile, resolve_profile,
@@ -726,7 +726,7 @@ async def validate_git_url(
key_path = None key_path = None
if data.ssh_key_id: if data.ssh_key_id:
from src.models import SSHKey from src.models import SSHKey
from src.services.ssh_keys import _get_fernet from src.services.shared.ssh_keys import _get_fernet
try: try:
ssh_key_uuid = uuid.UUID(data.ssh_key_id) ssh_key_uuid = uuid.UUID(data.ssh_key_id)
+6 -1
View File
@@ -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_history import get_commit_detail, get_commit_history
from src.utils.git_url_parser import parse_git_url 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"]) router = APIRouter(prefix="/projects", tags=["git-repositories"])
+17 -1
View File
@@ -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 fastapi.responses import StreamingResponse
from src.auth.dependencies import get_current_user_id 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"]) 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.auth.dependencies import get_current_user, get_db_session
from src.models.user import User from src.models.user import User
from src.models import UserConfig 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"]) 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 TerminalSessionModel
from src.models import ToolInstance from src.models import ToolInstance
from src.models import ToolType 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() router = APIRouter()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+13 -1
View File
@@ -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",
]
+80
View File
@@ -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.auth.dependencies import get_current_user_id, get_db_session
from src.models import ToolDefinitionManifest from src.models import ToolDefinitionManifest
from src.models import ToolType from src.models import ToolType
from src.services.manifest_compiler import ( from src.services.build.manifest_compiler import (
compile_compose, compile_compose,
compile_dockerfile, compile_dockerfile,
compile_entrypoint, compile_entrypoint,
@@ -11,7 +11,6 @@ from datetime import datetime
import httpx import httpx
from fastapi import ( from fastapi import (
APIRouter, APIRouter,
APIRouter as FastAPIRouter,
Depends, Depends,
HTTPException, HTTPException,
Request, Request,
@@ -27,16 +26,16 @@ from src.auth.dependencies import (
get_current_user_id, get_current_user_id,
get_db_session, get_db_session,
) )
from src.services.event_bus import InstanceEventBus from src.services.instance.event_bus import InstanceEventBus
from src.services.lifecycle_hooks import publish_lifecycle_event from src.services.instance.lifecycle_hooks import publish_lifecycle_event
from src.models import ConfigProfile from src.models import ConfigProfile
from src.models import GitRepository from src.models import GitRepository
from src.models.project import Project from src.models.project import Project
from src.models import SSHKey from src.models import SSHKey
from src.models import ToolInstance from src.models import ToolInstance
from src.models import ToolType from src.models import ToolType
from src.services.clone import check_dirty_state, clone_repository from src.services.git.clone import check_dirty_state, clone_repository
from src.services.config_profile_resolver import ( from src.services.config.config_profile_resolver import (
ConfigProfileCycleError, ConfigProfileCycleError,
ResolvedProfile, ResolvedProfile,
apply_resolved_profile, apply_resolved_profile,
@@ -61,14 +60,14 @@ from src.services.docker import (
write_config_files, write_config_files,
write_env_file, write_env_file,
) )
from src.services.tunnel import ( from src.services.shared.tunnel import (
check_tunnel_health, check_tunnel_health,
recreate_tunnel, recreate_tunnel,
start_tunnel, start_tunnel,
stop_tunnel, stop_tunnel,
) )
from src.services.docker_build import build_image from src.services.build.docker_build import build_image
from src.services.manifest_compiler import ( from src.services.build.manifest_compiler import (
compile_compose, compile_compose,
compile_dockerfile, compile_dockerfile,
compile_entrypoint, compile_entrypoint,
@@ -78,9 +77,9 @@ from src.services.manifest_compiler import (
merge_with_config, merge_with_config,
resolve_base, resolve_base,
) )
from src.services.permission_fixer import apply_mount_permissions, apply_ssh_permissions from src.services.shared.permission_fixer import apply_mount_permissions, apply_ssh_permissions
from src.services.readiness_probe import execute_probe from src.services.shared.readiness_probe import execute_probe
from src.services.ssh_keys import cleanup_ssh_key_files, prepare_ssh_key_files from src.services.shared.ssh_keys import cleanup_ssh_key_files, prepare_ssh_key_files
from src.schemas.tool import CreateInstanceRequest, StartInstanceRequest from src.schemas.tool import CreateInstanceRequest, StartInstanceRequest
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -1585,7 +1584,7 @@ async def start_instance(
# Mount selected SSH keys into container home dir # Mount selected SSH keys into container home dir
if instance.ssh_key_ids: 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 # Collect all valid keys first
ssh_keys_to_mount = [] ssh_keys_to_mount = []
@@ -2885,71 +2884,3 @@ async def proxy_to_instance(
status_code=response.status_code, status_code=response.status_code,
headers=response_headers, 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 import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from src.api.tool_types_validation import ( from src.api.tool.tool_types_validation import (
check_port_exposed, check_port_exposed,
validate_compose_yaml, validate_compose_yaml,
validate_required_variables, validate_required_variables,
+7 -1
View File
@@ -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"]
+14 -1
View File
@@ -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.auth.dependencies import get_current_user_id, get_db_session
from src.models import Workspace 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") 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 raise HTTPException(status_code=400, detail=str(exc)) from exc
if commit_message: if commit_message:
from src.services.git_operations import GitOperations from src.services.git.git_operations import GitOperations
git = GitOperations(workspace) git = GitOperations(workspace)
try: 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.auth.dependencies import get_current_user_id, get_db_session
from src.models import Workspace 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") 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 GitRepository
from src.models import ToolInstance from src.models import ToolInstance
from src.models import Workspace 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__) logger = logging.getLogger(__name__)
+1 -1
View File
@@ -10,7 +10,7 @@ from collections.abc import Callable
from fastapi import Request, Response from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware
from src.services.correlation import get_correlation_id from src.services.shared.correlation import get_correlation_id
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+24 -21
View File
@@ -7,27 +7,30 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from src.api.auth import router as auth_router from src.api.config import config_profiles_router, user_config_router
from src.api.dashboard import router as dashboard_router from src.api.project import git_repositories_router, projects_router
from src.api.events import router as events_router from src.api.system import (
from src.api.git_repositories import router as git_repositories_router dashboard_router,
from src.api.health import router as health_router events_router,
from src.api.projects import router as projects_router health_router,
from src.api.ssh_keys import router as ssh_keys_router instance_proxy_router,
from src.api.terminal import router as terminal_router notifications_router,
from src.api.instance_proxy import router as instance_proxy_router terminal_router,
from src.api.config_profiles import router as config_profiles_router )
from src.api.tool_definitions import router as tool_definitions_router from src.api.tool import (
from src.api.tool_instances import router as tool_instances_router sessions_router,
from src.api.tool_instances import sessions_router tool_definitions_router,
from src.api.tool_types import router as tool_types_router tool_instances_router,
from src.api.notifications import router as notifications_router tool_types_router,
from src.api.user_config import router as user_config_router )
from src.api.users import router as users_router from src.api.user import auth_router, ssh_keys_router, users_router
from src.api.workspace_files import router as workspace_files_router from src.api.workspace import (
from src.api.workspace_git import router as workspace_git_router all_workspaces_router,
from src.api.workspace_instances import router as workspace_instances_router workspace_files_router,
from src.api.workspaces import all_workspaces_router, router as workspaces_router workspace_git_router,
workspace_instances_router,
workspaces_router,
)
from src.config import Settings from src.config import Settings
from src.models import Notification # noqa: F401 Alembic model discovery from src.models import Notification # noqa: F401 Alembic model discovery
from src.models import TerminalSessionModel # noqa: F401 Alembic model discovery from src.models import TerminalSessionModel # noqa: F401 Alembic model discovery
+1 -1
View File
@@ -6,7 +6,7 @@ from datetime import datetime
from pydantic import BaseModel, ConfigDict, field_validator, model_validator from pydantic import BaseModel, ConfigDict, field_validator, model_validator
from src.api.tool_types_validation import ( from src.api.tool.tool_types_validation import (
check_port_exposed, check_port_exposed,
validate_compose_yaml, validate_compose_yaml,
validate_required_variables, validate_required_variables,
+25 -1
View File
@@ -1 +1,25 @@
"""Config module.""" """Config profile services module."""
from src.services.config.config_profile_resolver import (
ConfigProfileCycleError,
ConfigProfileNotFoundError,
ResolvedMount,
ResolvedProfile,
apply_resolved_profile,
check_include_cycle,
expand_container_path,
resolve_profile,
resolved_profile_to_dict,
)
__all__ = [
"ConfigProfileCycleError",
"ConfigProfileNotFoundError",
"ResolvedMount",
"ResolvedProfile",
"apply_resolved_profile",
"check_include_cycle",
"expand_container_path",
"resolve_profile",
"resolved_profile_to_dict",
]
+19 -1
View File
@@ -1 +1,19 @@
"""Git module.""" """Git services module."""
from src.services.git.clone import (
check_dirty_state,
clone_repository,
remove_clone_directory,
)
from src.services.git.git_operations import Commit, GitOperations, GitStatus
from src.services.git.git_service import GitService
__all__ = [
"check_dirty_state",
"clone_repository",
"remove_clone_directory",
"Commit",
"GitOperations",
"GitStatus",
"GitService",
]
+7 -1
View File
@@ -1 +1,7 @@
"""Instance module.""" """Instance lifecycle services module."""
from src.services.instance.event_bus import InstanceEventBus
from src.services.instance.health_monitor import HealthMonitor
from src.services.instance.lifecycle_hooks import publish_lifecycle_event
__all__ = ["InstanceEventBus", "HealthMonitor", "publish_lifecycle_event"]
@@ -12,11 +12,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
from src.database import SessionLocal from src.database import SessionLocal
from src.models import HealthCheck from src.models import HealthCheck
from src.models import ToolInstance from src.models import ToolInstance
from src.services.correlation import get_correlation_id from src.services.shared.correlation import get_correlation_id
from src.services.docker import get_container_status from src.services.docker import get_container_status
from src.services.tunnel import check_tunnel_health from src.services.shared.tunnel import check_tunnel_health
from src.services.event_bus import InstanceEventBus, InstanceEventPayload from src.services.instance.event_bus import InstanceEventBus, InstanceEventPayload
from src.services.notification_service import notification_service from src.services.shared.notification_service import notification_service
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -8,9 +8,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
from src.models import InstanceEvent from src.models import InstanceEvent
from src.models import ToolInstance from src.models import ToolInstance
from src.services.correlation import get_correlation_id from src.services.shared.correlation import get_correlation_id
from src.services.event_bus import InstanceEventBus, InstanceEventPayload from src.services.instance.event_bus import InstanceEventBus, InstanceEventPayload
from src.services.notification_service import notification_service from src.services.shared.notification_service import notification_service
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+51 -1
View File
@@ -1 +1,51 @@
"""Shared module.""" """Shared services module."""
from src.services.shared.correlation import CorrelationIdMiddleware, get_correlation_id
from src.services.shared.file_service import FileEntry, FileService
from src.services.shared.notification_service import NotificationService
from src.services.shared.permission_fixer import (
PermissionFixError,
apply_mount_permissions,
apply_ssh_permissions,
check_root_user_available,
)
from src.services.shared.readiness_probe import execute_probe
from src.services.shared.ssh_keys import (
cleanup_ssh_key_files,
prepare_ssh_key_files,
write_ssh_config,
)
from src.services.shared.tunnel import (
check_tunnel_health,
recreate_tunnel,
start_tunnel,
stop_tunnel,
)
from src.services.shared.workspace_manager import (
SyncResult,
WorkspaceHasInstancesError,
WorkspaceManager,
)
__all__ = [
"CorrelationIdMiddleware",
"FileEntry",
"FileService",
"NotificationService",
"PermissionFixError",
"SyncResult",
"WorkspaceHasInstancesError",
"WorkspaceManager",
"apply_mount_permissions",
"apply_ssh_permissions",
"check_root_user_available",
"check_tunnel_health",
"cleanup_ssh_key_files",
"execute_probe",
"get_correlation_id",
"prepare_ssh_key_files",
"recreate_tunnel",
"start_tunnel",
"stop_tunnel",
"write_ssh_config",
]
@@ -15,8 +15,8 @@ from typing import TYPE_CHECKING
from sqlalchemy import select from sqlalchemy import select
from src.models import Workspace from src.models import Workspace
from src.services.git_service import GitService from src.services.git.git_service import GitService
from src.services.ssh_keys import _get_fernet from src.services.shared.ssh_keys import _get_fernet
if TYPE_CHECKING: if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
+9 -1
View File
@@ -1 +1,9 @@
"""Terminal module.""" """Terminal services module."""
from src.services.terminal.terminal_manager import (
MaxSessionsExceededError,
TerminalManager,
)
from src.services.terminal.terminal_session import TerminalSession
__all__ = ["MaxSessionsExceededError", "TerminalManager", "TerminalSession"]
@@ -10,7 +10,7 @@ from sqlalchemy.dialects.postgresql import insert as pg_insert
from src.database import SessionLocal from src.database import SessionLocal
from src.models import TerminalSessionModel from src.models import TerminalSessionModel
from src.services.terminal_session import TerminalSession from src.services.terminal.terminal_session import TerminalSession
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+1 -21
View File
@@ -1,30 +1,10 @@
# Progress # Progress
## Status ## Status
In Progress — Backend structural refactoring In Progress
## Tasks ## Tasks
- [x] Phase 0: Create submodule directory structure (__init__.py files)
- [x] Phase 1: Model subpackages (tool, config, user, project, system)
- [x] Phase 2: Docker service package split (compose, container, config_staging, tunnel)
- [ ] Phase 3: Service subpackages (instance, config, git, build, terminal, shared)
- [ ] Phase 4: Schema extraction (tool, config, user, project, system)
- [ ] Phase 5: API router subpackages (tool, config, workspace, user, project, system)
- [ ] Phase 6: Auth dependency refactor
- [ ] Phase 7: Frontend reorganization
- [ ] Phase 8: Integration and verification
## Files Changed ## Files Changed
- `apps/api/src/services/docker.py` → deleted (split into package)
- `apps/api/src/services/docker/__init__.py` — re-exports all public functions
- `apps/api/src/services/docker/compose.py` — compose generation & commands
- `apps/api/src/services/docker/container.py` — container runtime queries
- `apps/api/src/services/docker/config_staging.py` — file staging into instances
- `apps/api/src/services/docker/tunnel.py` — cloudflared tunnel lifecycle
- `apps/api/src/services/tunnel.py` — thin backward-compat re-export wrapper
## Notes ## Notes
- All backward-compatible imports preserved via __init__.py re-exports
- `from src.services.docker import X` continues to work for all previously exported symbols
- `from src.services.tunnel import X` continues to work via re-export wrapper
- No behavior changes, pure structural refactor