Files
vtuber-awards/Backend/Data/SeedData.cs
T
AzuTear b53c7fb736 Add viewer-range categories, nomination tracking, dynamic showact form, session timeout, share URLs, and workflow-per-season
Features:
- Category viewer ranges + subcategory templates (admin group modal, tree workspace)
- Nomination enrichment via TwitchTracker API (NominationEnrichmentService,
  TwitchTrackerViewerStatsProvider) with admin tracking rules editor
- Nomination group tracker: CategoryGroupName as primary identifier,
  CategoryId stays as nullable legacy field; StreamerIdentity table
- Dynamic showact application form builder (AdminShowactFormBuilder,
  ShowactApplicationSchedule)
- Session idle timeout setting (AdminSessionTimeoutCard)
- Share URLs for X and Discord (SiteSettings, public extras)
- Workflow rules now stored per season (falls back to global SiteSettings)
- New admin routes: settings/access, settings/workflows, tracking-rules
- New admin review workspace with subcategory tabs
- AdminCategoriesView rebuilt with group/subcategory modals

Migrations (all additive):
- AddShareUrls, AddShowactDynamicForm, AddCategoryViewerRanges,
  AddSessionIdleTimeoutSettings, AddSeasonSubcategoryTemplates,
  AddNominationGroupTrackerIdentity, AddShowactApplicationSchedule,
  AddSeasonWorkflowRulesJson

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 23:32:21 +02:00

212 lines
15 KiB
C#

