using Foxchat.Core.Database; using Foxchat.Core.Federation; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using NodaTime; using Serilog; using Serilog.Events; namespace Foxchat.Core.Extensions; public static class ServiceCollectionExtensions { /// /// Adds Serilog to this service collection. This method also initializes Serilog so it should be called as early as possible, before any log calls. /// public static void AddSerilog(this WebApplicationBuilder builder) { var config = builder.Configuration.Get() ?? new(); var logCfg = new LoggerConfiguration() .Enrich.FromLogContext() .MinimumLevel.Is(config.Logging.LogEventLevel) // ASP.NET's built in request logs are extremely verbose, so we use Serilog's instead. // Serilog doesn't disable the built-in logs, so we do it here. .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", config.Logging.LogQueries ? LogEventLevel.Information : LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning) .WriteTo.Console(); if (config.Logging.SeqLogUrl != null) logCfg.WriteTo.Seq(config.Logging.SeqLogUrl, restrictedToMinimumLevel: LogEventLevel.Verbose); Log.Logger = logCfg.CreateLogger(); // AddSerilog doesn't seem to add an ILogger to the service collection, so add that manually. builder.Services.AddSerilog().AddSingleton(Log.Logger); } /// /// Adds the core Foxchat services to this service collection. /// public static IServiceCollection AddCoreServices(this IServiceCollection services) where T : IDatabaseContext { services.AddDbContext(); // NodaTime recommends only depending on the IClock interface, not the singleton. services.AddSingleton(SystemClock.Instance); // Some core services rely on an IDatabaseContext, not the server-specific context type. services.AddScoped(); services.AddSingleton(); return services; } public static T AddConfiguration(this WebApplicationBuilder builder, string? configFile = null) where T : class, new() { builder.Configuration.Sources.Clear(); builder.Configuration.AddConfiguration(configFile); var config = builder.Configuration.Get() ?? new(); var coreConfig = builder.Configuration.Get() ?? new(); builder.Services.AddSingleton(config); builder.Services.AddSingleton(coreConfig); return config; } public static IConfigurationBuilder AddConfiguration(this IConfigurationBuilder builder, string? configFile = null) { var file = Environment.GetEnvironmentVariable("FOXCHAT_CONFIG_FILE") ?? configFile ?? "config.ini"; return builder .SetBasePath(Directory.GetCurrentDirectory()) .AddIniFile(file, optional: false, reloadOnChange: true) .AddEnvironmentVariables(); } }