Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon committed Aug 17, 2024
2 parents 67e4bb8 + a46d0f7 commit cccd641
Show file tree
Hide file tree
Showing 23 changed files with 261 additions and 77 deletions.
13 changes: 10 additions & 3 deletions GuildWarsPartySearch/Config.Debug.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
{
"Logging": {
"LogLevel": {
"MinimumLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"GuildWarsPartySearch.Server.Middleware": "Information",
"GuildWarsPartySearch.Server.Services.Permissions.PermissionService": "Information",
"GuildWarsPartySearch.Server.Services.Database": "Information",
"GuildWarsPartySearch.Server.Endpoints.LiveFeed": "Information",
"GuildWarsPartySearch.Server.Endpoints.PostPartySearch": "Debug"
}
}
},
"EnvironmentOptions": {
Expand Down
13 changes: 10 additions & 3 deletions GuildWarsPartySearch/Config.Production.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
{
"Logging": {
"LogLevel": {
"MinimumLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"GuildWarsPartySearch.Server.Middleware": "Information",
"GuildWarsPartySearch.Server.Services.Permissions.PermissionService": "Information",
"GuildWarsPartySearch.Server.Services.Database": "Information",
"GuildWarsPartySearch.Server.Endpoints.LiveFeed": "Information",
"GuildWarsPartySearch.Server.Endpoints.PostPartySearch": "Debug"
}
}
},
"EnvironmentOptions": {
Expand Down
4 changes: 2 additions & 2 deletions GuildWarsPartySearch/Endpoints/LiveFeed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ await this.SendMessage(new PartySearchList
public override async Task SocketAccepted(CancellationToken cancellationToken)
{
var ipAddress = this.Context?.Request.HttpContext.GetClientIP();
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.SocketAccepted), ipAddress ?? string.Empty);
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.SocketAccepted), string.Empty);
if (!await this.liveFeedService.AddClient(this.WebSocket!, ipAddress, this.Context?.GetPermissionLevel() ?? Models.PermissionLevel.None, cancellationToken))
{
scopedLogger.LogError("Client rejected");
Expand All @@ -66,7 +66,7 @@ await this.SendMessage(new PartySearchList
public override Task SocketClosed()
{
var ipAddress = this.Context?.Request.HttpContext.GetClientIP();
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.SocketClosed), ipAddress ?? string.Empty);
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.SocketClosed), string.Empty);
this.liveFeedService.RemoveClient(this.WebSocket!, ipAddress);
scopedLogger.LogDebug("Client removed from livefeed");
return Task.CompletedTask;
Expand Down
30 changes: 18 additions & 12 deletions GuildWarsPartySearch/Endpoints/PostPartySearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,34 +71,37 @@ public override async Task SocketClosed()

