PlotDirector/PlotLine/Program.cs
2026-06-06 20:36:54 +01:00

161 lines
8.8 KiB
C#

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using PlotLine.Data;
using PlotLine.Models;
using PlotLine.Services;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.DataProtection;
namespace PlotLine;
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Services.AddControllersWithViews();
builder.Services.AddHttpContextAccessor();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(10);
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
});
builder.Services.Configure<EmailSettings>(builder.Configuration.GetSection("EmailSettings"));
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(builder.Environment.ContentRootPath, "App_Data", "DataProtectionKeys")));
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "PlotWeaver.Auth";
options.LoginPath = "/Account/Login";
options.LogoutPath = "/Account/Logout";
options.AccessDeniedPath = "/Account/AccessDenied";
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(14);
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = async context =>
{
var userIdValue = context.Principal?.FindFirstValue(ClaimTypes.NameIdentifier);
var securityStampValue = context.Principal?.FindFirstValue("PlotWeaver.SecurityStamp");
if (!int.TryParse(userIdValue, out var userId) || !Guid.TryParse(securityStampValue, out var securityStamp))
{
context.RejectPrincipal();
await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return;
}
using var scope = context.HttpContext.RequestServices.CreateScope();
var users = scope.ServiceProvider.GetRequiredService<IUserRepository>();
var user = await users.GetByIdAsync(userId);
var isLocked = user?.IsLocked == true && (!user.LockoutEndUtc.HasValue || user.LockoutEndUtc > DateTime.UtcNow);
if (user is null || user.SecurityStamp != securityStamp || isLocked)
{
context.RejectPrincipal();
await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
};
});
builder.Services.AddAuthorization();
builder.Services.AddSingleton<ISqlConnectionFactory, SqlConnectionFactory>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IAuthenticationRepository, AuthenticationRepository>();
builder.Services.AddScoped<IEmailQueueRepository, EmailQueueRepository>();
builder.Services.AddScoped<IProjectRepository, ProjectRepository>();
builder.Services.AddScoped<IBookRepository, BookRepository>();
builder.Services.AddScoped<IChapterRepository, ChapterRepository>();
builder.Services.AddScoped<ISceneRepository, SceneRepository>();
builder.Services.AddScoped<ILookupRepository, LookupRepository>();
builder.Services.AddScoped<ITimelineRepository, TimelineRepository>();
builder.Services.AddScoped<ITimelinePresetRepository, TimelinePresetRepository>();
builder.Services.AddScoped<ITimelineSettingsRepository, TimelineSettingsRepository>();
builder.Services.AddScoped<ISceneMetricTypeRepository, SceneMetricTypeRepository>();
builder.Services.AddScoped<IPlotRepository, PlotRepository>();
builder.Services.AddScoped<IAssetRepository, AssetRepository>();
builder.Services.AddScoped<ICharacterRepository, CharacterRepository>();
builder.Services.AddScoped<ILocationRepository, LocationRepository>();
builder.Services.AddScoped<IWarningRepository, WarningRepository>();
builder.Services.AddScoped<ISceneDependencyRepository, SceneDependencyRepository>();
builder.Services.AddScoped<IAnalyticsRepository, AnalyticsRepository>();
builder.Services.AddScoped<IStoryBibleRepository, StoryBibleRepository>();
builder.Services.AddScoped<IWriterWorkspaceRepository, WriterWorkspaceRepository>();
builder.Services.AddScoped<IExportRepository, ExportRepository>();
builder.Services.AddScoped<IProjectBackupRepository, ProjectBackupRepository>();
builder.Services.AddScoped<IProjectRestorePointRepository, ProjectRestorePointRepository>();
builder.Services.AddScoped<IDynamicsRepository, DynamicsRepository>();
builder.Services.AddScoped<IScenarioRepository, ScenarioRepository>();
builder.Services.AddScoped<IArchiveRepository, ArchiveRepository>();
builder.Services.AddScoped<IAcceptanceRepository, AcceptanceRepository>();
builder.Services.AddScoped<IImportRepository, ImportRepository>();
builder.Services.AddScoped<IProjectService, ProjectService>();
builder.Services.AddScoped<IBookService, BookService>();
builder.Services.AddScoped<IChapterService, ChapterService>();
builder.Services.AddScoped<ISceneService, SceneService>();
builder.Services.AddScoped<ITimelineService, TimelineService>();
builder.Services.AddScoped<ISceneMetricTypeService, SceneMetricTypeService>();
builder.Services.AddScoped<IPlotService, PlotService>();
builder.Services.AddScoped<IAssetService, AssetService>();
builder.Services.AddScoped<ICharacterService, CharacterService>();
builder.Services.AddScoped<ILocationService, LocationService>();
builder.Services.AddScoped<IContinuityValidationService, ContinuityValidationService>();
builder.Services.AddScoped<IAnalyticsService, AnalyticsService>();
builder.Services.AddScoped<IStoryBibleService, StoryBibleService>();
builder.Services.AddScoped<IRelationshipMapService, RelationshipMapService>();
builder.Services.AddScoped<IWriterWorkspaceService, WriterWorkspaceService>();
builder.Services.AddScoped<IExportService, ExportService>();
builder.Services.AddScoped<IProjectExportService, ProjectExportService>();
builder.Services.AddScoped<IProjectRestorePointService, ProjectRestorePointService>();
builder.Services.AddScoped<IProjectRestoreService, ProjectRestoreService>();
builder.Services.AddScoped<IDynamicsService, DynamicsService>();
builder.Services.AddScoped<IScenarioService, ScenarioService>();
builder.Services.AddScoped<IArchiveService, ArchiveService>();
builder.Services.AddScoped<IAcceptanceService, AcceptanceService>();
builder.Services.AddScoped<IImportService, ImportService>();
builder.Services.AddSingleton<IHelpService, HelpService>();
builder.Services.AddSingleton<IHelpMarkdownRenderer, HelpMarkdownRenderer>();
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddScoped<IPasswordService, PasswordService>();
builder.Services.AddScoped<ITwoFactorService, TwoFactorService>();
builder.Services.AddScoped<IEmailService, EmailService>();
builder.Services.AddScoped<IQueuedEmailSender, QueuedEmailSender>();
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
builder.Services.AddHostedService<EmailQueueWorker>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.MapStaticAssets();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.WithStaticAssets();
app.Run();
}
}