Refine admin workflows and team access

This commit is contained in:
AzuTear
2026-06-26 18:09:29 +02:00
parent b7804e10ea
commit 8b69dfbafb
71 changed files with 5066 additions and 751 deletions
@@ -75,9 +75,15 @@ public static class AdminSiteSettingsEndpoints
return Results.NotFound();
}
var urlValidationError = NormalizeSiteSettingsUrls(request, out var normalizedUrls, out var socialLinks);
if (urlValidationError is not null)
{
return urlValidationError;
}
settings.HostDisplayName = request.HostDisplayName.Trim();
settings.HostTagline = request.HostTagline.Trim();
settings.NewsletterUrl = request.NewsletterUrl.Trim();
settings.NewsletterUrl = normalizedUrls.NewsletterUrl;
settings.PrivacyEmail = request.PrivacyEmail.Trim();
var trimmedPrivacyContent = request.PrivacyPolicyContent.Trim();
var privacyChanged = !string.Equals(settings.PrivacyPolicyContent, trimmedPrivacyContent, StringComparison.Ordinal);
@@ -88,13 +94,13 @@ public static class AdminSiteSettingsEndpoints
settings.PrivacyPolicyUpdatedAt = DateTimeOffset.UtcNow;
}
settings.ImprintUrl = request.ImprintUrl.Trim();
settings.ImprintUrl = normalizedUrls.ImprintUrl;
settings.ImprintContent = request.ImprintContent.Trim();
settings.ContactUrl = request.ContactUrl.Trim();
settings.ContactUrl = normalizedUrls.ContactUrl;
settings.ContactContent = request.ContactContent.Trim();
settings.SponsorsUrl = request.SponsorsUrl.Trim();
settings.SponsorsUrl = normalizedUrls.SponsorsUrl;
settings.SponsorsContent = request.SponsorsContent.Trim();
settings.SocialLinksJson = JsonSerializer.Serialize(request.SocialLinks ?? []);
settings.SocialLinksJson = JsonSerializer.Serialize(socialLinks);
settings.FaqJson = JsonSerializer.Serialize(request.Faq ?? []);
adminAuditService.AddEntry(
@@ -107,7 +113,7 @@ public static class AdminSiteSettingsEndpoints
{
settings.HostDisplayName,
privacyChanged,
socialLinkCount = request.SocialLinks?.Length ?? 0,
socialLinkCount = socialLinks.Length,
faqCount = request.Faq?.Length ?? 0,
},
RequestMetadataReader.Read(context));
@@ -116,6 +122,73 @@ public static class AdminSiteSettingsEndpoints
return Results.Ok(new { saved = true });
}
private static IResult? NormalizeSiteSettingsUrls(
UpdateSiteSettingsRequest request,
out PublicSiteUrlSettings normalizedUrls,
out PublicSocialLinkDto[] socialLinks)
{
normalizedUrls = new PublicSiteUrlSettings();
socialLinks = [];
if (!TryNormalizePublicUrl(request.NewsletterUrl, "Newsletter-Link", out var newsletterUrl, out var errorMessage)
|| !TryNormalizePublicUrl(request.ImprintUrl, "Impressum-Link", out var imprintUrl, out errorMessage)
|| !TryNormalizePublicUrl(request.ContactUrl, "Kontakt-Link", out var contactUrl, out errorMessage)
|| !TryNormalizePublicUrl(request.SponsorsUrl, "Sponsoren-Link", out var sponsorsUrl, out errorMessage))
{
return Results.BadRequest(new { message = errorMessage });
}
normalizedUrls = new PublicSiteUrlSettings
{
NewsletterUrl = newsletterUrl,
ImprintUrl = imprintUrl,
ContactUrl = contactUrl,
SponsorsUrl = sponsorsUrl,
};
var normalizedSocialLinks = new List<PublicSocialLinkDto>();
foreach (var social in request.SocialLinks ?? [])
{
if (!TryNormalizePublicUrl(social.Url, $"Social-Link {social.Label}", out var normalizedUrl, out errorMessage))
{
return Results.BadRequest(new { message = errorMessage });
}
normalizedSocialLinks.Add(social with { Url = normalizedUrl });
}
socialLinks = normalizedSocialLinks.ToArray();
return null;
}
private static bool TryNormalizePublicUrl(string? value, string fieldName, out string normalizedUrl, out string errorMessage)
{
normalizedUrl = (value ?? string.Empty).Trim();
errorMessage = string.Empty;
if (string.IsNullOrWhiteSpace(normalizedUrl))
{
return true;
}
if (!Uri.TryCreate(normalizedUrl, UriKind.Absolute, out var uri)
|| (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps))
{
errorMessage = $"{fieldName} muss eine absolute http/https-URL sein.";
return false;
}
normalizedUrl = uri.ToString();
return true;
}
private sealed class PublicSiteUrlSettings
{
public string NewsletterUrl { get; set; } = string.Empty;
public string ImprintUrl { get; set; } = string.Empty;
public string ContactUrl { get; set; } = string.Empty;
public string SponsorsUrl { get; set; } = string.Empty;
}
private static async Task<IResult> GetOperationalSettings(AwardsDbContext db, IConfiguration configuration)
{
var settings = await db.SiteSettings.AsNoTracking().FirstOrDefaultAsync(item => item.Id == 1);