Files
ZelWiki/TightWiki/Program.cs
2025-01-22 23:31:03 +08:00

233 lines
11 KiB
C#

using Autofac;
using Autofac.Core;
using Autofac.Extensions.DependencyInjection;
using Dapper;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using TightWiki.Email;
using TightWiki.Engine;
using TightWiki.Engine.Implementation;
using TightWiki.Engine.Library.Interfaces;
using TightWiki.Library;
using TightWiki.Library.Interfaces;
using TightWiki.Models;
using TightWiki.Repository;
namespace TightWiki
{
public class Program
{
public static void Main(string[] args)
{
SqlMapper.AddTypeHandler(new GuidTypeHandler());
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("UsersConnection")));
ManagedDataStorage.Pages.SetConnectionString(builder.Configuration.GetConnectionString("PagesConnection"));
ManagedDataStorage.DeletedPages.SetConnectionString(builder.Configuration.GetConnectionString("DeletedPagesConnection"));
ManagedDataStorage.DeletedPageRevisions.SetConnectionString(builder.Configuration.GetConnectionString("DeletedPageRevisionsConnection"));
ManagedDataStorage.Statistics.SetConnectionString(builder.Configuration.GetConnectionString("StatisticsConnection"));
ManagedDataStorage.Emoji.SetConnectionString(builder.Configuration.GetConnectionString("EmojiConnection"));
ManagedDataStorage.Exceptions.SetConnectionString(builder.Configuration.GetConnectionString("ExceptionsConnection"));
ManagedDataStorage.Users.SetConnectionString(builder.Configuration.GetConnectionString("UsersConnection"));
ManagedDataStorage.Config.SetConnectionString(builder.Configuration.GetConnectionString("ConfigConnection"));
ConfigurationRepository.UpgradeDatabase();
ConfigurationRepository.ReloadEverything();
var membershipConfig = ConfigurationRepository.GetConfigurationEntryValuesByGroupName("Membership");
var requireConfirmedAccount = membershipConfig.Value<bool>("Require Email Verification");
// Add services to the container.
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllersWithViews(); // Adds support for controllers and views
builder.Services.AddSingleton<IWikiEmailSender, WikiEmailSender>();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = requireConfirmedAccount)
.AddEntityFrameworkStores<ApplicationDbContext>();
var ExternalAuthenticationConfig = ConfigurationRepository.GetConfigurationEntryValuesByGroupName("External Authentication");
var authentication = builder.Services.AddAuthentication();
if (ExternalAuthenticationConfig.Value<bool>("Google : Use Google Authentication"))
{
var clientId = ExternalAuthenticationConfig.Value<string>("Google : ClientId");
var clientSecret = ExternalAuthenticationConfig.Value<string>("Google : ClientSecret");
if (clientId != null && clientSecret != null && !string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret))
{
authentication.AddGoogle(options =>
{
options.ClientId = clientId;
options.ClientSecret = clientSecret;
options.Events = new OAuthEvents
{
OnRemoteFailure = context =>
{
context.Response.Redirect($"{GlobalConfiguration.BasePath}/Utility/Notify?ErrorMessage={Uri.EscapeDataString("External login was canceled.")}");
context.HandleResponse();
return Task.CompletedTask;
}
};
});
}
}
if (ExternalAuthenticationConfig.Value<bool>("Microsoft : Use Microsoft Authentication"))
{
var clientId = ExternalAuthenticationConfig.Value<string>("Microsoft : ClientId");
var clientSecret = ExternalAuthenticationConfig.Value<string>("Microsoft : ClientSecret");
if (clientId != null && clientSecret != null && !string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret))
{
authentication.AddMicrosoftAccount(options =>
{
options.ClientId = clientId;
options.ClientSecret = clientSecret;
options.Events = new OAuthEvents
{
OnRemoteFailure = context =>
{
context.Response.Redirect($"{GlobalConfiguration.BasePath}/Utility/Notify?ErrorMessage={Uri.EscapeDataString("External login was canceled.")}");
context.HandleResponse();
return Task.CompletedTask;
}
};
});
}
}
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterType<StandardFunctionHandler>().As<IStandardFunctionHandler>().SingleInstance();
containerBuilder.RegisterType<ScopeFunctionHandler>().As<IScopeFunctionHandler>().SingleInstance();
containerBuilder.RegisterType<ProcessingInstructionFunctionHandler>().As<IProcessingInstructionFunctionHandler>().SingleInstance();
containerBuilder.RegisterType<PostProcessingFunctionHandler>().As<IPostProcessingFunctionHandler>().SingleInstance();
containerBuilder.RegisterType<MarkupHandler>().As<IMarkupHandler>().SingleInstance();
containerBuilder.RegisterType<HeadingHandler>().As<IHeadingHandler>().SingleInstance();
containerBuilder.RegisterType<CommentHandler>().As<ICommentHandler>().SingleInstance();
containerBuilder.RegisterType<EmojiHandler>().As<IEmojiHandler>().SingleInstance();
containerBuilder.RegisterType<ExternalLinkHandler>().As<IExternalLinkHandler>().SingleInstance();
containerBuilder.RegisterType<InternalLinkHandler>().As<IInternalLinkHandler>().SingleInstance();
containerBuilder.RegisterType<ExceptionHandler>().As<IExceptionHandler>().SingleInstance();
containerBuilder.RegisterType<CompletionHandler>().As<ICompletionHandler>().SingleInstance();
containerBuilder.RegisterType<TightEngine>().As<ITightEngine>().SingleInstance();
});
var basePath = builder.Configuration.GetValue<string>("BasePath");
if (!string.IsNullOrEmpty(basePath))
{
GlobalConfiguration.BasePath = basePath;
builder.Services.ConfigureApplicationCookie(options =>
{
if (!string.IsNullOrEmpty(basePath))
{
options.LoginPath = new PathString($"{basePath}/Identity/Account/Login");
options.LogoutPath = new PathString($"{basePath}/Identity/Account/Logout");
options.AccessDeniedPath = new PathString($"{basePath}/Identity/Account/AccessDenied");
options.Cookie.Path = basePath; // Ensure the cookie is scoped to the sub-site path.
}
else
{
options.LoginPath = new PathString("/Identity/Account/Login");
options.LogoutPath = new PathString("/Identity/Account/Logout");
options.AccessDeniedPath = new PathString("/Identity/Account/AccessDenied");
options.Cookie.Path = "/"; // Use root path if no base path is set.
}
});
}
var app = builder.Build();
//Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!string.IsNullOrEmpty(basePath))
{
app.UsePathBase(basePath);
// Redirect root requests to basePath (something like '/TightWiki').
app.Use(async (context, next) =>
{
if (context.Request.Path == "/")
{
context.Response.Redirect(basePath);
return;
}
await next();
});
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Request.PathBase = basePath;
}
});
}
app.UseRouting();
app.UseAuthentication(); // Ensures the authentication middleware is configured
app.UseAuthorization();
app.MapRazorPages();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Page}/{action=Display}");
app.MapControllerRoute(
name: "Page_Edit",
pattern: "Page/{givenCanonical}/Edit");
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var userManager = services.GetRequiredService<UserManager<IdentityUser>>();
SecurityRepository.ValidateEncryptionAndCreateAdminUser(userManager);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
}
app.Run();
}
}
}