local asset server

This commit is contained in:
Spirtix 2023-12-02 17:20:02 +01:00
parent 7578ee7e97
commit 6bc5a0b140
3 changed files with 126 additions and 0 deletions

View File

@ -0,0 +1,13 @@
namespace sodoff.Configuration;
public class AssetServerConfig {
public bool Enabled { get; set; } = false;
public int Port { get; set; } = 5001;
public string URLPrefix { get; set; } = string.Empty;
public AssetServerMode Mode { get; set; }
public string ProviderURL { get; set; } = string.Empty;
public bool SubstituteMissingLocalAssets { get; set; } = false;
}
public enum AssetServerMode {
None, Partial, Full
}

View File

@ -0,0 +1,99 @@
using Microsoft.Extensions.Options;
using sodoff.Configuration;
namespace sodoff.Middleware;
public class AssetMiddleware
{
private readonly RequestDelegate _next;
private readonly IOptions<AssetServerConfig> config;
public AssetMiddleware(RequestDelegate next, IOptions<AssetServerConfig> config)
{
_next = next;
this.config = config;
}
public async Task Invoke(HttpContext context)
{
if (context.Connection.LocalPort == config.Value.Port)
await GetAssetAsync(context);
else
await _next(context);
}
private async Task GetAssetAsync(HttpContext context)
{
string path = context.Request.Path;
if (path is null || !string.IsNullOrEmpty(config.Value.URLPrefix) && !path.StartsWith("/" + config.Value.URLPrefix) || config.Value.Mode == AssetServerMode.None) {
context.Response.StatusCode = 400;
return;
}
string assetPath = path.Remove(0, config.Value.URLPrefix.Length + 1);
string localPath = GetLocalPath("assets/" + assetPath);
if (localPath == string.Empty) {
if (config.Value.Mode == AssetServerMode.Partial)
await GetRemoteAsset(context, assetPath);
else
context.Response.StatusCode = 404;
}
else {
context.Response.Headers["Content-Type"] = "application/octet-stream";
await context.Response.SendFileAsync(Path.GetFullPath(localPath));
}
}
private async Task GetRemoteAsset(HttpContext context, string path)
{
HttpClient client = new HttpClient();
try {
var response = await client.GetAsync(config.Value.ProviderURL + path);
string? contentType = response.Content.Headers.ContentType?.MediaType;
if (contentType is null) {
context.Response.StatusCode = 404;
}
else if (contentType.StartsWith("application/octet-stream") || contentType.StartsWith("image/jpeg")) {
context.Response.Headers["Content-Type"] = contentType;
await response.Content.CopyToAsync(context.Response.Body);
}
else {
context.Response.ContentType = contentType;
await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
}
}
catch (Exception) {
context.Response.StatusCode = 404;
}
}
private string GetLocalPath(string path)
{
if (File.Exists(path)) return path;
string[] qualityTiers = { "/High/", "/Mid/", "/Low/" };
if (config.Value.SubstituteMissingLocalAssets)
{
foreach (var tier in qualityTiers)
{
if (path.Contains(tier))
{
foreach (var otherTier in qualityTiers)
{
if (otherTier != tier)
{
string otherPath = path.Replace(tier, otherTier);
if (File.Exists(otherPath)) return otherPath;
}
}
}
}
}
return string.Empty;
}
}

View File

@ -1,4 +1,7 @@
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using sodoff.Configuration;
using sodoff.Middleware;
using sodoff.Model;
using sodoff.Services;
using sodoff.Utils;
@ -8,6 +11,7 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.Configure<AssetServerConfig>(builder.Configuration.GetSection("AssetServer"));
builder.Services.AddControllers(options => {
options.OutputFormatters.Add(new XmlSerializerOutputFormatter(new XmlWriterSettings() { OmitXmlDeclaration = false }));
options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
@ -27,6 +31,13 @@ builder.Services.AddScoped<InventoryService>();
builder.Services.AddScoped<AchievementService>();
builder.Services.AddScoped<GameDataService>();
bool assetServer = builder.Configuration.GetSection("AssetServer").GetValue<bool>("Enabled");
int assetPort = builder.Configuration.GetSection("AssetServer").GetValue<int>("Port");
if (assetServer)
builder.Services.Configure<KestrelServerOptions>(options => {
options.ListenAnyIP(assetPort);
});
var app = builder.Build();
using var scope = app.Services.CreateScope();
@ -35,6 +46,9 @@ scope.ServiceProvider.GetRequiredService<DBContext>().Database.EnsureCreated();
// Configure the HTTP request pipeline.
if (assetServer)
app.UseMiddleware<AssetMiddleware>();
app.UseAuthorization();
app.MapControllers();