You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
6.0 KiB

# -*- coding: utf-8 -*-
"""System configuration endpoints."""
from __future__ import annotations
import logging
from fastapi import APIRouter, Depends, HTTPException, Query
from api.deps import get_system_config_service
from api.v1.schemas.common import ErrorResponse
from api.v1.schemas.system_config import (
SystemConfigConflictResponse,
SystemConfigResponse,
SystemConfigSchemaResponse,
SystemConfigValidationErrorResponse,
UpdateSystemConfigRequest,
UpdateSystemConfigResponse,
ValidateSystemConfigRequest,
ValidateSystemConfigResponse,
)
from src.services.system_config_service import ConfigConflictError, ConfigValidationError, SystemConfigService
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get(
"/config",
response_model=SystemConfigResponse,
responses={
200: {"description": "Configuration loaded"},
401: {"description": "Unauthorized", "model": ErrorResponse},
500: {"description": "Internal server error", "model": ErrorResponse},
},
summary="Get system configuration",
description="Read current configuration from .env and return raw values.",
)
def get_system_config(
include_schema: bool = Query(True, description="Whether to include schema metadata"),
service: SystemConfigService = Depends(get_system_config_service),
) -> SystemConfigResponse:
"""Load and return current system configuration."""
try:
payload = service.get_config(include_schema=include_schema)
return SystemConfigResponse.model_validate(payload)
except Exception as exc:
logger.error("Failed to load system configuration: %s", exc, exc_info=True)
raise HTTPException(
status_code=500,
detail={
"error": "internal_error",
"message": "Failed to load system configuration",
},
)
@router.put(
"/config",
response_model=UpdateSystemConfigResponse,
responses={
200: {"description": "Configuration updated"},
400: {"description": "Validation failed", "model": SystemConfigValidationErrorResponse},
409: {"description": "Version conflict", "model": SystemConfigConflictResponse},
500: {"description": "Internal server error", "model": ErrorResponse},
},
summary="Update system configuration",
description="Update key-value pairs in .env. Mask token preserves existing secret values.",
)
def update_system_config(
request: UpdateSystemConfigRequest,
service: SystemConfigService = Depends(get_system_config_service),
) -> UpdateSystemConfigResponse:
"""Validate and persist system configuration updates."""
try:
payload = service.update(
config_version=request.config_version,
items=[item.model_dump() for item in request.items],
mask_token=request.mask_token,
reload_now=request.reload_now,
)
return UpdateSystemConfigResponse.model_validate(payload)
except ConfigValidationError as exc:
raise HTTPException(
status_code=400,
detail={
"error": "validation_failed",
"message": "System configuration validation failed",
"issues": exc.issues,
},
)
except ConfigConflictError as exc:
raise HTTPException(
status_code=409,
detail={
"error": "config_version_conflict",
"message": "Configuration has changed, please reload and retry",
"current_config_version": exc.current_version,
},
)
except Exception as exc:
logger.error("Failed to update system configuration: %s", exc, exc_info=True)
raise HTTPException(
status_code=500,
detail={
"error": "internal_error",
"message": "Failed to update system configuration",
},
)
@router.post(
"/config/validate",
response_model=ValidateSystemConfigResponse,
responses={
200: {"description": "Validation completed"},
500: {"description": "Internal server error", "model": ErrorResponse},
},
summary="Validate system configuration",
description="Validate submitted configuration values without writing to .env.",
)
def validate_system_config(
request: ValidateSystemConfigRequest,
service: SystemConfigService = Depends(get_system_config_service),
) -> ValidateSystemConfigResponse:
"""Run pre-save validation only."""
try:
payload = service.validate(items=[item.model_dump() for item in request.items])
return ValidateSystemConfigResponse.model_validate(payload)
except Exception as exc:
logger.error("Failed to validate system configuration: %s", exc, exc_info=True)
raise HTTPException(
status_code=500,
detail={
"error": "internal_error",
"message": "Failed to validate system configuration",
},
)
@router.get(
"/config/schema",
response_model=SystemConfigSchemaResponse,
responses={
200: {"description": "Schema loaded"},
500: {"description": "Internal server error", "model": ErrorResponse},
},
summary="Get system configuration schema",
description="Return categorized field metadata used for dynamic settings form rendering.",
)
def get_system_config_schema(
service: SystemConfigService = Depends(get_system_config_service),
) -> SystemConfigSchemaResponse:
"""Return schema metadata for system configuration fields."""
try:
payload = service.get_schema()
return SystemConfigSchemaResponse.model_validate(payload)
except Exception as exc:
logger.error("Failed to load system configuration schema: %s", exc, exc_info=True)
raise HTTPException(
status_code=500,
detail={
"error": "internal_error",
"message": "Failed to load system configuration schema",
},
)