4 Commits

Author SHA1 Message Date
LD50
3e433c3cbe 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
2026-01-01 17:39:08 +01:00
8cbc77eb1d Added swagger and elmah return-to-front-end button 2026-01-01 17:38:48 +01:00
LD50
977a8f1637 Merge pull request #73 from LD-Reborn/68-returnurl-does-not-work
Fixed ReturnUrl not working
2026-01-01 16:12:51 +01:00
65ed78462d Fixed ReturnUrl not working 2026-01-01 16:02:30 +01:00
7 changed files with 223 additions and 2 deletions

View File

@@ -14,6 +14,8 @@ using System.Configuration;
using Microsoft.OpenApi.Models;
using Shared.Models;
using Microsoft.AspNetCore.ResponseCompression;
using System.Net;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
@@ -140,6 +142,57 @@ var app = builder.Build();
app.UseAuthentication();
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.MapHealthChecks("/healthz");
@@ -161,7 +214,7 @@ app.Use(async (context, next) =>
{
if (!context.User.Identity?.IsAuthenticated ?? true)
{
context.Response.Redirect("/Account/Login");
context.Response.Redirect($"/Account/Login?ReturnUrl={WebUtility.UrlEncode("/swagger")}");
return;
}
@@ -179,6 +232,8 @@ app.UseSwagger();
app.UseSwaggerUI(options =>
{
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.

View File

@@ -1,3 +1,4 @@
@using Microsoft.Extensions.Primitives
@using Server.Services
@inject LocalizationService T
@{
@@ -9,6 +10,10 @@
<h1>Login</h1>
<form asp-action="Login" method="post" class="mt-4" style="max-width: 400px; margin: auto;">
<div class="form-group mb-3">
@if (Context.Request.Query.TryGetValue("ReturnUrl", out StringValues returnUrl))
{
<input type="hidden" name="ReturnUrl" value="@(returnUrl)" />
}
<label for="username" class="form-label">@T["Username"]</label>
<input autofocus type="text" class="form-control" id="username" name="username" autocomplete="username" required>
</div>

View File

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