forked from SoDOff-Project/sodoff
local asset server
This commit is contained in:
parent
7578ee7e97
commit
6bc5a0b140
13
src/Configuration/AssetServerConfig.cs
Normal file
13
src/Configuration/AssetServerConfig.cs
Normal 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
|
||||||
|
}
|
99
src/Middleware/AssetMiddleware.cs
Normal file
99
src/Middleware/AssetMiddleware.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
using sodoff.Configuration;
|
||||||
|
using sodoff.Middleware;
|
||||||
using sodoff.Model;
|
using sodoff.Model;
|
||||||
using sodoff.Services;
|
using sodoff.Services;
|
||||||
using sodoff.Utils;
|
using sodoff.Utils;
|
||||||
@ -8,6 +11,7 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.Configure<AssetServerConfig>(builder.Configuration.GetSection("AssetServer"));
|
||||||
builder.Services.AddControllers(options => {
|
builder.Services.AddControllers(options => {
|
||||||
options.OutputFormatters.Add(new XmlSerializerOutputFormatter(new XmlWriterSettings() { OmitXmlDeclaration = false }));
|
options.OutputFormatters.Add(new XmlSerializerOutputFormatter(new XmlWriterSettings() { OmitXmlDeclaration = false }));
|
||||||
options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
|
options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
|
||||||
@ -27,6 +31,13 @@ builder.Services.AddScoped<InventoryService>();
|
|||||||
builder.Services.AddScoped<AchievementService>();
|
builder.Services.AddScoped<AchievementService>();
|
||||||
builder.Services.AddScoped<GameDataService>();
|
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();
|
var app = builder.Build();
|
||||||
|
|
||||||
using var scope = app.Services.CreateScope();
|
using var scope = app.Services.CreateScope();
|
||||||
@ -35,6 +46,9 @@ scope.ServiceProvider.GetRequiredService<DBContext>().Database.EnsureCreated();
|
|||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
|
|
||||||
|
if (assetServer)
|
||||||
|
app.UseMiddleware<AssetMiddleware>();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user