Skip to content

Commit

Permalink
common startup logic, fix heavens shit, some other shit might be bugg…
Browse files Browse the repository at this point in the history
…ed, who knows, im to tired to figure someone elses bs out rn
  • Loading branch information
LucHeart committed Nov 9, 2024
1 parent 85727f4 commit bd1f1b4
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 486 deletions.
165 changes: 8 additions & 157 deletions API/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
using Asp.Versioning;
using System.Text;
using Asp.Versioning.ApiExplorer;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models;
using Npgsql;
using OpenShock.API.Realtime;
using OpenShock.API.Services;
using OpenShock.API.Services.Account;
using OpenShock.API.Services.Email.Mailjet;
using OpenShock.API.Services.Email.Smtp;
using OpenShock.Common;
using OpenShock.Common.Authentication;
using OpenShock.Common.Authentication.Handlers;
using OpenShock.Common.Authentication.Services;
using OpenShock.Common.Constants;
using OpenShock.Common.DataAnnotations;
using OpenShock.Common.ExceptionHandle;
using OpenShock.Common.Hubs;
using OpenShock.Common.JsonSerialization;
using OpenShock.Common.Models;
using OpenShock.Common.OpenShockDb;
using OpenShock.Common.Problems;
using OpenShock.Common.Redis;
using OpenShock.Common.Services.Device;
using OpenShock.Common.Services.LCGNodeProvisioner;
Expand All @@ -37,24 +27,11 @@
using Scalar.AspNetCore;
using Semver;
using Serilog;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
using WebSocketOptions = Microsoft.AspNetCore.Builder.WebSocketOptions;

namespace OpenShock.API;

public sealed class Startup
{
private readonly ForwardedHeadersOptions _forwardedSettings = new()
{
ForwardedHeaders = ForwardedHeaders.All,
RequireHeaderSymmetry = false,
ForwardLimit = null,
ForwardedForHeaderName = "CF-Connecting-IP"
};

private readonly ApiConfig _apiConfig;

Expand Down Expand Up @@ -88,49 +65,10 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ApiConfig>(_apiConfig);

// ----------------- DATABASE -----------------

// How do I do this now with EFCore?!
#pragma warning disable CS0618
NpgsqlConnection.GlobalTypeMapper.MapEnum<ControlType>();
NpgsqlConnection.GlobalTypeMapper.MapEnum<PermissionType>();
NpgsqlConnection.GlobalTypeMapper.MapEnum<ShockerModelType>();
NpgsqlConnection.GlobalTypeMapper.MapEnum<RankType>();
NpgsqlConnection.GlobalTypeMapper.MapEnum<OtaUpdateStatus>();
#pragma warning restore CS0618
services.AddDbContextPool<OpenShockContext>(builder =>
{
builder.UseNpgsql(_apiConfig.Db.Conn);
if (_apiConfig.Db.Debug)
{
builder.EnableSensitiveDataLogging();
builder.EnableDetailedErrors();
}
});

services.AddPooledDbContextFactory<OpenShockContext>(builder =>
{
builder.UseNpgsql(_apiConfig.Db.Conn);
if (_apiConfig.Db.Debug)
{
builder.EnableSensitiveDataLogging();
builder.EnableDetailedErrors();
}
});


var commonServices = services.AddOpenShockServices(_apiConfig);

services.AddMemoryCache();
services.AddHttpContextAccessor();

services.AddScoped<IClientAuthService<LinkUser>, ClientAuthService<LinkUser>>();
services.AddScoped<IClientAuthService<Device>, ClientAuthService<Device>>();
services.AddScoped<IUserReferenceService, UserReferenceService>();


services.AddSingleton<ILCGNodeProvisioner, LCGNodeProvisioner>();



services.AddSingleton(x =>
{
var config = x.GetRequiredService<ApiConfig>();
Expand All @@ -141,8 +79,7 @@ public void ConfigureServices(IServiceCollection services)
};
});
services.AddHttpClient<ICloudflareTurnstileService, CloudflareTurnstileService>();



