2023-02-18 08:52:17 +01:00
using Microsoft.AspNetCore.HttpOverrides ;
using Microsoft.Extensions.Options ;
2026-10-01 08:05:56 +02:00
using MongoDB.Bson ;
using MongoDB.Bson.Serialization ;
using MongoDB.Bson.Serialization.Serializers ;
2023-02-18 08:52:17 +01:00
using MongoDB.Driver ;
using MongoDB.Entities ;
using Serilog ;
2023-02-19 00:43:43 +01:00
using PrivaPub.Data ;
using PrivaPub.Extensions ;
2026-10-01 11:52:31 +02:00
using PrivaPub.Api.Mastodon.Auth ;
2026-10-01 11:02:21 +02:00
using PrivaPub.Infrastructure ;
2026-10-01 10:58:39 +02:00
using PrivaPub.Infrastructure.Cli ;
2026-10-01 11:01:08 +02:00
using PrivaPub.Infrastructure.Data ;
2026-10-01 10:46:09 +02:00
using PrivaPub.Infrastructure.Http ;
2023-02-19 00:43:43 +01:00
using PrivaPub.Middleware ;
using PrivaPub.Models ;
using PrivaPub.Services ;
using PrivaPub.StaticServices ;
2023-02-18 08:52:17 +01:00
try
{
var builder = WebApplication . CreateBuilder ( args );
builder . WebHost . ConfigureKestrel ( serverOptions =>
{
if ( builder . Environment . IsProduction ())
{
serverOptions . ListenLocalhost ( 6970
//, options =>
//{
// options.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
//}
);
serverOptions . UseSystemd ();
serverOptions . AddServerHeader = false ;
}
});
builder . Host . UseSerilog (( context , config ) =>
{
config . ReadFrom . Configuration ( context . Configuration );
});
try
{
2023-02-19 00:43:43 +01:00
builder . Services . PrivaPubAppSettingsConfiguration ( builder . Configuration )
. PrivaPubWorkersConfiguration ()
. PrivaPubAuthServicesConfiguration ( builder . Configuration )
. PrivaPubInternalizationConfiguration ( builder . Configuration )
. PrivaPubOptimizationConfiguration ()
. PrivaPubDataBaseConfiguration ()
. PrivaPubServicesConfiguration ()
2026-10-01 10:46:09 +02:00
. PrivaPubFederationConfiguration ( builder . Configuration )
2023-02-19 00:43:43 +01:00
. PrivaPubCORSConfiguration ()
2026-10-01 11:02:21 +02:00
. PrivaPubRateLimiting ()
2026-10-01 11:52:31 +02:00
. PrivaPubOAuth ( builder . Environment )
. AddScoped < PrivaPub . Api . Mastodon . Mappers . MastodonMapper >()
2026-10-01 12:05:58 +02:00
. AddScoped < PrivaPub . Api . Mastodon . Mappers . AccountSearch >()
2026-10-01 12:22:08 +02:00
. Configure < PrivaPub . Domain . Media . MediaOptions >( builder . Configuration . GetSection ( "Media" ))
. AddSingleton < PrivaPub . Domain . Media . IMediaService , PrivaPub . Domain . Media . MediaService >()
. AddSingleton < PrivaPub . Domain . Media . IMediaProxy , PrivaPub . Domain . Media . MediaProxy >()
. AddHostedService < PrivaPub . Domain . Media . MediaJanitor >()
2023-02-19 00:43:43 +01:00
. PrivaPubMiddlewareConfiguration ();
2023-02-18 08:52:17 +01:00
}
catch ( Exception ex )
{
Log . ForContext < Program >(). Fatal ( ex , "{0}.{1}()" , nameof ( Program ), "ConfigureServices" );
throw ;
}
2026-10-01 10:46:09 +02:00
var federationOptions = builder . Configuration . GetSection ( "Federation" ). Get < FederationOptions >() ?? new ();
if ( builder . Environment . IsProduction () && ( federationOptions . AllowPrivateNetworks || federationOptions . AllowPlainHttp ))
throw new InvalidOperationException ( "Federation:AllowPrivateNetworks and Federation:AllowPlainHttp are for test networks and must stay off in Production." );
2023-02-18 08:52:17 +01:00
try
{
2026-10-01 08:05:56 +02:00
BsonSerializer . RegisterSerializer ( new GuidSerializer ( GuidRepresentation . Standard ));
2023-02-18 08:52:17 +01:00
var mongoSettings = builder . Configuration . GetSection ( nameof ( MongoSettings )). Get < MongoSettings >();
await DB . InitAsync ( mongoSettings . Database , MongoClientSettings . FromConnectionString ( mongoSettings . ConnectionString ));
2026-10-01 11:01:08 +02:00
EntityMaps . Warm ();
2026-10-01 10:54:17 +02:00
await DB . Default . MigrateAsync < Program >();
2026-10-01 11:01:08 +02:00
await Indexes . Create ();
2026-10-01 10:58:39 +02:00
if ( args is [ "admin" , ..])
{
Environment . ExitCode = await AdminCommands . Run ( args [ 1. .]);
return ;
}
2023-02-18 08:52:17 +01:00
}
catch ( Exception ex )
{
Log . ForContext < Program >(). Fatal ( ex , $"{nameof(Program)}.{nameof(Program)}() DB Instantiation" );
throw ;
}
var app = default ( WebApplication );
try
{
app = builder . Build ();
}
catch ( Exception ex )
{
Log . ForContext < Program >(). Fatal ( ex , "{0}.{1}()" , nameof ( Program ), "Build" );
throw ;
}
try
{
var localizationService = app . Services . GetService < RequestLocalizationOptionsService >();
if ( app . Environment . IsProduction ())
{
app . UseResponseCompression ();
app . UseForwardedHeaders ( new ()
{
ForwardedHeaders = ForwardedHeaders . XForwardedFor | ForwardedHeaders . XForwardedProto
});
}
2026-10-01 08:05:56 +02:00
2026-10-01 10:58:39 +02:00
if ( app . Environment . IsDevelopment ())
{
app . UseSwagger ();
app . UseSwaggerUI ();
}
2023-02-18 08:52:17 +01:00
app . UseHttpsRedirection ();
app . UseCors ( "DefaultCORS" );
app . UseStaticFiles ();
2026-10-01 12:22:08 +02:00
var mediaRoot = app . Services . GetRequiredService < PrivaPub . Domain . Media . IMediaService >(). Root ;
Directory . CreateDirectory ( mediaRoot );
app . UseStaticFiles ( new StaticFileOptions
{
FileProvider = new Microsoft . Extensions . FileProviders . PhysicalFileProvider ( mediaRoot ),
RequestPath = "/media/files" ,
OnPrepareResponse = context =>
{
context . Context . Response . Headers [ "X-Content-Type-Options" ] = "nosniff" ;
context . Context . Response . Headers [ "Content-Security-Policy" ] = "default-src 'none'; sandbox" ;
context . Context . Response . Headers [ "Cache-Control" ] = "public, max-age=31536000, immutable" ;
}
});
2023-02-18 08:52:17 +01:00
app . UseRequestLocalization ( await localizationService . Get ());
app . UseRouting ();
2026-10-01 11:02:21 +02:00
app . UseRateLimiter ();
2023-02-18 08:52:17 +01:00
app . UseAuthentication ();
app . UseAuthorization ();
//app.UseWhen(context => context.Request.Path.StartsWithSegments("/peasants") ||
// context.Request.Path.StartsWithSegments("/users"),
// app => app.UseSignatureVerification().UseDigestVerification());
2026-10-01 08:05:56 +02:00
app . MapGet ( "/build.json" , () => Results . Json ( new
{
commit = BuildInfo . Commit ,
buildRef = BuildInfo . Ref ,
builtAt = BuildInfo . BuiltAt ,
}));
2023-02-18 08:52:17 +01:00
app . MapControllers ();
2026-10-01 11:23:03 +02:00
app . MapRazorPages ();
2023-02-18 08:52:17 +01:00
//app.MapFallbackToFile("index.html");
}
catch ( Exception ex )
{
Log . ForContext < Program >(). Fatal ( ex , "{0}.{1}()" , nameof ( Program ), "Use" );
throw ;
}
Log . ForContext < Program >(). Information ( $"Starting collAnon at {nameof(Program)}()" );
try
{
var dbClient = app . Services . GetService ( typeof ( DbEntities )) as DbEntities ;
var passwordHasher = app . Services . GetService ( typeof ( IPasswordHasher )) as IPasswordHasher ;
await dbClient . Init ( passwordHasher );
2026-10-01 11:25:05 +02:00
await app . Services . GetRequiredService < PrivaPub . Federation . Moderation . IDomainBlocks >(). Reload ( CancellationToken . None );
2023-02-18 08:52:17 +01:00
}
catch ( Exception ex )
{
Log . ForContext < Program >(). Warning ( ex , $"{nameof(Program)}.{nameof(Program)}() DB Init" );
}
await app . RunAsync ();
}
catch ( Exception ex )
{
Log . ForContext < Program >(). Fatal ( ex , $"{nameof(Program)}.{nameof(Program)}()" );
2026-10-01 08:05:56 +02:00
Environment . ExitCode = 1 ;
2023-02-18 08:52:17 +01:00
}