Files
Denso/srcs/RobotNet10/Commons/RobotNet10.CustomConfiguration/Controllers/ConfigController.cs
2026-07-03 16:31:37 +07:00

448 lines
15 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RobotNet10.CustomConfiguration.DTOs;
using RobotNet10.CustomConfiguration.DTOs.Requests;
using RobotNet10.CustomConfiguration.Helpers;
using RobotNet10.CustomConfiguration.Models;
using RobotNet10.CustomConfiguration.Services;
namespace RobotNet10.CustomConfiguration.Controllers;
/// <summary>
/// API controller cho quản lý configuration files
/// </summary>
[ApiController]
[Route("api/configs")]
[Authorize]
public class ConfigController(IConfigService configService) : ControllerBase
{
private readonly IConfigService _configService = configService;
// ==========================================
// CONFIG FILE MANAGEMENT
// ==========================================
/// <summary>
/// Tạo config mới
/// </summary>
[HttpPost]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<ConfigFileDto>> CreateConfig([FromBody] CreateConfigRequest request)
{
try
{
// Validate request
if (string.IsNullOrWhiteSpace(request.ConfigType))
{
return BadRequest(new { error = "ConfigType cannot be empty" });
}
// Check if ConfigType already exists
if (await _configService.ConfigTypeExistsAsync(request.ConfigType))
{
return Conflict(new { error = $"Config with type '{request.ConfigType}' already exists" });
}
// Convert DTOs to Models
var variables = request.Variables.Select(MapDtoToVariable).ToList();
// Create config
var config = await _configService.CreateConfigAsync(
request.ConfigType,
variables,
request.Description
);
var dto = MapConfigToDto(config);
return CreatedAtAction(
nameof(GetConfigById),
new { id = config.Id },
dto);
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
/// <summary>
/// Lấy tất cả configs (metadata only)
/// </summary>
[HttpGet]
[ProducesResponseType(typeof(List<ConfigFileMetadataDto>), StatusCodes.Status200OK)]
public async Task<ActionResult<List<ConfigFileMetadataDto>>> GetAllConfigs([FromQuery] string? search)
{
var configs = await _configService.SearchConfigsAsync(search);
var dtos = configs.Select(MapMetadataToDto).ToList();
return Ok(dtos);
}
/// <summary>
/// Lấy config theo ID
/// </summary>
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ConfigFileDto>> GetConfigById(Guid id)
{
var config = await _configService.GetConfigByIdAsync(id);
if (config == null)
{
return NotFound(new { error = $"Config with ID '{id}' not found" });
}
return Ok(MapConfigToDto(config));
}
/// <summary>
/// Lấy config theo ConfigType
/// </summary>
[HttpGet("by-type/{configType}")]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ConfigFileDto>> GetConfigByType(string configType)
{
if (string.IsNullOrWhiteSpace(configType))
{
return BadRequest(new { error = "ConfigType cannot be empty" });
}
var config = await _configService.GetConfigByTypeAsync(configType);
if (config == null)
{
return NotFound(new { error = $"Config with type '{configType}' not found" });
}
return Ok(MapConfigToDto(config));
}
/// <summary>
/// Kiểm tra ConfigType có tồn tại không
/// </summary>
[HttpGet("exists/{configType}")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
public async Task<ActionResult<bool>> ConfigTypeExists(string configType)
{
var exists = await _configService.ConfigTypeExistsAsync(configType);
return Ok(exists);
}
/// <summary>
/// Cập nhật config
/// </summary>
[HttpPut("{id:guid}")]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ConfigFileDto>> UpdateConfig(Guid id, [FromBody] UpdateConfigRequest request)
{
try
{
// Convert DTOs to Models if provided
List<ConfigVariable>? variables = null;
if (request.Variables != null)
{
variables = [.. request.Variables.Select(MapDtoToVariable)];
}
var config = await _configService.UpdateConfigAsync(id, variables, request.Description);
return Ok(MapConfigToDto(config));
}
catch (KeyNotFoundException)
{
return NotFound(new { error = $"Config with ID '{id}' not found" });
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
/// <summary>
/// Xóa config
/// </summary>
[HttpDelete("{id:guid}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> DeleteConfig(Guid id)
{
var deleted = await _configService.DeleteConfigAsync(id);
if (!deleted)
{
return NotFound(new { error = $"Config with ID '{id}' not found" });
}
return NoContent();
}
// ==========================================
// IMPORT/EXPORT
// ==========================================
/// <summary>
/// Import config từ JSON file
/// </summary>
[HttpPost("import")]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<ConfigFileDto>> ImportConfig(
IFormFile file,
[FromForm] string configType,
[FromForm] string? description = null)
{
try
{
if (file == null || file.Length == 0)
{
return BadRequest(new { error = "File is required" });
}
if (!file.FileName.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
{
return BadRequest(new { error = "Only JSON files are supported" });
}
if (string.IsNullOrWhiteSpace(configType))
{
return BadRequest(new { error = "ConfigType is required" });
}
// Validate ConfigType doesn't exist
var exists = await _configService.ConfigTypeExistsAsync(configType);
if (exists)
{
return Conflict(new { error = $"Config with type '{configType}' already exists" });
}
using var stream = file.OpenReadStream();
// Import config with description (if provided, it overrides description from file)
// Note: CreateConfigAsync inside ImportConfigFromJsonAsync already validates before saving
var config = await _configService.ImportConfigFromJsonAsync(stream, configType, description);
var dto = MapConfigToDto(config);
return CreatedAtAction(
nameof(GetConfigById),
new { id = config.Id },
dto);
}
catch (InvalidOperationException ex)
{
return Conflict(new { error = ex.Message });
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
/// <summary>
/// Export config ra JSON file
/// </summary>
[HttpGet("{id:guid}/export")]
[ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult> ExportConfig(Guid id)
{
try
{
var config = await _configService.GetConfigByIdAsync(id);
if (config == null)
{
return NotFound(new { error = $"Config with ID '{id}' not found" });
}
var stream = await _configService.ExportConfigToJsonAsync(config);
// Ensure stream is at the beginning
if (stream.CanSeek && stream.Position != 0)
{
stream.Position = 0;
}
// Ensure stream has data
if (stream.Length == 0)
{
return StatusCode(500, new { error = "Export stream is empty" });
}
return File(stream, "application/json", $"{config.ConfigType}.config.json");
}
catch (KeyNotFoundException)
{
return NotFound(new { error = $"Config with ID '{id}' not found" });
}
catch (Exception ex)
{
return StatusCode(500, new { error = $"Error exporting config: {ex.Message}" });
}
}
// ==========================================
// VARIABLE MANAGEMENT
// ==========================================
/// <summary>
/// Cập nhật giá trị của một variable
/// </summary>
[HttpPut("{id:guid}/variables/{variableName}")]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ConfigFileDto>> UpdateVariable(
Guid id,
string variableName,
[FromBody] UpdateVariableRequest request)
{
if (string.IsNullOrWhiteSpace(variableName))
{
return BadRequest(new { error = "Variable name cannot be empty" });
}
try
{
var config = await _configService.UpdateVariableAsync(id, variableName, request.Value);
return Ok(MapConfigToDto(config));
}
catch (KeyNotFoundException ex)
{
return NotFound(new { error = ex.Message });
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
/// <summary>
/// Thêm variable mới vào config
/// </summary>
[HttpPost("{id:guid}/variables")]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ConfigFileDto>> AddVariable(
Guid id,
[FromBody] ConfigVariableDto variableDto)
{
if (string.IsNullOrWhiteSpace(variableDto.Name))
{
return BadRequest(new { error = "Variable name cannot be empty" });
}
if (string.IsNullOrWhiteSpace(variableDto.Type))
{
return BadRequest(new { error = "Variable type cannot be empty" });
}
try
{
var variable = MapDtoToVariable(variableDto);
var config = await _configService.AddVariableAsync(id, variable);
return Ok(MapConfigToDto(config));
}
catch (KeyNotFoundException ex)
{
return NotFound(new { error = ex.Message });
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
/// <summary>
/// Xóa variable khỏi config
/// </summary>
[HttpDelete("{id:guid}/variables/{variableName}")]
[ProducesResponseType(typeof(ConfigFileDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ConfigFileDto>> RemoveVariable(Guid id, string variableName)
{
if (string.IsNullOrWhiteSpace(variableName))
{
return BadRequest(new { error = "Variable name cannot be empty" });
}
try
{
var config = await _configService.RemoveVariableAsync(id, variableName);
return Ok(MapConfigToDto(config));
}
catch (KeyNotFoundException ex)
{
return NotFound(new { error = ex.Message });
}
catch (Exception ex)
{
return BadRequest(new { error = ex.Message });
}
}
// ==========================================
// MAPPING HELPERS
// ==========================================
private static ConfigFileDto MapConfigToDto(ConfigFile config)
{
return new ConfigFileDto
{
Id = config.Id,
ConfigType = config.ConfigType,
Variables = [.. config.Variables.Select(MapVariableToDto)],
CreatedAt = config.CreatedAt,
UpdatedAt = config.UpdatedAt,
Description = config.Description
};
}
private static ConfigFileMetadataDto MapMetadataToDto(ConfigFileMetadata metadata)
{
return new ConfigFileMetadataDto
{
Id = metadata.Id,
ConfigType = metadata.ConfigType,
CreatedAt = metadata.CreatedAt,
UpdatedAt = metadata.UpdatedAt,
Description = metadata.Description
};
}
private static ConfigVariableDto MapVariableToDto(ConfigVariable variable)
{
return new ConfigVariableDto
{
Name = variable.Name,
Type = variable.Type.ToString().ToLower(),
Value = variable.Value,
Min = variable.Min,
Max = variable.Max,
Roles = variable.Roles,
EnumValues = variable.EnumValues
};
}
private static ConfigVariable MapDtoToVariable(ConfigVariableDto dto)
{
// Parse type string to enum
var type = VariableTypeConverter.ParseType(dto.Type);
return new ConfigVariable
{
Name = dto.Name,
Type = type,
Value = dto.Value,
Min = dto.Min,
Max = dto.Max,
Roles = dto.Roles ?? string.Empty,
EnumValues = dto.EnumValues
};
}
}