using System.Reflection; using API; using API.Schema.LibraryContext; using API.Schema.MangaContext; using API.Schema.NotificationsContext; using Asp.Versioning; using Asp.Versioning.Builder; using Asp.Versioning.Conventions; using log4net; using log4net.Config; using Microsoft.EntityFrameworkCore; using Microsoft.OpenApi; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Npgsql; XmlConfigurator.ConfigureAndWatch(new FileInfo("Log4Net.config.xml")); ILog Log = LogManager.GetLogger("Startup"); Log.Info("Logger Configured."); Log.Info("Starting up"); WebApplicationBuilder builder = WebApplication.CreateBuilder(args); builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); Log.Debug("Adding API-Explorer-helpers..."); builder.Services.AddApiVersioning(option => { option.AssumeDefaultVersionWhenUnspecified = true; option.DefaultApiVersion = new ApiVersion(2); option.ReportApiVersions = true; option.ApiVersionReader = ApiVersionReader.Combine( new UrlSegmentApiVersionReader(), new QueryStringApiVersionReader("api-version"), new HeaderApiVersionReader("X-Version"), new MediaTypeApiVersionReader("x-version")); }) .AddMvc(options => { options.Conventions.Add(new VersionByNamespaceConvention()); }) .AddApiExplorer(options => { options.GroupNameFormat = "'v'V"; options.SubstituteApiVersionInUrl = true; }); builder.Services.AddEndpointsApiExplorer(); builder.Services.ConfigureOptions(); builder.Services.AddSwaggerGenNewtonsoftSupport().AddSwaggerGen(opt => { string xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; opt.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); }); Log.Debug("Adding Database-Connection..."); NpgsqlConnectionStringBuilder connectionStringBuilder = new() { Host = Environment.GetEnvironmentVariable("POSTGRES_HOST") ?? "tranga-pg:5432", Database = Environment.GetEnvironmentVariable("POSTGRES_DB") ?? "postgres", Username = Environment.GetEnvironmentVariable("POSTGRES_USER") ?? "postgres", Password = Environment.GetEnvironmentVariable("POSTGRES_PASSWORD") ?? "postgres", ConnectionLifetime = 300, Timeout = int.Parse(Environment.GetEnvironmentVariable("POSTGRES_CONNECTION_TIMEOUT") ?? "30"), ReadBufferSize = 65536, WriteBufferSize = 65536, CommandTimeout = int.Parse(Environment.GetEnvironmentVariable("POSTGRES_COMMAND_TIMEOUT") ?? "60"), ApplicationName = "Tranga" }; builder.Services.AddDbContext(options => options.UseNpgsql(connectionStringBuilder.ConnectionString)); builder.Services.AddDbContext(options => options.UseNpgsql(connectionStringBuilder.ConnectionString)); builder.Services.AddDbContext(options => options.UseNpgsql(connectionStringBuilder.ConnectionString)); builder.Services.AddControllers(options => { options.AllowEmptyInputInBodyModelBinding = true; }); builder.Services.AddControllers().AddNewtonsoftJson(opts => { opts.SerializerSettings.Converters.Add(new StringEnumConverter()); opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); builder.Services.AddScoped(_ => LogManager.GetLogger("API")); builder.WebHost.UseUrls("http://*:6531"); Log.Info("Starting app..."); WebApplication app = builder.Build(); app.UseCors("AllowAll"); ApiVersionSet apiVersionSet = app.NewApiVersionSet() .HasApiVersion(new ApiVersion(2)) .ReportApiVersions() .Build(); app.UseCors("AllowAll"); Log.Debug("Mapping Controllers..."); app.MapControllers() .WithApiVersionSet(apiVersionSet) .MapToApiVersion(2); Log.Debug("Adding Swagger..."); app.UseSwagger(opts => { opts.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; opts.RouteTemplate = "swagger/{documentName}/swagger.json"; }); app.UseSwaggerUI(); app.UseHttpsRedirection(); try //Connect to DB and apply migrations { Log.Debug("Applying Migrations..."); using (IServiceScope scope = app.Services.CreateScope()) { MangaContext context = scope.ServiceProvider.GetRequiredService(); await context.Database.MigrateAsync(CancellationToken.None); if (!context.FileLibraries.Any()) { await context.FileLibraries.AddAsync(new(Tranga.Settings.DefaultDownloadLocation, "Default FileLibrary"), CancellationToken.None); await context.Sync(CancellationToken.None, reason: "Add default library"); } } using (IServiceScope scope = app.Services.CreateScope()) { NotificationsContext context = scope.ServiceProvider.GetRequiredService(); await context.Database.MigrateAsync(CancellationToken.None); context.Notifications.ExecuteDelete(); string[] emojis = [ "(•‿•)", "(づ \u25d5‿\u25d5 )づ", "( \u02d8\u25bd\u02d8)っ\u2668", "=\uff3e\u25cf \u22cf \u25cf\uff3e=", "(ΦωΦ)", "(\u272a\u3268\u272a)", "( ノ・o・ )ノ", "(〜^\u2207^ )〜", "~(\u2267ω\u2266)~", "૮ \u00b4• ﻌ \u00b4• ა", "(\u02c3ᆺ\u02c2)", "(=\ud83d\udf66 \u0f1d \ud83d\udf66=)" ]; await context.Notifications.AddAsync( new("Tranga Started", emojis[Random.Shared.Next(0, emojis.Length - 1)], NotificationUrgency.High), CancellationToken.None); await context.Sync(CancellationToken.None, reason: "Startup notification"); } using (IServiceScope scope = app.Services.CreateScope()) { LibraryContext context = scope.ServiceProvider.GetRequiredService(); await context.Database.MigrateAsync(CancellationToken.None); await context.Sync(CancellationToken.None, reason: "Startup library"); } } catch (Exception e) { Log.Debug("Migrations failed!", e); return; } Log.Info("Starting Tranga."); Tranga.ServiceProvider = app.Services; Tranga.StartupTasks(); Tranga.AddDefaultWorkers(); Log.Info("Running app."); app.Run();