// ----------------- MAIL SETUP -----------------
var emailConfig = _apiConfig.Mail;
switch (emailConfig.Type)
Expand All @@ -164,28 +101,7 @@ public void ConfigureServices(IServiceCollection services)
default:
throw new Exception("Unknown mail type");
}

services.AddWebEncoders();
services.TryAddSingleton<TimeProvider>(provider => TimeProvider.System);
new AuthenticationBuilder(services)
.AddScheme<AuthenticationSchemeOptions, LoginSessionAuthentication>(
OpenShockAuthSchemas.SessionTokenCombo, _ => { })
.AddScheme<AuthenticationSchemeOptions, DeviceAuthentication>(
OpenShockAuthSchemas.DeviceToken, _ => { });
services.AddAuthenticationCore();
services.AddAuthorization();

services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.SetIsOriginAllowed(s => true);
builder.AllowAnyHeader();
builder.AllowCredentials();
builder.AllowAnyMethod();
builder.SetPreflightMaxAge(TimeSpan.FromHours(24));
});
});

services.AddSignalR()
.AddOpenShockStackExchangeRedis(options => { options.Configuration = commonServices.RedisConfig; })
.AddJsonProtocol(options =>
Expand All @@ -198,45 +114,6 @@ public void ConfigureServices(IServiceCollection services)
services.AddScoped<IDeviceUpdateService, DeviceUpdateService>();
services.AddScoped<IOtaService, OtaService>();
services.AddScoped<IAccountService, AccountService>();
services.AddScoped<ISessionService, SessionService>();

var apiVersioningBuilder = services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
});
services.AddControllers().AddJsonOptions(x =>
{
x.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
x.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
x.JsonSerializerOptions.Converters.Add(new PermissionTypeConverter());
x.JsonSerializerOptions.Converters.Add(new CustomJsonStringEnumConverter());
});

apiVersioningBuilder.AddApiExplorer(setup =>
{
setup.GroupNameFormat = "VVV";
setup.SubstituteApiVersionInUrl = true;
});

services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.PropertyNameCaseInsensitive = true;
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.SerializerOptions.Converters.Add(new PermissionTypeConverter());
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

services.AddProblemDetails();

services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new ValidationProblem(context.ModelState);
return problemDetails.ToObjectResult(context.HttpContext);
};
});

services.AddSwaggerGen(options =>
{
Expand Down Expand Up @@ -282,7 +159,7 @@ public void ConfigureServices(IServiceCollection services)
options.SupportNonNullableReferenceTypes();
}
);

services.ConfigureOptions<ConfigureSwaggerOptions>();
//services.AddHealthChecks().AddCheck<DatabaseHealthCheck>("database");

Expand All @@ -293,28 +170,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
ILogger<Startup> logger)
{
ApplicationLogging.LoggerFactory = loggerFactory;
foreach (var proxy in TrustedProxiesFetcher.GetTrustedProxies())
{
var split = proxy.Split('/');
_forwardedSettings.KnownNetworks.Add(new IPNetwork(IPAddress.Parse(split[0]), int.Parse(split[1])));
}

app.UseForwardedHeaders(_forwardedSettings);
app.UseSerilogRequestLogging();

app.ConfigureExceptionHandler();

// global cors policy
app.UseCors();

// Redis

var redisConnection = app.ApplicationServices.GetRequiredService<IRedisConnectionProvider>().Connection;

redisConnection.CreateIndex(typeof(LoginSession));
redisConnection.CreateIndex(typeof(DeviceOnline));
redisConnection.CreateIndex(typeof(DevicePair));
redisConnection.CreateIndex(typeof(LcgNode));
app.UseCommonOpenShockServices();

if (!_apiConfig.Db.SkipMigration)
{
Expand Down Expand Up @@ -342,13 +199,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
description.GroupName.ToUpperInvariant());
});

