37ccaa4fdc
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.
115 lines
3.3 KiB
Python
115 lines
3.3 KiB
Python
"""Workspace file API endpoints."""
|
|
|
|
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
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.shared.file_service import FileService
|
|
|
|
router = APIRouter(prefix="/workspaces/{workspace_id}/files")
|
|
|
|
|
|
async def _get_workspace(
|
|
session: AsyncSession,
|
|
workspace_id: uuid.UUID,
|
|
user_id: uuid.UUID,
|
|
) -> Workspace:
|
|
from sqlalchemy import select
|
|
|
|
result = await session.execute(
|
|
select(Workspace).where(
|
|
Workspace.id == workspace_id,
|
|
Workspace.user_id == user_id,
|
|
)
|
|
)
|
|
workspace = result.scalar_one_or_none()
|
|
if not workspace:
|
|
raise HTTPException(status_code=404, detail="Workspace not found")
|
|
return workspace
|
|
|
|
|
|
@router.get("/")
|
|
async def list_files(
|
|
workspace_id: uuid.UUID,
|
|
path: str = "",
|
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
|
session: AsyncSession = Depends(get_db_session),
|
|
) -> dict:
|
|
"""List files in a workspace directory."""
|
|
workspace = await _get_workspace(session, workspace_id, user_id)
|
|
service = FileService()
|
|
try:
|
|
entries = service.list_directory(workspace, path)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
return {
|
|
"entries": [
|
|
{
|
|
"name": e.name,
|
|
"path": e.path,
|
|
"type": e.type,
|
|
"size": e.size,
|
|
}
|
|
for e in entries
|
|
],
|
|
}
|
|
|
|
|
|
@router.get("/content")
|
|
async def get_file_content(
|
|
workspace_id: uuid.UUID,
|
|
path: str,
|
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
|
session: AsyncSession = Depends(get_db_session),
|
|
) -> dict:
|
|
"""Get the content of a text file."""
|
|
workspace = await _get_workspace(session, workspace_id, user_id)
|
|
service = FileService()
|
|
try:
|
|
content = service.read_file(workspace, path)
|
|
except FileNotFoundError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
return {"content": content, "path": path}
|
|
|
|
|
|
@router.post("/content")
|
|
async def write_file(
|
|
workspace_id: uuid.UUID,
|
|
data: dict,
|
|
user_id: uuid.UUID = Depends(get_current_user_id),
|
|
session: AsyncSession = Depends(get_db_session),
|
|
) -> dict:
|
|
"""Write a file and optionally commit."""
|
|
workspace = await _get_workspace(session, workspace_id, user_id)
|
|
service = FileService()
|
|
|
|
file_path = data.get("path", "").strip()
|
|
content = data.get("content", "")
|
|
commit_message = data.get("message", "").strip()
|
|
|
|
if not file_path:
|
|
raise HTTPException(status_code=400, detail="File path is required")
|
|
|
|
try:
|
|
service.write_file(workspace, file_path, content)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
if commit_message:
|
|
from src.services.git.git_operations import GitOperations
|
|
|
|
git = GitOperations(workspace)
|
|
try:
|
|
await git.commit(commit_message)
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
|
|
return {"status": "saved", "path": file_path}
|