public override async Task ExecuteAsync(PostPartySearchRequest? message, CancellationToken cancellationToken)
{
if (this.Context?.Items.TryGetValue(UserAgentRequired.UserAgentKey, out var userAgentValue) is not true ||
userAgentValue is not string userAgent)
{
throw new InvalidOperationException("Unable to extract user agent on client disconnect");
}

var scopedLogger = this.logger.CreateScopedLogger(nameof(this.ExecuteAsync), string.Empty);
try
{
if (this.Context?.Items.TryGetValue(UserAgentRequired.UserAgentKey, out var userAgentValue) is not true ||
userAgentValue is not string userAgent)
{
throw new InvalidOperationException("Unable to extract user agent on client disconnect");
}

var currentBot = await this.botStatusService.GetBot(userAgent, cancellationToken);
var allBots = (await this.botStatusService.GetBots(cancellationToken)).ToList();
if (message?.Map == Map.None &&
message.District == 0)
{
scopedLogger.LogError("Detected faulty update. Will not update location");
scopedLogger.LogDebug("Detected empty update message. Will not update location");
var faultyResponse = await Success;
if (message.GetFullList)
{
var party_searches = await this.partySearchService.GetAllPartySearches(cancellationToken);
faultyResponse.PartySearches = FilterResults(party_searches, currentBot, allBots);

faultyResponse.PartySearches = allBots.Where(b => b.Id != currentBot?.Id).Select(b => new PartySearch
{
Map = b.Map,
District = b.District
}).ToList();
}

await this.SendMessage(faultyResponse, cancellationToken);
return;
}

await this.botStatusService.RecordBotUpdateActivity(userAgent, message?.Map ?? Map.None, message?.District ?? -1, this.Context.RequestAborted);
scopedLogger.LogDebug($"Posted update. Map: {message?.Map?.Id}. District: {message?.District}. Searches: {message?.PartySearchEntries?.Count}");
var result = await this.partySearchService.PostPartySearch(message, cancellationToken);
var response = await result.Switch<Task<PostPartySearchResponse>>(
onSuccess: async parsedResult =>
Expand All @@ -118,8 +121,11 @@ await this.liveFeedService.PushUpdate(new PartySearch
var response = await Success;
if (parsedResult?.GetFullList is true)
{
var party_searches = await this.partySearchService.GetAllPartySearches(cancellationToken);
response.PartySearches = FilterResults(party_searches, currentBot, allBots);
response.PartySearches = allBots.Where(b => b.Id != currentBot?.Id).Select(b => new PartySearch
{
Map = b.Map,
District = b.District
}).ToList();
}
return response;
},
Expand Down
3 changes: 2 additions & 1 deletion GuildWarsPartySearch/Endpoints/StatusController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ public async Task<IActionResult> GetActiveMaps()
{
District = b.District,
MapId = b.Map?.Id ?? -1,
LastUpdate = b.LastSeen ?? DateTime.MinValue
MapName = b?.Map?.Name ?? default,
LastUpdate = b?.LastSeen ?? DateTime.MinValue
}));
}
}
4 changes: 2 additions & 2 deletions GuildWarsPartySearch/Endpoints/WebSocketRouteBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public sealed override Task ExecuteAsync(WebSocketMessageType type, byte[] data,
}
catch(Exception ex)
{
var scoppedLogger = this.Context!.RequestServices.GetRequiredService<ILogger<WebSocketRouteBase>>().CreateScopedLogger(nameof(this.ExecuteAsync), string.Empty);
var scoppedLogger = this.Context!.RequestServices.GetRequiredService<ILogger<WebSocketRouteBase<TReceiveType>>>().CreateScopedLogger(nameof(this.ExecuteAsync), string.Empty);
scoppedLogger.LogError(ex, "Failed to process data");
throw;
}
Expand Down Expand Up @@ -109,7 +109,7 @@ public Task SendMessage(TSendType sendType, CancellationToken cancellationToken)
}
catch(Exception ex )
{
var scoppedLogger = this.Context!.RequestServices.GetRequiredService<ILogger<WebSocketRouteBase>>().CreateScopedLogger(nameof(this.SendMessage), typeof(TSendType).Name);
var scoppedLogger = this.Context!.RequestServices.GetRequiredService<ILogger<WebSocketRouteBase<TReceiveType, TSendType>>>().CreateScopedLogger(nameof(this.SendMessage), string.Empty);
scoppedLogger.LogError(ex, "Failed to send data");
throw;
}
Expand Down
40 changes: 40 additions & 0 deletions GuildWarsPartySearch/Extensions/HttpContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using GuildWarsPartySearch.Server.Models;
using Microsoft.CorrelationVector;
using Serilog.Context;
using System.Core.Extensions;

namespace GuildWarsPartySearch.Server.Extensions;

public static class HttpContextExtensions
{
private const string CorrelationVectorKey = "CorrelationVector";
private const string ApiKey = "ApiKey";
private const string PermissionLevelKey = "PermissionLevel";
private const string PermissionReasonKey = "PermissionReason";
private const string ClientIPKey = "ClientIP";
Expand Down Expand Up @@ -57,4 +61,40 @@ public static string GetClientIP(this HttpContext context)

return ipStr;
}

public static void SetApiKey(this HttpContext context, string apiKey)
{
context.ThrowIfNull()
.Items.Add(ApiKey, apiKey);
}

