Merge pull request #75 from LD-Reborn/72-swagger-and-elmah-have-no-return-to-front-end-button

Added swagger and elmah return-to-front-end button
This commit is contained in:
LD50
2026-01-01 17:39:08 +01:00
committed by GitHub
6 changed files with 216 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ using Microsoft.OpenApi.Models;
using Shared.Models; using Shared.Models;
using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.ResponseCompression;
using System.Net; using System.Net;
using System.Text;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -141,6 +142,57 @@ var app = builder.Build();
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
// Configure Elmah
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/elmah"))
{
context.Response.OnStarting(() =>
{
context.Response.Headers.Append(
"Content-Security-Policy",
"default-src 'self' 'unsafe-inline' 'unsafe-eval'"
);
return Task.CompletedTask;
});
}
await next();
});
app.Use(async (context, next) =>
{
if (!context.Request.Path.StartsWithSegments("/elmah"))
{
await next();
return;
}
var originalBody = context.Response.Body;
using var memStream = new MemoryStream();
context.Response.Body = memStream;
await next();
memStream.Position = 0;
var html = await new StreamReader(memStream).ReadToEndAsync();
if (context.Response.ContentType?.Contains("text/html") == true)
{
html = html.Replace(
"</head>",
"""
<link rel="stylesheet" href="/elmah-ui/custom.css" />
<script src="/elmah-ui/custom.js"></script>
</head>
"""
);
}
var bytes = Encoding.UTF8.GetBytes(html);
context.Response.ContentLength = bytes.Length;
await originalBody.WriteAsync(bytes);
context.Response.Body = originalBody;
});
app.UseElmah(); app.UseElmah();
app.MapHealthChecks("/healthz"); app.MapHealthChecks("/healthz");
@@ -180,6 +232,8 @@ app.UseSwagger();
app.UseSwaggerUI(options => app.UseSwaggerUI(options =>
{ {
options.EnablePersistAuthorization(); options.EnablePersistAuthorization();
options.InjectStylesheet("/swagger-ui/custom.css");
options.InjectJavascript("/swagger-ui/custom.js");
}); });
//app.UseElmahExceptionPage(); // Messes with JSON response for API calls. Leaving this here so I don't accidentally put this in again later on. //app.UseElmahExceptionPage(); // Messes with JSON response for API calls. Leaving this here so I don't accidentally put this in again later on.

View File

@@ -1,7 +1,10 @@
@using System.Globalization @using System.Globalization
@using Server.Services @using Server.Services
@using System.Net
@inject LocalizationService T @inject LocalizationService T
@{
var currentUrl = WebUtility.HtmlEncode(Context.Request.Path);
}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName"> <html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName">
<head> <head>
@@ -59,6 +62,18 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Searchdomains">@T["Searchdomains"]</a> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Searchdomains">@T["Searchdomains"]</a>
</li> </li>
@if (User.IsInRole("Admin") || User.IsInRole("Swagger"))
{
<li class="nav-item">
<a class="nav-link text-dark" href="/swagger/index.html?ReturnUrl=@(currentUrl)">@T["Swagger"]</a>
</li>
}
@if (User.IsInRole("Admin"))
{
<li class="nav-item">
<a class="nav-link text-dark" href="/elmah?ReturnUrl=@(currentUrl)">@T["Elmah"]</a>
</li>
}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Logout">@T["Logout"]</a> <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Logout">@T["Logout"]</a>
</li> </li>

View File

@@ -0,0 +1,54 @@
.elmah-return-btn {
position: fixed;
top: 6px;
right: 24px;
z-index: 9999;
display: flex;
align-items: center;
height: 44px;
min-width: 44px;
padding: 0 14px;
background: #85ea2d;
color: black;
border-radius: 999px;
font-weight: 600;
text-decoration: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
overflow: hidden;
white-space: nowrap;
justify-content: center;
text-decoration: none !important;
transition:
top 0.25s ease,
background-color 0.2s ease;
}
/* hidden label */
.elmah-return-btn::before {
content: "Return to Front-end";
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 0;
opacity: 0;
transition:
max-width 0.3s ease,
opacity 0.2s ease;
}
/* expand on hover */
.elmah-return-btn:hover::before {
max-width: 220px;
padding: 0.5rem;
opacity: 1;
}
/* hover colors */
.elmah-return-btn:hover {
background: #0b5ed7;
color: white;
}

View File

@@ -0,0 +1,10 @@
document.addEventListener('DOMContentLoaded', async () => {
const url = new URL(window.location.href);
const btn = document.createElement("a");
btn.href = url.searchParams.get('ReturnUrl') ?? "/";
btn.innerText = "⎋";
btn.setAttribute("aria-label", "Return to Front-End");
btn.className = "elmah-return-btn";
document.body.appendChild(btn);
});

View File

@@ -0,0 +1,58 @@
.swagger-return-btn {
position: fixed;
top: 6px;
left: 24px;
z-index: 9999;
display: flex;
align-items: center;
height: 44px;
min-width: 44px;
padding: 0 14px;
background: #85ea2d;
color: black;
border-radius: 999px;
font-weight: 600;
text-decoration: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
overflow: hidden;
white-space: nowrap;
justify-content: center;
transition:
top 0.25s ease,
background-color 0.2s ease;
}
/* hidden label */
.swagger-return-btn::after {
content: "Return to Front-end";
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 0;
opacity: 0;
transition:
max-width 0.3s ease,
opacity 0.2s ease;
}
/* expand on hover */
.swagger-return-btn:hover::after {
max-width: 220px;
padding: 0.5rem;
opacity: 1;
}
/* hover colors */
.swagger-return-btn:hover {
background: #0b5ed7;
color: white;
}
/* scrolled state */
.swagger-return-btn.scrolled {
top: 24px;
}

View File

@@ -0,0 +1,24 @@
document.addEventListener('DOMContentLoaded', async () => {
const url = new URL(window.location.href);
const btn = document.createElement("a");
btn.href = url.searchParams.get('ReturnUrl') ?? "/";
btn.innerText = "⎋";
btn.setAttribute("aria-label", "Return to Front-End");
btn.className = "swagger-return-btn";
document.body.appendChild(btn);
const togglePosition = () => {
if (window.scrollY > 0) {
btn.classList.add("scrolled");
} else {
btn.classList.remove("scrolled");
}
};
// Initial state
togglePosition();
// On scroll
window.addEventListener("scroll", togglePosition, { passive: true });
});