Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion samples/WebApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@
app.UseAuthentication();
app.UseAuthorization();

var serverSubPath = bool.Parse(builder.Configuration["SerilogUi:AddServerSubPath"] ?? "false") ? "logs/" : "";
app.UseSerilogUi(options => options
.WithHomeUrl("/#Test")
.WithServerSubPath(serverSubPath)
.WithAuthenticationType(AuthenticationType.Jwt)
.WithExpandedDropdownsByDefault()
.EnableAuthorizationOnAppRoutes()
.InjectJavascript("/js/serilog-ui/custom.js")
.InjectJavascript($"/{serverSubPath}js/serilog-ui/custom.js")
);

app.MapControllerRoute(
Expand Down
5 changes: 3 additions & 2 deletions samples/WebApp/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:40034",
"applicationUrl": "http://localhost:40034/logs",
"sslPort": 44304
}
},
Expand All @@ -23,7 +23,8 @@
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Development",
"SerilogUi:AddServerSubPath": "true"
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions samples/WebApp/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@
},
"SerilogUi": {
"UserName": "BasicSampleUser",
"Password": "BasicSamplePwd"
"Password": "BasicSamplePwd",
"AddServerSubPath": false
},
"Serilog": {
"Using": [
"Serilog.Sinks.MongoDB"
],
"Using": ["Serilog.Sinks.MongoDB"],
"MinimumLevel": "Debug",
"WriteTo": [
{
Expand Down
16 changes: 14 additions & 2 deletions src/Serilog.Ui.Web/Endpoints/SerilogUiAppRoutes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public Task RedirectHomeAsync()

private async Task<string> LoadStream(Stream stream, UiOptions options)
{
StringBuilder htmlStringBuilder = new StringBuilder(await new StreamReader(stream).ReadToEndAsync());
StringBuilder htmlStringBuilder = new(await new StreamReader(stream).ReadToEndAsync());
string authType = options.Authorization.AuthenticationType.ToString();

var feOpts = new
Expand All @@ -63,7 +63,7 @@ private async Task<string> LoadStream(Stream stream, UiOptions options)
options.ShowBrand,
options.HomeUrl,
BlockHomeAccess,
options.RoutePrefix,
RoutePrefix = ConstructRoutesPrefix(options),
options.ExpandDropdownsByDefault
};
string encodeAuthOpts = Uri.EscapeDataString(JsonSerializer.Serialize(feOpts, JsonSerializerOptionsFactory.GetDefaultOptions));
Expand All @@ -75,4 +75,16 @@ private async Task<string> LoadStream(Stream stream, UiOptions options)

return htmlStringBuilder.ToString();
}

private static string ConstructRoutesPrefix(UiOptions options)
{
var safeHostPath = string.IsNullOrWhiteSpace(options.ServerSubPath) ? "" : options.ServerSubPath;
var hostPathWithoutInitialSlash = safeHostPath.StartsWith("/", StringComparison.OrdinalIgnoreCase) ? safeHostPath[1..] : safeHostPath;
var hostPathWithDivider = !string.IsNullOrWhiteSpace(hostPathWithoutInitialSlash) &&
!hostPathWithoutInitialSlash.EndsWith('/')
? $"{hostPathWithoutInitialSlash}/"
: hostPathWithoutInitialSlash;

return $"{hostPathWithDivider}{options.RoutePrefix}";
}
}
23 changes: 23 additions & 0 deletions src/Serilog.Ui.Web/Models/UiOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public class UiOptions(ProvidersOptions options)
/// <value>The route prefix.</value>
public string RoutePrefix { get; private set; } = "serilog-ui";

/// <summary>
/// Gets the server sub-path deployment.
/// </summary>
/// <value>The server sub path.</value>
public string ServerSubPath { get; private set; } = string.Empty;

/// <summary>
/// Get the option to auto-expand dropdowns in the log viewer.
/// </summary>
Expand Down Expand Up @@ -126,6 +132,23 @@ public UiOptions WithRoutePrefix(string routePrefix)
return this;
}

