Add team roles and content management updates

This commit is contained in:
AzuTear
2026-06-25 19:52:46 +02:00
parent 54293f4c45
commit 693769e5a8
126 changed files with 6966 additions and 738 deletions
+138 -15
View File
@@ -16,10 +16,22 @@ public static class AdminSiteSettingsEndpoints
public static RouteGroupBuilder MapAdminSiteSettingsEndpoints(this RouteGroupBuilder group)
{
group.MapGet("/site-settings", GetSiteSettings).WithName("GetAdminSiteSettings").WithOpenApi();
group.MapPut("/site-settings", UpdateSiteSettings).WithName("UpdateAdminSiteSettings").WithOpenApi();
group.MapGet("/operational-settings", GetOperationalSettings).WithName("GetAdminOperationalSettings").WithOpenApi();
group.MapPut("/operational-settings", UpdateOperationalSettings).WithName("UpdateAdminOperationalSettings").WithOpenApi();
group.MapGet("/site-settings", GetSiteSettings)
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequirePermission(context, next, AdminPermissionCatalog.Content))
.WithName("GetAdminSiteSettings")
.WithOpenApi();
group.MapPut("/site-settings", UpdateSiteSettings)
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Content))
.WithName("UpdateAdminSiteSettings")
.WithOpenApi();
group.MapGet("/operational-settings", GetOperationalSettings)
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequirePermission(context, next, AdminPermissionCatalog.Settings))
.WithName("GetAdminOperationalSettings")
.WithOpenApi();
group.MapPut("/operational-settings", UpdateOperationalSettings)
.AddEndpointFilter((context, next) => AdminEndpointConventions.RequireWritePermission(context, next, AdminPermissionCatalog.Settings))
.WithName("UpdateAdminOperationalSettings")
.WithOpenApi();
return group;
}
@@ -40,8 +52,11 @@ public static class AdminSiteSettingsEndpoints
settings.PrivacyPolicyUpdatedBy,
settings.PrivacyPolicyUpdatedAt,
settings.ImprintUrl,
settings.ImprintContent,
settings.ContactUrl,
settings.ContactContent,
settings.SponsorsUrl,
settings.SponsorsContent,
SeasonMappings.ReadSocialLinks(settings),
SeasonMappings.ReadFaqItems(settings)));
}
@@ -53,10 +68,6 @@ public static class AdminSiteSettingsEndpoints
IAdminAuditService adminAuditService)
{
var session = AdminEndpointConventions.CurrentSession(context);
if (!AdminRoles.CanManageContent(session.Role))
{
return Results.Json(new { message = "Landingpage content requires a content admin, admin or owner role." }, statusCode: StatusCodes.Status403Forbidden);
}
var settings = await db.SiteSettings.FirstOrDefaultAsync(item => item.Id == 1);
if (settings is null)
@@ -78,8 +89,11 @@ public static class AdminSiteSettingsEndpoints
}
settings.ImprintUrl = request.ImprintUrl.Trim();
settings.ImprintContent = request.ImprintContent.Trim();
settings.ContactUrl = request.ContactUrl.Trim();
settings.ContactContent = request.ContactContent.Trim();
settings.SponsorsUrl = request.SponsorsUrl.Trim();
settings.SponsorsContent = request.SponsorsContent.Trim();
settings.SocialLinksJson = JsonSerializer.Serialize(request.SocialLinks ?? []);
settings.FaqJson = JsonSerializer.Serialize(request.Faq ?? []);
@@ -111,6 +125,7 @@ public static class AdminSiteSettingsEndpoints
}
var usesDatabaseDemo = settings.DemoLoginManagedByDatabase || HasDatabaseDemoCredentials(settings);
var twitchSettings = ReadEffectiveTwitchSettings(settings, configuration);
return Results.Ok(new AdminOperationalSettingsResponse(
usesDatabaseDemo,
usesDatabaseDemo ? settings.DemoLoginEnabled : IsDemoLoginEnabled(configuration),
@@ -118,6 +133,12 @@ public static class AdminSiteSettingsEndpoints
usesDatabaseDemo ? HasDatabaseDemoCredentials(settings) : !string.IsNullOrWhiteSpace(ReadDemoSetting(configuration, "Password", "VTSA_DEMO_ADMIN_PASSWORD")),
usesDatabaseDemo ? settings.DemoLoginTwitchUserId : ReadDemoSetting(configuration, "TwitchUserId", "VTSA_DEMO_ADMIN_TWITCH_ID"),
usesDatabaseDemo ? settings.DemoLoginDisplayName : ReadDemoSetting(configuration, "DisplayName", "VTSA_DEMO_ADMIN_DISPLAY_NAME"),
settings.TwitchAuthManagedByDatabase,
twitchSettings.Configured,
twitchSettings.ClientId,
twitchSettings.ClientSecretSet,
twitchSettings.RedirectUri,
twitchSettings.Scope,
settings.MaintenanceModeEnabled,
string.IsNullOrWhiteSpace(settings.MaintenanceTitle) ? FallbackMaintenanceTitle : settings.MaintenanceTitle,
string.IsNullOrWhiteSpace(settings.MaintenanceMessage)
@@ -144,12 +165,18 @@ public static class AdminSiteSettingsEndpoints
return Results.NotFound();
}
var before = CreateOperationalSettingsSnapshot(settings);
var before = CreateOperationalSettingsSnapshot(settings, configuration);
var loginIdentifier = request.DemoLoginEmail.Trim();
var twitchUserId = request.DemoLoginTwitchUserId.Trim();
var displayName = request.DemoLoginDisplayName.Trim();
var newPassword = request.DemoLoginPassword?.Trim() ?? string.Empty;
var fallbackPassword = ReadDemoSetting(configuration, "Password", "VTSA_DEMO_ADMIN_PASSWORD");
var twitchClientId = request.TwitchClientId.Trim();
var twitchClientSecret = request.TwitchClientSecret?.Trim() ?? string.Empty;
var twitchRedirectUri = request.TwitchRedirectUri.Trim();
var twitchScope = request.TwitchScope.Trim();
var existingTwitchSecretAvailable = !string.IsNullOrWhiteSpace(settings.TwitchClientSecret)
|| !string.IsNullOrWhiteSpace(ReadTwitchSetting(configuration, "ClientSecret", "VTSA_TWITCH_CLIENT_SECRET"));
if (request.DemoLoginEnabled)
{
@@ -168,9 +195,22 @@ public static class AdminSiteSettingsEndpoints
}
}
if (!string.IsNullOrWhiteSpace(newPassword) && newPassword.Length < 12)
if (!string.IsNullOrWhiteSpace(newPassword) && newPassword.Length < 10)
{
return Results.BadRequest(new { message = "Das Demo-Passwort muss mindestens 12 Zeichen lang sein." });
return Results.BadRequest(new { message = "Das Demo-Passwort muss mindestens 10 Zeichen lang sein." });
}
if (HasAnyTwitchAuthSetting(twitchClientId, twitchClientSecret, twitchRedirectUri, twitchScope)
&& string.IsNullOrWhiteSpace(twitchClientId))
{
return Results.BadRequest(new { message = "Twitch OAuth braucht eine Client-ID." });
}
if (!string.IsNullOrWhiteSpace(twitchClientId)
&& string.IsNullOrWhiteSpace(twitchClientSecret)
&& !existingTwitchSecretAvailable)
{
return Results.BadRequest(new { message = "Twitch OAuth braucht beim ersten Speichern ein Client Secret." });
}
settings.DemoLoginManagedByDatabase = true;
@@ -178,6 +218,22 @@ public static class AdminSiteSettingsEndpoints
settings.DemoLoginEmail = loginIdentifier;
settings.DemoLoginTwitchUserId = string.IsNullOrWhiteSpace(twitchUserId) ? "jayuhime_admin" : twitchUserId;
settings.DemoLoginDisplayName = string.IsNullOrWhiteSpace(displayName) ? "Jayuhime Admin" : displayName;
settings.TwitchClientId = twitchClientId;
settings.TwitchRedirectUri = twitchRedirectUri;
settings.TwitchScope = twitchScope;
if (!string.IsNullOrWhiteSpace(twitchClientSecret))
{
settings.TwitchClientSecret = twitchClientSecret;
}
else if (string.IsNullOrWhiteSpace(twitchClientId) && string.IsNullOrWhiteSpace(twitchRedirectUri) && string.IsNullOrWhiteSpace(twitchScope))
{
settings.TwitchClientSecret = string.Empty;
}
settings.TwitchAuthManagedByDatabase = HasAnyTwitchAuthSetting(
settings.TwitchClientId,
settings.TwitchClientSecret,
settings.TwitchRedirectUri,
settings.TwitchScope);
var passwordToPersist = !string.IsNullOrWhiteSpace(newPassword)
? newPassword
@@ -201,8 +257,9 @@ public static class AdminSiteSettingsEndpoints
var changes = BuildOperationalSettingChanges(
before,
CreateOperationalSettingsSnapshot(settings),
!string.IsNullOrWhiteSpace(passwordToPersist));
CreateOperationalSettingsSnapshot(settings, configuration),
!string.IsNullOrWhiteSpace(passwordToPersist),
!string.IsNullOrWhiteSpace(twitchClientSecret));
adminAuditService.AddEntry(
session.TwitchUserId,
@@ -225,6 +282,7 @@ public static class AdminSiteSettingsEndpoints
{
saved = true,
demoLoginPasswordSet = HasDatabaseDemoCredentials(settings),
twitchClientSecretSet = HasEffectiveTwitchClientSecret(settings, configuration),
});
}
@@ -250,6 +308,9 @@ public static class AdminSiteSettingsEndpoints
private static string ReadDemoSetting(IConfiguration configuration, string key, string environmentKey) =>
configuration[environmentKey] ?? configuration[$"DemoAdmin:{key}"] ?? string.Empty;
private static string ReadTwitchSetting(IConfiguration configuration, string key, string environmentKey) =>
configuration[environmentKey] ?? configuration[$"TwitchAuth:{key}"] ?? string.Empty;
private static string ReadDemoLoginIdentifier(IConfiguration configuration)
{
var configuredLogin = ReadDemoSetting(configuration, "Login", "VTSA_DEMO_ADMIN_LOGIN");
@@ -263,7 +324,35 @@ public static class AdminSiteSettingsEndpoints
&& !string.IsNullOrWhiteSpace(settings.DemoLoginPasswordHash)
&& !string.IsNullOrWhiteSpace(settings.DemoLoginPasswordSalt);
private static OperationalSettingsSnapshot CreateOperationalSettingsSnapshot(SiteSettings settings) =>
private static bool HasAnyTwitchAuthSetting(params string[] values) =>
values.Any(value => !string.IsNullOrWhiteSpace(value));
private static bool HasEffectiveTwitchClientSecret(SiteSettings settings, IConfiguration configuration) =>
!string.IsNullOrWhiteSpace(settings.TwitchClientSecret)
|| !string.IsNullOrWhiteSpace(ReadTwitchSetting(configuration, "ClientSecret", "VTSA_TWITCH_CLIENT_SECRET"));
private static TwitchSettingsSnapshot ReadEffectiveTwitchSettings(SiteSettings settings, IConfiguration configuration)
{
var clientId = !string.IsNullOrWhiteSpace(settings.TwitchClientId)
? settings.TwitchClientId
: ReadTwitchSetting(configuration, "ClientId", "VTSA_TWITCH_CLIENT_ID");
var redirectUri = !string.IsNullOrWhiteSpace(settings.TwitchRedirectUri)
? settings.TwitchRedirectUri
: ReadTwitchSetting(configuration, "RedirectUri", "VTSA_TWITCH_REDIRECT_URI");
var scope = !string.IsNullOrWhiteSpace(settings.TwitchScope)
? settings.TwitchScope
: ReadTwitchSetting(configuration, "Scope", "VTSA_TWITCH_SCOPE");
var secretSet = HasEffectiveTwitchClientSecret(settings, configuration);
return new(
clientId,
secretSet,
redirectUri,
scope,
!string.IsNullOrWhiteSpace(clientId) && secretSet);
}
private static OperationalSettingsSnapshot CreateOperationalSettingsSnapshot(SiteSettings settings, IConfiguration configuration) =>
new(
settings.DemoLoginManagedByDatabase,
settings.DemoLoginEnabled,
@@ -271,6 +360,11 @@ public static class AdminSiteSettingsEndpoints
HasDatabaseDemoCredentials(settings),
settings.DemoLoginTwitchUserId,
settings.DemoLoginDisplayName,
settings.TwitchAuthManagedByDatabase,
settings.TwitchClientId,
HasEffectiveTwitchClientSecret(settings, configuration),
settings.TwitchRedirectUri,
settings.TwitchScope,
settings.MaintenanceModeEnabled,
string.IsNullOrWhiteSpace(settings.MaintenanceTitle) ? FallbackMaintenanceTitle : settings.MaintenanceTitle,
string.IsNullOrWhiteSpace(settings.MaintenanceMessage) ? FallbackMaintenanceMessage : settings.MaintenanceMessage);
@@ -278,7 +372,8 @@ public static class AdminSiteSettingsEndpoints
private static object[] BuildOperationalSettingChanges(
OperationalSettingsSnapshot before,
OperationalSettingsSnapshot after,
bool passwordChanged)
bool passwordChanged,
bool twitchClientSecretChanged)
{
var changes = new List<object>();
@@ -287,6 +382,22 @@ public static class AdminSiteSettingsEndpoints
AddOperationalChange(changes, "demoLoginEmail", "Demo Login", before.DemoLoginEmail, after.DemoLoginEmail);
AddOperationalChange(changes, "demoLoginTwitchUserId", "Demo Twitch-ID", before.DemoLoginTwitchUserId, after.DemoLoginTwitchUserId);
AddOperationalChange(changes, "demoLoginDisplayName", "Demo Anzeigename", before.DemoLoginDisplayName, after.DemoLoginDisplayName);
AddOperationalChange(changes, "twitchAuthManagedByDatabase", "Twitch OAuth Quelle", before.TwitchAuthManagedByDatabase, after.TwitchAuthManagedByDatabase);
AddOperationalChange(changes, "twitchClientId", "Twitch Client-ID", before.TwitchClientId, after.TwitchClientId);
AddOperationalChange(changes, "twitchRedirectUri", "Twitch Redirect URI", before.TwitchRedirectUri, after.TwitchRedirectUri);
AddOperationalChange(changes, "twitchScope", "Twitch Scope", before.TwitchScope, after.TwitchScope);
if (before.TwitchClientSecretSet != after.TwitchClientSecretSet || twitchClientSecretChanged)
{
changes.Add(new
{
field = "twitchClientSecret",
label = "Twitch Client Secret",
@from = before.TwitchClientSecretSet ? "gesetzt" : "nicht gesetzt",
to = twitchClientSecretChanged ? "neu gesetzt" : after.TwitchClientSecretSet ? "gesetzt" : "nicht gesetzt",
sensitive = true,
});
}
if (passwordChanged)
{
@@ -347,7 +458,19 @@ public static class AdminSiteSettingsEndpoints
bool DemoLoginPasswordSet,
string DemoLoginTwitchUserId,
string DemoLoginDisplayName,
bool TwitchAuthManagedByDatabase,
string TwitchClientId,
bool TwitchClientSecretSet,
string TwitchRedirectUri,
string TwitchScope,
bool MaintenanceModeEnabled,
string MaintenanceTitle,
string MaintenanceMessage);
private sealed record TwitchSettingsSnapshot(
string ClientId,
bool ClientSecretSet,
string RedirectUri,
string Scope,
bool Configured);
}