2025-09-06 21:44:52 -03:00
|
|
|
//Elecciones.Api/Program.cs
|
2025-08-14 13:12:16 -03:00
|
|
|
using Elecciones.Database;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2025-08-15 17:31:51 -03:00
|
|
|
using Serilog;
|
2025-08-29 09:54:22 -03:00
|
|
|
using Elecciones.Core.Services;
|
|
|
|
|
using Elecciones.Infrastructure.Services;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
|
|
|
using Microsoft.IdentityModel.Tokens;
|
|
|
|
|
using Elecciones.Database.Entities;
|
|
|
|
|
using System.Text.Json.Serialization;
|
2025-09-03 17:54:49 -03:00
|
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
2025-08-14 13:12:16 -03:00
|
|
|
|
2025-08-15 17:31:51 -03:00
|
|
|
// Esta es la estructura estándar y recomendada.
|
2025-08-14 12:37:57 -03:00
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
|
|
2025-09-06 21:44:52 -03:00
|
|
|
// 1. Registra el servicio del interruptor como un Singleton.
|
|
|
|
|
// Esto asegura que toda la aplicación comparta la MISMA instancia del interruptor.
|
|
|
|
|
builder.Services.AddSingleton<LoggingSwitchService>();
|
|
|
|
|
|
|
|
|
|
builder.Host.UseSerilog((context, services, configuration) =>
|
|
|
|
|
{
|
|
|
|
|
// 2. Obtenemos la instancia del interruptor que acabamos de registrar.
|
|
|
|
|
var loggingSwitch = services.GetRequiredService<LoggingSwitchService>();
|
|
|
|
|
|
|
|
|
|
configuration
|
|
|
|
|
.ReadFrom.Configuration(context.Configuration)
|
|
|
|
|
.ReadFrom.Services(services)
|
|
|
|
|
.Enrich.FromLogContext()
|
|
|
|
|
// 3. Establecemos el nivel mínimo de logging controlado por el interruptor.
|
|
|
|
|
.MinimumLevel.ControlledBy(loggingSwitch.LevelSwitch)
|
|
|
|
|
.WriteTo.Console()
|
|
|
|
|
.WriteTo.File("logs/api-.log", rollingInterval: RollingInterval.Day); // o "logs/worker-.log"
|
|
|
|
|
});
|
2025-08-14 13:12:16 -03:00
|
|
|
|
2025-08-15 17:31:51 -03:00
|
|
|
// 2. Añadir servicios al contenedor.
|
2025-08-14 13:12:16 -03:00
|
|
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
|
|
|
|
builder.Services.AddDbContext<EleccionesDbContext>(options =>
|
|
|
|
|
options.UseSqlServer(connectionString));
|
|
|
|
|
|
2025-08-29 09:54:22 -03:00
|
|
|
builder.Services.AddScoped<IPasswordHasher, PasswordHasher>();
|
2025-08-14 13:12:16 -03:00
|
|
|
|
2025-08-29 09:54:22 -03:00
|
|
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
|
|
|
|
{
|
|
|
|
|
// Esto le dice al serializador que maneje las referencias circulares
|
|
|
|
|
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
|
2025-08-14 13:12:16 -03:00
|
|
|
builder.Services.AddCors(options =>
|
|
|
|
|
{
|
2025-08-29 09:54:22 -03:00
|
|
|
options.AddPolicy(name: MyAllowSpecificOrigins,
|
|
|
|
|
policy =>
|
|
|
|
|
{
|
|
|
|
|
policy.WithOrigins(
|
2025-09-03 18:56:01 -03:00
|
|
|
"http://localhost:5173",
|
2025-09-03 17:06:39 -03:00
|
|
|
"http://localhost:5174",
|
2025-09-03 18:56:01 -03:00
|
|
|
"http://192.168.5.128:8700",
|
2025-09-03 17:06:39 -03:00
|
|
|
"https://www.eldia.com",
|
2025-09-05 13:40:33 -03:00
|
|
|
"https://extras.eldia.com",
|
|
|
|
|
"https://eldia.mustang.cloud"
|
2025-08-30 11:31:45 -03:00
|
|
|
)
|
2025-08-29 09:54:22 -03:00
|
|
|
.AllowAnyHeader()
|
|
|
|
|
.AllowAnyMethod();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
|
|
|
.AddJwtBearer(options =>
|
2025-08-14 13:12:16 -03:00
|
|
|
{
|
2025-08-29 09:54:22 -03:00
|
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
2025-08-14 15:51:19 -03:00
|
|
|
{
|
2025-08-29 09:54:22 -03:00
|
|
|
ValidateIssuer = true,
|
|
|
|
|
ValidateAudience = true,
|
|
|
|
|
ValidateLifetime = true,
|
|
|
|
|
ValidateIssuerSigningKey = true,
|
|
|
|
|
ValidIssuer = builder.Configuration["Jwt:Issuer"],
|
|
|
|
|
ValidAudience = builder.Configuration["Jwt:Audience"],
|
|
|
|
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
|
|
|
|
|
};
|
2025-08-14 13:12:16 -03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
|
builder.Services.AddSwaggerGen();
|
|
|
|
|
|
2025-08-14 12:37:57 -03:00
|
|
|
|
2025-09-03 17:54:49 -03:00
|
|
|
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
|
|
|
|
{
|
|
|
|
|
options.ForwardedHeaders =
|
|
|
|
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
|
|
|
|
options.KnownNetworks.Clear();
|
|
|
|
|
options.KnownProxies.Clear();
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-15 17:31:51 -03:00
|
|
|
// 3. Construir la aplicación.
|
2025-08-14 12:37:57 -03:00
|
|
|
var app = builder.Build();
|
|
|
|
|
|
2025-09-06 21:44:52 -03:00
|
|
|
// --- LÓGICA PARA LEER EL NIVEL DE LOGGING AL INICIO ---
|
|
|
|
|
// Creamos un scope temporal para leer la configuración de la BD
|
|
|
|
|
using (var scope = app.Services.CreateScope()) // O 'host.Services.CreateScope()'
|
|
|
|
|
{
|
|
|
|
|
var services = scope.ServiceProvider;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// El resto de la lógica no cambia
|
|
|
|
|
var dbContext = services.GetRequiredService<EleccionesDbContext>();
|
|
|
|
|
var loggingSwitchService = services.GetRequiredService<LoggingSwitchService>();
|
|
|
|
|
|
|
|
|
|
var logLevelConfig = await dbContext.Configuraciones
|
|
|
|
|
.AsNoTracking()
|
|
|
|
|
.FirstOrDefaultAsync(c => c.Clave == "Logging_Level");
|
|
|
|
|
|
|
|
|
|
if (logLevelConfig != null)
|
|
|
|
|
{
|
|
|
|
|
loggingSwitchService.SetLoggingLevel(logLevelConfig.Valor);
|
|
|
|
|
Console.WriteLine($"--> Nivel de logging inicial establecido desde la BD a: {logLevelConfig.Valor}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
// Si hay un error (ej. la BD no está disponible al arrancar), se usará el nivel por defecto 'Information'.
|
|
|
|
|
Console.WriteLine($"--> No se pudo establecer el nivel de logging desde la BD: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 17:54:49 -03:00
|
|
|
app.UseForwardedHeaders();
|
|
|
|
|
|
2025-08-29 09:54:22 -03:00
|
|
|
// Seeder para el usuario admin
|
|
|
|
|
using (var scope = app.Services.CreateScope())
|
|
|
|
|
{
|
|
|
|
|
var services = scope.ServiceProvider;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var context = services.GetRequiredService<EleccionesDbContext>();
|
|
|
|
|
var hasher = services.GetRequiredService<IPasswordHasher>();
|
|
|
|
|
if (!context.AdminUsers.Any())
|
|
|
|
|
{
|
|
|
|
|
var (hash, salt) = hasher.HashPassword("PTP847elec");
|
|
|
|
|
context.AdminUsers.Add(new Elecciones.Database.Entities.AdminUser
|
|
|
|
|
{
|
|
|
|
|
Username = "admin",
|
|
|
|
|
PasswordHash = hash,
|
|
|
|
|
PasswordSalt = salt
|
|
|
|
|
});
|
|
|
|
|
context.SaveChanges();
|
|
|
|
|
Console.WriteLine("--> Admin user seeded.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
var logger = services.GetRequiredService<ILogger<Program>>();
|
|
|
|
|
logger.LogError(ex, "An error occurred while seeding the database.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Seeder para las bancas vacías
|
|
|
|
|
using (var scope = app.Services.CreateScope())
|
|
|
|
|
{
|
|
|
|
|
var services = scope.ServiceProvider;
|
|
|
|
|
var context = services.GetRequiredService<EleccionesDbContext>();
|
|
|
|
|
if (!context.Bancadas.Any())
|
|
|
|
|
{
|
|
|
|
|
var bancas = new List<Bancada>();
|
|
|
|
|
// 92 bancas de diputados
|
2025-08-30 11:31:45 -03:00
|
|
|
for (int i = 1; i <= 92; i++) // Bucle de 1 a 92
|
2025-08-29 09:54:22 -03:00
|
|
|
{
|
2025-08-30 11:31:45 -03:00
|
|
|
bancas.Add(new Bancada
|
|
|
|
|
{
|
|
|
|
|
Camara = Elecciones.Core.Enums.TipoCamara.Diputados,
|
|
|
|
|
NumeroBanca = i // Asignamos el número de banca
|
|
|
|
|
});
|
2025-08-29 09:54:22 -03:00
|
|
|
}
|
|
|
|
|
// 46 bancas de senadores
|
2025-08-30 11:31:45 -03:00
|
|
|
for (int i = 1; i <= 46; i++) // Bucle de 1 a 46
|
2025-08-29 09:54:22 -03:00
|
|
|
{
|
2025-08-30 11:31:45 -03:00
|
|
|
bancas.Add(new Bancada
|
|
|
|
|
{
|
|
|
|
|
Camara = Elecciones.Core.Enums.TipoCamara.Senadores,
|
|
|
|
|
NumeroBanca = i // Asignamos el número de banca
|
|
|
|
|
});
|
2025-08-29 09:54:22 -03:00
|
|
|
}
|
|
|
|
|
context.Bancadas.AddRange(bancas);
|
|
|
|
|
context.SaveChanges();
|
2025-08-30 11:31:45 -03:00
|
|
|
Console.WriteLine("--> Seeded 138 bancas físicas.");
|
2025-08-29 09:54:22 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Seeder para las configuraciones por defecto
|
|
|
|
|
using (var scope = app.Services.CreateScope())
|
|
|
|
|
{
|
|
|
|
|
var services = scope.ServiceProvider;
|
|
|
|
|
var context = services.GetRequiredService<EleccionesDbContext>();
|
2025-09-06 21:44:52 -03:00
|
|
|
|
|
|
|
|
// Lista de configuraciones por defecto a asegurar
|
|
|
|
|
var defaultConfiguraciones = new Dictionary<string, string>
|
2025-08-29 09:54:22 -03:00
|
|
|
{
|
2025-09-06 21:44:52 -03:00
|
|
|
{ "MostrarOcupantes", "true" },
|
|
|
|
|
{ "TickerResultadosCantidad", "3" },
|
|
|
|
|
{ "ConcejalesResultadosCantidad", "5" },
|
|
|
|
|
{ "Worker_Resultados_Activado", "false" },
|
|
|
|
|
{ "Worker_Bajas_Activado", "false" },
|
|
|
|
|
{ "Worker_Prioridad", "Resultados" },
|
|
|
|
|
{ "Logging_Level", "Information" }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
foreach (var config in defaultConfiguraciones)
|
2025-09-01 14:04:40 -03:00
|
|
|
{
|
2025-09-06 21:44:52 -03:00
|
|
|
if (!context.Configuraciones.Any(c => c.Clave == config.Key))
|
|
|
|
|
{
|
|
|
|
|
context.Configuraciones.Add(new Configuracion { Clave = config.Key, Valor = config.Value });
|
|
|
|
|
}
|
2025-09-01 14:04:40 -03:00
|
|
|
}
|
2025-09-06 21:44:52 -03:00
|
|
|
|
|
|
|
|
context.SaveChanges();
|
|
|
|
|
Console.WriteLine("--> Seeded default configurations.");
|
2025-08-29 09:54:22 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configurar el pipeline de peticiones HTTP.
|
2025-08-15 17:31:51 -03:00
|
|
|
// Añadimos el logging de peticiones de Serilog aquí.
|
|
|
|
|
app.UseSerilogRequestLogging();
|
2025-08-14 13:12:16 -03:00
|
|
|
|
2025-08-14 12:37:57 -03:00
|
|
|
if (app.Environment.IsDevelopment())
|
|
|
|
|
{
|
2025-08-14 13:12:16 -03:00
|
|
|
app.UseSwagger();
|
|
|
|
|
app.UseSwaggerUI();
|
2025-08-14 12:37:57 -03:00
|
|
|
}
|
|
|
|
|
|
2025-08-29 09:54:22 -03:00
|
|
|
// 1. Redirección a HTTPS (si se usa)
|
|
|
|
|
//app.UseHttpsRedirection();
|
|
|
|
|
|
|
|
|
|
// 2. Middleware de Enrutamiento. ¡CLAVE!
|
|
|
|
|
// Determina a qué endpoint irá la petición.
|
|
|
|
|
app.UseRouting();
|
|
|
|
|
|
|
|
|
|
// 3. Middleware de CORS.
|
|
|
|
|
// Ahora se ejecuta sabiendo a qué endpoint se dirige la petición.
|
|
|
|
|
app.UseCors(MyAllowSpecificOrigins);
|
|
|
|
|
|
|
|
|
|
// 4. Middleware de Autenticación.
|
|
|
|
|
// Identifica quién es el usuario a partir del token.
|
|
|
|
|
app.UseAuthentication();
|
|
|
|
|
|
|
|
|
|
// 5. Middleware de Autorización.
|
|
|
|
|
// Verifica si el usuario identificado tiene permiso para acceder al endpoint.
|
2025-08-14 13:12:16 -03:00
|
|
|
app.UseAuthorization();
|
2025-08-29 09:54:22 -03:00
|
|
|
|
|
|
|
|
// 6. Mapea los controladores a los endpoints que el enrutador descubrió.
|
2025-08-14 13:12:16 -03:00
|
|
|
app.MapControllers();
|
|
|
|
|
|
2025-08-15 17:31:51 -03:00
|
|
|
// 5. Ejecutar la aplicación.
|
2025-08-14 13:12:16 -03:00
|
|
|
app.Run();
|