326 lines
12 KiB
C#
326 lines
12 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using OnlyOneAccessTemplate.Services;
|
|
using OnlyOneAccessTemplate.Models;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Microsoft.AspNetCore.Http.Extensions;
|
|
|
|
namespace OnlyOneAccessTemplate.Controllers
|
|
{
|
|
[Route("api/module-management")]
|
|
[ApiController]
|
|
public class ModuleManagementController : ControllerBase
|
|
{
|
|
private readonly IModuleService _moduleService;
|
|
private readonly ILogger<ModuleManagementController> _logger;
|
|
private readonly IConfiguration _configuration;
|
|
|
|
public ModuleManagementController(
|
|
IModuleService moduleService,
|
|
ILogger<ModuleManagementController> logger,
|
|
IConfiguration configuration)
|
|
{
|
|
_moduleService = moduleService;
|
|
_logger = logger;
|
|
_configuration = configuration;
|
|
}
|
|
|
|
[HttpPost("register")]
|
|
public async Task<IActionResult> RegisterModule([FromBody] ModuleRegistrationRequest request)
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation("Registrando novo módulo: {ModuleId}", request.ModuleId);
|
|
|
|
// Verificar se módulo já existe
|
|
var existing = await _moduleService.GetModuleConfigAsync(request.ModuleId);
|
|
if (existing != null)
|
|
{
|
|
return BadRequest(new { success = false, message = "Módulo já existe" });
|
|
}
|
|
|
|
// Testar conectividade com o módulo
|
|
var healthUrl = $"{request.BaseUrl.TrimEnd('/')}/{request.HealthEndpoint?.TrimStart('/') ?? "api/converter/health"}";
|
|
var isHealthy = await TestModuleHealth(healthUrl);
|
|
|
|
// Gerar API Key
|
|
var apiKey = GenerateApiKey();
|
|
|
|
// Criar configuração
|
|
var moduleConfig = new ModuleConfig
|
|
{
|
|
ModuleId = request.ModuleId,
|
|
Name = request.Name,
|
|
Url = $"{request.BaseUrl.TrimEnd('/')}/modules/{request.ModuleId}",
|
|
RequestBy = request.ModuleId,
|
|
IsActive = request.AutoActivate.HasValue && request.AutoActivate.Value && isHealthy,
|
|
CacheMinutes = request.CacheMinutes ?? 5,
|
|
|
|
// Proxy Configuration
|
|
UseProxy = true,
|
|
ProxyEndpoint = $"/api/modules/{request.ModuleId}",
|
|
ProxyMappings = new Dictionary<string, string>
|
|
{
|
|
["convert"] = $"{request.BaseUrl}/api/converter/convert",
|
|
["config"] = $"{request.BaseUrl}/api/converter/config",
|
|
["health"] = $"{request.BaseUrl}/api/converter/health"
|
|
},
|
|
|
|
// Security
|
|
ApiKey = apiKey,
|
|
AllowedOrigins = request.AllowedOrigins ?? new List<string> { Request.GetDisplayUrl() },
|
|
RateLimitPerMinute = request.RateLimitPerMinute ?? 60,
|
|
|
|
// Menu
|
|
MenuTitle = request.MenuTitle ?? request.Name,
|
|
MenuDescription = request.MenuDescription,
|
|
MenuIcon = request.MenuIcon ?? "fas fa-exchange-alt",
|
|
MenuCategory = request.MenuCategory ?? "Conversores",
|
|
MenuOrder = request.MenuOrder ?? 0,
|
|
ShowInMenu = request.ShowInMenu ?? true,
|
|
|
|
// SEO
|
|
SeoTitles = request.SeoTitles ?? new Dictionary<string, string>(),
|
|
SeoDescriptions = request.SeoDescriptions ?? new Dictionary<string, string>(),
|
|
SeoKeywords = request.SeoKeywords ?? new Dictionary<string, string>(),
|
|
|
|
// Technical
|
|
HealthEndpoint = request.HealthEndpoint ?? "/api/converter/health",
|
|
HealthCheckIntervalMinutes = request.HealthCheckIntervalMinutes ?? 5,
|
|
AutoStart = request.AutoActivate ?? true,
|
|
Version = request.Version ?? "1.0.0",
|
|
IsHealthy = isHealthy,
|
|
LastHealthCheck = DateTime.UtcNow,
|
|
|
|
// Developer
|
|
DeveloperName = request.DeveloperName,
|
|
DeveloperEmail = request.DeveloperEmail,
|
|
Repository = request.Repository,
|
|
Documentation = request.Documentation,
|
|
|
|
// Headers
|
|
Headers = new Dictionary<string, string>
|
|
{
|
|
["X-API-Key"] = apiKey,
|
|
["User-Agent"] = "ConvertIt-MainApp/1.0"
|
|
}
|
|
};
|
|
|
|
await _moduleService.SaveModuleConfigAsync(moduleConfig);
|
|
|
|
_logger.LogInformation("Módulo {ModuleId} registrado com sucesso", request.ModuleId);
|
|
|
|
return Ok(new
|
|
{
|
|
success = true,
|
|
message = "Módulo registrado com sucesso",
|
|
moduleId = request.ModuleId,
|
|
apiKey = apiKey,
|
|
proxyEndpoint = moduleConfig.ProxyEndpoint,
|
|
isHealthy = isHealthy,
|
|
isActive = moduleConfig.IsActive
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao registrar módulo {ModuleId}", request.ModuleId);
|
|
return StatusCode(500, new { success = false, message = ex.Message });
|
|
}
|
|
}
|
|
|
|
[HttpGet("modules")]
|
|
public async Task<IActionResult> ListModules()
|
|
{
|
|
try
|
|
{
|
|
var modules = await _moduleService.GetAllActiveModulesAsync();
|
|
|
|
var result = modules.Select(m => new
|
|
{
|
|
moduleId = m.ModuleId,
|
|
name = m.Name,
|
|
isActive = m.IsActive,
|
|
isHealthy = m.IsHealthy,
|
|
lastHealthCheck = m.LastHealthCheck,
|
|
version = m.Version,
|
|
menuTitle = m.MenuTitle,
|
|
menuCategory = m.MenuCategory,
|
|
showInMenu = m.ShowInMenu,
|
|
developerName = m.DeveloperName,
|
|
proxyEndpoint = m.ProxyEndpoint
|
|
});
|
|
|
|
return Ok(new { success = true, modules = result });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao listar módulos");
|
|
return StatusCode(500, new { success = false, message = ex.Message });
|
|
}
|
|
}
|
|
|
|
[HttpPost("modules/{moduleId}/toggle")]
|
|
public async Task<IActionResult> ToggleModule(string moduleId)
|
|
{
|
|
try
|
|
{
|
|
var module = await _moduleService.GetModuleConfigAsync(moduleId);
|
|
if (module == null)
|
|
{
|
|
return NotFound(new { success = false, message = "Módulo não encontrado" });
|
|
}
|
|
|
|
module.IsActive = !module.IsActive;
|
|
module.UpdatedAt = DateTime.UtcNow;
|
|
|
|
await _moduleService.SaveModuleConfigAsync(module);
|
|
|
|
_logger.LogInformation("Módulo {ModuleId} {Status}", moduleId,
|
|
module.IsActive ? "ativado" : "desativado");
|
|
|
|
return Ok(new
|
|
{
|
|
success = true,
|
|
moduleId = moduleId,
|
|
isActive = module.IsActive,
|
|
message = $"Módulo {(module.IsActive ? "ativado" : "desativado")} com sucesso"
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao alternar módulo {ModuleId}", moduleId);
|
|
return StatusCode(500, new { success = false, message = ex.Message });
|
|
}
|
|
}
|
|
|
|
[HttpPost("modules/{moduleId}/health-check")]
|
|
public async Task<IActionResult> CheckModuleHealth(string moduleId)
|
|
{
|
|
try
|
|
{
|
|
var module = await _moduleService.GetModuleConfigAsync(moduleId);
|
|
if (module == null)
|
|
{
|
|
return NotFound(new { success = false, message = "Módulo não encontrado" });
|
|
}
|
|
|
|
var healthUrl = $"{GetBaseUrl(module.Url)}{module.HealthEndpoint}";
|
|
var isHealthy = await TestModuleHealth(healthUrl);
|
|
|
|
module.IsHealthy = isHealthy;
|
|
module.LastHealthCheck = DateTime.UtcNow;
|
|
await _moduleService.SaveModuleConfigAsync(module);
|
|
|
|
return Ok(new
|
|
{
|
|
success = true,
|
|
moduleId = moduleId,
|
|
isHealthy = isHealthy,
|
|
lastCheck = module.LastHealthCheck,
|
|
healthUrl = healthUrl
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao verificar saúde do módulo {ModuleId}", moduleId);
|
|
return StatusCode(500, new { success = false, message = ex.Message });
|
|
}
|
|
}
|
|
|
|
[HttpDelete("modules/{moduleId}")]
|
|
public async Task<IActionResult> UnregisterModule(string moduleId)
|
|
{
|
|
try
|
|
{
|
|
var module = await _moduleService.GetModuleConfigAsync(moduleId);
|
|
if (module == null)
|
|
{
|
|
return NotFound(new { success = false, message = "Módulo não encontrado" });
|
|
}
|
|
|
|
// Aqui você implementaria a remoção (dependendo de como está armazenado)
|
|
// Por enquanto, vamos apenas desativar
|
|
module.IsActive = false;
|
|
module.UpdatedAt = DateTime.UtcNow;
|
|
await _moduleService.SaveModuleConfigAsync(module);
|
|
|
|
_logger.LogInformation("Módulo {ModuleId} desregistrado", moduleId);
|
|
|
|
return Ok(new
|
|
{
|
|
success = true,
|
|
message = "Módulo desregistrado com sucesso"
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Erro ao desregistrar módulo {ModuleId}", moduleId);
|
|
return StatusCode(500, new { success = false, message = ex.Message });
|
|
}
|
|
}
|
|
|
|
private async Task<bool> TestModuleHealth(string healthUrl)
|
|
{
|
|
try
|
|
{
|
|
using var client = new HttpClient();
|
|
client.Timeout = TimeSpan.FromSeconds(10);
|
|
var response = await client.GetAsync(healthUrl);
|
|
return response.IsSuccessStatusCode;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private string GenerateApiKey()
|
|
{
|
|
using var rng = RandomNumberGenerator.Create();
|
|
var bytes = new byte[32];
|
|
rng.GetBytes(bytes);
|
|
return Convert.ToBase64String(bytes);
|
|
}
|
|
|
|
private string GetBaseUrl(string fullUrl)
|
|
{
|
|
var uri = new Uri(fullUrl);
|
|
return $"{uri.Scheme}://{uri.Host}:{uri.Port}";
|
|
}
|
|
}
|
|
|
|
public class ModuleRegistrationRequest
|
|
{
|
|
public string ModuleId { get; set; } = "";
|
|
public string Name { get; set; } = "";
|
|
public string BaseUrl { get; set; } = "";
|
|
public bool? AutoActivate { get; set; } = true;
|
|
public int? CacheMinutes { get; set; } = 5;
|
|
public List<string>? AllowedOrigins { get; set; }
|
|
public int? RateLimitPerMinute { get; set; } = 60;
|
|
|
|
// Menu
|
|
public string? MenuTitle { get; set; }
|
|
public string? MenuDescription { get; set; }
|
|
public string? MenuIcon { get; set; }
|
|
public string? MenuCategory { get; set; }
|
|
public int? MenuOrder { get; set; }
|
|
public bool? ShowInMenu { get; set; } = true;
|
|
|
|
// SEO
|
|
public Dictionary<string, string>? SeoTitles { get; set; }
|
|
public Dictionary<string, string>? SeoDescriptions { get; set; }
|
|
public Dictionary<string, string>? SeoKeywords { get; set; }
|
|
|
|
// Technical
|
|
public string? HealthEndpoint { get; set; }
|
|
public int? HealthCheckIntervalMinutes { get; set; }
|
|
public string? Version { get; set; }
|
|
|
|
// Developer
|
|
public string? DeveloperName { get; set; }
|
|
public string? DeveloperEmail { get; set; }
|
|
public string? Repository { get; set; }
|
|
public string? Documentation { get; set; }
|
|
}
|
|
} |