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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,135 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Backend.Migrations
{
/// <inheritdoc />
public partial class AddVoteBallotSubmitterUniqueIndex : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_VoteBallots_SeasonId",
table: "VoteBallots");
migrationBuilder.AddColumn<bool>(
name: "TwitchAuthManagedByDatabase",
table: "SiteSettings",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<string>(
name: "TwitchClientId",
table: "SiteSettings",
type: "character varying(120)",
maxLength: 120,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "TwitchClientSecret",
table: "SiteSettings",
type: "character varying(180)",
maxLength: 180,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "TwitchRedirectUri",
table: "SiteSettings",
type: "character varying(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "TwitchScope",
table: "SiteSettings",
type: "character varying(300)",
maxLength: 300,
nullable: false,
defaultValue: "");
migrationBuilder.UpdateData(
table: "SiteSettings",
keyColumn: "Id",
keyValue: 1,
columns: new[] { "TwitchAuthManagedByDatabase", "TwitchClientId", "TwitchClientSecret", "TwitchRedirectUri", "TwitchScope" },
values: new object[] { false, "", "", "", "" });
migrationBuilder.Sql("""
DELETE FROM "VoteEntries"
WHERE "BallotId" IN (
SELECT "Id"
FROM (
SELECT
"Id",
ROW_NUMBER() OVER (
PARTITION BY "SeasonId", "SubmittedByTwitchId"
ORDER BY "SubmittedAt" DESC, "Id" DESC
) AS duplicate_rank
FROM "VoteBallots"
) ranked_ballots
WHERE duplicate_rank > 1
);
DELETE FROM "VoteBallots"
WHERE "Id" IN (
SELECT "Id"
FROM (
SELECT
"Id",
ROW_NUMBER() OVER (
PARTITION BY "SeasonId", "SubmittedByTwitchId"
ORDER BY "SubmittedAt" DESC, "Id" DESC
) AS duplicate_rank
FROM "VoteBallots"
) ranked_ballots
WHERE duplicate_rank > 1
);
""");
migrationBuilder.CreateIndex(
name: "IX_VoteBallots_SeasonId_SubmittedByTwitchId",
table: "VoteBallots",
columns: new[] { "SeasonId", "SubmittedByTwitchId" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_VoteBallots_SeasonId_SubmittedByTwitchId",
table: "VoteBallots");
migrationBuilder.DropColumn(
name: "TwitchAuthManagedByDatabase",
table: "SiteSettings");
migrationBuilder.DropColumn(
name: "TwitchClientId",
table: "SiteSettings");
migrationBuilder.DropColumn(
name: "TwitchClientSecret",
table: "SiteSettings");
migrationBuilder.DropColumn(
name: "TwitchRedirectUri",
table: "SiteSettings");
migrationBuilder.DropColumn(
name: "TwitchScope",
table: "SiteSettings");
migrationBuilder.CreateIndex(
name: "IX_VoteBallots_SeasonId",
table: "VoteBallots",
column: "SeasonId");
}
}
}
@@ -966,6 +966,29 @@ namespace Backend.Migrations
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<bool>("TwitchAuthManagedByDatabase")
.HasColumnType("boolean");
b.Property<string>("TwitchClientId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("TwitchClientSecret")
.IsRequired()
.HasMaxLength(180)
.HasColumnType("character varying(180)");
b.Property<string>("TwitchRedirectUri")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<string>("TwitchScope")
.IsRequired()
.HasMaxLength(300)
.HasColumnType("character varying(300)");
b.HasKey("Id");
b.ToTable("SiteSettings");
@@ -999,7 +1022,12 @@ namespace Backend.Migrations
RiskRulesJson = "[{\"key\":\"resubmitted_ballot\",\"label\":\"Ballot erneut gespeichert\",\"enabled\":true,\"threshold\":1,\"windowMinutes\":360,\"severity\":\"low\",\"description\":\"Erstellt einen Hinweis, wenn ein User sein Voting erneut speichert.\"},{\"key\":\"rapid_vote_updates\",\"label\":\"Voting-Burst\",\"enabled\":true,\"threshold\":3,\"windowMinutes\":10,\"severity\":\"high\",\"description\":\"Erstellt einen Hinweis, wenn ein User sehr schnell mehrere Voting-Aenderungen ausloest.\"},{\"key\":\"resubmitted_nomination\",\"label\":\"Nominierung erneut eingereicht\",\"enabled\":true,\"threshold\":1,\"windowMinutes\":360,\"severity\":\"low\",\"description\":\"Erstellt einen Hinweis, wenn ein User in derselben Kategorie erneut nominiert.\"},{\"key\":\"rapid_nomination_burst\",\"label\":\"Nominierungs-Burst\",\"enabled\":true,\"threshold\":10,\"windowMinutes\":10,\"severity\":\"high\",\"description\":\"Erstellt einen Hinweis, wenn ein User sehr viele Nominierungen in kurzer Zeit sendet.\"},{\"key\":\"duplicate_clip_submission\",\"label\":\"Doppelter Clip\",\"enabled\":true,\"threshold\":1,\"windowMinutes\":360,\"severity\":\"medium\",\"description\":\"Erstellt einen Hinweis, wenn ein User denselben Clip erneut einreicht.\"},{\"key\":\"rapid_clip_burst\",\"label\":\"Clip-Burst\",\"enabled\":true,\"threshold\":5,\"windowMinutes\":10,\"severity\":\"high\",\"description\":\"Erstellt einen Hinweis, wenn ein User sehr viele Clips in kurzer Zeit sendet.\"},{\"key\":\"rapid_login_ip\",\"label\":\"Login-Burst pro IP\",\"enabled\":true,\"threshold\":3,\"windowMinutes\":15,\"severity\":\"medium\",\"description\":\"Erstellt einen Hinweis, wenn mehrere neue Sessions von derselben IP entstehen.\"},{\"key\":\"rapid_demo_login_ip\",\"label\":\"Demo-Login-Burst pro IP\",\"enabled\":true,\"threshold\":3,\"windowMinutes\":15,\"severity\":\"medium\",\"description\":\"Erstellt einen Hinweis, wenn mehrere Demo-Admin-Sessions von derselben IP entstehen.\"}]",
SocialLinksJson = "[{\"label\":\"Twitch\",\"platform\":\"twitch\",\"url\":\"https://twitch.tv/jayuhime\",\"icon\":\"twitch\",\"showOnHost\":true,\"showOnCommunity\":true},{\"label\":\"YouTube\",\"platform\":\"youtube\",\"url\":\"https://youtube.com/c/Jayuhime\",\"icon\":\"youtube\",\"showOnHost\":true,\"showOnCommunity\":true},{\"label\":\"X\",\"platform\":\"x\",\"url\":\"https://x.com/jayuhime\",\"icon\":\"x\",\"showOnHost\":true,\"showOnCommunity\":true},{\"label\":\"Instagram\",\"platform\":\"instagram\",\"url\":\"https://instagram.com/jayuhime\",\"icon\":\"instagram\",\"showOnHost\":true,\"showOnCommunity\":true},{\"label\":\"Discord\",\"platform\":\"discord\",\"url\":\"https://discord.gg/jayuhime\",\"icon\":\"discord\",\"showOnHost\":true,\"showOnCommunity\":true}]",
SponsorsContent = "Sponsoren & Partner\nHier koennen Sponsor:innen, Medienpartner, Community-Partner und Supporter des VTuber Star Awards vorgestellt werden.\n\nPartner werden im Rahmen der Show und auf den oeffentlichen Kontaktflaechen genannt, sobald sie final bestaetigt sind.",
SponsorsUrl = "https://vtuber-star-awards.de/partner"
SponsorsUrl = "https://vtuber-star-awards.de/partner",
TwitchAuthManagedByDatabase = false,
TwitchClientId = "",
TwitchClientSecret = "",
TwitchRedirectUri = "",
TwitchScope = ""
});
});
@@ -1011,14 +1039,6 @@ namespace Backend.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("CreatedByTwitchId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("BoundTwitchDisplayName")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
@@ -1027,6 +1047,14 @@ namespace Backend.Migrations
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("CreatedByTwitchId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(120)
@@ -1076,10 +1104,10 @@ namespace Backend.Migrations
b.HasKey("Id");
b.HasIndex("Login")
b.HasIndex("BoundTwitchUserId")
.IsUnique();
b.HasIndex("BoundTwitchUserId")
b.HasIndex("Login")
.IsUnique();
b.ToTable("TeamMembers");
@@ -1197,7 +1225,8 @@ namespace Backend.Migrations
b.HasKey("Id");
b.HasIndex("SeasonId");
b.HasIndex("SeasonId", "SubmittedByTwitchId")
.IsUnique();
b.ToTable("VoteBallots");