using Backend.Domain;
using Backend.Services;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
namespace Backend.Data;
public static class SeedData
{
public static void Apply(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SiteSettings>().HasData(
new SiteSettings
{
Id = 1,
HostDisplayName = "Jayuhime",
HostTagline = "VTuber & Award Host",
NewsletterUrl = "https://vtuber-star-awards.de/newsletter",
PrivacyEmail = "datenschutz@vtuber-star-awards.de",
PrivacyPolicyContent = """
Verantwortliche:r
VTuber Star Awards, vertreten durch Jayuhime. Kontakt: datenschutz@vtuber-star-awards.de
Welche Daten wir verarbeiten
Bei Teilnahme (Voting, Nominierung, Clip-Einreichung) verarbeiten wir ausschließlich deine Twitch-User-ID sowie den Zeitstempel deiner Aktion.
Für Show-Erinnerungen speichern wir optional deine E-Mail-Adresse.
Rechtsgrundlage
Verarbeitung auf Basis von Art. 6 Abs. 1 lit. b DSGVO. Für optionale Erinnerungen gilt Art. 6 Abs. 1 lit. a DSGVO.
Zweck der Verarbeitung
Durchführung des VTuber Star Awards, Sicherstellung fairer Abstimmung, Spam-Prävention sowie Admin-Review von Einreichungen.
Löschfristen
Alle Teilnahmedaten werden spätestens 6 Monate nach der Award-Show automatisch gelöscht. E-Mail-Adressen werden nach der Show entfernt.
Deine Rechte
Du hast das Recht auf Auskunft, Löschung, Berichtigung, Widerspruch und Beschwerde bei der zuständigen Aufsichtsbehörde.
Weitergabe an Dritte
Keine Weitergabe zu Werbezwecken. Technische Dienstleister verarbeiten Daten ausschließlich im Rahmen der Auftragsverarbeitung.
""",
PrivacyPolicyUpdatedBy = "seed",
PrivacyPolicyUpdatedAt = new DateTimeOffset(2026, 6, 23, 0, 0, 0, TimeSpan.Zero),
ImprintUrl = "https://vtuber-star-awards.de/impressum",
ImprintContent = SeedCatalog.DefaultImprintContent,
ContactUrl = "https://vtuber-star-awards.de/kontakt",
ContactContent = SeedCatalog.DefaultContactContent,
SponsorsUrl = "https://vtuber-star-awards.de/partner",
SponsorsContent = SeedCatalog.DefaultSponsorsContent,
ShowactsUrl = "https://vtuber-star-awards.de/showacts",
ShowactsContent = "Informationen zu Showact-Bewerbungen, Ablauf und Kontakt für die Award-Show.",
RiskRulesJson = RiskRuleSettings.Serialize(RiskRuleSettings.Defaults),
WorkflowRulesJson = WorkflowRuleSettings.Serialize(WorkflowRuleSettings.Defaults),
TrackingRulesJson = TrackingRulesSettings.Serialize(new TrackingRulesConfiguration(
TrackingRulesSettings.DefaultSource,
TrackingRulesSettings.DefaultImportantMetrics,
TrackingRulesSettings.DefaultOptionalMetrics,
TrackingRulesSettings.DefaultFlags)),
ViewerStatsProviderBaseUrl = TrackingRulesSettings.DefaultBaseUrl,
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.",
NominationLinkBlacklistJson = NominationLinkBlacklistSettings.Serialize(NominationLinkBlacklistSettings.Defaults),
ClipSubmissionsEnabled = false,
ClipReviewEnabled = true,
ClipSubmissionDisabledMessage = "Clip-Einreichungen sind aktuell geschlossen.",
ShowactApplicationsEnabled = false,
ShowactApplicationDisabledMessage = "Showact-Bewerbungen sind aktuell geschlossen.",
SessionIdleTimeoutHours = 3,
SponsorsVisible = true,
SocialLinksJson = JsonSerializer.Serialize(new[]
{
new { label = "Twitch", platform = "twitch", url = "https://twitch.tv/jayuhime", icon = "twitch", showOnHost = true, showOnCommunity = true },
new { label = "YouTube", platform = "youtube", url = "https://youtube.com/c/Jayuhime", icon = "youtube", showOnHost = true, showOnCommunity = true },
new { label = "X", platform = "x", url = "https://x.com/jayuhime", icon = "x", showOnHost = true, showOnCommunity = true },
new { label = "Instagram", platform = "instagram", url = "https://instagram.com/jayuhime", icon = "instagram", showOnHost = true, showOnCommunity = true },
new { label = "Discord", platform = "discord", url = "https://discord.gg/jayuhime", icon = "discord", showOnHost = true, showOnCommunity = true },
}),
FaqJson = JsonSerializer.Serialize(new[]
{
new { question = "Wer darf nominiert werden?", answer = "Jede:r aktive deutschsprachige VTuber kann nominiert werden — unabhängig von Follower-Zahl oder Plattform. Die Community schlägt in der Nominierungsphase ihre Favorit:innen vor." },
new { question = "Wie funktioniert das Voting?", answer = "Du meldest dich ausschließlich mit deinem Twitch-Account an — nur so kannst du teilnehmen. Das hält das Voting fair und bot-frei. Danach vergibst du pro Kategorie eine Stimme, änderbar bis zum Ende der Phase." },
new { question = "Was kostet die Teilnahme?", answer = "Gar nichts. Nominieren, voten und die Show schauen ist komplett kostenlos — der VTuber Star Award ist ein Community-Event von Fans für Fans." },
new { question = "Wann und wo findet die Award-Show statt?", answer = "Die große Live-Show wird von Jayuhime gehostet und auf Twitch & YouTube gestreamt. Den genauen Termin findest du im Countdown oben — sei live dabei, wenn die Stars gekürt werden!" },
new { question = "Ich wurde nominiert — was nun?", answer = "Glückwunsch! Du erhältst eine Benachrichtigung mit allen Infos. Teile deine Nominierung gern mit deiner Community und ruf zum Voten auf — jede Stimme zählt." },
}),
});
modelBuilder.Entity<Season>().HasData(
new Season
{
Id = 1,
Year = 2026,
Name = "VTuber Star Awards 2026",
ShowStreamUrl = "https://twitch.tv/jayuhime",
IsCurrent = true,
IsCommunityOnly = true,
CurrentPhase = "Community Voting",
NominationStartsAt = new DateOnly(2026, 5, 1),
NominationEndsAt = new DateOnly(2026, 5, 31),
VotingStartsAt = new DateOnly(2026, 6, 1),
VotingEndsAt = new DateOnly(2026, 6, 30),
ReviewStartsAt = new DateOnly(2026, 7, 1),
ReviewEndsAt = new DateOnly(2026, 7, 10),
ShowDate = new DateOnly(2026, 7, 20),
ShowStartsAt = new TimeOnly(20, 0),
},
new Season
{
Id = 2,
Year = 2025,
Name = "VTuber Star Awards 2025",
ShowStreamUrl = "https://twitch.tv/jayuhime",
IsCurrent = false,
IsCommunityOnly = true,
CurrentPhase = "Archived",
NominationStartsAt = new DateOnly(2025, 5, 1),
NominationEndsAt = new DateOnly(2025, 5, 31),
VotingStartsAt = new DateOnly(2025, 6, 1),
VotingEndsAt = new DateOnly(2025, 6, 30),
ReviewStartsAt = new DateOnly(2025, 7, 1),
ReviewEndsAt = new DateOnly(2025, 7, 10),
ShowDate = new DateOnly(2025, 7, 20),
ShowStartsAt = new TimeOnly(20, 0),
},
new Season
{
Id = 3,
Year = 2024,
Name = "VTuber Star Awards 2024",
ShowStreamUrl = "https://youtube.com/c/Jayuhime",
IsCurrent = false,
IsCommunityOnly = true,
CurrentPhase = "Archived",
NominationStartsAt = new DateOnly(2024, 5, 1),
NominationEndsAt = new DateOnly(2024, 5, 31),
VotingStartsAt = new DateOnly(2024, 6, 1),
VotingEndsAt = new DateOnly(2024, 6, 30),
ReviewStartsAt = new DateOnly(2024, 7, 1),
ReviewEndsAt = new DateOnly(2024, 7, 10),
ShowDate = new DateOnly(2024, 7, 20),
ShowStartsAt = new TimeOnly(20, 0),
},
new Season
{
Id = 4,
Year = 2023,
Name = "VTuber Star Awards 2023",
ShowStreamUrl = "https://twitch.tv/jayuhime",
IsCurrent = false,
IsCommunityOnly = true,
CurrentPhase = "Archived",
NominationStartsAt = new DateOnly(2023, 5, 1),
NominationEndsAt = new DateOnly(2023, 5, 31),
VotingStartsAt = new DateOnly(2023, 6, 1),
VotingEndsAt = new DateOnly(2023, 6, 30),
ReviewStartsAt = new DateOnly(2023, 7, 1),
ReviewEndsAt = new DateOnly(2023, 7, 10),
ShowDate = new DateOnly(2023, 7, 20),
ShowStartsAt = new TimeOnly(20, 0),
});
modelBuilder.Entity<Category>().HasData(
new Category { Id = 1, SeasonId = 1, GroupName = "Main Awards", Name = "VTuber des Jahres", Slug = "vtuber-des-jahres", Description = "Die größte Auszeichnung des Jahres.", SortOrder = 1, MaxNomineesPerUser = 3 },
new Category { Id = 2, SeasonId = 1, GroupName = "Performance", Name = "Bestes Live Event", Slug = "bestes-live-event", Description = "Events, Konzerte und 3D-Shows.", SortOrder = 2, MaxNomineesPerUser = 3 },
new Category { Id = 3, SeasonId = 1, GroupName = "Clips & Highlights", Name = "Clip des Jahres", Slug = "clip-des-jahres", Description = "Der lustigste oder emotionalste Clip des Jahres.", SortOrder = 3, MaxNomineesPerUser = 3 },
new Category { Id = 4, SeasonId = 1, GroupName = "Main Awards", Name = "Beste Community", Slug = "beste-community", Description = "Die aktivste und freundlichste Community.", SortOrder = 4, MaxNomineesPerUser = 3 },
new Category { Id = 5, SeasonId = 2, GroupName = "Main Awards", Name = "VTuber des Jahres", Slug = "vtuber-des-jahres", Description = "Archivkategorie 2025.", SortOrder = 1, MaxNomineesPerUser = 3 },
new Category { Id = 6, SeasonId = 2, GroupName = "Performance", Name = "Bestes Live Event", Slug = "bestes-live-event", Description = "Archivkategorie 2025.", SortOrder = 2, MaxNomineesPerUser = 3 },
new Category { Id = 7, SeasonId = 2, GroupName = "Clips & Highlights", Name = "Clip des Jahres", Slug = "clip-des-jahres", Description = "Archivkategorie 2025.", SortOrder = 3, MaxNomineesPerUser = 3 },
new Category { Id = 8, SeasonId = 3, GroupName = "Main Awards", Name = "VTuber des Jahres", Slug = "vtuber-des-jahres", Description = "Archivkategorie 2024.", SortOrder = 1, MaxNomineesPerUser = 3 },
new Category { Id = 9, SeasonId = 3, GroupName = "Clips & Highlights", Name = "Clip des Jahres", Slug = "clip-des-jahres", Description = "Archivkategorie 2024.", SortOrder = 2, MaxNomineesPerUser = 3 },
new Category { Id = 10, SeasonId = 4, GroupName = "Main Awards", Name = "VTuber des Jahres", Slug = "vtuber-des-jahres", Description = "Archivkategorie 2023.", SortOrder = 1, MaxNomineesPerUser = 3 });
modelBuilder.Entity<Candidate>().HasData(
new Candidate { Id = 1, SeasonId = 1, CategoryId = 1, DisplayName = "Hoshimi Miyu", ChannelSlug = "@hoshimimiyu", Platform = "Twitch" },
new Candidate { Id = 2, SeasonId = 1, CategoryId = 1, DisplayName = "Kurainu", ChannelSlug = "@kurainu", Platform = "Twitch" },
new Candidate { Id = 3, SeasonId = 1, CategoryId = 1, DisplayName = "Shiro Ch.", ChannelSlug = "@shiroch", Platform = "Twitch" },
new Candidate { Id = 4, SeasonId = 1, CategoryId = 2, DisplayName = "Kurainu 3D Live", ChannelSlug = "@kurainu", Platform = "Twitch" },
new Candidate { Id = 5, SeasonId = 1, CategoryId = 2, DisplayName = "Aoi Sakura Showcase", ChannelSlug = "@aoisakura", Platform = "YouTube" },
new Candidate { Id = 6, SeasonId = 1, CategoryId = 3, DisplayName = "Pyonkichi Kingdom", ChannelSlug = "@pyonkichikingdom", Platform = "Twitch" },
new Candidate { Id = 7, SeasonId = 1, CategoryId = 4, DisplayName = "Moonrelay", ChannelSlug = "@moonrelay", Platform = "Twitch" },
new Candidate { Id = 8, SeasonId = 2, CategoryId = 5, DisplayName = "Hoshimi Miyu", ChannelSlug = "@hoshimimiyu", Platform = "Twitch" },
new Candidate { Id = 9, SeasonId = 2, CategoryId = 6, DisplayName = "Kurainu 3D Live", ChannelSlug = "@kurainu", Platform = "Twitch" },
new Candidate { Id = 10, SeasonId = 2, CategoryId = 7, DisplayName = "Pyonkichi Kingdom", ChannelSlug = "@pyonkichikingdom", Platform = "Twitch" },
new Candidate { Id = 11, SeasonId = 3, CategoryId = 8, DisplayName = "Aoi Sakura", ChannelSlug = "@aoisakura", Platform = "YouTube" },
new Candidate { Id = 12, SeasonId = 3, CategoryId = 9, DisplayName = "Starbyte", ChannelSlug = "@starbyte", Platform = "Twitch" },
new Candidate { Id = 13, SeasonId = 4, CategoryId = 10, DisplayName = "Tenshi Vox", ChannelSlug = "@tenshivox", Platform = "Twitch" });
modelBuilder.Entity<AwardResult>().HasData(
new AwardResult { Id = 1, SeasonId = 2, CategoryId = 5, CandidateId = 8, CategoryName = "VTuber des Jahres" },
new AwardResult { Id = 2, SeasonId = 2, CategoryId = 6, CandidateId = 9, CategoryName = "Bestes Live Event" },
new AwardResult { Id = 3, SeasonId = 2, CategoryId = 7, CandidateId = 10, CategoryName = "Clip des Jahres" },
new AwardResult { Id = 4, SeasonId = 3, CategoryId = 8, CandidateId = 11, CategoryName = "VTuber des Jahres" },
new AwardResult { Id = 5, SeasonId = 3, CategoryId = 9, CandidateId = 12, CategoryName = "Clip des Jahres" },
new AwardResult { Id = 6, SeasonId = 4, CategoryId = 10, CandidateId = 13, CategoryName = "VTuber des Jahres" });
modelBuilder.Entity<Nomination>().HasData(
new Nomination { Id = 1, SeasonId = 1, CategoryId = 1, SubmittedByTwitchId = "twitch_hoshi", CandidateText = "Hoshimi Miyu", CreatedAt = new DateTimeOffset(2026, 6, 10, 13, 0, 0, TimeSpan.Zero) },
new Nomination { Id = 2, SeasonId = 1, CategoryId = 2, SubmittedByTwitchId = "twitch_kurainu", CandidateText = "Kurainu 3D Live", CreatedAt = new DateTimeOffset(2026, 6, 10, 14, 0, 0, TimeSpan.Zero) });
modelBuilder.Entity<VoteBallot>().HasData(
new VoteBallot { Id = 1, SeasonId = 1, SubmittedByTwitchId = "twitch_vote_1", Status = "submitted", SubmittedAt = new DateTimeOffset(2026, 6, 11, 12, 0, 0, TimeSpan.Zero) },
new VoteBallot { Id = 2, SeasonId = 1, SubmittedByTwitchId = "twitch_vote_2", Status = "submitted", SubmittedAt = new DateTimeOffset(2026, 6, 11, 12, 5, 0, TimeSpan.Zero) });
modelBuilder.Entity<VoteEntry>().HasData(
new VoteEntry { Id = 1, BallotId = 1, CategoryId = 1, CandidateId = 1 },
new VoteEntry { Id = 2, BallotId = 1, CategoryId = 2, CandidateId = 4 },
new VoteEntry { Id = 3, BallotId = 2, CategoryId = 1, CandidateId = 2 },
new VoteEntry { Id = 4, BallotId = 2, CategoryId = 3, CandidateId = 6 });
}
}