Microservicio de análisis de accesibilidad web desarrollado en .NET 9 con Clean Architecture. Proporciona análisis WCAG 2.1/2.2 comprehensivos con soporte multi-motor y mapeo automático de criterios de conformidad.
⚡ Nota: Este microservicio forma parte de un ecosistema donde el Gateway gestiona rate limiting, caching (Redis), circuit breaker y load balancing. El microservicio se enfoca en su lógica de dominio específica.
Microservicio empresarial para:
- Análisis de accesibilidad con múltiples motores (axe-core, IBM Equal Access)
- Gestión de análisis con consultas avanzadas por múltiples criterios
- Gestión de resultados con mapeo a niveles WCAG (A, AA, AAA)
- Gestión de errores con clasificación por tipo y severidad
- i18n integrado con soporte multiidioma (es, en)
- Soporte multi-motor (axe-core, IBM Equal Access, custom)
- Mapeo automático a niveles WCAG (A, AA, AAA)
- Clasificación por severidad (Critical, Serious, Moderate, Minor)
- Análisis por herramienta configurable
- Almacenamiento persistente de análisis
- CRUD completo de análisis con validación
- Consulta por usuario, fecha, estado, herramienta
- Filtrado avanzado multi-criterio
- Estadísticas y métricas agregadas
- Auditoría completa de análisis
- Registro detallado de violaciones
- Clasificación por tipo y severidad
- Agrupación por análisis
- Trazabilidad completa
- Vinculación con resultados
- Resultados por nivel WCAG (A, AA, AAA)
- Resultados por severidad
- Vinculación análisis-resultados
- Consultas optimizadas con índices
- Métricas de conformidad
- Endpoints compuestos optimizados: Análisis + Resultados + Errores en una sola llamada
- Reduce latencia eliminando múltiples peticiones HTTP
- Autenticación y autorización integradas
- Control de acceso por usuario y rol
- Datos coherentes en una transacción
- Autenticación JWT integrada
- Gateway Secret para comunicación entre servicios
- Validación con FluentValidation
- Control de acceso a operaciones sensibles
- Soporte multiidioma (es, en)
- Mensajes de error localizados
- Content negotiation automático
- Headers de idioma en responses
- Prometheus Metrics integrado
- Métricas de negocio personalizadas
- Endpoint
/metricsexpuesto - Monitoreo de análisis y performance
- Histogramas de duración de operaciones
- Contadores por herramienta y severidad
- Database connectivity check
- Application health monitoring
- Memory usage tracking
- Endpoints de salud personalizados
┌───────────────────────────────────────────────────┐
│ 🔬 ANALYSIS MICROSERVICE API │
│ (Port 5002) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Controllers │ │ Middleware │ │ Health │ │
│ │ (4 APIs) │ │ (Gateway) │ │ Checks │ │
│ │ +Composite │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └──────────┘ │
│ │ │ │ │
│ └────────────────┴───────────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ APPLICATION │ │
│ │ Services │ │
│ │ +Composite Svc│ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ DOMAIN │ │
│ │ Entities │ │
│ │ Interfaces │ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │INFRASTRUCTURE │ │
│ │ EF Core │ │
│ │ Repositories│ │
│ └───────┬───────┘ │
└──────────────────────┼───────────────────────────┘
│
▼
┌──────────────┐
│ MySQL DB │
│(analysis_db) │
└──────────────┘
Clean Architecture con 4 capas:
- API: Controllers, Middleware, Health Checks
- Application: Services, DTOs, Use Cases
- Domain: Entities, Interfaces, Business Logic
- Infrastructure: EF Core, Repositories, MySQL
- .NET 9.0 SDK
- MySQL 8.0+
- Docker & Docker Compose (opcional)
# Clonar repositorio
git clone https://github.com/your-org/accessibility-ms-analysis.git
cd accessibility-ms-analysis
# Configurar base de datos
mysql -u root -p < init-analysis-db.sql
# Configurar variables de entorno
cp .env.example .env
# Editar .env con tus credenciales de MySQL
# Restaurar dependencias
dotnet restore
# Compilar
dotnet build --configuration Release
# Ejecutar
dotnet run --project src/Analysis.Api/Analysis.Api.csproj# Levantar todos los servicios
docker-compose up -d
# Ver logs
docker-compose logs -f analysis-api
# Verificar estado
docker-compose ps
# Detener servicios
docker-compose down# Health check
curl http://localhost:5002/health
# Crear análisis de prueba
curl -X POST http://localhost:5002/api/analysis \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","userId":1,"tool":"axe-core"}'
# Obtener análisis completo (con resultados y errores) - NUEVO ⭐
curl -X GET http://localhost:5002/api/compositeanalysis/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Accept-Language: es"| Método | Endpoint | Descripción |
|---|---|---|
| GET | /api/analysis |
Listar todos los análisis |
| POST | /api/analysis |
Crear nuevo análisis |
| GET | /api/analysis/{id} |
Obtener análisis por ID |
| DELETE | /api/analysis/{id} |
Eliminar análisis por ID |
| GET | /api/analysis/by-user/{userId} |
Análisis por usuario |
| GET | /api/analysis/by-date |
Análisis por rango de fechas |
| GET | /api/analysis/by-tool/{tool} |
Análisis por herramienta |
| GET | /api/analysis/by-status/{status} |
Análisis por estado |
| DELETE | /api/analysis/all |
Eliminar todos los análisis |
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /api/result |
Listar todos los resultados |
| POST | /api/result |
Crear nuevo resultado |
| GET | /api/result/{id} |
Obtener resultado por ID |
| DELETE | /api/result/{id} |
Eliminar resultado por ID |
| GET | /api/result/by-analysis/{id} |
Resultados por análisis |
| GET | /api/result/by-level |
Resultados por nivel WCAG |
| GET | /api/result/by-severity |
Resultados por severidad |
| DELETE | /api/result/all |
Eliminar todos los resultados |
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /api/error |
Listar todos los errores |
| POST | /api/error |
Crear nuevo error |
| GET | /api/error/{id} |
Obtener error por ID |
| DELETE | /api/error/{id} |
Eliminar error por ID |
| GET | /api/error/by-result/{id} |
Errores por resultado |
| DELETE | /api/error/all |
Eliminar todos los errores |
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /api/compositeanalysis/{id} |
Obtener análisis completo (con resultados y errores) |
| GET | /api/compositeanalysis/by-user |
Obtener todos los análisis completos de un usuario |
Características:
- ✅ Una sola llamada al API: Retorna análisis + resultados + errores en una respuesta
- 🔐 Autenticación JWT requerida: Todos los endpoints requieren token válido
- 🛡️ Control de acceso: Usuario solo accede a sus propios análisis (admin accede a todos)
- 🚀 Optimizado: Reduce latencia al eliminar múltiples llamadas HTTP
- 📦 Datos coherentes: Toda la información en una transacción
Ejemplo de respuesta completa:
{
"analysis": {
"id": 1,
"userId": 123,
"toolUsed": "axe",
"status": "completed",
"results": [
{
"id": 1,
"wcagCriterion": "1.1.1",
"severity": "critical",
"errors": [
{
"id": 1,
"errorCode": "image-alt",
"description": "Missing alt attribute",
"location": "img.hero line 45"
}
]
}
]
}
}Documentación completa: Ver docs/CompositeAnalysisController.md
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /health |
Health check general |
| GET | /health/ready |
Readiness probe |
| GET | /health/live |
Liveness probe |
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /metrics |
Métricas Prometheus |
Total: 29 endpoints disponibles (incluye 2 nuevos endpoints compuestos)
Estado General: ✅ 381/383 tests exitosos (99.5%)
Cobertura Total: 95.6% (1194/1249 líneas cubiertas)
| Capa | Cobertura | Tests | Estado |
|---|---|---|---|
| Analysis.Api | 95.23% | Controllers + Middleware | ✅ |
| AnalysisController | 95%+ | CRUD Análisis | ✅ |
| ResultController | 95%+ | CRUD Resultados | ✅ |
| ErrorController | 95%+ | CRUD Errores | ✅ |
| CompositeAnalysisController | 100% | API Compuesta (Nuevo) | ✅ ⭐ |
| Analysis.Application | 95.69% | Services + DTOs | ✅ |
| CompositeAnalysisService | 100% | Servicio Compuesto | ✅ ⭐ |
| Analysis.Domain | 100% | Entities + Interfaces | ✅ |
| Analysis.Infrastructure | 0% | Repositories + EF |
Métricas detalladas:
- Cobertura de líneas: 95.6% (1194/1249)
- Cobertura de ramas: 81.77%
- Tiempo de ejecución: ~20s para 383 tests
- Tasa de éxito: 99.5%
# Todos los tests con cobertura
.\manage-tests.ps1 -GenerateCoverage -OpenReport
# Solo tests unitarios
.\manage-tests.ps1 -TestType Unit
# Tests de integración
.\manage-tests.ps1 -TestType Integration
# Ver dashboard interactivo
Start-Process .\test-dashboard.htmlUnit Tests:
- Validación de entidades (Analysis, Result, Error)
- Lógica de servicios (AnalysisService, ResultService, ErrorService, CompositeAnalysisService)
- DTOs y mappers (incluye CompleteDtos)
- Validadores de dominio
- Tests completos para API compuesta (10+ escenarios)
Integration Tests:
- Controllers con base de datos en memoria
- CompositeAnalysisController con autenticación JWT
- Repositorios con MySQL real
- Health checks completos
- Middleware de gateway secret
E2E Tests:
- Flows completos de análisis
- Creación de análisis + resultados + errores
- Consultas por múltiples criterios
- Mapeo WCAG automático
- Obtención de análisis completos con una sola llamada
El microservicio expone métricas detalladas en el endpoint /metrics para monitoreo con Prometheus/Grafana.
Contadores (Counters):
# Total de análisis realizados (por herramienta y estado)
analyses_performed_total{tool="axe_core|ibm_equal_access|custom", status="pending|completed|failed"}
# Total de consultas realizadas (por tipo de operación)
analyses_queries_total{operation="get_all|get_by_user|get_by_tool|get_by_status|get_by_date"}
# Total de resultados creados (por severidad)
analysis_results_created_total{severity="critical|serious|moderate|minor"}
# Total de errores detectados (por tipo)
analysis_errors_created_total{type="violation|warning|notice"}
Histogramas (Histograms):
# Duración de análisis en segundos
analysis_duration_seconds{tool="axe_core|ibm_equal_access|custom"}
# Duración de consultas en segundos
analysis_query_duration_seconds{operation="get_all|get_by_user|..."}
Gauges:
# Análisis actualmente en progreso
analyses_in_progress
# Ver todas las métricas
curl http://localhost:5002/metrics
# Filtrar métricas específicas
curl http://localhost:5002/metrics | grep "analyses_performed_total"
# Verificar análisis en progreso
curl http://localhost:5002/metrics | grep "analyses_in_progress"# Panel 1: Análisis por herramienta
sum(rate(analyses_performed_total[5m])) by (tool)
# Panel 2: Tasa de errores
rate(analyses_performed_total{status="failed"}[5m])
# Panel 3: Duración promedio de análisis
histogram_quantile(0.95, rate(analysis_duration_seconds_bucket[5m]))
# Panel 4: Análisis en curso
analyses_in_progress# Health check básico
curl http://localhost:5002/health
# Readiness (listo para recibir tráfico)
curl http://localhost:5002/health/ready
# Liveness (proceso está vivo)
curl http://localhost:5002/health/liveRespuesta Health Check:
{
"status": "Healthy",
"totalDuration": "00:00:00.0234567",
"entries": {
"database": {
"status": "Healthy",
"description": "Database connection is healthy",
"duration": "00:00:00.0123456"
},
"self": {
"status": "Healthy",
"description": "Analysis API is running",
"duration": "00:00:00.0001234"
}
}
}┌──────────┐ JWT ┌─────────┐ X-User-* ┌──────────┐ IUserContext ┌────────────┐
│ Client │ ───────> │ Gateway │ ──────────> │ Middleware│ ──────────────> │ Controller │
└──────────┘ └─────────┘ └──────────┘ └────────────┘
│ │ │
Valida JWT Extrae headers Valida IsAuthenticated
+ Secret + Crea UserContext + Usa UserId/Role
1. GatewaySecretValidationMiddleware
// Valida que la petición provenga del Gateway autorizado
// Header requerido: X-Gateway-Secret
// Configuración: appsettings.json -> GatewaySettings:Secret2. UserContextMiddleware
// Extrae información de usuario de headers propagados por Gateway
// Headers procesados:
// - X-User-Id → UserId
// - X-User-Email → Email
// - X-User-Role → Role
// - X-User-Name → Name
// Inyecta IUserContext en controllers vía DIpublic interface IUserContext
{
bool IsAuthenticated { get; }
Guid UserId { get; }
string Email { get; }
string Role { get; }
string Name { get; }
}public class AnalysisController(
IAnalysisService service,
IUserContext userContext) : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateAnalysisDto dto)
{
// ✅ Validación de autenticación
if (!userContext.IsAuthenticated)
return Unauthorized(new { message = "User not authenticated" });
// ✅ Usar UserId del contexto (no del body)
dto.UserId = userContext.UserId;
var result = await service.CreateAnalysisAsync(dto);
return CreatedAtAction(...);
}
}appsettings.json:
{
"GatewaySettings": {
"Secret": "your-secret-key-here",
"AllowedOrigins": ["http://localhost:8080"]
},
"Jwt": {
"SecretKey": "your-jwt-secret-key-min-32-chars",
"Issuer": "accessibility-gateway",
"Audience": "accessibility-microservices"
}
}Generar Secrets:
# Generar JWT Secret Key
.\Generate-JwtSecretKey.ps1
# Validar configuración JWT
.\Validate-JwtConfig.ps1# Build image
docker build -t accessibility-analysis:latest .
# Run standalone
docker run -d \
--name analysis-api \
-p 5002:5002 \
-e ConnectionStrings__AnalysisDb="Server=mysql;Database=analysis_db;..." \
-e GatewaySecret="your-gateway-secret" \
accessibility-analysis:latestversion: "3.8"
services:
analysis-api:
image: accessibility-analysis:latest
ports:
- "5002:5002"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__AnalysisDb=Server=mysql-analysis;Database=analysis_db;Uid=root;Pwd=password
- GatewaySecret=your-gateway-secret
depends_on:
- mysql-analysis
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5002/health"]
interval: 30s
mysql-analysis:
image: mysql:8.0
ports:
- "3308:3306"
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=analysis_db
volumes:
- mysql-analysis-data:/var/lib/mysql
- ./init-analysis-db.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
mysql-analysis-data:# ASP.NET Core
ASPNETCORE_ENVIRONMENT=Production|Development
ASPNETCORE_URLS=http://+:5002
# Base de Datos
ConnectionStrings__AnalysisDb=Server=localhost;Database=analysis_db;Uid=root;Pwd=password
# Gateway Configuration
GatewaySecret=your-super-secret-gateway-key
# Localization
DefaultLanguage=es
SupportedLanguages=es,en
# Logging
Serilog__MinimumLevel=Information
Serilog__WriteTo__Console=true-- Crear base de datos
CREATE DATABASE analysis_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Ejecutar script de inicialización
SOURCE init-analysis-db.sql;1. Generate-JwtSecretKey.ps1
Genera una clave secreta segura para JWT de forma automática.
# Ejecutar script
.\Generate-JwtSecretKey.ps1
# Salida:
# ✅ JWT Secret Key generada exitosamente
# 🔑 Clave: AbCdEf12...GhIjKl34 (32+ caracteres)
# 📝 Agregar en appsettings.json -> Jwt:SecretKeyCaracterísticas:
- Genera claves de 32+ caracteres automáticamente
- Usa RNGCryptoServiceProvider (cryptographically secure)
- Valida longitud mínima requerida
- Formato Base64 URL-safe
2. Validate-JwtConfig.ps1
Valida la configuración JWT en appsettings.json antes de deployment.
# Ejecutar validación
.\Validate-JwtConfig.ps1
# Salida exitosa:
# ✅ Jwt:SecretKey existe
# ✅ Longitud: 44 caracteres (>= 32 requeridos)
# ✅ Jwt:Issuer configurado: accessibility-gateway
# ✅ Jwt:Audience configurado: accessibility-microservices
# ✅ Configuración JWT válida para producción
# Salida con errores:
# ❌ Jwt:SecretKey no encontrada en appsettings.json
# ❌ SecretKey muy corta: 16 caracteres (mínimo 32)
# ⚠️ Considere ejecutar .\Generate-JwtSecretKey.ps1Validaciones:
- ✅ Existencia de
Jwt:SecretKey - ✅ Longitud mínima (32 caracteres)
- ✅ Configuración de
IssueryAudience - ✅ Formato JSON válido
3. init-test-databases.ps1 / .sh
Inicializa bases de datos de prueba para testing local.
# Windows
.\init-test-databases.ps1
# Linux/Mac
chmod +x init-test-databases.sh
./init-test-databases.shFuncionalidad:
- Crea contenedor MySQL para testing
- Ejecuta script
init-analysis-db.sql - Configura usuario y permisos
- Verifica conexión antes de salir
Variables de entorno requeridas:
MYSQL_TEST_ROOT_PASSWORD=test_root_pass
MYSQL_TEST_DATABASE=analysis_test_db
MYSQL_TEST_USER=test_user
MYSQL_TEST_PASSWORD=test_pass4. manage-tests.ps1
Script unificado para ejecutar tests con diferentes configuraciones.
# Ejecutar todos los tests
.\manage-tests.ps1
# Tests con cobertura
.\manage-tests.ps1 -Coverage
# Tests por categoría
.\manage-tests.ps1 -Category Unit
.\manage-tests.ps1 -Category Integration
# Tests con reporte HTML
.\manage-tests.ps1 -Coverage -HtmlReportCaracterísticas:
- Ejecuta xUnit con configuración personalizada
- Genera reportes de cobertura (Coverlet)
- Filtra por categoría (Unit/Integration/E2E)
- Exporta resultados a
TestResults/
DatabaseManager.cs
Utilidad para gestión de esquema de base de datos en testing.
public class DatabaseManager
{
// Crear esquema completo desde cero
public static async Task CreateSchemaAsync(string connectionString);
// Limpiar todos los datos (mantiene estructura)
public static async Task CleanDatabaseAsync(string connectionString);
// Resetear base de datos (drop + recreate)
public static async Task ResetDatabaseAsync(string connectionString);
// Verificar conexión
public static async Task<bool> CanConnectAsync(string connectionString);
}Uso en tests:
[Fact]
public async Task Integration_Test_Example()
{
// Arrange: Limpiar estado previo
await DatabaseManager.CleanDatabaseAsync(_connectionString);
// Act: Ejecutar test
var result = await _service.CreateAnalysisAsync(dto);
// Assert
Assert.NotNull(result);
}init-analysis-db.sql
Script de inicialización de base de datos con esquema completo.
-- Estructura:
-- 1. Creación de base de datos
-- 2. Tablas principales (analyses, results, errors)
-- 3. Índices para performance
-- 4. Foreign keys y relaciones
-- 5. Usuario y permisos
-- Ejecutar manualmente:
mysql -u root -p < init-analysis-db.sqlTablas creadas:
analyses- Análisis de accesibilidadresults- Resultados WCAG por análisiserrors- Errores detectados por análisis
Problema anterior: Para obtener un análisis completo con sus resultados y errores, se necesitaban múltiples llamadas:
// ❌ Enfoque anterior (3+ llamadas)
const analysis = await fetch("/api/analysis/1");
const results = await fetch("/api/result/by-analysis?analysisId=1");
for (const result of results) {
const errors = await fetch(`/api/error/by-result?resultId=${result.id}`);
result.errors = errors;
}Solución actual: Una sola llamada obtiene todo:
// ✅ Enfoque nuevo (1 llamada)
const complete = await fetch("/api/compositeanalysis/1", {
headers: { Authorization: `Bearer ${token}` },
});
// Ya tiene analysis.results[].errors incluidos| Aspecto | Antes (Múltiples Llamadas) | Ahora (API Compuesta) |
|---|---|---|
| Latencia | 3+ peticiones HTTP | 1 petición HTTP |
| Performance | ~300-500ms | ~100-150ms |
| Código Frontend | 15-20 líneas | 3-5 líneas |
| Coherencia de datos | Posibles inconsistencias | Datos en una transacción |
| Complejidad | Alta (lógica en frontend) | Baja (centralizada) |
GET /api/compositeanalysis/{id}
Authorization: Bearer {token}
Accept-Language: esRespuesta:
{
"analysis": {
"id": 1,
"userId": 123,
"dateAnalysis": "2025-11-10T10:30:00Z",
"toolUsed": "axe",
"wcagLevel": "AA",
"results": [
{
"id": 1,
"wcagCriterion": "1.1.1",
"level": "A",
"severity": "critical",
"errors": [
{
"id": 1,
"errorCode": "image-alt",
"description": "Missing alt attribute",
"location": "img.hero line 45"
}
]
}
]
},
"message": "Análisis encontrado exitosamente"
}GET /api/compositeanalysis/by-user?userId=123
Authorization: Bearer {token}
Accept-Language: es- ✅ Autenticación JWT obligatoria
- ✅ Usuario solo accede a sus propios análisis
- ✅ Administradores pueden acceder a todos los análisis
- ✅ Validación de permisos en cada petición
JavaScript/Fetch:
async function getCompleteAnalysis(analysisId, token) {
const response = await fetch(`/api/compositeanalysis/${analysisId}`, {
headers: {
Authorization: `Bearer ${token}`,
"Accept-Language": "es",
},
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
console.log(`Análisis: ${data.analysis.summaryResult}`);
console.log(`Resultados: ${data.analysis.results.length}`);
data.analysis.results.forEach((r) => {
console.log(` - ${r.wcagCriterion}: ${r.errors.length} errores`);
});
return data.analysis;
}C#:
public async Task<CompleteAnalysisDto> GetCompleteAnalysisAsync(int id, string token)
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
client.DefaultRequestHeaders.Add("Accept-Language", "es");
var response = await client.GetAsync($"/api/compositeanalysis/{id}");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<CompleteAnalysisDto>();
return result;
}- 📖 Documentación completa del API Compuesta
- 🔧 Guía de configuración del Gateway
- 📋 Detalles de implementación
El controlador compuesto incluye una suite completa de tests:
- ✅ CompositeAnalysisServiceTests (13 tests unitarios)
- ✅ CompositeAnalysisControllerTests (10 tests de integración)
- ✅ CompleteDtosTests (12 tests de DTOs)
Ejecutar tests:
# Tests del servicio compuesto
dotnet test --filter "FullyQualifiedName~CompositeAnalysisServiceTests"
# Tests del controlador compuesto
dotnet test --filter "FullyQualifiedName~CompositeAnalysisControllerTests"
# Tests de DTOs completos
dotnet test --filter "FullyQualifiedName~CompleteDtosTests"IMPORTANT: This software and associated documentation files (the "Software") are the exclusive property of Geovanny Camacho and are protected by copyright laws and international treaty provisions.
## 🛠️ Stack Tecnológico
- **Runtime:** .NET 9.0
- **Framework:** ASP.NET Core Web API
- **ORM:** Entity Framework Core 9.0
- **Database:** MySQL 8.0+
- **Authentication:** Gateway Secret
- **Logging:** Serilog
- **Testing:** xUnit + Moq + FluentAssertions
- **Coverage:** Coverlet + ReportGenerator
- **Container:** Docker + Docker Compose
## � License
**Proprietary Software License v1.0**
Copyright (c) 2025 Geovanny Camacho. All rights reserved.
**IMPORTANT:** This software and associated documentation files (the "Software") are the exclusive property of Geovanny Camacho and are protected by copyright laws and international treaty provisions.
### TERMS AND CONDITIONS
1. **OWNERSHIP**: The Software is licensed, not sold. Geovanny Camacho retains all right, title, and interest in and to the Software, including all intellectual property rights.
2. **RESTRICTIONS**: You may NOT:
- Copy, modify, or create derivative works of the Software
- Distribute, transfer, sublicense, lease, lend, or rent the Software
- Reverse engineer, decompile, or disassemble the Software
- Remove or alter any proprietary notices or labels on the Software
- Use the Software for any commercial purpose without explicit written permission
- Share access credentials or allow unauthorized access to the Software
3. **CONFIDENTIALITY**: The Software contains trade secrets and confidential information. You agree to maintain the confidentiality of the Software and not disclose it to any third party.
4. **TERMINATION**: This license is effective until terminated. Your rights under this license will terminate automatically without notice if you fail to comply with any of its terms.
5. **NO WARRANTY**: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
6. **LIMITATION OF LIABILITY**: IN NO EVENT SHALL GEOVANNY CAMACHO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7. **GOVERNING LAW**: This license shall be governed by and construed in accordance with the laws of the jurisdiction in which Geovanny Camacho resides, without regard to its conflict of law provisions.
8. **ENTIRE AGREEMENT**: This license constitutes the entire agreement between you and Geovanny Camacho regarding the Software and supersedes all prior or contemporaneous understandings.
**FOR LICENSING INQUIRIES:**
Geovanny Camacho
Email: fgiocl@outlook.com
**By using this Software, you acknowledge that you have read this license, understand it, and agree to be bound by its terms and conditions.**
---
**Author:** Geovanny Camacho (fgiocl@outlook.com)
**Last Update:** 05/11/2025