Files
vtuber-awards/Backend/Services/RiskRuleSettings.cs
T

88 lines
3.8 KiB
C#

using System.Text.Json;
using Backend.Domain;
namespace Backend.Services;
public sealed record RiskRuleSetting(
string Key,
string Label,
bool Enabled,
int Threshold,
int WindowMinutes,
string Severity,
string Description);
public static class RiskRuleSettings
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
public static RiskRuleSetting[] Defaults { get; } =
[
new("resubmitted_ballot", "Ballot erneut gespeichert", true, 1, 360, "low", "Erstellt einen Hinweis, wenn ein User sein Voting erneut speichert."),
new("rapid_vote_updates", "Voting-Burst", true, 3, 10, "high", "Erstellt einen Hinweis, wenn ein User sehr schnell mehrere Voting-Aenderungen ausloest."),
new("resubmitted_nomination", "Nominierung erneut eingereicht", true, 1, 360, "low", "Erstellt einen Hinweis, wenn ein User in derselben Kategorie erneut nominiert."),
new("rapid_nomination_burst", "Nominierungs-Burst", true, 10, 10, "high", "Erstellt einen Hinweis, wenn ein User sehr viele Nominierungen in kurzer Zeit sendet."),
new("duplicate_clip_submission", "Doppelter Clip", true, 1, 360, "medium", "Erstellt einen Hinweis, wenn ein User denselben Clip erneut einreicht."),
new("rapid_clip_burst", "Clip-Burst", true, 5, 10, "high", "Erstellt einen Hinweis, wenn ein User sehr viele Clips in kurzer Zeit sendet."),
new("rapid_login_ip", "Login-Burst pro IP", true, 3, 15, "medium", "Erstellt einen Hinweis, wenn mehrere neue Sessions von derselben IP entstehen."),
new("rapid_demo_login_ip", "Demo-Login-Burst pro IP", true, 3, 15, "medium", "Erstellt einen Hinweis, wenn mehrere Demo-Admin-Sessions von derselben IP entstehen."),
];
public static RiskRuleSetting[] Read(SiteSettings? settings)
{
var storedRules = Parse(settings?.RiskRulesJson);
return Defaults
.Select(defaultRule =>
{
var storedRule = storedRules.FirstOrDefault(item => string.Equals(item.Key, defaultRule.Key, StringComparison.OrdinalIgnoreCase));
return storedRule is null ? defaultRule : Normalize(storedRule, defaultRule);
})
.ToArray();
}
public static string Serialize(IEnumerable<RiskRuleSetting> rules) =>
JsonSerializer.Serialize(rules.Select(rule => Normalize(rule, Defaults.FirstOrDefault(item => item.Key == rule.Key) ?? rule)), JsonOptions);
public static RiskRuleSetting Find(IEnumerable<RiskRuleSetting> rules, string key) =>
rules.FirstOrDefault(item => string.Equals(item.Key, key, StringComparison.OrdinalIgnoreCase))
?? Defaults.First(item => item.Key == key);
private static RiskRuleSetting[] Parse(string? json)
{
if (string.IsNullOrWhiteSpace(json))
{
return [];
}
try
{
return JsonSerializer.Deserialize<RiskRuleSetting[]>(json, JsonOptions) ?? [];
}
catch (JsonException)
{
return [];
}
}
private static RiskRuleSetting Normalize(RiskRuleSetting rule, RiskRuleSetting fallback)
{
var severity = rule.Severity.Trim().ToLowerInvariant() switch
{
"high" => "high",
"medium" => "medium",
"low" => "low",
_ => fallback.Severity,
};
return rule with
{
Key = fallback.Key,
Label = string.IsNullOrWhiteSpace(rule.Label) ? fallback.Label : rule.Label.Trim(),
Threshold = Math.Clamp(rule.Threshold, 1, 500),
WindowMinutes = Math.Clamp(rule.WindowMinutes, 1, 1440),
Severity = severity,
Description = string.IsNullOrWhiteSpace(rule.Description) ? fallback.Description : rule.Description.Trim(),
};
}
}