/// <summary>
/// Sets the server hosting base-path.
/// If the application is deployed under a server sub-path, this property provides the additional sub-path to
/// the dashboard to help the client-side routing.<br/>
/// If provided, the final routing result will be:<br/><c>{server-base-path}/{UiOptions.ServerSubPath}/{UiOptions.RoutePrefix}</c>.
/// </summary>
/// <example>
/// Server root: "server.com"
/// IF the application is deployed at root level ("server.com/{my-app}") => NOT set ServerSubPath
/// IF the application is deployed at sub-path level ("server.com/{additional-path}/{my-app}") => MUST set ServerSubPath
/// </example>
public UiOptions WithServerSubPath(string serverSubPath)
{
ServerSubPath = serverSubPath;
return this;
}

internal void Validate()
{
if (RoutePrefix.EndsWith('/')) throw new ArgumentException($"{nameof(RoutePrefix)} can't end with a slash.");
Expand Down
32 changes: 19 additions & 13 deletions tests/Serilog.Ui.Web.Tests/Endpoints/SerilogUiAppRoutesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,25 @@ public SerilogUiAppRoutesTest()
_sut = new SerilogUiAppRoutes(_contextAccessor, _streamLoaderMock);
}

[Fact]
public async Task It_gets_app_home()
[Theory]
[InlineData(null, "test")]
[InlineData("", "test")]
[InlineData(" ", "test")]
[InlineData("sub-path", "sub-path/test")]
[InlineData("sub-path/", "sub-path/test")]
[InlineData("/sub-path/", "sub-path/test")]
public async Task It_gets_app_home(string? serverSubPath, string expectedRoutePrefix)
{
// Arrange
_sut.SetOptions(new UiOptions(new ProvidersOptions())
{
BodyContent = "<div>body-test</div>",
HeadContent = "<div>head-test</div>"
}
.WithAuthenticationType(AuthenticationType.Jwt)
.WithRoutePrefix("test")
.WithHomeUrl("home-url")
var body = "<div>body-test</div>";
var head = "<div>head-test</div>";
var baseOpts = new UiOptions(new()) { BodyContent = body, HeadContent = head };
_sut
.SetOptions(baseOpts
.WithAuthenticationType(AuthenticationType.Jwt)
.WithRoutePrefix("test")
.WithServerSubPath(serverSubPath!)
.WithHomeUrl("home-url")
);
_testContext.Request.Path = "/serilog-ui-url/";
_testContext.Response.Body = new MemoryStream();
Expand All @@ -75,9 +82,8 @@ public async Task It_gets_app_home()
"<body><div id=\"serilog-ui-app\"></div>" +
"<script>const config = '%7B%22authType%22%3A%22Jwt%22%2C%22columnsInfo%22%3A%7B%7D%2C%22" +
"disabledSortOnKeys%22%3A%5B%5D%2C%22renderExceptionAsStringKeys%22%3A%5B%5D%2C%22showBrand%22%3Atrue%2C%22homeUrl%22%3A%22home-url" +
"%22%2C%22blockHomeAccess%22%3Afalse%2C%22routePrefix%22%3A%22" +
"test%22%2C%22expandDropdownsByDefault%22%3Afalse%7D';</script><div>body-test</div></body></html>"
);
"%22%2C%22blockHomeAccess%22%3Afalse%2C%22routePrefix%22%3A%22" + Uri.EscapeDataString(expectedRoutePrefix) +
"%22%2C%22expandDropdownsByDefault%22%3Afalse%7D';</script><div>body-test</div></body></html>");
}

[Fact]
Expand Down
3 changes: 3 additions & 0 deletions tests/Serilog.Ui.Web.Tests/Models/UiOptionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public void It_gets_default_options()

sut.HomeUrl.Should().Be("/");
sut.RoutePrefix.Should().Be("serilog-ui");
sut.ServerSubPath.Should().BeEmpty();
sut.BodyContent.Should().BeEmpty();
sut.HeadContent.Should().BeEmpty();
sut.ShowBrand.Should().BeTrue();
Expand All @@ -33,12 +34,14 @@ public void It_sets_options()
.WithHomeUrl("test")
.WithAuthenticationType(AuthenticationType.Basic)
.WithRoutePrefix("prefix")
.WithServerSubPath("log")
.HideSerilogUiBrand()
.WithExpandedDropdownsByDefault()
.EnableAuthorizationOnAppRoutes();

sut.HomeUrl.Should().Be("test");
sut.RoutePrefix.Should().Be("prefix");
sut.ServerSubPath.Should().Be("log");
sut.ShowBrand.Should().BeFalse();
sut.ExpandDropdownsByDefault.Should().BeTrue();
sut.Authorization.AuthenticationType.Should().Be(AuthenticationType.Basic);
Expand Down
Loading