public static string GetApiKey(this HttpContext context)
{
context.ThrowIfNull();
if (!context.Items.TryGetValue(ApiKey, out var apiKey) ||
apiKey is not string apiKeyStr)
{
throw new InvalidOperationException("Unable to extract API Key from context");
}

return apiKeyStr;
}

public static void SetCorrelationVector(this HttpContext context, CorrelationVector cv)
{
context.ThrowIfNull()
.Items.Add(CorrelationVectorKey, cv);
}

public static CorrelationVector GetCorrelationVector(this HttpContext context)
{
context.ThrowIfNull();
if (!context.Items.TryGetValue(CorrelationVectorKey, out var cvVal) ||
cvVal is not CorrelationVector cv)
{
throw new InvalidOperationException("Unable to extract API Key from context");
}

return cv;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ public static WebApplicationBuilder ConfigureExtended<TOptions>(this WebApplicat
return builder;
}

public static IConfigurationSection GetRequiredSection<TOptions>(this ConfigurationManager configurationManager)
{
configurationManager.ThrowIfNull();
return configurationManager.GetRequiredSection(GetOptionsName<TOptions>());
}

private static string GetOptionsName<TOptions>()
{
var maybeAttribute = typeof(TOptions).GetCustomAttributes(false).OfType<OptionsNameAttribute>().FirstOrDefault();
Expand Down
3 changes: 3 additions & 0 deletions GuildWarsPartySearch/GuildWarsPartySearch.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

<ItemGroup>
<PackageReference Include="App.Metrics.AspNetCore.All" Version="4.3.0" />
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.7" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.7.0" />
Expand Down
13 changes: 8 additions & 5 deletions GuildWarsPartySearch/Launch/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// See https://aka.ms/new-console-template for more information
using App.Metrics;
using GuildWarsPartySearch.Server.Extensions;
using GuildWarsPartySearch.Server.Middleware;
using GuildWarsPartySearch.Server.Options;
using Microsoft.Extensions.FileProviders;
Expand All @@ -26,9 +27,9 @@ private static async Task Main()

builder.Configuration.AddConfiguration(config);

var options = builder.Configuration.GetRequiredSection(nameof(ServerOptions)).Get<ServerOptions>();
var environmentOptions = builder.Configuration.GetRequiredSection(nameof(EnvironmentOptions)).Get<EnvironmentOptions>()!;
var contentOptions = builder.Configuration.GetRequiredSection(nameof(ContentOptions)).Get<ContentOptions>()!;
var options = builder.Configuration.GetRequiredSection<ServerOptions>().Get<ServerOptions>();
var environmentOptions = builder.Configuration.GetRequiredSection<EnvironmentOptions>().Get<EnvironmentOptions>()!;
var contentOptions = builder.Configuration.GetRequiredSection<ContentOptions>().Get<ContentOptions>()!;

builder.Environment.EnvironmentName = environmentOptions.Name?.ThrowIfNull()!;

Expand Down Expand Up @@ -56,7 +57,7 @@ private static async Task Main()
c.EnableAnnotations();
});
builder.Services.AddSingleton(jsonOptions);
builder.Logging.SetupLogging();
builder.Logging.SetupLogging(config);
builder.Services.SetupServices();
builder.Services.AddControllers();
builder.Services.AddMetrics(metrics);
Expand All @@ -78,8 +79,10 @@ private static async Task Main()
}

var app = builder.Build();
app.UseMiddleware<IPExtractingMiddleware>()
app.UseMiddleware<CorrelationVectorMiddleware>()
.UseMiddleware<IPExtractingMiddleware>()
.UseMiddleware<PermissioningMiddleware>()
.UseMiddleware<LoggingEnrichmentMiddleware>()
.UseMiddleware<HeaderLoggingMiddleware>()
.UseSwagger()
.UseWebSockets()
Expand Down
35 changes: 30 additions & 5 deletions GuildWarsPartySearch/Launch/ServerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
using GuildWarsPartySearch.Server.Services.Processing;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Options;
using Serilog;
using Serilog.Settings.Configuration;
using Serilog.Sinks.File;
using System.Core.Extensions;
using System.Extensions;
using System.Text.Json.Serialization;
Expand All @@ -38,18 +41,34 @@ public static WebApplicationBuilder SetupHostedServices(this WebApplicationBuild
public static IConfigurationBuilder SetupConfiguration(this IConfigurationBuilder builder)
{
builder.ThrowIfNull()
.SetBasePath(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly()?.Location) ?? throw new InvalidOperationException("Unable to figure out base directory"))
.SetBasePath(GetBasePath())
.AddJsonFile("Config.json", false)
.AddEnvironmentVariables();

return builder;
}

