mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-15 19:22:19 +08:00
feat(web): make webui work at any base path
This commit is contained in:
parent
8428ca286a
commit
ca755d5ec5
|
@ -11,12 +11,12 @@ namespace BililiveRecorder.Web
|
||||||
public class BasicAuthMiddleware
|
public class BasicAuthMiddleware
|
||||||
{
|
{
|
||||||
private readonly RequestDelegate next;
|
private readonly RequestDelegate next;
|
||||||
private readonly ManifestEmbeddedFileProvider fileProvider;
|
private readonly CompositeFileProvider fileProvider;
|
||||||
private const string BasicAndSpace = "Basic ";
|
private const string BasicAndSpace = "Basic ";
|
||||||
|
|
||||||
private static string? Html401Page;
|
private static string? Html401Page;
|
||||||
|
|
||||||
public BasicAuthMiddleware(RequestDelegate next, ManifestEmbeddedFileProvider fileProvider)
|
public BasicAuthMiddleware(RequestDelegate next, CompositeFileProvider fileProvider)
|
||||||
{
|
{
|
||||||
this.next = next ?? throw new ArgumentNullException(nameof(next));
|
this.next = next ?? throw new ArgumentNullException(nameof(next));
|
||||||
this.fileProvider = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
|
this.fileProvider = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AngleSharp" Version="0.17.1" />
|
||||||
<PackageReference Include="AutoMapper" Version="11.0.1" />
|
<PackageReference Include="AutoMapper" Version="11.0.1" />
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||||
<PackageReference Include="GraphQL.MicrosoftDI" Version="4.8.0" />
|
<PackageReference Include="GraphQL.MicrosoftDI" Version="4.8.0" />
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
<PackageReference Include="GraphQL.SystemReactive" Version="4.8.0" />
|
<PackageReference Include="GraphQL.SystemReactive" Version="4.8.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.8" />
|
||||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.8" />
|
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.8" />
|
||||||
|
<PackageReference Include="NUglify" Version="1.20.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
92
BililiveRecorder.Web/DynamicHtmlController.cs
Normal file
92
BililiveRecorder.Web/DynamicHtmlController.cs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AngleSharp.Html;
|
||||||
|
using AngleSharp.Html.Parser;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
using NUglify;
|
||||||
|
|
||||||
|
namespace BililiveRecorder.Web
|
||||||
|
{
|
||||||
|
[Controller]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public sealed class DynamicHtmlController : Controller
|
||||||
|
{
|
||||||
|
private static string? cachedIndexHtml;
|
||||||
|
private readonly CompositeFileProvider fileProvider;
|
||||||
|
|
||||||
|
public DynamicHtmlController(CompositeFileProvider fileProvider)
|
||||||
|
{
|
||||||
|
this.fileProvider = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/", Name = "Home Page"), HttpGet]
|
||||||
|
public ActionResult GetHomePage()
|
||||||
|
{
|
||||||
|
if (cachedIndexHtml is null)
|
||||||
|
{
|
||||||
|
using var file = this.fileProvider.GetFileInfo("/index.html").CreateReadStream();
|
||||||
|
using var reader = new StreamReader(file, Encoding.UTF8);
|
||||||
|
var html = reader.ReadToEnd();
|
||||||
|
cachedIndexHtml = html
|
||||||
|
.Replace("__VERSION__", GitVersionInformation.FullSemVer)
|
||||||
|
.Replace("__FULL_VERSION__", GitVersionInformation.InformationalVersion)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Content(cachedIndexHtml, "text/html", Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/ui/", Name = "WebUI Html"), HttpGet]
|
||||||
|
public async Task GetWebUIAsync()
|
||||||
|
{
|
||||||
|
var parser = new HtmlParser();
|
||||||
|
var fileInfo = this.fileProvider.GetFileInfo("/ui/index.html");
|
||||||
|
|
||||||
|
using var injectionScriptStream = new StreamReader(this.fileProvider.GetFileInfo(".webui_injection.js").CreateReadStream());
|
||||||
|
var scriptContent = await injectionScriptStream.ReadToEndAsync();
|
||||||
|
|
||||||
|
using var stream = fileInfo.CreateReadStream();
|
||||||
|
using var document = await parser.ParseDocumentAsync(stream).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var spaPath = this.HttpContext.Items.ContainsKey("webui-spa-path") ? ((PathString)this.HttpContext.Items["webui-spa-path"]!) : this.Request.Path;
|
||||||
|
|
||||||
|
spaPath.StartsWithSegments("/ui", out var remaining);
|
||||||
|
|
||||||
|
var head = document.Head!;
|
||||||
|
var template = document.CreateElement("template");
|
||||||
|
template.Id = "delayed-init";
|
||||||
|
head.AppendChild(template);
|
||||||
|
|
||||||
|
var scripts = document.QuerySelectorAll("script[type='module']");
|
||||||
|
var css = document.QuerySelectorAll("link[rel='stylesheet']");
|
||||||
|
|
||||||
|
foreach (var script in scripts)
|
||||||
|
{
|
||||||
|
script.Remove();
|
||||||
|
template.AppendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var node in css)
|
||||||
|
{
|
||||||
|
node.Remove();
|
||||||
|
template.AppendChild(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
var initScript = document.CreateElement("script");
|
||||||
|
initScript.TextContent = Uglify.Js(scriptContent).Code;
|
||||||
|
initScript.SetAttribute("data-href", remaining);
|
||||||
|
|
||||||
|
head.AppendChild(initScript);
|
||||||
|
|
||||||
|
this.Response.StatusCode = 200;
|
||||||
|
this.Response.ContentType = "text/html; encoding=utf-8";
|
||||||
|
|
||||||
|
using var writer = new StreamWriter(this.Response.Body);
|
||||||
|
document.ToHtml(writer, new MinifyMarkupFormatter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,38 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.FileProviders;
|
|
||||||
|
|
||||||
namespace BililiveRecorder.Web
|
|
||||||
{
|
|
||||||
[Controller, Route("/", Name = "Home Page")]
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
public sealed class IndexController : Controller
|
|
||||||
{
|
|
||||||
private static string? result;
|
|
||||||
private readonly ManifestEmbeddedFileProvider fileProvider;
|
|
||||||
|
|
||||||
public IndexController(ManifestEmbeddedFileProvider fileProvider)
|
|
||||||
{
|
|
||||||
this.fileProvider = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public ActionResult Get()
|
|
||||||
{
|
|
||||||
if (result is null)
|
|
||||||
{
|
|
||||||
using var file = this.fileProvider.GetFileInfo("/index.html").CreateReadStream();
|
|
||||||
using var reader = new StreamReader(file, Encoding.UTF8);
|
|
||||||
var html = reader.ReadToEnd();
|
|
||||||
result = html
|
|
||||||
.Replace("__VERSION__", GitVersionInformation.FullSemVer)
|
|
||||||
.Replace("__FULL_VERSION__", GitVersionInformation.InformationalVersion)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.Content(result, "text/html", Encoding.UTF8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -74,7 +74,9 @@ namespace BililiveRecorder.Web
|
||||||
;
|
;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
services.AddSingleton(new ManifestEmbeddedFileProvider(typeof(Startup).Assembly));
|
services
|
||||||
|
.AddSingleton(new ManifestEmbeddedFileProvider(typeof(Startup).Assembly))
|
||||||
|
.AddSingleton(sp => new CompositeFileProvider(sp.GetRequiredService<IWebHostEnvironment>().WebRootFileProvider, sp.GetRequiredService<ManifestEmbeddedFileProvider>()));
|
||||||
|
|
||||||
// Graphql API
|
// Graphql API
|
||||||
GraphQL.MicrosoftDI.GraphQLBuilderExtensions.AddGraphQL(services)
|
GraphQL.MicrosoftDI.GraphQLBuilderExtensions.AddGraphQL(services)
|
||||||
|
@ -152,6 +154,7 @@ namespace BililiveRecorder.Web
|
||||||
{
|
{
|
||||||
if (originalPath.StartsWithSegments("/ui"))
|
if (originalPath.StartsWithSegments("/ui"))
|
||||||
{
|
{
|
||||||
|
context.Items["webui-spa-path"] = originalPath;
|
||||||
context.Request.Path = "/ui/";
|
context.Request.Path = "/ui/";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -193,11 +196,11 @@ namespace BililiveRecorder.Web
|
||||||
ctp.Mappings[".mjs"] = "text/javascript; charset=utf-8";
|
ctp.Mappings[".mjs"] = "text/javascript; charset=utf-8";
|
||||||
ctp.Mappings[".json"] = "application/json; charset=utf-8";
|
ctp.Mappings[".json"] = "application/json; charset=utf-8";
|
||||||
|
|
||||||
var manifestEmbeddedFileProvider = app.ApplicationServices.GetRequiredService<ManifestEmbeddedFileProvider>();
|
var compositeFileProvider = app.ApplicationServices.GetRequiredService<CompositeFileProvider>();
|
||||||
var sharedStaticFiles = new SharedOptions()
|
var sharedStaticFiles = new SharedOptions()
|
||||||
{
|
{
|
||||||
// 在运行的 exe 旁边新建一个 wwwroot 文件夹,会优先使用里面的内容,然后 fallback 到打包的资源文件
|
// 在运行的 exe 旁边新建一个 wwwroot 文件夹,会优先使用里面的内容,然后 fallback 到打包的资源文件
|
||||||
FileProvider = new CompositeFileProvider(env.WebRootFileProvider, manifestEmbeddedFileProvider),
|
FileProvider = compositeFileProvider,
|
||||||
RedirectToAppendTrailingSlash = true,
|
RedirectToAppendTrailingSlash = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
28
BililiveRecorder.Web/embeded/.webui_injection.js
Normal file
28
BililiveRecorder.Web/embeded/.webui_injection.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
(function () {
|
||||||
|
const currentScript = document.currentScript;
|
||||||
|
if (currentScript && "string" === typeof currentScript.dataset.href) {
|
||||||
|
const SERVER_PATH = currentScript.dataset.href;
|
||||||
|
const baseTag = document.getElementsByTagName('base')[0];
|
||||||
|
console.log("SERVER_PATH: " + SERVER_PATH);
|
||||||
|
const pathname = location.pathname;
|
||||||
|
console.log("location.pathname: " + pathname);
|
||||||
|
if (SERVER_PATH.length === 0) {
|
||||||
|
let BASE = pathname + '/';
|
||||||
|
baseTag.href = BASE;
|
||||||
|
console.log("base path: " + BASE);
|
||||||
|
} else if (pathname.endsWith(SERVER_PATH)) {
|
||||||
|
var i = pathname.lastIndexOf(SERVER_PATH);
|
||||||
|
if (i > -1) {
|
||||||
|
let BASE = pathname.slice(0, i) + '/';
|
||||||
|
baseTag.href = BASE;
|
||||||
|
console.log("base path: " + BASE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('????');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const init = document.getElementById('delayed-init');
|
||||||
|
document.head.append(init.content.cloneNode(true));
|
||||||
|
init.remove();
|
||||||
|
currentScript.remove();
|
||||||
|
})()
|
|
@ -1 +1 @@
|
||||||
Subproject commit 08eeaf6789ba1e0825c7e5027c2fb8d3c9108207
|
Subproject commit 517c2a659f26f4fea088036650de502aa7f8621f
|
Loading…
Reference in New Issue
Block a user