Introdotta la funzionalità di esplorazione delle aste pubbliche di Bidoo senza login, accessibile dal menu principale. Aggiunti nuovi modelli (`BidooBrowserAuction`, `BidooCategoryInfo`) e servizio (`BidooBrowserService`) per scraping e polling delle aste e categorie. Creata la pagina Blazor `AuctionBrowser.razor` con griglia responsive, badge, filtri per categoria, caricamento incrementale e aggiornamento automatico degli stati. Aggiornati i servizi in `Program.cs` e aggiunti nuovi stili CSS per la UI moderna. Le aste possono essere aggiunte rapidamente al monitor personale. Parsing robusto e fallback su categorie predefinite in caso di errori.
631 lines
23 KiB
C#
631 lines
23 KiB
C#
using AutoBidder.Services;
|
|
using AutoBidder.Data;
|
|
using AutoBidder.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Web;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.DataProtection;
|
|
using System.Data.Common;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// FORCE ASPNETCORE_URLS to prevent any override
|
|
// Questo garantisce che il container ascolti SEMPRE sulla porta configurata
|
|
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ASPNETCORE_URLS")))
|
|
{
|
|
builder.WebHost.UseUrls("http://+:8080");
|
|
}
|
|
else
|
|
{
|
|
builder.WebHost.UseUrls(Environment.GetEnvironmentVariable("ASPNETCORE_URLS")!);
|
|
}
|
|
|
|
// Configura Kestrel solo per HTTPS opzionale
|
|
// HTTP è gestito da ASPNETCORE_URLS (default: http://+:8080 nel Dockerfile)
|
|
var enableHttps = builder.Configuration.GetValue<bool>("Kestrel:EnableHttps", false);
|
|
|
|
if (enableHttps)
|
|
{
|
|
builder.WebHost.ConfigureKestrel(options =>
|
|
{
|
|
try
|
|
{
|
|
// In produzione, cerca il certificato da configurazione
|
|
var certPath = builder.Configuration["Kestrel:Certificates:Default:Path"];
|
|
var certPassword = builder.Configuration["Kestrel:Certificates:Default:Password"];
|
|
|
|
if (!string.IsNullOrEmpty(certPath) && File.Exists(certPath))
|
|
{
|
|
options.ListenAnyIP(8443, listenOptions =>
|
|
{
|
|
listenOptions.UseHttps(certPath, certPassword);
|
|
Console.WriteLine($"[Kestrel] HTTPS enabled with certificate: {certPath}");
|
|
});
|
|
}
|
|
else if (builder.Environment.IsDevelopment())
|
|
{
|
|
// Certificato di sviluppo SOLO in ambiente Development
|
|
options.ListenAnyIP(5001, listenOptions =>
|
|
{
|
|
listenOptions.UseHttps();
|
|
Console.WriteLine("[Kestrel] HTTPS enabled with development certificate");
|
|
});
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[Kestrel] HTTPS requested but no certificate found");
|
|
Console.WriteLine("[Kestrel] Running in HTTP-only mode");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[Kestrel] Failed to enable HTTPS: {ex.Message}");
|
|
Console.WriteLine("[Kestrel] Running in HTTP-only mode");
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[Kestrel] HTTPS disabled - running in HTTP-only mode");
|
|
Console.WriteLine("[Kestrel] Use a reverse proxy (nginx/traefik) for SSL termination");
|
|
Console.WriteLine($"[Kestrel] Listening on: {Environment.GetEnvironmentVariable("ASPNETCORE_URLS") ?? "http://+:8080"}");
|
|
}
|
|
|
|
// Add services to the container
|
|
builder.Services.AddRazorPages();
|
|
builder.Services.AddServerSideBlazor();
|
|
|
|
// Configura Data Protection per evitare CryptographicException
|
|
var dataProtectionPath = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"AutoBidder",
|
|
"DataProtection-Keys"
|
|
);
|
|
|
|
if (!Directory.Exists(dataProtectionPath))
|
|
{
|
|
Directory.CreateDirectory(dataProtectionPath);
|
|
}
|
|
|
|
builder.Services.AddDataProtection()
|
|
.PersistKeysToFileSystem(new DirectoryInfo(dataProtectionPath))
|
|
.SetApplicationName("AutoBidder");
|
|
|
|
// ============================================
|
|
// CONFIGURAZIONE AUTENTICAZIONE E SICUREZZA
|
|
// ============================================
|
|
|
|
// Database per Identity (SQLite)
|
|
var identityDbPath = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"AutoBidder",
|
|
"identity.db"
|
|
);
|
|
|
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
{
|
|
options.UseSqlite($"Data Source={identityDbPath}");
|
|
});
|
|
|
|
// ASP.NET Core Identity
|
|
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
|
{
|
|
// Password settings (SICUREZZA FORTE)
|
|
options.Password.RequireDigit = true;
|
|
options.Password.RequireLowercase = true;
|
|
options.Password.RequireUppercase = true;
|
|
options.Password.RequireNonAlphanumeric = true;
|
|
options.Password.RequiredLength = 12;
|
|
options.Password.RequiredUniqueChars = 4;
|
|
|
|
// Lockout settings (protezione brute-force)
|
|
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
|
|
options.Lockout.MaxFailedAccessAttempts = 5;
|
|
options.Lockout.AllowedForNewUsers = true;
|
|
|
|
// User settings
|
|
options.User.RequireUniqueEmail = false;
|
|
options.SignIn.RequireConfirmedAccount = false;
|
|
})
|
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
|
.AddDefaultTokenProviders();
|
|
|
|
// Cookie configuration (SICUREZZA TAILSCALE)
|
|
builder.Services.ConfigureApplicationCookie(options =>
|
|
{
|
|
options.Cookie.Name = "AutoBidder.Auth";
|
|
options.Cookie.HttpOnly = true;
|
|
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; // HTTP su Tailscale OK
|
|
options.Cookie.SameSite = SameSiteMode.Lax;
|
|
options.ExpireTimeSpan = TimeSpan.FromDays(7);
|
|
options.SlidingExpiration = true;
|
|
|
|
// Redirect per autenticazione (Razor Pages)
|
|
options.LoginPath = "/Account/Login";
|
|
options.LogoutPath = "/Account/Logout";
|
|
options.AccessDeniedPath = "/Account/Login";
|
|
});
|
|
|
|
// Authorization
|
|
builder.Services.AddAuthorization();
|
|
builder.Services.AddCascadingAuthenticationState();
|
|
|
|
// Configura HTTPS Redirection per produzione
|
|
if (!builder.Environment.IsDevelopment())
|
|
{
|
|
builder.Services.AddHsts(options =>
|
|
{
|
|
options.MaxAge = TimeSpan.FromDays(365);
|
|
options.IncludeSubDomains = true;
|
|
options.Preload = true;
|
|
});
|
|
}
|
|
|
|
// Configura Database SQLite per statistiche (fallback locale)
|
|
builder.Services.AddDbContext<StatisticsContext>(options =>
|
|
{
|
|
var dbPath = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
"AutoBidder",
|
|
"statistics.db"
|
|
);
|
|
|
|
// Crea directory se non esiste
|
|
var directory = Path.GetDirectoryName(dbPath);
|
|
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
|
{
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
options.UseSqlite($"Data Source={dbPath}");
|
|
});
|
|
|
|
// Configura Database PostgreSQL per statistiche avanzate
|
|
var usePostgres = builder.Configuration.GetValue<bool>("Database:UsePostgres", false);
|
|
if (usePostgres)
|
|
{
|
|
try
|
|
{
|
|
var connString = builder.Environment.IsProduction()
|
|
? builder.Configuration.GetConnectionString("PostgresStatsProduction")
|
|
: builder.Configuration.GetConnectionString("PostgresStats");
|
|
|
|
// Sostituisci variabili ambiente in production
|
|
if (builder.Environment.IsProduction())
|
|
{
|
|
connString = connString?
|
|
.Replace("${POSTGRES_USER}", Environment.GetEnvironmentVariable("POSTGRES_USER") ?? "autobidder")
|
|
.Replace("${POSTGRES_PASSWORD}", Environment.GetEnvironmentVariable("POSTGRES_PASSWORD") ?? "");
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(connString))
|
|
{
|
|
builder.Services.AddDbContext<AutoBidder.Data.PostgresStatsContext>(options =>
|
|
{
|
|
options.UseNpgsql(connString, npgsqlOptions =>
|
|
{
|
|
npgsqlOptions.EnableRetryOnFailure(3);
|
|
npgsqlOptions.CommandTimeout(30);
|
|
});
|
|
});
|
|
|
|
Console.WriteLine("[Startup] PostgreSQL configured for statistics");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[Startup] PostgreSQL connection string not found - using SQLite only");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[Startup] PostgreSQL configuration failed: {ex.Message} - using SQLite only");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[Startup] PostgreSQL disabled in configuration - using SQLite only");
|
|
}
|
|
|
|
// Registra servizi applicazione come Singleton per condividere stato
|
|
var htmlCacheService = new HtmlCacheService(
|
|
maxConcurrentRequests: 3,
|
|
requestsPerSecond: 5,
|
|
cacheExpiration: TimeSpan.FromMinutes(5),
|
|
maxRetries: 2
|
|
);
|
|
|
|
var auctionMonitor = new AuctionMonitor();
|
|
htmlCacheService.OnLog += (msg) => Console.WriteLine(msg);
|
|
|
|
builder.Services.AddSingleton(auctionMonitor);
|
|
builder.Services.AddSingleton(htmlCacheService);
|
|
builder.Services.AddSingleton(sp => new SessionService(auctionMonitor.GetApiClient()));
|
|
builder.Services.AddSingleton<DatabaseService>();
|
|
builder.Services.AddSingleton<ApplicationStateService>();
|
|
builder.Services.AddSingleton<BidooBrowserService>();
|
|
builder.Services.AddScoped<StatsService>(sp =>
|
|
{
|
|
var db = sp.GetRequiredService<DatabaseService>();
|
|
|
|
// Prova a ottenere PostgreSQL context (potrebbe essere null)
|
|
AutoBidder.Data.PostgresStatsContext? postgresDb = null;
|
|
try
|
|
{
|
|
postgresDb = sp.GetService<AutoBidder.Data.PostgresStatsContext>();
|
|
}
|
|
catch
|
|
{
|
|
// PostgreSQL non disponibile, usa solo SQLite
|
|
}
|
|
|
|
return new StatsService(db, postgresDb);
|
|
});
|
|
builder.Services.AddScoped<AuctionStateService>();
|
|
|
|
// Configura SignalR per real-time updates
|
|
builder.Services.AddSignalR(options =>
|
|
{
|
|
options.MaximumReceiveMessageSize = 102400; // 100KB
|
|
options.EnableDetailedErrors = true;
|
|
});
|
|
|
|
var app = builder.Build();
|
|
|
|
// ============================================
|
|
// INIZIALIZZAZIONE DATABASE IDENTITY
|
|
// ============================================
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
try
|
|
{
|
|
var identityDb = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
|
|
|
// Crea database Identity
|
|
await identityDb.Database.EnsureCreatedAsync();
|
|
Console.WriteLine("[Identity] Database initialized");
|
|
|
|
// Crea utente admin se non esiste
|
|
var adminUsername = Environment.GetEnvironmentVariable("ADMIN_USERNAME") ?? "admin";
|
|
var adminPassword = Environment.GetEnvironmentVariable("ADMIN_PASSWORD");
|
|
|
|
// Password di default se non configurata (stessa per debug e container)
|
|
if (string.IsNullOrEmpty(adminPassword))
|
|
{
|
|
adminPassword = "Admin@Password123!";
|
|
}
|
|
|
|
var existingAdmin = await userManager.FindByNameAsync(adminUsername);
|
|
if (existingAdmin == null)
|
|
{
|
|
var adminUser = new ApplicationUser
|
|
{
|
|
UserName = adminUsername,
|
|
Email = $"{adminUsername}@autobidder.local",
|
|
EmailConfirmed = true,
|
|
IsActive = true,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
var result = await userManager.CreateAsync(adminUser, adminPassword);
|
|
if (result.Succeeded)
|
|
{
|
|
Console.WriteLine($"[Identity] Admin user created: {adminUsername}");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[Identity] Failed to create admin user: {string.Join(", ", result.Errors.Select(e => e.Description))}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[Identity] Admin user exists: {adminUsername}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[Identity] Initialization error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
// ??? NUOVO: Inizializza DatabaseService
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
var databaseService = scope.ServiceProvider.GetRequiredService<DatabaseService>();
|
|
|
|
try
|
|
{
|
|
Console.WriteLine("[DB] Initializing main database...");
|
|
await databaseService.InitializeDatabaseAsync();
|
|
|
|
var dbInfo = await databaseService.GetDatabaseInfoAsync();
|
|
Console.WriteLine($"[DB] Database initialized successfully:");
|
|
Console.WriteLine($"[DB] Path: {dbInfo.Path}");
|
|
Console.WriteLine($"[DB] Size: {dbInfo.SizeFormatted}");
|
|
Console.WriteLine($"[DB] Version: {dbInfo.Version}");
|
|
Console.WriteLine($"[DB] Auctions: {dbInfo.AuctionsCount}");
|
|
Console.WriteLine($"[DB] Bid History: {dbInfo.BidHistoryCount}");
|
|
Console.WriteLine($"[DB] Product Stats: {dbInfo.ProductStatsCount}");
|
|
|
|
// Verifica salute database
|
|
var isHealthy = await databaseService.CheckDatabaseHealthAsync();
|
|
Console.WriteLine($"[DB] Database health check: {(isHealthy ? "OK" : "FAILED")}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[DB ERROR] Failed to initialize database: {ex.Message}");
|
|
Console.WriteLine($"[DB ERROR] Stack trace: {ex.StackTrace}");
|
|
}
|
|
}
|
|
|
|
// Crea database statistiche se non esiste (senza migrations)
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
var db = scope.ServiceProvider.GetRequiredService<StatisticsContext>();
|
|
|
|
try
|
|
{
|
|
// Log percorso database
|
|
var connection = db.Database.GetDbConnection();
|
|
Console.WriteLine($"[STATS DB] Database path: {connection.DataSource}");
|
|
|
|
// Verifica se database esiste
|
|
var dbExists = db.Database.CanConnect();
|
|
Console.WriteLine($"[STATS DB] Database exists: {dbExists}");
|
|
|
|
// Forza creazione tabelle se non esistono
|
|
if (!dbExists || !db.ProductStats.Any())
|
|
{
|
|
Console.WriteLine("[STATS DB] Creating database schema...");
|
|
db.Database.EnsureDeleted(); // Elimina database vecchio
|
|
db.Database.EnsureCreated(); // Ricrea con schema aggiornato
|
|
Console.WriteLine("[STATS DB] Database schema created successfully");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[STATS DB] Database already exists with {db.ProductStats.Count()} records");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[STATS DB ERROR] Failed to initialize database: {ex.Message}");
|
|
Console.WriteLine($"[STATS DB ERROR] Stack trace: {ex.StackTrace}");
|
|
|
|
// Prova a ricreare forzatamente
|
|
try
|
|
{
|
|
Console.WriteLine("[STATS DB] Attempting forced recreation...");
|
|
db.Database.EnsureDeleted();
|
|
db.Database.EnsureCreated();
|
|
Console.WriteLine("[STATS DB] Forced recreation successful");
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
Console.WriteLine($"[STATS DB ERROR] Forced recreation failed: {ex2.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Inizializza PostgreSQL per statistiche avanzate
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
try
|
|
{
|
|
var postgresDb = scope.ServiceProvider.GetService<AutoBidder.Data.PostgresStatsContext>();
|
|
|
|
if (postgresDb != null)
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Initializing PostgreSQL statistics database...");
|
|
|
|
var autoCreateSchema = app.Configuration.GetValue<bool>("Database:AutoCreateSchema", true);
|
|
|
|
if (autoCreateSchema)
|
|
{
|
|
// Usa il metodo EnsureSchemaAsync che gestisce la creazione automatica
|
|
var schemaCreated = await postgresDb.EnsureSchemaAsync();
|
|
|
|
if (schemaCreated)
|
|
{
|
|
// Valida che tutte le tabelle siano state create
|
|
var schemaValid = await postgresDb.ValidateSchemaAsync();
|
|
|
|
if (schemaValid)
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Statistics features ENABLED");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Schema validation failed");
|
|
Console.WriteLine("[PostgreSQL] Statistics features DISABLED (missing tables)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Cannot connect to database");
|
|
Console.WriteLine("[PostgreSQL] Statistics features will use SQLite fallback");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Auto-create schema disabled");
|
|
|
|
// Prova comunque a validare lo schema esistente
|
|
try
|
|
{
|
|
var schemaValid = await postgresDb.ValidateSchemaAsync();
|
|
if (schemaValid)
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Existing schema validated successfully");
|
|
Console.WriteLine("[PostgreSQL] Statistics features ENABLED");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Statistics features DISABLED (schema not found)");
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Statistics features DISABLED (schema not found)");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[PostgreSQL] Not configured - Statistics will use SQLite only");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[PostgreSQL ERROR] Initialization failed: {ex.Message}");
|
|
Console.WriteLine($"[PostgreSQL ERROR] Stack trace: {ex.StackTrace}");
|
|
Console.WriteLine($"[PostgreSQL] Statistics features will use SQLite fallback");
|
|
}
|
|
}
|
|
|
|
// ??? NUOVO: Ripristina aste salvate e riprendi monitoraggio se configurato
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
try
|
|
{
|
|
Console.WriteLine("[STARTUP] Loading saved auctions...");
|
|
|
|
// Carica impostazioni
|
|
var settings = AutoBidder.Utilities.SettingsManager.Load();
|
|
Console.WriteLine($"[STARTUP] Remember auction states: {settings.RememberAuctionStates}");
|
|
Console.WriteLine($"[STARTUP] Default start on load: {settings.DefaultStartAuctionsOnLoad}");
|
|
|
|
// Carica aste salvate
|
|
var savedAuctions = AutoBidder.Utilities.PersistenceManager.LoadAuctions();
|
|
Console.WriteLine($"[STARTUP] Found {savedAuctions.Count} saved auctions");
|
|
|
|
if (savedAuctions.Count > 0)
|
|
{
|
|
var monitor = scope.ServiceProvider.GetRequiredService<AuctionMonitor>();
|
|
var appState = scope.ServiceProvider.GetRequiredService<ApplicationStateService>();
|
|
|
|
// Aggiungi tutte le aste al monitor E a ApplicationStateService
|
|
foreach (var auction in savedAuctions)
|
|
{
|
|
monitor.AddAuction(auction);
|
|
Console.WriteLine($"[STARTUP] Loaded auction: {auction.Name} (ID: {auction.AuctionId})");
|
|
}
|
|
|
|
// Popola ApplicationStateService con le aste caricate
|
|
appState.SetAuctions(savedAuctions);
|
|
Console.WriteLine($"[STARTUP] Populated ApplicationStateService with {savedAuctions.Count} auctions");
|
|
|
|
// Gestisci comportamento di avvio
|
|
if (settings.RememberAuctionStates)
|
|
{
|
|
// Modalità "Ricorda Stato": mantiene lo stato salvato di ogni asta
|
|
var activeAuctions = savedAuctions.Where(a => a.IsActive && !a.IsPaused).ToList();
|
|
|
|
if (activeAuctions.Any())
|
|
{
|
|
Console.WriteLine($"[STARTUP] Resuming monitoring for {activeAuctions.Count} active auctions");
|
|
monitor.Start();
|
|
appState.IsMonitoringActive = true;
|
|
appState.AddLog($"[STARTUP] Ripristinato stato salvato: {activeAuctions.Count} aste attive");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[STARTUP] No active auctions to resume");
|
|
appState.AddLog("[STARTUP] Nessuna asta attiva salvata");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Modalità "Default": applica DefaultStartAuctionsOnLoad a tutte le aste
|
|
switch (settings.DefaultStartAuctionsOnLoad)
|
|
{
|
|
case "Active":
|
|
// Avvia tutte le aste
|
|
Console.WriteLine("[STARTUP] Starting all auctions (Active mode)");
|
|
foreach (var auction in savedAuctions)
|
|
{
|
|
auction.IsActive = true;
|
|
auction.IsPaused = false;
|
|
}
|
|
monitor.Start();
|
|
appState.IsMonitoringActive = true;
|
|
appState.AddLog($"[AUTO-START] Avviate automaticamente {savedAuctions.Count} aste");
|
|
break;
|
|
|
|
case "Paused":
|
|
// Mette in pausa tutte le aste
|
|
Console.WriteLine("[STARTUP] Starting in paused mode");
|
|
foreach (var auction in savedAuctions)
|
|
{
|
|
auction.IsActive = true;
|
|
auction.IsPaused = true;
|
|
}
|
|
monitor.Start();
|
|
appState.IsMonitoringActive = true;
|
|
appState.AddLog($"[AUTO-START] Aste in pausa: {savedAuctions.Count}");
|
|
break;
|
|
|
|
case "Stopped":
|
|
default:
|
|
// Ferma tutte le aste (default)
|
|
Console.WriteLine("[STARTUP] Starting in stopped mode");
|
|
foreach (var auction in savedAuctions)
|
|
{
|
|
auction.IsActive = false;
|
|
auction.IsPaused = false;
|
|
}
|
|
appState.AddLog($"[STARTUP] Aste fermate all'avvio: {savedAuctions.Count}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("[STARTUP] No saved auctions found");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[STARTUP ERROR] Failed to load auctions: {ex.Message}");
|
|
Console.WriteLine($"[STARTUP ERROR] Stack trace: {ex.StackTrace}");
|
|
}
|
|
}
|
|
|
|
// Configure the HTTP request pipeline
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseExceptionHandler("/Error");
|
|
|
|
// Abilita HSTS solo se HTTPS è attivo
|
|
if (enableHttps)
|
|
{
|
|
app.UseHsts();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
app.UseDeveloperExceptionPage();
|
|
}
|
|
|
|
// Abilita HTTPS redirection solo se HTTPS è configurato
|
|
if (enableHttps)
|
|
{
|
|
app.UseHttpsRedirection();
|
|
}
|
|
|
|
app.UseStaticFiles();
|
|
app.UseRouting();
|
|
|
|
// ============================================
|
|
// MIDDLEWARE AUTENTICAZIONE E AUTORIZZAZIONE
|
|
// ============================================
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.MapRazorPages(); // ? AGGIUNTO: abilita Razor Pages (Login, Logout)
|
|
app.MapBlazorHub();
|
|
app.MapFallbackToPage("/_Host");
|
|
|
|
app.Run();
|