refactor: fix user.id references and add missing docker imports (Task 3.4 prep)

- Fix user_id → user.id in tool_instances.py (4 occurrences)
- Add get_container_status and get_container_logs imports

Refs: repo-restructure Task 3.4
This commit is contained in:
Developer
2026-06-02 19:57:53 +00:00
parent f6003b75ca
commit fdd1d21bc7
3 changed files with 14 additions and 99 deletions
+6 -95
View File
@@ -4,111 +4,22 @@ import logging
import uuid
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field, field_validator
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from src.auth.dependencies import get_current_user_id, get_db_session
from src.models.config_folder import ConfigFolder
from src.schemas.config_folder import (
ConfigFolderCreate,
ConfigFolderUpdate,
ConfigFolderResponse,
ProjectOverrideCreate,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/config-folders", tags=["config-folders"])
MAX_FOLDER_SIZE_MB = 10
MAX_FOLDER_SIZE_BYTES = MAX_FOLDER_SIZE_MB * 1024 * 1024
class ConfigFolderCreate(BaseModel):
name: str = Field(description="Folder name (unique per user)")
description: str | None = Field(default=None, description="Optional description")
mount_path: str = Field(description="Default mount path in container")
files: dict = Field(default_factory=dict, description="Files as {path: content}")
@field_validator("mount_path")
@classmethod
def validate_mount_path(cls, v: str) -> str:
if not v.startswith("/"):
raise ValueError("Mount path must be absolute (start with /)")
return v
@field_validator("files")
@classmethod
def validate_files(cls, v: dict) -> dict:
total_size = 0
for path, content in v.items():
# Check for path traversal
if ".." in path or path.startswith("/"):
raise ValueError(f"Invalid file path: {path}")
total_size += len(content.encode("utf-8"))
if total_size > MAX_FOLDER_SIZE_BYTES:
raise ValueError(f"Total folder size exceeds {MAX_FOLDER_SIZE_MB}MB limit")
return v
class ConfigFolderUpdate(BaseModel):
name: str | None = Field(default=None, description="Folder name")
description: str | None = Field(default=None, description="Optional description")
mount_path: str | None = Field(default=None, description="Default mount path")
files: dict | None = Field(default=None, description="Files as {path: content}")
is_active: bool | None = Field(default=None, description="Active/inactive toggle")
@field_validator("mount_path")
@classmethod
def validate_mount_path(cls, v: str | None) -> str | None:
if v is None:
return v
if not v.startswith("/"):
raise ValueError("Mount path must be absolute (start with /)")
return v
@field_validator("files")
@classmethod
def validate_files(cls, v: dict | None) -> dict | None:
if v is None:
return v
total_size = 0
for path, content in v.items():
# Check for path traversal
if ".." in path or path.startswith("/"):
raise ValueError(f"Invalid file path: {path}")
total_size += len(content.encode("utf-8"))
if total_size > MAX_FOLDER_SIZE_BYTES:
raise ValueError(f"Total folder size exceeds {MAX_FOLDER_SIZE_MB}MB limit")
return v
class ProjectOverrideCreate(BaseModel):
mount_path: str | None = Field(default=None, description="Override mount path")
files: dict = Field(default_factory=dict, description="Override files")
@field_validator("mount_path")
@classmethod
def validate_mount_path(cls, v: str | None) -> str | None:
if v is None:
return v
if not v.startswith("/"):
raise ValueError("Mount path must be absolute (start with /)")
return v
class ConfigFolderResponse(BaseModel):
id: str
user_id: str
name: str
description: str | None
mount_path: str
files: dict
project_overrides: dict | None
is_active: bool
created_at: str
updated_at: str
@router.get("", summary="List config folders", description="Get all config folders for the current user.")
async def list_config_folders(
+6 -4
View File
@@ -33,7 +33,9 @@ from src.services.docker import (
execute_compose_command,
find_free_port,
get_container_id,
get_container_logs,
get_container_name,
get_container_status,
recreate_tunnel,
render_compose_template,
start_cloudflared_tunnel,
@@ -266,7 +268,7 @@ async def create_instance(
status_code=status.HTTP_404_NOT_FOUND,
detail="config profile not found",
)
if config_profile.user_id != user_id:
if config_profile.user_id != user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="config profile does not belong to user",
@@ -577,7 +579,7 @@ async def start_instance(
status_code=status.HTTP_404_NOT_FOUND,
detail="config profile not found",
)
if selected_profile.user_id != user_id:
if selected_profile.user_id != user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="config profile does not belong to user",
@@ -912,7 +914,7 @@ async def restart_instance(
status_code=status.HTTP_404_NOT_FOUND,
detail="config profile not found",
)
if stored_profile.user_id != user_id:
if stored_profile.user_id != user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="config profile does not belong to user",
@@ -1297,7 +1299,7 @@ async def proxy_to_instance(
)
# Verify ownership
if instance.owner_id != user_id:
if instance.owner_id != user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="not authorized to access this instance",
+2
View File
@@ -1,5 +1,7 @@
"""Config folder request/response schemas."""
import uuid
from pydantic import BaseModel, Field