feat(tool-configs): add frontend tool config management page

- Create ToolConfigsPage with tool type selector, config list, and add/edit form
- Support both env and file config types
- Add route /tool-configs and navigation item
- Update API client with tool config endpoints
- Build passes successfully
This commit is contained in:
Fusion
2026-05-20 11:13:25 +02:00
parent 63ae706dd0
commit ad09ffa6ec
6 changed files with 481 additions and 3 deletions
+35 -2
View File
@@ -18,6 +18,7 @@ from src.auth.dependencies import get_current_user_id
from src.auth.dependencies import get_db_session
from src.models.git_repository import GitRepository
from src.models.project import Project
from src.models.tool_config import ToolConfig
from src.models.tool_instance import ToolInstance
from src.models.tool_type import ToolType
from src.models.user import User
@@ -31,6 +32,8 @@ from src.services.docker import (
get_container_status,
render_compose_template,
write_compose_file,
write_config_files,
write_env_file,
)
router = APIRouter(prefix="/projects", tags=["tool-instances"])
@@ -342,9 +345,39 @@ async def start_instance(
instance.status = "building"
await session.commit()
# Execute docker compose up
# Fetch tool configs for this tool type
env_vars = {}
config_files = {}
config_query = select(ToolConfig).where(
ToolConfig.user_id == user_id,
ToolConfig.tool_type_id == instance.tool_type_id,
).where(
(ToolConfig.project_id == project_id) | (ToolConfig.project_id.is_(None))
)
config_result = await session.execute(config_query)
configs = config_result.scalars().all()
for config in configs:
if config.config_type == "env":
env_vars[config.key] = config.value
elif config.config_type == "file" and config.file_path:
config_files[config.file_path] = config.value
# Write env file and config files
instance_dir = os.path.dirname(instance.compose_path)
env_file_path = None
if env_vars:
env_file_path = write_env_file(instance_dir, env_vars)
if config_files:
write_config_files(instance_dir, config_files)
# Execute docker compose up with env file
returncode, stdout, stderr = execute_compose_command(
instance.compose_path, "up"
instance.compose_path, "up", env_file=env_file_path
)
if returncode != 0:
+41 -1
View File
@@ -56,8 +56,44 @@ def write_compose_file(instance_dir: str, content: str) -> str:
return str(compose_path)
def write_env_file(instance_dir: str, env_vars: dict[str, str]) -> str:
"""Write environment variables to a .env file.
Args:
instance_dir: Path to instance directory
env_vars: Dictionary of env var names to values
Returns:
Path to the env file
"""
env_path = Path(instance_dir) / ".env"
lines = [f'{key}="{value}"' for key, value in env_vars.items()]
env_path.write_text("\n".join(lines) + "\n")
return str(env_path)
def write_config_files(instance_dir: str, files: dict[str, str]) -> None:
"""Write config files to the instance directory.
Args:
instance_dir: Path to instance directory
files: Dictionary of file paths (relative to instance dir) to content
"""
instance_path = Path(instance_dir)
for file_path, content in files.items():
# Ensure the path is within the instance directory (security)
full_path = instance_path / file_path
try:
full_path.resolve().relative_to(instance_path.resolve())
except ValueError:
raise ValueError(f"File path '{file_path}' escapes instance directory")
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content)
def execute_compose_command(
compose_path: str, action: str, timeout: int = 60
compose_path: str, action: str, timeout: int = 60, env_file: str | None = None
) -> tuple[int, str, str]:
"""Execute a docker compose command.
@@ -65,6 +101,7 @@ def execute_compose_command(
compose_path: Path to docker-compose.yml
action: The compose action (up, down, start, stop, restart)
timeout: Command timeout in seconds
env_file: Optional path to .env file for environment variables
Returns:
Tuple of (returncode, stdout, stderr)
@@ -72,6 +109,9 @@ def execute_compose_command(
instance_dir = Path(compose_path).parent
cmd = ["docker", "compose", "-f", compose_path]
if env_file:
cmd.extend(["--env-file", env_file])
if action == "up":
cmd.extend(["up", "-d"])