//
using System;
using Backend.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Backend.Migrations
{
[DbContext(typeof(AwardsDbContext))]
[Migration("20260628205353_AddSeasonWorkflowRulesJson")]
partial class AddSeasonWorkflowRulesJson
{
///
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Backend.Domain.AdminAuditEntry", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("ActionType")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("AdminTwitchUserId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("CreatedFromIp")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("EntityId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("EntityType")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("MetadataJson")
.IsRequired()
.HasColumnType("text");
b.Property("Summary")
.IsRequired()
.HasMaxLength(240)
.HasColumnType("character varying(240)");
b.Property("UserAgent")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.HasKey("Id");
b.ToTable("AdminAuditEntries");
});
modelBuilder.Entity("Backend.Domain.AwardResult", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("CandidateId")
.HasColumnType("integer");
b.Property("CategoryId")
.HasColumnType("integer");
b.Property("CategoryName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SeasonId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("CandidateId");
b.HasIndex("CategoryId");
b.HasIndex("SeasonId", "CategoryId")
.IsUnique();
b.ToTable("Results");
b.HasData(
new
{
Id = 1,
CandidateId = 8,
CategoryId = 5,
CategoryName = "VTuber des Jahres",
SeasonId = 2
},
new
{
Id = 2,
CandidateId = 9,
CategoryId = 6,
CategoryName = "Bestes Live Event",
SeasonId = 2
},
new
{
Id = 3,
CandidateId = 10,
CategoryId = 7,
CategoryName = "Clip des Jahres",
SeasonId = 2
},
new
{
Id = 4,
CandidateId = 11,
CategoryId = 8,
CategoryName = "VTuber des Jahres",
SeasonId = 3
},
new
{
Id = 5,
CandidateId = 12,
CategoryId = 9,
CategoryName = "Clip des Jahres",
SeasonId = 3
},
new
{
Id = 6,
CandidateId = 13,
CategoryId = 10,
CategoryName = "VTuber des Jahres",
SeasonId = 4
});
});
modelBuilder.Entity("Backend.Domain.Candidate", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("AcceptanceNote")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("AcceptanceStatus")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(30)
.HasColumnType("character varying(30)")
.HasDefaultValue("open");
b.Property("CategoryId")
.HasColumnType("integer");
b.Property("ChannelSlug")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("ClipCompilationPlatform")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("ClipCompilationTitle")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property("ClipCompilationUrl")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("ClipEmbedStatus")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(30)
.HasColumnType("character varying(30)")
.HasDefaultValue("unchecked");
b.Property("DisplayName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("NominationTally")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(0);
b.Property("Platform")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("StreamerIdentityId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("SeasonId");
b.HasIndex("StreamerIdentityId");
b.ToTable("Candidates");
b.HasData(
new
{
Id = 1,
AcceptanceStatus = "open",
CategoryId = 1,
ChannelSlug = "@hoshimimiyu",
ClipEmbedStatus = "unchecked",
DisplayName = "Hoshimi Miyu",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 1
},
new
{
Id = 2,
AcceptanceStatus = "open",
CategoryId = 1,
ChannelSlug = "@kurainu",
ClipEmbedStatus = "unchecked",
DisplayName = "Kurainu",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 1
},
new
{
Id = 3,
AcceptanceStatus = "open",
CategoryId = 1,
ChannelSlug = "@shiroch",
ClipEmbedStatus = "unchecked",
DisplayName = "Shiro Ch.",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 1
},
new
{
Id = 4,
AcceptanceStatus = "open",
CategoryId = 2,
ChannelSlug = "@kurainu",
ClipEmbedStatus = "unchecked",
DisplayName = "Kurainu 3D Live",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 1
},
new
{
Id = 5,
AcceptanceStatus = "open",
CategoryId = 2,
ChannelSlug = "@aoisakura",
ClipEmbedStatus = "unchecked",
DisplayName = "Aoi Sakura Showcase",
NominationTally = 0,
Platform = "YouTube",
SeasonId = 1
},
new
{
Id = 6,
AcceptanceStatus = "open",
CategoryId = 3,
ChannelSlug = "@pyonkichikingdom",
ClipEmbedStatus = "unchecked",
DisplayName = "Pyonkichi Kingdom",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 1
},
new
{
Id = 7,
AcceptanceStatus = "open",
CategoryId = 4,
ChannelSlug = "@moonrelay",
ClipEmbedStatus = "unchecked",
DisplayName = "Moonrelay",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 1
},
new
{
Id = 8,
AcceptanceStatus = "open",
CategoryId = 5,
ChannelSlug = "@hoshimimiyu",
ClipEmbedStatus = "unchecked",
DisplayName = "Hoshimi Miyu",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 2
},
new
{
Id = 9,
AcceptanceStatus = "open",
CategoryId = 6,
ChannelSlug = "@kurainu",
ClipEmbedStatus = "unchecked",
DisplayName = "Kurainu 3D Live",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 2
},
new
{
Id = 10,
AcceptanceStatus = "open",
CategoryId = 7,
ChannelSlug = "@pyonkichikingdom",
ClipEmbedStatus = "unchecked",
DisplayName = "Pyonkichi Kingdom",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 2
},
new
{
Id = 11,
AcceptanceStatus = "open",
CategoryId = 8,
ChannelSlug = "@aoisakura",
ClipEmbedStatus = "unchecked",
DisplayName = "Aoi Sakura",
NominationTally = 0,
Platform = "YouTube",
SeasonId = 3
},
new
{
Id = 12,
AcceptanceStatus = "open",
CategoryId = 9,
ChannelSlug = "@starbyte",
ClipEmbedStatus = "unchecked",
DisplayName = "Starbyte",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 3
},
new
{
Id = 13,
AcceptanceStatus = "open",
CategoryId = 10,
ChannelSlug = "@tenshivox",
ClipEmbedStatus = "unchecked",
DisplayName = "Tenshi Vox",
NominationTally = 0,
Platform = "Twitch",
SeasonId = 4
});
});
modelBuilder.Entity("Backend.Domain.Category", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("Description")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("GroupName")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("MaxNomineesPerUser")
.HasColumnType("integer");
b.Property("Name")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("Slug")
.IsRequired()
.HasColumnType("text");
b.Property("SortOrder")
.HasColumnType("integer");
b.Property("ViewerRangeMax")
.HasColumnType("integer");
b.Property("ViewerRangeMin")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("SeasonId", "Slug")
.IsUnique();
b.ToTable("Categories");
b.HasData(
new
{
Id = 1,
Description = "Die größte Auszeichnung des Jahres.",
GroupName = "Main Awards",
MaxNomineesPerUser = 3,
Name = "VTuber des Jahres",
SeasonId = 1,
Slug = "vtuber-des-jahres",
SortOrder = 1
},
new
{
Id = 2,
Description = "Events, Konzerte und 3D-Shows.",
GroupName = "Performance",
MaxNomineesPerUser = 3,
Name = "Bestes Live Event",
SeasonId = 1,
Slug = "bestes-live-event",
SortOrder = 2
},
new
{
Id = 3,
Description = "Der lustigste oder emotionalste Clip des Jahres.",
GroupName = "Clips & Highlights",
MaxNomineesPerUser = 3,
Name = "Clip des Jahres",
SeasonId = 1,
Slug = "clip-des-jahres",
SortOrder = 3
},
new
{
Id = 4,
Description = "Die aktivste und freundlichste Community.",
GroupName = "Main Awards",
MaxNomineesPerUser = 3,
Name = "Beste Community",
SeasonId = 1,
Slug = "beste-community",
SortOrder = 4
},
new
{
Id = 5,
Description = "Archivkategorie 2025.",
GroupName = "Main Awards",
MaxNomineesPerUser = 3,
Name = "VTuber des Jahres",
SeasonId = 2,
Slug = "vtuber-des-jahres",
SortOrder = 1
},
new
{
Id = 6,
Description = "Archivkategorie 2025.",
GroupName = "Performance",
MaxNomineesPerUser = 3,
Name = "Bestes Live Event",
SeasonId = 2,
Slug = "bestes-live-event",
SortOrder = 2
},
new
{
Id = 7,
Description = "Archivkategorie 2025.",
GroupName = "Clips & Highlights",
MaxNomineesPerUser = 3,
Name = "Clip des Jahres",
SeasonId = 2,
Slug = "clip-des-jahres",
SortOrder = 3
},
new
{
Id = 8,
Description = "Archivkategorie 2024.",
GroupName = "Main Awards",
MaxNomineesPerUser = 3,
Name = "VTuber des Jahres",
SeasonId = 3,
Slug = "vtuber-des-jahres",
SortOrder = 1
},
new
{
Id = 9,
Description = "Archivkategorie 2024.",
GroupName = "Clips & Highlights",
MaxNomineesPerUser = 3,
Name = "Clip des Jahres",
SeasonId = 3,
Slug = "clip-des-jahres",
SortOrder = 2
},
new
{
Id = 10,
Description = "Archivkategorie 2023.",
GroupName = "Main Awards",
MaxNomineesPerUser = 3,
Name = "VTuber des Jahres",
SeasonId = 4,
Slug = "vtuber-des-jahres",
SortOrder = 1
});
});
modelBuilder.Entity("Backend.Domain.ClipSubmission", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("CandidateId")
.HasColumnType("integer");
b.Property("CategoryId")
.HasColumnType("integer");
b.Property("ClipUrl")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("CreatedFromIp")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("Creator")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("Platform")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("ReviewNote")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("ReviewedAt")
.HasColumnType("timestamp with time zone");
b.Property("ReviewedByTwitchId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property("SubmittedByTwitchId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.HasKey("Id");
b.HasIndex("CandidateId");
b.HasIndex("SeasonId", "Status");
b.ToTable("ClipSubmissions");
});
modelBuilder.Entity("Backend.Domain.Nomination", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("AvgViewers")
.HasColumnType("integer");
b.Property("CandidateId")
.HasColumnType("integer");
b.Property("CandidateText")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("CategoryGroupName")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(80)
.HasColumnType("character varying(80)")
.HasDefaultValue("");
b.Property("CategoryId")
.HasColumnType("integer");
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("FollowersGained")
.HasColumnType("integer");
b.Property("HoursStreamed")
.HasColumnType("integer");
b.Property("HoursWatched")
.HasColumnType("integer");
b.Property("PeakViewers")
.HasColumnType("integer");
b.Property("ResolvedChannel")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("ResolvedPlatform")
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("ReviewNote")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("ReviewedAt")
.HasColumnType("timestamp with time zone");
b.Property("ReviewedByTwitchId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property("StreamUrl")
.HasMaxLength(300)
.HasColumnType("character varying(300)");
b.Property("StreamerIdentityId")
.HasColumnType("integer");
b.Property("SubmittedByTwitchId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SuggestedCategoryId")
.HasColumnType("integer");
b.Property("TrackerCheckedAt")
.HasColumnType("timestamp with time zone");
b.Property("TrackerStatus")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasDefaultValue("pending");
b.Property("TrackingFlagsJson")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValue("[]");
b.Property("TrackingReviewNote")
.HasMaxLength(1000)
.HasColumnType("character varying(1000)");
b.Property("TrackingReviewStatus")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(30)
.HasColumnType("character varying(30)")
.HasDefaultValue("clear");
b.Property("TrackingReviewedAt")
.HasColumnType("timestamp with time zone");
b.Property("TrackingReviewedByTwitchId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.HasKey("Id");
b.HasIndex("CandidateId");
b.HasIndex("CategoryId");
b.HasIndex("StreamerIdentityId");
b.HasIndex("SuggestedCategoryId");
b.HasIndex("SeasonId", "Status");
b.HasIndex("SeasonId", "CategoryGroupName", "Status");
b.HasIndex("SeasonId", "StreamerIdentityId", "CategoryGroupName");
b.ToTable("Nominations");
b.HasData(
new
{
Id = 1,
CandidateText = "Hoshimi Miyu",
CategoryGroupName = "",
CategoryId = 1,
CreatedAt = new DateTimeOffset(new DateTime(2026, 6, 10, 13, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
SeasonId = 1,
Status = "pending",
SubmittedByTwitchId = "twitch_hoshi",
TrackerStatus = "pending",
TrackingFlagsJson = "[]",
TrackingReviewStatus = "clear"
},
new
{
Id = 2,
CandidateText = "Kurainu 3D Live",
CategoryGroupName = "",
CategoryId = 2,
CreatedAt = new DateTimeOffset(new DateTime(2026, 6, 10, 14, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
SeasonId = 1,
Status = "pending",
SubmittedByTwitchId = "twitch_kurainu",
TrackerStatus = "pending",
TrackingFlagsJson = "[]",
TrackingReviewStatus = "clear"
});
});
modelBuilder.Entity("Backend.Domain.RiskFlag", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("CreatedFromIp")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("MetadataJson")
.IsRequired()
.HasColumnType("text");
b.Property("ReviewNote")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("ReviewedAt")
.HasColumnType("timestamp with time zone");
b.Property("ReviewedByTwitchId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("Severity")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property("Source")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property("Summary")
.IsRequired()
.HasMaxLength(240)
.HasColumnType("character varying(240)");
b.Property("TwitchUserId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("Type")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("UserAgent")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.HasKey("Id");
b.HasIndex("SeasonId");
b.ToTable("RiskFlags");
});
modelBuilder.Entity("Backend.Domain.Season", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("CurrentPhase")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("character varying(60)");
b.Property("IsCommunityOnly")
.HasColumnType("boolean");
b.Property("IsCurrent")
.HasColumnType("boolean");
b.Property("Name")
.IsRequired()
.HasMaxLength(160)
.HasColumnType("character varying(160)");
b.Property("NominationEndsAt")
.HasColumnType("date");
b.Property("NominationStartsAt")
.HasColumnType("date");
b.Property("ReviewEndsAt")
.HasColumnType("date");
b.Property("ReviewStartsAt")
.HasColumnType("date");
b.Property("ShowDate")
.HasColumnType("date");
b.Property("ShowStartsAt")
.HasColumnType("time without time zone");
b.Property("ShowStreamUrl")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("SubcategoryTemplatesJson")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValue("[]");
b.Property("VotingEndsAt")
.HasColumnType("date");
b.Property("VotingStartsAt")
.HasColumnType("date");
b.Property("WorkflowRulesJson")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValue("[]");
b.Property("Year")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Year")
.IsUnique();
b.ToTable("Seasons");
b.HasData(
new
{
Id = 1,
CurrentPhase = "Community Voting",
IsCommunityOnly = true,
IsCurrent = true,
Name = "VTuber Star Awards 2026",
NominationEndsAt = new DateOnly(2026, 5, 31),
NominationStartsAt = new DateOnly(2026, 5, 1),
ReviewEndsAt = new DateOnly(2026, 7, 10),
ReviewStartsAt = new DateOnly(2026, 7, 1),
ShowDate = new DateOnly(2026, 7, 20),
ShowStartsAt = new TimeOnly(20, 0, 0),
ShowStreamUrl = "https://twitch.tv/jayuhime",
SubcategoryTemplatesJson = "[]",
VotingEndsAt = new DateOnly(2026, 6, 30),
VotingStartsAt = new DateOnly(2026, 6, 1),
WorkflowRulesJson = "[]",
Year = 2026
},
new
{
Id = 2,
CurrentPhase = "Archived",
IsCommunityOnly = true,
IsCurrent = false,
Name = "VTuber Star Awards 2025",
NominationEndsAt = new DateOnly(2025, 5, 31),
NominationStartsAt = new DateOnly(2025, 5, 1),
ReviewEndsAt = new DateOnly(2025, 7, 10),
ReviewStartsAt = new DateOnly(2025, 7, 1),
ShowDate = new DateOnly(2025, 7, 20),
ShowStartsAt = new TimeOnly(20, 0, 0),
ShowStreamUrl = "https://twitch.tv/jayuhime",
SubcategoryTemplatesJson = "[]",
VotingEndsAt = new DateOnly(2025, 6, 30),
VotingStartsAt = new DateOnly(2025, 6, 1),
WorkflowRulesJson = "[]",
Year = 2025
},
new
{
Id = 3,
CurrentPhase = "Archived",
IsCommunityOnly = true,
IsCurrent = false,
Name = "VTuber Star Awards 2024",
NominationEndsAt = new DateOnly(2024, 5, 31),
NominationStartsAt = new DateOnly(2024, 5, 1),
ReviewEndsAt = new DateOnly(2024, 7, 10),
ReviewStartsAt = new DateOnly(2024, 7, 1),
ShowDate = new DateOnly(2024, 7, 20),
ShowStartsAt = new TimeOnly(20, 0, 0),
ShowStreamUrl = "https://youtube.com/c/Jayuhime",
SubcategoryTemplatesJson = "[]",
VotingEndsAt = new DateOnly(2024, 6, 30),
VotingStartsAt = new DateOnly(2024, 6, 1),
WorkflowRulesJson = "[]",
Year = 2024
},
new
{
Id = 4,
CurrentPhase = "Archived",
IsCommunityOnly = true,
IsCurrent = false,
Name = "VTuber Star Awards 2023",
NominationEndsAt = new DateOnly(2023, 5, 31),
NominationStartsAt = new DateOnly(2023, 5, 1),
ReviewEndsAt = new DateOnly(2023, 7, 10),
ReviewStartsAt = new DateOnly(2023, 7, 1),
ShowDate = new DateOnly(2023, 7, 20),
ShowStartsAt = new TimeOnly(20, 0, 0),
ShowStreamUrl = "https://twitch.tv/jayuhime",
SubcategoryTemplatesJson = "[]",
VotingEndsAt = new DateOnly(2023, 6, 30),
VotingStartsAt = new DateOnly(2023, 6, 1),
WorkflowRulesJson = "[]",
Year = 2023
});
});
modelBuilder.Entity("Backend.Domain.ShowactApplication", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("ArtistName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("ContactDiscord")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("ContactEmail")
.IsRequired()
.HasMaxLength(180)
.HasColumnType("character varying(180)");
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("CreatedFromIp")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("Description")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("character varying(1000)");
b.Property("FieldResponsesJson")
.IsRequired()
.HasColumnType("text");
b.Property("PerformanceType")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("PlatformUrl")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("ReferenceUrl")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("ReviewNote")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("ReviewedAt")
.HasColumnType("timestamp with time zone");
b.Property("ReviewedByTwitchId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property("TechnicalNotes")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("character varying(1000)");
b.Property("UserAgent")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.HasKey("Id");
b.HasIndex("SeasonId", "Status");
b.ToTable("ShowactApplications");
});
modelBuilder.Entity("Backend.Domain.SiteSettings", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("ClipAdminMenuVisible")
.HasColumnType("boolean");
b.Property("ClipReviewEnabled")
.ValueGeneratedOnAdd()
.HasColumnType("boolean")
.HasDefaultValue(true);
b.Property("ClipSubmissionDisabledMessage")
.IsRequired()
.HasMaxLength(240)
.HasColumnType("character varying(240)");
b.Property("ClipSubmissionsEnabled")
.ValueGeneratedOnAdd()
.HasColumnType("boolean")
.HasDefaultValue(false);
b.Property("ContactContent")
.IsRequired()
.HasColumnType("text");
b.Property("ContactUrl")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("DemoLoginDisplayName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("DemoLoginEmail")
.IsRequired()
.HasMaxLength(180)
.HasColumnType("character varying(180)");
b.Property("DemoLoginEnabled")
.HasColumnType("boolean");
b.Property("DemoLoginManagedByDatabase")
.HasColumnType("boolean");
b.Property("DemoLoginPasswordHash")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("DemoLoginPasswordSalt")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("DemoLoginTwitchUserId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("FaqJson")
.IsRequired()
.HasColumnType("text");
b.Property("HostDisplayName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("HostTagline")
.IsRequired()
.HasMaxLength(160)
.HasColumnType("character varying(160)");
b.Property("ImprintContent")
.IsRequired()
.HasColumnType("text");
b.Property("ImprintUrl")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("MaintenanceMessage")
.IsRequired()
.HasMaxLength(600)
.HasColumnType("character varying(600)");
b.Property("MaintenanceModeEnabled")
.HasColumnType("boolean");
b.Property("MaintenanceTitle")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("NewsletterUrl")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("NominationLinkBlacklistJson")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValue("[]");
b.Property("PrivacyEmail")
.IsRequired()
.HasMaxLength(160)
.HasColumnType("character varying(160)");
b.Property("PrivacyPolicyContent")
.IsRequired()
.HasColumnType("text");
b.Property("PrivacyPolicyUpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property("PrivacyPolicyUpdatedBy")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("RiskRulesJson")
.IsRequired()
.HasColumnType("text");
b.Property("SessionIdleTimeoutHours")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(3);
b.Property("ShareDiscordUrl")
.IsRequired()
.HasColumnType("text");
b.Property("ShareXUrl")
.IsRequired()
.HasColumnType("text");
b.Property("ShowactApplicationDisabledMessage")
.IsRequired()
.HasMaxLength(240)
.HasColumnType("character varying(240)");
b.Property("ShowactApplicationEndsAt")
.HasColumnType("date");
b.Property("ShowactApplicationStartsAt")
.HasColumnType("date");
b.Property("ShowactApplicationsEnabled")
.ValueGeneratedOnAdd()
.HasColumnType("boolean")
.HasDefaultValue(false);
b.Property("ShowactFormSchemaJson")
.IsRequired()
.HasColumnType("text");
b.Property("ShowactsContent")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValue("");
b.Property("ShowactsUrl")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("SocialLinksJson")
.IsRequired()
.HasColumnType("text");
b.Property("SponsorsContent")
.IsRequired()
.HasColumnType("text");
b.Property("SponsorsUrl")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("SponsorsVisible")
.ValueGeneratedOnAdd()
.HasColumnType("boolean")
.HasDefaultValue(true);
b.Property("TrackingReviewNotes")
.IsRequired()
.HasColumnType("text");
b.Property("TrackingRulesJson")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValue("[]");
b.Property("TwitchAuthManagedByDatabase")
.HasColumnType("boolean");
b.Property("TwitchClientId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("TwitchClientSecret")
.IsRequired()
.HasMaxLength(180)
.HasColumnType("character varying(180)");
b.Property("TwitchRedirectUri")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("TwitchScope")
.IsRequired()
.HasMaxLength(300)
.HasColumnType("character varying(300)");
b.Property("ViewerStatsProviderBaseUrl")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property("WorkflowRulesJson")
.IsRequired()
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValue("[]");
b.HasKey("Id");
b.ToTable("SiteSettings");
b.HasData(
new
{
Id = 1,
ClipAdminMenuVisible = true,
ClipReviewEnabled = true,
ClipSubmissionDisabledMessage = "Clip-Einreichungen sind aktuell geschlossen.",
ClipSubmissionsEnabled = false,
ContactContent = "Kontakt zum Award-Team\nDu hast Fragen zur Nominierung, zum Voting, zu Clips oder zur Show? Schreib dem Team über die hinterlegte Kontaktseite.\n\nDatenschutzfragen\nFür Datenschutzanfragen nutze bitte die Datenschutz-E-Mail aus diesem Footer.\n\nCommunity & Kooperationen\nSocial Links und Partneranfragen werden vom Admin-Team gepflegt und koordiniert.",
ContactUrl = "https://vtuber-star-awards.de/kontakt",
DemoLoginDisplayName = "Jayuhime Admin",
DemoLoginEmail = "",
DemoLoginEnabled = false,
DemoLoginManagedByDatabase = false,
DemoLoginPasswordHash = "",
DemoLoginPasswordSalt = "",
DemoLoginTwitchUserId = "jayuhime_admin",
FaqJson = "[{\"question\":\"Wer darf nominiert werden?\",\"answer\":\"Jede:r aktive deutschsprachige VTuber kann nominiert werden \\u2014 unabh\\u00E4ngig von Follower-Zahl oder Plattform. Die Community schl\\u00E4gt in der Nominierungsphase ihre Favorit:innen vor.\"},{\"question\":\"Wie funktioniert das Voting?\",\"answer\":\"Du meldest dich ausschlie\\u00DFlich mit deinem Twitch-Account an \\u2014 nur so kannst du teilnehmen. Das h\\u00E4lt das Voting fair und bot-frei. Danach vergibst du pro Kategorie eine Stimme, \\u00E4nderbar bis zum Ende der Phase.\"},{\"question\":\"Was kostet die Teilnahme?\",\"answer\":\"Gar nichts. Nominieren, voten und die Show schauen ist komplett kostenlos \\u2014 der VTuber Star Award ist ein Community-Event von Fans f\\u00FCr Fans.\"},{\"question\":\"Wann und wo findet die Award-Show statt?\",\"answer\":\"Die gro\\u00DFe Live-Show wird von Jayuhime gehostet und auf Twitch \\u0026 YouTube gestreamt. Den genauen Termin findest du im Countdown oben \\u2014 sei live dabei, wenn die Stars gek\\u00FCrt werden!\"},{\"question\":\"Ich wurde nominiert \\u2014 was nun?\",\"answer\":\"Gl\\u00FCckwunsch! Du erh\\u00E4ltst eine Benachrichtigung mit allen Infos. Teile deine Nominierung gern mit deiner Community und ruf zum Voten auf \\u2014 jede Stimme z\\u00E4hlt.\"}]",
HostDisplayName = "Jayuhime",
HostTagline = "VTuber & Award Host",
ImprintContent = "Anbieter\nVTuber Star Awards, vertreten durch Jayuhime.\n\nKontakt\nNutze für organisatorische Fragen bitte die Kontaktseite oder die hinterlegte Kontaktadresse.\n\nHinweis\nDieses Impressum ist ein redaktioneller Platzhalter und sollte vor dem Livegang mit den tatsächlichen Anbieterangaben ersetzt werden.",
ImprintUrl = "https://vtuber-star-awards.de/impressum",
MaintenanceMessage = "Die Award-Galaxie wird gerade liebevoll poliert. Schau gleich wieder vorbei.",
MaintenanceModeEnabled = false,
MaintenanceTitle = "Sternenpause",
NewsletterUrl = "https://vtuber-star-awards.de/newsletter",
NominationLinkBlacklistJson = "[{\"Url\":\"https://kick.com/\"},{\"Url\":\"https://www.twitch.tv/\"},{\"Url\":\"https://www.youtube.com/\"}]",
PrivacyEmail = "datenschutz@vtuber-star-awards.de",
PrivacyPolicyContent = "Verantwortliche:r\nVTuber Star Awards, vertreten durch Jayuhime. Kontakt: datenschutz@vtuber-star-awards.de\n\nWelche Daten wir verarbeiten\nBei Teilnahme (Voting, Nominierung, Clip-Einreichung) verarbeiten wir ausschließlich deine Twitch-User-ID sowie den Zeitstempel deiner Aktion.\nFür Show-Erinnerungen speichern wir optional deine E-Mail-Adresse.\n\nRechtsgrundlage\nVerarbeitung auf Basis von Art. 6 Abs. 1 lit. b DSGVO. Für optionale Erinnerungen gilt Art. 6 Abs. 1 lit. a DSGVO.\n\nZweck der Verarbeitung\nDurchführung des VTuber Star Awards, Sicherstellung fairer Abstimmung, Spam-Prävention sowie Admin-Review von Einreichungen.\n\nLöschfristen\nAlle Teilnahmedaten werden spätestens 6 Monate nach der Award-Show automatisch gelöscht. E-Mail-Adressen werden nach der Show entfernt.\n\nDeine Rechte\nDu hast das Recht auf Auskunft, Löschung, Berichtigung, Widerspruch und Beschwerde bei der zuständigen Aufsichtsbehörde.\n\nWeitergabe an Dritte\nKeine Weitergabe zu Werbezwecken. Technische Dienstleister verarbeiten Daten ausschließlich im Rahmen der Auftragsverarbeitung.",
PrivacyPolicyUpdatedAt = new DateTimeOffset(new DateTime(2026, 6, 23, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
PrivacyPolicyUpdatedBy = "seed",
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.\"}]",
SessionIdleTimeoutHours = 3,
ShareDiscordUrl = "",
ShareXUrl = "",
ShowactApplicationDisabledMessage = "Showact-Bewerbungen sind aktuell geschlossen.",
ShowactApplicationsEnabled = false,
ShowactFormSchemaJson = "[]",
ShowactsContent = "Informationen zu Showact-Bewerbungen, Ablauf und Kontakt für die Award-Show.",
ShowactsUrl = "https://vtuber-star-awards.de/showacts",
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 können Sponsor:innen, Medienpartner, Community-Partner und Supporter des VTuber Star Awards vorgestellt werden.\n\nPartner werden im Rahmen der Show und auf den öffentlichen Kontaktflächen genannt, sobald sie final bestätigt sind.",
SponsorsUrl = "https://vtuber-star-awards.de/partner",
SponsorsVisible = true,
TrackingReviewNotes = "Fallback-Quellen für manuelle Reviews:\\n- SullyGnome\\n- Twitch-Kanal direkt\\n\\nNutze diese Notizen für Edge Cases und manuelle Tier-Entscheidungen.",
TrackingRulesJson = "{\"source\":{\"providerKey\":\"twitchtracker\",\"baseUrl\":\"https://twitchtracker.com/api\",\"notesSummary\":\"TwitchTracker Basic API liefert aktuell Channel-Summary-Daten fuer 30 Tage. Andere Zeitfenster bleiben konfigurierbar, werden aber als manueller Review-Fall markiert.\",\"showManualReviewNotesInReview\":true},\"importantMetrics\":[{\"key\":\"avg_viewers\",\"label\":\"Avg Viewer\",\"enabled\":true,\"sourceSupport\":\"auto\",\"description\":\"Durchschnittliche Viewer fuer den gewaehlten Zeitraum.\",\"requiredForAutoClassification\":true,\"showInReview\":true,\"showInAdminSummary\":true,\"manualOverrideAllowed\":true,\"windowKey\":\"90d\",\"autoSupportedWindowKeys\":[\"30d\"],\"providerFieldKey\":\"avg_viewers\",\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null},{\"key\":\"tracker_status\",\"label\":\"Tracker-Status\",\"enabled\":true,\"sourceSupport\":\"auto\",\"description\":\"Zeigt, ob der TwitchTracker-Lookup sauber aufgeloest werden konnte.\",\"requiredForAutoClassification\":true,\"showInReview\":true,\"showInAdminSummary\":true,\"manualOverrideAllowed\":false,\"windowKey\":\"30d\",\"autoSupportedWindowKeys\":[\"30d\"],\"providerFieldKey\":\"tracker_status\",\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null},{\"key\":\"tracker_checked_at\",\"label\":\"Letzter Tracker-Check\",\"enabled\":true,\"sourceSupport\":\"auto\",\"description\":\"Zeitpunkt der letzten automatischen Datenaufloesung.\",\"requiredForAutoClassification\":true,\"showInReview\":true,\"showInAdminSummary\":false,\"manualOverrideAllowed\":false,\"windowKey\":\"30d\",\"autoSupportedWindowKeys\":[\"30d\"],\"providerFieldKey\":\"tracker_checked_at\",\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null}],\"optionalMetrics\":[{\"key\":\"hours_streamed\",\"label\":\"Hours Streamed\",\"enabled\":true,\"sourceSupport\":\"auto\",\"description\":\"Gesamte Streamstunden im gewaehlten Zeitraum.\",\"requiredForAutoClassification\":false,\"showInReview\":true,\"showInAdminSummary\":true,\"manualOverrideAllowed\":true,\"windowKey\":\"30d\",\"autoSupportedWindowKeys\":[\"30d\"],\"providerFieldKey\":\"hours_streamed\",\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null},{\"key\":\"hours_watched\",\"label\":\"Hours Watched\",\"enabled\":true,\"sourceSupport\":\"auto\",\"description\":\"Gesamte Watch Time fuer den gewaehlten Zeitraum.\",\"requiredForAutoClassification\":false,\"showInReview\":true,\"showInAdminSummary\":true,\"manualOverrideAllowed\":true,\"windowKey\":\"30d\",\"autoSupportedWindowKeys\":[\"30d\"],\"providerFieldKey\":\"hours_watched\",\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null},{\"key\":\"peak_viewers\",\"label\":\"Peak Viewer\",\"enabled\":true,\"sourceSupport\":\"auto\",\"description\":\"Hoechster gleichzeitiger Zuschauerwert im Zeitraum.\",\"requiredForAutoClassification\":false,\"showInReview\":true,\"showInAdminSummary\":true,\"manualOverrideAllowed\":true,\"windowKey\":\"30d\",\"autoSupportedWindowKeys\":[\"30d\"],\"providerFieldKey\":\"peak_viewers\",\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null},{\"key\":\"followers_gained\",\"label\":\"Follower Growth\",\"enabled\":true,\"sourceSupport\":\"auto\",\"description\":\"Follower-Zuwachs im gewaehlten Zeitraum.\",\"requiredForAutoClassification\":false,\"showInReview\":true,\"showInAdminSummary\":true,\"manualOverrideAllowed\":true,\"windowKey\":\"30d\",\"autoSupportedWindowKeys\":[\"30d\"],\"providerFieldKey\":\"followers_gained\",\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null},{\"key\":\"category_fit\",\"label\":\"Category Fit\",\"enabled\":false,\"sourceSupport\":\"context_only\",\"description\":\"Admin-Einschaetzung, ob die Person inhaltlich zur Unterkategorie passt.\",\"requiredForAutoClassification\":false,\"showInReview\":true,\"showInAdminSummary\":false,\"manualOverrideAllowed\":true,\"windowKey\":\"90d\",\"autoSupportedWindowKeys\":[],\"providerFieldKey\":null,\"topCount\":null,\"minPrimaryCategorySharePercent\":null,\"minPrimaryCategoryHours\":null,\"maxDistinctCategoriesBeforeFlag\":null,\"ignoredCategories\":[],\"matchAwardCategoryAgainstTopCategories\":false,\"flagIfAwardCategoryNotInTopX\":false,\"flagIfCategorySpreadTooWide\":false,\"flagIfNoCategoryContextAvailable\":false,\"minValue\":null,\"maxValue\":null},{\"key\":\"top_categories_context\",\"label\":\"Top Categories Context\",\"enabled\":false,\"sourceSupport\":\"context_only\",\"description\":\"Manueller Kontext aus zuletzt meistgestreamten Kategorien oder Games des Channels.\",\"requiredForAutoClassification\":false,\"showInReview\":true,\"showInAdminSummary\":true,\"manualOverrideAllowed\":true,\"windowKey\":\"90d\",\"autoSupportedWindowKeys\":[],\"providerFieldKey\":null,\"topCount\":5,\"minPrimaryCategorySharePercent\":60,\"minPrimaryCategoryHours\":20,\"maxDistinctCategoriesBeforeFlag\":6,\"ignoredCategories\":[\"Just Chatting\",\"Special Events\"],\"matchAwardCategoryAgainstTopCategories\":true,\"flagIfAwardCategoryNotInTopX\":true,\"flagIfCategorySpreadTooWide\":true,\"flagIfNoCategoryContextAvailable\":true,\"minValue\":null,\"maxValue\":null}],\"flags\":[{\"key\":\"tracker_unresolved\",\"label\":\"Tracker-Link nicht aufloesbar\",\"enabled\":true,\"severity\":\"high\",\"description\":\"Der Stream-Link konnte nicht sauber in einen Twitch-Channel aufgeloest werden.\",\"autoTriggerEnabled\":true,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false},{\"key\":\"unsupported_platform\",\"label\":\"Plattform nicht unterstuetzt\",\"enabled\":true,\"severity\":\"medium\",\"description\":\"Die Nominierung zeigt auf eine Plattform ohne automatische TwitchTracker-Daten.\",\"autoTriggerEnabled\":true,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false},{\"key\":\"no_tracker_data\",\"label\":\"Keine Tracker-Daten\",\"enabled\":true,\"severity\":\"medium\",\"description\":\"TwitchTracker hat keinen belastbaren Summary-Wert geliefert.\",\"autoTriggerEnabled\":true,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false},{\"key\":\"missing_required_metric\",\"label\":\"Pflichtmetrik fehlt\",\"enabled\":true,\"severity\":\"high\",\"description\":\"Mindestens eine aktivierte Pflichtmetrik fuer die Auto-Klassifizierung fehlt.\",\"autoTriggerEnabled\":true,\"requiresManualReview\":true,\"blocksApproval\":true,\"adminNoteRequiredOnOverride\":false},{\"key\":\"manual_review_required\",\"label\":\"Manuelle Pruefung noetig\",\"enabled\":true,\"severity\":\"medium\",\"description\":\"Die Auto-Daten reichen nicht fuer eine sichere Review-Entscheidung.\",\"autoTriggerEnabled\":true,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false},{\"key\":\"low_confidence_small_channel\",\"label\":\"Low Confidence Small Channel\",\"enabled\":false,\"severity\":\"low\",\"description\":\"Kleine Kanaele koennen manuell tiefer geprueft werden.\",\"autoTriggerEnabled\":false,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false},{\"key\":\"insufficient_activity_context\",\"label\":\"Zu wenig Aktivitaetskontext\",\"enabled\":false,\"severity\":\"low\",\"description\":\"Ohne zusaetzliche Kontextdaten wie Streamstunden ist die Bewertung unsicher.\",\"autoTriggerEnabled\":false,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false},{\"key\":\"category_fit_needs_review\",\"label\":\"Category Fit manuell pruefen\",\"enabled\":false,\"severity\":\"low\",\"description\":\"Unterkategorie muss inhaltlich manuell bestaetigt werden.\",\"autoTriggerEnabled\":false,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false},{\"key\":\"unsupported_metric_window\",\"label\":\"Gewaehltes Zeitfenster nicht auto-verfuegbar\",\"enabled\":true,\"severity\":\"medium\",\"description\":\"Die aktuelle TwitchTracker API liefert diese Metrik nicht fuer das konfigurierte Zeitfenster.\",\"autoTriggerEnabled\":true,\"requiresManualReview\":true,\"blocksApproval\":false,\"adminNoteRequiredOnOverride\":false}]}",
TwitchAuthManagedByDatabase = false,
TwitchClientId = "",
TwitchClientSecret = "",
TwitchRedirectUri = "",
TwitchScope = "",
ViewerStatsProviderBaseUrl = "https://twitchtracker.com/api",
WorkflowRulesJson = "[{\"key\":\"max_finalists_per_category\",\"label\":\"Finale Kandidat:innen pro Kategorie\",\"enabled\":true,\"limit\":4,\"mode\":\"block\",\"description\":\"Begrenzt, wie viele finale Kandidat:innen in einer Kategorie vorbereitet werden.\"},{\"key\":\"max_candidate_appearances\",\"label\":\"Kandidaturen pro Person\",\"enabled\":true,\"limit\":2,\"mode\":\"warn\",\"description\":\"Warnt oder blockiert, wenn dieselbe Person in zu vielen Kategorien final auftaucht.\"},{\"key\":\"max_winner_placements\",\"label\":\"Gewinnerpl\\u00E4tze pro Person\",\"enabled\":true,\"limit\":1,\"mode\":\"block\",\"description\":\"Verhindert oder warnt, wenn dieselbe Person mehrfach als Gewinner:in gesetzt wird.\"},{\"key\":\"winner_requires_clip\",\"label\":\"Gewinner braucht Clip-Link\",\"enabled\":true,\"limit\":1,\"mode\":\"block\",\"description\":\"Verhindert oder warnt, wenn ein Gewinner ohne gepflegten YouTube-/Twitch-Clip gesetzt wird.\"},{\"key\":\"recommended_nominators_per_subcategory\",\"label\":\"Empfohlene Nominierer pro Unterkategorie\",\"enabled\":true,\"limit\":4,\"mode\":\"warn\",\"description\":\"Zeigt in der Kategorie-\\u00DCbersicht an, ab wann eine Unterkategorie nominierungsseitig gut getragen ist. Diese Regel blockiert nichts.\"}]"
});
});
modelBuilder.Entity("Backend.Domain.Sponsor", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("Description")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("IsVisible")
.HasColumnType("boolean");
b.Property("LogoUrl")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property("Name")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("SortOrder")
.HasColumnType("integer");
b.Property("Tier")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property("WebsiteUrl")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.HasKey("Id");
b.HasIndex("SeasonId", "IsVisible", "SortOrder");
b.ToTable("Sponsors");
});
modelBuilder.Entity("Backend.Domain.StreamerIdentity", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("DisplayName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("LastResolvedAt")
.HasColumnType("timestamp with time zone");
b.Property("Login")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("NormalizedKey")
.IsRequired()
.HasMaxLength(180)
.HasColumnType("character varying(180)");
b.Property("Platform")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("ProfileUrl")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.HasKey("Id");
b.HasIndex("NormalizedKey")
.IsUnique();
b.ToTable("StreamerIdentities");
});
modelBuilder.Entity("Backend.Domain.TeamMember", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("BoundTwitchDisplayName")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("BoundTwitchUserId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("CreatedByTwitchId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("DisplayName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("IsActive")
.HasColumnType("boolean");
b.Property("LastLoginAt")
.HasColumnType("timestamp with time zone");
b.Property("Login")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("MustChangePassword")
.HasColumnType("boolean");
b.Property("PasswordHash")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("PasswordResetAt")
.HasColumnType("timestamp with time zone");
b.Property("PasswordSalt")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("Role")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("TwitchBoundAt")
.HasColumnType("timestamp with time zone");
b.Property("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property("UpdatedByTwitchId")
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.HasKey("Id");
b.HasIndex("BoundTwitchUserId")
.IsUnique();
b.HasIndex("Login")
.IsUnique();
b.ToTable("TeamMembers");
});
modelBuilder.Entity("Backend.Domain.TeamRolePermission", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("PermissionsJson")
.IsRequired()
.HasColumnType("text");
b.Property("Role")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property("UpdatedByTwitchId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.HasKey("Id");
b.HasIndex("Role")
.IsUnique();
b.ToTable("TeamRolePermissions");
});
modelBuilder.Entity("Backend.Domain.UserSession", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property("CreatedFromIp")
.IsRequired()
.HasMaxLength(80)
.HasColumnType("character varying(80)");
b.Property("DisplayName")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("IsActive")
.HasColumnType("boolean");
b.Property("LastSeenAt")
.HasColumnType("timestamp with time zone");
b.Property("Role")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)");
b.Property("SessionToken")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("TwitchUserId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("character varying(120)");
b.Property("UserAgent")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.HasKey("Id");
b.HasIndex("SessionToken")
.IsUnique();
b.ToTable("UserSessions");
});
modelBuilder.Entity("Backend.Domain.VoteBallot", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
b.Property("SeasonId")
.HasColumnType("integer");
b.Property("Status")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("character varying(30)");
b.Property("SubmittedAt")
.HasColumnType("timestamp with time zone");
b.Property