public static ILoggingBuilder SetupLogging(this ILoggingBuilder builder)
public static ILoggingBuilder SetupLogging(this ILoggingBuilder builder, IConfigurationRoot config)
{
builder.ThrowIfNull()
.ClearProviders()
.AddConsole(o => o.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] ");
.AddSerilog(logger: new LoggerConfiguration()
.ReadFrom.Configuration(config, readerOptions: new ConfigurationReaderOptions
{
SectionName = "Logging"
})
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss}] {Level:u4}: [{ClientIP}] [{ApiKey}] [{CorrelationVector}] [{SourceContext}]{NewLine}{Message:lj}{NewLine}{Exception}",
theme: Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme.Colored)
.WriteTo.File(
path: Path.Combine(GetBasePath(), "logs/log-.txt"),
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7,
fileSizeLimitBytes: 10 * 1024 * 1024,
rollOnFileSizeLimit: true,
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss}] {Level:u4}: [{ClientIP}] [{ApiKey}] [{CorrelationVector}] [{SourceContext}] - {Message:lj}{NewLine}{Exception}")
.CreateLogger());

return builder;
}
Expand All @@ -76,8 +95,7 @@ public static IServiceCollection SetupServices(this IServiceCollection services)
services.AddSingleton(sp =>
{
var options = sp.GetRequiredService<IOptions<SQLiteDatabaseOptions>>();
var absolutePath = Path.Combine(
Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly()?.Location) ?? throw new InvalidOperationException("Unable to figure out base directory"), options.Value.Path);
var absolutePath = Path.Combine(GetBasePath(), options.Value.Path);
SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());
var connection = new SqliteConnection($"Data Source={absolutePath}");
connection.Open();
Expand All @@ -90,9 +108,11 @@ public static IServiceCollection SetupServices(this IServiceCollection services)
services.AddScoped<IPExtractingMiddleware>();
services.AddScoped<PermissioningMiddleware>();
services.AddScoped<HeaderLoggingMiddleware>();
services.AddScoped<CorrelationVectorMiddleware>();
services.AddScoped<UserAgentRequired>();
services.AddScoped<AdminPermissionRequired>();
services.AddScoped<BotPermissionRequired>();
services.AddScoped<LoggingEnrichmentMiddleware>();
services.AddScoped<IPartySearchService, PartySearchService>();
services.AddScoped<ICharNameValidator, CharNameValidator>();
services.AddScoped<IPermissionService, PermissionService>();
Expand All @@ -107,4 +127,9 @@ public static WebApplication SetupRoutes(this WebApplication app)

return app;
}

private static string GetBasePath()
{
return Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly()?.Location) ?? throw new InvalidOperationException("Unable to get base path");
}
}
24 changes: 24 additions & 0 deletions GuildWarsPartySearch/Middleware/CorrelationVectorMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

using GuildWarsPartySearch.Server.Extensions;
using Microsoft.CorrelationVector;

namespace GuildWarsPartySearch.Server.Middleware;

public sealed class CorrelationVectorMiddleware : IMiddleware
{
private const string CorrelationVectorHeader = "X-Correlation-Vector";

public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var cv = new CorrelationVector();
if (context.Items.TryGetValue(CorrelationVectorHeader, out var correlationVectorVal) &&
correlationVectorVal is string correlationVectorStr)
{
cv = CorrelationVector.Parse(correlationVectorStr);
cv.Increment();
}

context.SetCorrelationVector(cv);
return next(context);
}
}
Loading

0 comments on commit cccd641

Please sign in to comment.