Initial VTuber Awards implementation
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
using Backend.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Backend.Data;
|
||||
|
||||
public sealed class AwardsDbContext(DbContextOptions<AwardsDbContext> options) : DbContext(options)
|
||||
{
|
||||
public DbSet<Season> Seasons => Set<Season>();
|
||||
public DbSet<Category> Categories => Set<Category>();
|
||||
public DbSet<Candidate> Candidates => Set<Candidate>();
|
||||
public DbSet<AwardResult> Results => Set<AwardResult>();
|
||||
public DbSet<Nomination> Nominations => Set<Nomination>();
|
||||
public DbSet<VoteBallot> VoteBallots => Set<VoteBallot>();
|
||||
public DbSet<VoteEntry> VoteEntries => Set<VoteEntry>();
|
||||
public DbSet<UserSession> UserSessions => Set<UserSession>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Season>(entity =>
|
||||
{
|
||||
entity.HasIndex(item => item.Year).IsUnique();
|
||||
entity.Property(item => item.Name).HasMaxLength(160);
|
||||
entity.Property(item => item.CurrentPhase).HasMaxLength(60);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Category>(entity =>
|
||||
{
|
||||
entity.HasIndex(item => new { item.SeasonId, item.Slug }).IsUnique();
|
||||
entity.Property(item => item.GroupName).HasMaxLength(80);
|
||||
entity.Property(item => item.Name).HasMaxLength(120);
|
||||
entity.Property(item => item.Description).HasMaxLength(400);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Candidate>(entity =>
|
||||
{
|
||||
entity.Property(item => item.DisplayName).HasMaxLength(120);
|
||||
entity.Property(item => item.ChannelSlug).HasMaxLength(120);
|
||||
entity.Property(item => item.Platform).HasMaxLength(40);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Nomination>(entity =>
|
||||
{
|
||||
entity.Property(item => item.SubmittedByTwitchId).HasMaxLength(120);
|
||||
entity.Property(item => item.CandidateText).HasMaxLength(120);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<VoteBallot>(entity =>
|
||||
{
|
||||
entity.Property(item => item.SubmittedByTwitchId).HasMaxLength(120);
|
||||
entity.Property(item => item.Status).HasMaxLength(30);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AwardResult>(entity =>
|
||||
{
|
||||
entity.Property(item => item.CategoryName).HasMaxLength(120);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<UserSession>(entity =>
|
||||
{
|
||||
entity.HasIndex(item => item.SessionToken).IsUnique();
|
||||
entity.Property(item => item.SessionToken).HasMaxLength(120);
|
||||
entity.Property(item => item.TwitchUserId).HasMaxLength(120);
|
||||
entity.Property(item => item.DisplayName).HasMaxLength(120);
|
||||
entity.Property(item => item.Role).HasMaxLength(40);
|
||||
});
|
||||
|
||||
SeedData.Apply(modelBuilder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace Backend.Data;
|
||||
|
||||
public sealed class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AwardsDbContext>
|
||||
{
|
||||
public AwardsDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder<AwardsDbContext>();
|
||||
var connectionString = Environment.GetEnvironmentVariable("VTSA_POSTGRES")
|
||||
?? "Host=localhost;Port=5432;Database=vtuber_star_awards;Username=postgres;Password=postgres";
|
||||
|
||||
optionsBuilder.UseNpgsql(connectionString);
|
||||
return new AwardsDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using Backend.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Backend.Data;
|
||||
|
||||
public static class SeedData
|
||||
{
|
||||
public static void Apply(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Season>().HasData(
|
||||
new Season
|
||||
{
|
||||
Id = 1,
|
||||
Year = 2026,
|
||||
Name = "VTuber Star Awards 2026",
|
||||
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),
|
||||
},
|
||||
new Season
|
||||
{
|
||||
Id = 2,
|
||||
Year = 2025,
|
||||
Name = "VTuber Star Awards 2025",
|
||||
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),
|
||||
},
|
||||
new Season
|
||||
{
|
||||
Id = 3,
|
||||
Year = 2024,
|
||||
Name = "VTuber Star Awards 2024",
|
||||
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),
|
||||
},
|
||||
new Season
|
||||
{
|
||||
Id = 4,
|
||||
Year = 2023,
|
||||
Name = "VTuber Star Awards 2023",
|
||||
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),
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Category>().HasData(
|
||||
new Category { Id = 1, SeasonId = 1, GroupName = "Main Awards", Name = "VTuber des Jahres", Slug = "vtuber-des-jahres", Description = "Die groesste 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, CandidateId = 8, CategoryName = "VTuber des Jahres" },
|
||||
new AwardResult { Id = 2, SeasonId = 2, CandidateId = 9, CategoryName = "Bestes Live Event" },
|
||||
new AwardResult { Id = 3, SeasonId = 2, CandidateId = 10, CategoryName = "Clip des Jahres" },
|
||||
new AwardResult { Id = 4, SeasonId = 3, CandidateId = 11, CategoryName = "VTuber des Jahres" },
|
||||
new AwardResult { Id = 5, SeasonId = 3, CandidateId = 12, CategoryName = "Clip des Jahres" },
|
||||
new AwardResult { Id = 6, SeasonId = 4, 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 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Backend.Data;
|
||||
|
||||
public static class SessionBootstrapper
|
||||
{
|
||||
public static Task EnsureAsync(AwardsDbContext db) =>
|
||||
db.Database.ExecuteSqlRawAsync(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "UserSessions" (
|
||||
"Id" uuid NOT NULL PRIMARY KEY,
|
||||
"SessionToken" character varying(120) NOT NULL,
|
||||
"TwitchUserId" character varying(120) NOT NULL,
|
||||
"DisplayName" character varying(120) NOT NULL,
|
||||
"Role" character varying(40) NOT NULL,
|
||||
"CreatedAt" timestamp with time zone NOT NULL,
|
||||
"LastSeenAt" timestamp with time zone NOT NULL,
|
||||
"IsActive" boolean NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "IX_UserSessions_SessionToken"
|
||||
ON "UserSessions" ("SessionToken");
|
||||
""");
|
||||
}
|
||||
Reference in New Issue
Block a user