app.UseWebSockets(new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(1)
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
Expand Down
7 changes: 6 additions & 1 deletion Common/Authentication/Handlers/LoginSessionAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

namespace OpenShock.Common.Authentication.Handlers;

public sealed class LoginSessionAuthentication : AuthenticationHandler<AuthenticationSchemeOptions>
public sealed class LoginSessionAuthentication : AuthenticationHandler<AuthenticationSchemeOptions>, IDisposable
{
private readonly IClientAuthService<LinkUser> _authService;
private readonly IUserReferenceService _userReferenceService;
Expand Down Expand Up @@ -141,4 +141,9 @@ protected override Task HandleChallengeAsync(AuthenticationProperties properties
_authResultError.AddContext(Context);
return Context.Response.WriteAsJsonAsync(_authResultError, _serializerOptions, contentType: "application/problem+json");
}

public void Dispose()
{
Console.WriteLine("AUTH CONTEXT FUCKED OFF");
}
}
67 changes: 20 additions & 47 deletions Common/ExceptionHandle/ExceptionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,34 @@

namespace OpenShock.Common.ExceptionHandle;

public static class ExceptionHandler
public sealed class OpenShockExceptionHandler : IExceptionHandler
{
private static readonly ILogger Logger = ApplicationLogging.CreateLogger(typeof(ExceptionHandler));
private static readonly ILogger Logger = ApplicationLogging.CreateLogger(typeof(OpenShockExceptionHandler));
private static readonly ILogger LoggerRequestInfo = ApplicationLogging.CreateLogger("RequestInfo");

private readonly IProblemDetailsService _problemDetailsService;

/// <summary>
/// Configures two middlewares used to handle exceptions globally.
/// </summary>
/// <param name="app"></param>
public static void ConfigureExceptionHandler(this IApplicationBuilder app)
public OpenShockExceptionHandler(IProblemDetailsService problemDetailsService)
{
// Enable request body buffering. Needed to allow rewinding the body reader,
// if the body has already been read before.
// Runs before the request action is executed and body is read.
app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next.Invoke();
});
_problemDetailsService = problemDetailsService;
}

public async ValueTask<bool> TryHandleAsync(HttpContext context, Exception exception, CancellationToken cancellationToken)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await PrintRequestInfo(context);

// Use the built in exception handler middleware, to capture unhandled exceptions.
app.UseExceptionHandler(appError =>
{
// Action to be executed when an exception is thrown.
appError.Run(async context =>
{
// Get the exception details by getting the IExceptionHandlerFeature from our HttpContext.
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
// This should not be null, otherwise no exception was thrown or some other error in ASP.NET occurred.
if (contextFeature == null) return;
var responseObject = ExceptionError.Exception;
responseObject.AddContext(context);

// Any other exception has been thrown, return a InternalServerError, always print full request infos.
// Side note: Exception logging is done by Microsoft Diagnostics middleware already, so no need to do
// it again manually.
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await PrintRequestInfo(context);
var jsonOptions = context.RequestServices.GetRequiredService<IOptions<JsonOptions>>();
var responseObject = ExceptionError.Exception;
responseObject.AddContext(context);
await context.Response.WriteAsJsonAsync(responseObject, jsonOptions.Value.SerializerOptions, contentType: "application/problem+json");
});
return await _problemDetailsService.TryWriteAsync(new ProblemDetailsContext
{
HttpContext = context,
Exception = exception,
ProblemDetails = responseObject
});
}

/// <summary>
/// This method prints all relevant info that could be useful for debugging purposes.
/// Also contains redundant data like method, path and correlation id for readability purposes.
/// </summary>
/// <param name="context"></param>

private static async Task PrintRequestInfo(HttpContext context)
{
// Rewind our body reader, so we can read it again.
Expand Down
Loading

0 comments on commit bd1f1b4

Please sign in to comment.