File size: 5,252 Bytes
fc06b79 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | using Microsoft.EntityFrameworkCore;
using ContactManagementAPI.Data;
using ContactManagementAPI.Services;
using Microsoft.Data.Sqlite;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.FileProviders;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllersWithViews()
.AddJsonOptions(options =>
{
// Prevent circular references
options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
});
builder.Services.AddRazorPages();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSession(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.IdleTimeout = TimeSpan.FromHours(2); // Reduced from 8 to 2 hours to prevent memory buildup
});
// Add memory cache with size limit
builder.Services.AddMemoryCache(options =>
{
options.SizeLimit = 1024; // Limit cache size
});
// Configure Antiforgery with SameSite settings
builder.Services.AddAntiforgery(options =>
{
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.SameSite = SameSiteMode.Lax;
});
// Configure database connection (SQLite preferred for portable installs)
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
var useSqlite = connectionString.TrimStart().StartsWith("Data Source=", StringComparison.OrdinalIgnoreCase);
if (useSqlite)
{
var sqliteBuilder = new SqliteConnectionStringBuilder(connectionString);
var sqliteDbPathOverride = Environment.GetEnvironmentVariable("SQLITE_DB_PATH");
var dataSource = sqliteBuilder.DataSource;
if (!string.IsNullOrWhiteSpace(sqliteDbPathOverride))
{
dataSource = sqliteDbPathOverride;
}
if (string.IsNullOrWhiteSpace(dataSource))
{
dataSource = "ContactManagement.db";
}
if (!Path.IsPathRooted(dataSource))
{
var appDataFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"ContactManagementSystem");
Directory.CreateDirectory(appDataFolder);
dataSource = Path.Combine(appDataFolder, dataSource);
}
sqliteBuilder.DataSource = dataSource;
connectionString = sqliteBuilder.ToString();
}
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
if (useSqlite)
{
options.UseSqlite(connectionString);
}
else
{
options.UseSqlServer(connectionString);
}
// Optimize EF Core for better memory management
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
options.EnableSensitiveDataLogging(false);
options.EnableDetailedErrors(false);
});
// Add custom services
builder.Services.AddScoped<FileUploadService>();
builder.Services.AddScoped<AuthorizationService>();
builder.Services.AddScoped<UserContextService>();
builder.Services.AddScoped<ImportExportService>();
builder.Services.AddScoped<ContactStatisticsService>();
builder.Services.AddScoped<AdminHistoryService>();
// Configure CORS if needed for future API consumption
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
var app = builder.Build();
// Support reverse proxies/load balancers in cloud hosting
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
var disableHttpsRedirection =
app.Configuration.GetValue<bool>("DisableHttpsRedirection") ||
string.Equals(Environment.GetEnvironmentVariable("DISABLE_HTTPS_REDIRECTION"), "true", StringComparison.OrdinalIgnoreCase);
// Ensure database exists and seed defaults
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
if (useSqlite)
{
dbContext.Database.EnsureCreated();
}
else
{
dbContext.Database.Migrate();
}
SeedData.Initialize(dbContext);
}
// Configure the HTTP request pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
if (!disableHttpsRedirection)
{
app.UseHttpsRedirection();
}
app.UseStaticFiles();
var uploadsRoot = Environment.GetEnvironmentVariable("UPLOADS_ROOT");
if (!string.IsNullOrWhiteSpace(uploadsRoot) && Directory.Exists(uploadsRoot))
{
Directory.CreateDirectory(Path.Combine(uploadsRoot, "photos"));
Directory.CreateDirectory(Path.Combine(uploadsRoot, "documents"));
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(uploadsRoot),
RequestPath = "/uploads"
});
}
app.UseRouting();
app.UseSession();
app.UseCors("AllowAll");
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();
|